1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webkit/plugins/ppapi/message_channel.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "ppapi/shared_impl/ppapi_globals.h"
14 #include "ppapi/shared_impl/var.h"
15 #include "ppapi/shared_impl/var_tracker.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMMessageEvent.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSerializedScriptValue.h"
24 #include "v8/include/v8.h"
25 #include "webkit/plugins/ppapi/host_array_buffer_var.h"
26 #include "webkit/plugins/ppapi/npapi_glue.h"
27 #include "webkit/plugins/ppapi/plugin_module.h"
28 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
30 using ppapi::ArrayBufferVar
;
31 using ppapi::PpapiGlobals
;
32 using ppapi::StringVar
;
33 using WebKit::WebBindings
;
34 using WebKit::WebElement
;
35 using WebKit::WebDOMEvent
;
36 using WebKit::WebDOMMessageEvent
;
37 using WebKit::WebPluginContainer
;
38 using WebKit::WebSerializedScriptValue
;
46 const char kPostMessage
[] = "postMessage";
48 // Helper function to get the MessageChannel that is associated with an
50 MessageChannel
* ToMessageChannel(NPObject
* object
) {
51 return static_cast<MessageChannel::MessageChannelNPObject
*>(object
)->
55 NPObject
* ToPassThroughObject(NPObject
* object
) {
56 MessageChannel
* channel
= ToMessageChannel(object
);
57 return channel
? channel
->passthrough_object() : NULL
;
60 // Helper function to determine if a given identifier is equal to kPostMessage.
61 bool IdentifierIsPostMessage(NPIdentifier identifier
) {
62 return WebBindings::getStringIdentifier(kPostMessage
) == identifier
;
65 // Converts the given PP_Var to a v8::Value, returning true on success.
66 // False means that the given variant is invalid. In this case, |result| will
67 // be set to an empty handle.
68 bool PPVarToV8Value(PP_Var var
, v8::Handle
<v8::Value
>* result
) {
70 case PP_VARTYPE_UNDEFINED
:
71 *result
= v8::Undefined();
77 *result
= (var
.value
.as_bool
== PP_TRUE
) ? v8::True() : v8::False();
79 case PP_VARTYPE_INT32
:
80 *result
= v8::Integer::New(var
.value
.as_int
);
82 case PP_VARTYPE_DOUBLE
:
83 *result
= v8::Number::New(var
.value
.as_double
);
85 case PP_VARTYPE_STRING
: {
86 StringVar
* string
= StringVar::FromPPVar(var
);
91 const std::string
& value
= string
->value();
92 // TODO(dmichael): We should consider caching the V8 string in the host-
93 // side StringVar, so that we only have to convert/copy once if a
94 // string is sent more than once.
95 *result
= v8::String::New(value
.c_str(), value
.size());
98 case PP_VARTYPE_ARRAY_BUFFER
: {
99 ArrayBufferVar
* buffer
= ArrayBufferVar::FromPPVar(var
);
104 HostArrayBufferVar
* host_buffer
=
105 static_cast<HostArrayBufferVar
*>(buffer
);
107 v8::Local
<v8::Value
>::New(host_buffer
->webkit_buffer().toV8Value());
110 case PP_VARTYPE_OBJECT
:
111 case PP_VARTYPE_ARRAY
:
112 case PP_VARTYPE_DICTIONARY
:
113 // These are not currently supported.
121 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
122 // This currently just copies the value. For a string Var, the result is a
123 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
125 // TODO(dmichael): Bypass this step for out-of-process plugins, since a copy
126 // happens already when the Var is serialized.
127 PP_Var
CopyPPVar(const PP_Var
& var
) {
128 if (var
.type
== PP_VARTYPE_OBJECT
) {
129 // Objects are not currently supported.
131 return PP_MakeUndefined();
132 } else if (var
.type
== PP_VARTYPE_STRING
) {
133 StringVar
* string
= StringVar::FromPPVar(var
);
135 return PP_MakeUndefined();
136 return StringVar::StringToPPVar(string
->value());
137 } else if (var
.type
== PP_VARTYPE_ARRAY_BUFFER
) {
138 ArrayBufferVar
* buffer
= ArrayBufferVar::FromPPVar(var
);
140 return PP_MakeUndefined();
141 PP_Var new_buffer_var
= PpapiGlobals::Get()->GetVarTracker()->
142 MakeArrayBufferPPVar(buffer
->ByteLength());
143 DCHECK(new_buffer_var
.type
== PP_VARTYPE_ARRAY_BUFFER
);
144 if (new_buffer_var
.type
!= PP_VARTYPE_ARRAY_BUFFER
)
145 return PP_MakeUndefined();
146 ArrayBufferVar
* new_buffer
= ArrayBufferVar::FromPPVar(new_buffer_var
);
149 return PP_MakeUndefined();
150 memcpy(new_buffer
->Map(), buffer
->Map(), buffer
->ByteLength());
151 return new_buffer_var
;
157 //------------------------------------------------------------------------------
158 // Implementations of NPClass functions. These are here to:
159 // - Implement postMessage behavior.
160 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
161 // with GetInstanceObject() objects.
162 //------------------------------------------------------------------------------
163 NPObject
* MessageChannelAllocate(NPP npp
, NPClass
* the_class
) {
164 return new MessageChannel::MessageChannelNPObject
;
167 void MessageChannelDeallocate(NPObject
* object
) {
168 MessageChannel::MessageChannelNPObject
* instance
=
169 static_cast<MessageChannel::MessageChannelNPObject
*>(object
);
173 bool MessageChannelHasMethod(NPObject
* np_obj
, NPIdentifier name
) {
177 // We only handle a function called postMessage.
178 if (IdentifierIsPostMessage(name
))
181 // Other method names we will pass to the passthrough object, if we have one.
182 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
184 return WebBindings::hasMethod(NULL
, passthrough
, name
);
188 bool MessageChannelInvoke(NPObject
* np_obj
, NPIdentifier name
,
189 const NPVariant
* args
, uint32 arg_count
,
194 // We only handle a function called postMessage.
195 if (IdentifierIsPostMessage(name
) && (arg_count
== 1)) {
196 MessageChannel
* message_channel
= ToMessageChannel(np_obj
);
197 if (message_channel
) {
198 PP_Var
argument(NPVariantToPPVar(message_channel
->instance(), &args
[0]));
199 message_channel
->PostMessageToNative(argument
);
200 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(argument
);
206 // Other method calls we will pass to the passthrough object, if we have one.
207 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
209 return WebBindings::invoke(NULL
, passthrough
, name
, args
, arg_count
,
215 bool MessageChannelInvokeDefault(NPObject
* np_obj
,
216 const NPVariant
* args
,
222 // Invoke on the passthrough object, if we have one.
223 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
225 return WebBindings::invokeDefault(NULL
, passthrough
, args
, arg_count
,
231 bool MessageChannelHasProperty(NPObject
* np_obj
, NPIdentifier name
) {
235 // Invoke on the passthrough object, if we have one.
236 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
238 return WebBindings::hasProperty(NULL
, passthrough
, name
);
242 bool MessageChannelGetProperty(NPObject
* np_obj
, NPIdentifier name
,
247 // Don't allow getting the postMessage function.
248 if (IdentifierIsPostMessage(name
))
251 // Invoke on the passthrough object, if we have one.
252 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
254 return WebBindings::getProperty(NULL
, passthrough
, name
, result
);
258 bool MessageChannelSetProperty(NPObject
* np_obj
, NPIdentifier name
,
259 const NPVariant
* variant
) {
263 // Don't allow setting the postMessage function.
264 if (IdentifierIsPostMessage(name
))
267 // Invoke on the passthrough object, if we have one.
268 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
270 return WebBindings::setProperty(NULL
, passthrough
, name
, variant
);
274 bool MessageChannelEnumerate(NPObject
*np_obj
, NPIdentifier
**value
,
279 // Invoke on the passthrough object, if we have one, to enumerate its
281 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
283 bool success
= WebBindings::enumerate(NULL
, passthrough
, value
, count
);
285 // Add postMessage to the list and return it.
286 if (std::numeric_limits
<size_t>::max() / sizeof(NPIdentifier
) <=
289 NPIdentifier
* new_array
= static_cast<NPIdentifier
*>(
290 std::malloc(sizeof(NPIdentifier
) * (*count
+ 1)));
291 std::memcpy(new_array
, *value
, sizeof(NPIdentifier
)*(*count
));
292 new_array
[*count
] = WebBindings::getStringIdentifier(kPostMessage
);
300 // Otherwise, build an array that includes only postMessage.
301 *value
= static_cast<NPIdentifier
*>(malloc(sizeof(NPIdentifier
)));
302 (*value
)[0] = WebBindings::getStringIdentifier(kPostMessage
);
307 NPClass message_channel_class
= {
308 NP_CLASS_STRUCT_VERSION
,
309 &MessageChannelAllocate
,
310 &MessageChannelDeallocate
,
312 &MessageChannelHasMethod
,
313 &MessageChannelInvoke
,
314 &MessageChannelInvokeDefault
,
315 &MessageChannelHasProperty
,
316 &MessageChannelGetProperty
,
317 &MessageChannelSetProperty
,
319 &MessageChannelEnumerate
,
324 // MessageChannel --------------------------------------------------------------
325 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
328 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
330 MessageChannel::MessageChannel(PluginInstance
* instance
)
331 : instance_(instance
),
332 passthrough_object_(NULL
),
334 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
335 early_message_queue_state_(QUEUE_MESSAGES
) {
336 // Now create an NPObject for receiving calls to postMessage. This sets the
337 // reference count to 1. We release it in the destructor.
338 NPObject
* obj
= WebBindings::createObject(NULL
, &message_channel_class
);
340 np_object_
= static_cast<MessageChannel::MessageChannelNPObject
*>(obj
);
341 np_object_
->message_channel
= weak_ptr_factory_
.GetWeakPtr();
344 void MessageChannel::PostMessageToJavaScript(PP_Var message_data
) {
345 // Serialize the message data.
346 v8::HandleScope scope
;
347 // Because V8 is probably not on the stack for Native->JS calls, we need to
348 // enter the appropriate context for the plugin.
349 v8::Local
<v8::Context
> context
=
350 instance_
->container()->element().document().frame()->
351 mainWorldScriptContext();
352 v8::Context::Scope
context_scope(context
);
354 v8::Local
<v8::Value
> v8_val
;
355 if (!PPVarToV8Value(message_data
, &v8_val
)) {
360 WebSerializedScriptValue serialized_val
=
361 WebSerializedScriptValue::serialize(v8_val
);
363 if (instance_
->module()->IsProxied()) {
364 if (early_message_queue_state_
!= SEND_DIRECTLY
) {
365 // We can't just PostTask here; the messages would arrive out of
366 // order. Instead, we queue them up until we're ready to post
368 early_message_queue_
.push_back(serialized_val
);
370 // The proxy sent an asynchronous message, so the plugin is already
371 // unblocked. Therefore, there's no need to PostTask.
372 DCHECK(early_message_queue_
.size() == 0);
373 PostMessageToJavaScriptImpl(serialized_val
);
376 MessageLoop::current()->PostTask(
378 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl
,
379 weak_ptr_factory_
.GetWeakPtr(),
384 void MessageChannel::StopQueueingJavaScriptMessages() {
385 // We PostTask here instead of draining the message queue directly
386 // since we haven't finished initializing the WebPluginImpl yet, so
387 // the plugin isn't available in the DOM.
388 early_message_queue_state_
= DRAIN_PENDING
;
389 MessageLoop::current()->PostTask(
391 base::Bind(&MessageChannel::DrainEarlyMessageQueue
,
392 weak_ptr_factory_
.GetWeakPtr()));
395 void MessageChannel::QueueJavaScriptMessages() {
396 if (early_message_queue_state_
== DRAIN_PENDING
)
397 early_message_queue_state_
= DRAIN_CANCELLED
;
399 early_message_queue_state_
= QUEUE_MESSAGES
;
402 void MessageChannel::DrainEarlyMessageQueue() {
403 // Take a reference on the PluginInstance. This is because JavaScript code
404 // may delete the plugin, which would destroy the PluginInstance and its
405 // corresponding MessageChannel.
406 scoped_refptr
<PluginInstance
> instance_ref(instance_
);
408 if (early_message_queue_state_
== DRAIN_CANCELLED
) {
409 early_message_queue_state_
= QUEUE_MESSAGES
;
412 DCHECK(early_message_queue_state_
== DRAIN_PENDING
);
414 while (!early_message_queue_
.empty()) {
415 PostMessageToJavaScriptImpl(early_message_queue_
.front());
416 early_message_queue_
.pop_front();
418 early_message_queue_state_
= SEND_DIRECTLY
;
421 void MessageChannel::PostMessageToJavaScriptImpl(
422 const WebSerializedScriptValue
& message_data
) {
425 WebPluginContainer
* container
= instance_
->container();
426 // It's possible that container() is NULL if the plugin has been removed from
427 // the DOM (but the PluginInstance is not destroyed yet).
432 container
->element().document().createEvent("MessageEvent");
433 WebDOMMessageEvent msg_event
= event
.to
<WebDOMMessageEvent
>();
434 msg_event
.initMessageEvent("message", // type
437 message_data
, // data
441 // [*] Note that the |origin| is only specified for cross-document and server-
442 // sent messages, while |source| is only specified for cross-document
444 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
445 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
446 // at least, postMessage on Workers does not provide the origin or source.
447 // TODO(dmichael): Add origin if we change to a more iframe-like origin
448 // policy (see crbug.com/81537)
450 container
->element().dispatchEvent(msg_event
);
453 void MessageChannel::PostMessageToNative(PP_Var message_data
) {
454 if (instance_
->module()->IsProxied()) {
455 // In the proxied case, the copy will happen via serializiation, and the
456 // message is asynchronous. Therefore there's no need to copy the Var, nor
458 PostMessageToNativeImpl(message_data
);
460 // Make a copy of the message data for the Task we will run.
461 PP_Var
var_copy(CopyPPVar(message_data
));
463 MessageLoop::current()->PostTask(FROM_HERE
,
464 base::Bind(&MessageChannel::PostMessageToNativeImpl
,
465 weak_ptr_factory_
.GetWeakPtr(),
470 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data
) {
471 instance_
->HandleMessage(message_data
);
474 MessageChannel::~MessageChannel() {
475 WebBindings::releaseObject(np_object_
);
476 if (passthrough_object_
)
477 WebBindings::releaseObject(passthrough_object_
);
480 void MessageChannel::SetPassthroughObject(NPObject
* passthrough
) {
481 // Retain the passthrough object; We need to ensure it lives as long as this
484 WebBindings::retainObject(passthrough
);
486 // If we had a passthrough set already, release it. Note that we retain the
487 // incoming passthrough object first, so that we behave correctly if anyone
489 // SetPassthroughObject(passthrough_object());
490 if (passthrough_object_
)
491 WebBindings::releaseObject(passthrough_object_
);
493 passthrough_object_
= passthrough
;
497 } // namespace webkit