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 "content/renderer/pepper/message_channel.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "content/renderer/pepper/host_array_buffer_var.h"
14 #include "content/renderer/pepper/npapi_glue.h"
15 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
16 #include "content/renderer/pepper/plugin_module.h"
17 #include "content/renderer/pepper/v8_var_converter.h"
18 #include "ppapi/shared_impl/ppapi_globals.h"
19 #include "ppapi/shared_impl/scoped_pp_var.h"
20 #include "ppapi/shared_impl/var.h"
21 #include "ppapi/shared_impl/var_tracker.h"
22 #include "third_party/WebKit/public/web/WebBindings.h"
23 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebDOMMessageEvent.h"
25 #include "third_party/WebKit/public/web/WebElement.h"
26 #include "third_party/WebKit/public/web/WebFrame.h"
27 #include "third_party/WebKit/public/web/WebNode.h"
28 #include "third_party/WebKit/public/web/WebPluginContainer.h"
29 #include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
30 #include "v8/include/v8.h"
32 using ppapi::ArrayBufferVar
;
33 using ppapi::PpapiGlobals
;
34 using ppapi::StringVar
;
35 using WebKit::WebBindings
;
36 using WebKit::WebElement
;
37 using WebKit::WebDOMEvent
;
38 using WebKit::WebDOMMessageEvent
;
39 using WebKit::WebPluginContainer
;
40 using WebKit::WebSerializedScriptValue
;
46 const char kPostMessage
[] = "postMessage";
47 const char kV8ToVarConversionError
[] = "Failed to convert a PostMessage "
48 "argument from a JavaScript value to a PP_Var. It may have cycles or be of "
49 "an unsupported type.";
50 const char kVarToV8ConversionError
[] = "Failed to convert a PostMessage "
51 "argument from a PP_Var to a Javascript value. It may have cycles or be of "
52 "an unsupported type.";
54 // Helper function to get the MessageChannel that is associated with an
56 MessageChannel
* ToMessageChannel(NPObject
* object
) {
57 return static_cast<MessageChannel::MessageChannelNPObject
*>(object
)->
58 message_channel
.get();
61 NPObject
* ToPassThroughObject(NPObject
* object
) {
62 MessageChannel
* channel
= ToMessageChannel(object
);
63 return channel
? channel
->passthrough_object() : NULL
;
66 // Helper function to determine if a given identifier is equal to kPostMessage.
67 bool IdentifierIsPostMessage(NPIdentifier identifier
) {
68 return WebBindings::getStringIdentifier(kPostMessage
) == identifier
;
71 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
72 // This currently just copies the value. For a string Var, the result is a
73 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
74 PP_Var
CopyPPVar(const PP_Var
& var
) {
76 case PP_VARTYPE_UNDEFINED
:
79 case PP_VARTYPE_INT32
:
80 case PP_VARTYPE_DOUBLE
:
82 case PP_VARTYPE_STRING
: {
83 StringVar
* string
= StringVar::FromPPVar(var
);
85 return PP_MakeUndefined();
86 return StringVar::StringToPPVar(string
->value());
88 case PP_VARTYPE_ARRAY_BUFFER
: {
89 ArrayBufferVar
* buffer
= ArrayBufferVar::FromPPVar(var
);
91 return PP_MakeUndefined();
92 PP_Var new_buffer_var
= PpapiGlobals::Get()->GetVarTracker()->
93 MakeArrayBufferPPVar(buffer
->ByteLength());
94 DCHECK(new_buffer_var
.type
== PP_VARTYPE_ARRAY_BUFFER
);
95 if (new_buffer_var
.type
!= PP_VARTYPE_ARRAY_BUFFER
)
96 return PP_MakeUndefined();
97 ArrayBufferVar
* new_buffer
= ArrayBufferVar::FromPPVar(new_buffer_var
);
100 return PP_MakeUndefined();
101 memcpy(new_buffer
->Map(), buffer
->Map(), buffer
->ByteLength());
102 return new_buffer_var
;
104 case PP_VARTYPE_OBJECT
:
105 case PP_VARTYPE_ARRAY
:
106 case PP_VARTYPE_DICTIONARY
:
107 case PP_VARTYPE_RESOURCE
:
108 // These types are not supported by PostMessage in-process.
110 return PP_MakeUndefined();
113 return PP_MakeUndefined();
116 //------------------------------------------------------------------------------
117 // Implementations of NPClass functions. These are here to:
118 // - Implement postMessage behavior.
119 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
120 // with GetInstanceObject() objects.
121 //------------------------------------------------------------------------------
122 NPObject
* MessageChannelAllocate(NPP npp
, NPClass
* the_class
) {
123 return new MessageChannel::MessageChannelNPObject
;
126 void MessageChannelDeallocate(NPObject
* object
) {
127 MessageChannel::MessageChannelNPObject
* instance
=
128 static_cast<MessageChannel::MessageChannelNPObject
*>(object
);
132 bool MessageChannelHasMethod(NPObject
* np_obj
, NPIdentifier name
) {
136 // We only handle a function called postMessage.
137 if (IdentifierIsPostMessage(name
))
140 // Other method names we will pass to the passthrough object, if we have one.
141 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
143 return WebBindings::hasMethod(NULL
, passthrough
, name
);
147 bool MessageChannelInvoke(NPObject
* np_obj
, NPIdentifier name
,
148 const NPVariant
* args
, uint32 arg_count
,
153 // We only handle a function called postMessage.
154 if (IdentifierIsPostMessage(name
) && (arg_count
== 1)) {
155 MessageChannel
* message_channel
= ToMessageChannel(np_obj
);
156 if (message_channel
) {
157 message_channel
->NPVariantToPPVar(&args
[0]);
163 // Other method calls we will pass to the passthrough object, if we have one.
164 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
166 return WebBindings::invoke(NULL
, passthrough
, name
, args
, arg_count
,
172 bool MessageChannelInvokeDefault(NPObject
* np_obj
,
173 const NPVariant
* args
,
179 // Invoke on the passthrough object, if we have one.
180 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
182 return WebBindings::invokeDefault(NULL
, passthrough
, args
, arg_count
,
188 bool MessageChannelHasProperty(NPObject
* np_obj
, NPIdentifier name
) {
192 // Invoke on the passthrough object, if we have one.
193 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
195 return WebBindings::hasProperty(NULL
, passthrough
, name
);
199 bool MessageChannelGetProperty(NPObject
* np_obj
, NPIdentifier name
,
204 // Don't allow getting the postMessage function.
205 if (IdentifierIsPostMessage(name
))
208 // Invoke on the passthrough object, if we have one.
209 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
211 return WebBindings::getProperty(NULL
, passthrough
, name
, result
);
215 bool MessageChannelSetProperty(NPObject
* np_obj
, NPIdentifier name
,
216 const NPVariant
* variant
) {
220 // Don't allow setting the postMessage function.
221 if (IdentifierIsPostMessage(name
))
224 // Invoke on the passthrough object, if we have one.
225 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
227 return WebBindings::setProperty(NULL
, passthrough
, name
, variant
);
231 bool MessageChannelEnumerate(NPObject
*np_obj
, NPIdentifier
**value
,
236 // Invoke on the passthrough object, if we have one, to enumerate its
238 NPObject
* passthrough
= ToPassThroughObject(np_obj
);
240 bool success
= WebBindings::enumerate(NULL
, passthrough
, value
, count
);
242 // Add postMessage to the list and return it.
243 if (std::numeric_limits
<size_t>::max() / sizeof(NPIdentifier
) <=
244 static_cast<size_t>(*count
) + 1) // Else, "always false" x64 warning.
246 NPIdentifier
* new_array
= static_cast<NPIdentifier
*>(
247 std::malloc(sizeof(NPIdentifier
) * (*count
+ 1)));
248 std::memcpy(new_array
, *value
, sizeof(NPIdentifier
)*(*count
));
249 new_array
[*count
] = WebBindings::getStringIdentifier(kPostMessage
);
257 // Otherwise, build an array that includes only postMessage.
258 *value
= static_cast<NPIdentifier
*>(malloc(sizeof(NPIdentifier
)));
259 (*value
)[0] = WebBindings::getStringIdentifier(kPostMessage
);
264 NPClass message_channel_class
= {
265 NP_CLASS_STRUCT_VERSION
,
266 &MessageChannelAllocate
,
267 &MessageChannelDeallocate
,
269 &MessageChannelHasMethod
,
270 &MessageChannelInvoke
,
271 &MessageChannelInvokeDefault
,
272 &MessageChannelHasProperty
,
273 &MessageChannelGetProperty
,
274 &MessageChannelSetProperty
,
276 &MessageChannelEnumerate
,
281 // MessageChannel --------------------------------------------------------------
282 struct MessageChannel::VarConversionResult
{
283 VarConversionResult(const ppapi::ScopedPPVar
& r
, bool s
)
286 conversion_completed(true) {}
287 VarConversionResult()
289 conversion_completed(false) {}
290 ppapi::ScopedPPVar result
;
292 bool conversion_completed
;
295 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
298 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
300 MessageChannel::MessageChannel(PepperPluginInstanceImpl
* instance
)
301 : instance_(instance
),
302 passthrough_object_(NULL
),
304 early_message_queue_state_(QUEUE_MESSAGES
),
305 weak_ptr_factory_(this) {
306 // Now create an NPObject for receiving calls to postMessage. This sets the
307 // reference count to 1. We release it in the destructor.
308 NPObject
* obj
= WebBindings::createObject(instance_
->instanceNPP(),
309 &message_channel_class
);
311 np_object_
= static_cast<MessageChannel::MessageChannelNPObject
*>(obj
);
312 np_object_
->message_channel
= weak_ptr_factory_
.GetWeakPtr();
315 void MessageChannel::NPVariantToPPVar(const NPVariant
* variant
) {
316 converted_var_queue_
.push_back(VarConversionResult());
317 std::list
<VarConversionResult
>::iterator result_iterator
=
318 --converted_var_queue_
.end();
319 switch (variant
->type
) {
320 case NPVariantType_Void
:
321 NPVariantToPPVarComplete(result_iterator
,
322 ppapi::ScopedPPVar(PP_MakeUndefined()), true);
324 case NPVariantType_Null
:
325 NPVariantToPPVarComplete(result_iterator
,
326 ppapi::ScopedPPVar(PP_MakeNull()), true);
328 case NPVariantType_Bool
:
329 NPVariantToPPVarComplete(result_iterator
,
331 PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant
)))),
334 case NPVariantType_Int32
:
335 NPVariantToPPVarComplete(result_iterator
,
337 PP_MakeInt32(NPVARIANT_TO_INT32(*variant
))),
340 case NPVariantType_Double
:
341 NPVariantToPPVarComplete(result_iterator
,
343 PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant
))),
346 case NPVariantType_String
:
347 NPVariantToPPVarComplete(result_iterator
,
348 ppapi::ScopedPPVar(ppapi::ScopedPPVar::PassRef(),
349 StringVar::StringToPPVar(
350 NPVARIANT_TO_STRING(*variant
).UTF8Characters
,
351 NPVARIANT_TO_STRING(*variant
).UTF8Length
)),
354 case NPVariantType_Object
: {
355 // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
356 // shouldn't result in a deep copy.
357 v8::Handle
<v8::Value
> v8_value
= WebBindings::toV8Value(variant
);
358 V8VarConverter(instance_
->pp_instance()).FromV8Value(
359 v8_value
, v8::Context::GetCurrent(),
360 base::Bind(&MessageChannel::NPVariantToPPVarComplete
,
361 weak_ptr_factory_
.GetWeakPtr(), result_iterator
));
365 NPVariantToPPVarComplete(result_iterator
,
366 ppapi::ScopedPPVar(PP_MakeUndefined()), false);
369 void MessageChannel::PostMessageToJavaScript(PP_Var message_data
) {
370 v8::HandleScope
scope(v8::Isolate::GetCurrent());
372 // Because V8 is probably not on the stack for Native->JS calls, we need to
373 // enter the appropriate context for the plugin.
374 WebPluginContainer
* container
= instance_
->container();
375 // It's possible that container() is NULL if the plugin has been removed from
376 // the DOM (but the PluginInstance is not destroyed yet).
380 v8::Local
<v8::Context
> context
=
381 container
->element().document().frame()->mainWorldScriptContext();
382 // If the page is being destroyed, the context may be empty.
383 if (context
.IsEmpty())
385 v8::Context::Scope
context_scope(context
);
387 v8::Handle
<v8::Value
> v8_val
;
388 if (!V8VarConverter(instance_
->pp_instance()).ToV8Value(
389 message_data
, context
, &v8_val
)) {
390 PpapiGlobals::Get()->LogWithSource(instance_
->pp_instance(),
391 PP_LOGLEVEL_ERROR
, std::string(), kVarToV8ConversionError
);
395 // This is for backward compatibility. It usually makes sense for us to return
396 // a string object rather than a string primitive because it allows multiple
397 // references to the same string (as with PP_Var strings). However, prior to
398 // implementing dictionary and array, vars we would return a string primitive
399 // here. Changing it to an object now will break existing code that uses
400 // strict comparisons for strings returned from PostMessage. e.g. x === "123"
401 // will no longer return true. So if the only value to return is a string
402 // object, just return the string primitive.
403 if (v8_val
->IsStringObject())
404 v8_val
= v8_val
->ToString();
406 WebSerializedScriptValue serialized_val
=
407 WebSerializedScriptValue::serialize(v8_val
);
409 if (instance_
->module()->IsProxied()) {
410 if (early_message_queue_state_
!= SEND_DIRECTLY
) {
411 // We can't just PostTask here; the messages would arrive out of
412 // order. Instead, we queue them up until we're ready to post
414 early_message_queue_
.push_back(serialized_val
);
416 // The proxy sent an asynchronous message, so the plugin is already
417 // unblocked. Therefore, there's no need to PostTask.
418 DCHECK(early_message_queue_
.size() == 0);
419 PostMessageToJavaScriptImpl(serialized_val
);
422 base::MessageLoop::current()->PostTask(
424 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl
,
425 weak_ptr_factory_
.GetWeakPtr(),
430 void MessageChannel::StopQueueingJavaScriptMessages() {
431 // We PostTask here instead of draining the message queue directly
432 // since we haven't finished initializing the PepperWebPluginImpl yet, so
433 // the plugin isn't available in the DOM.
434 early_message_queue_state_
= DRAIN_PENDING
;
435 base::MessageLoop::current()->PostTask(
437 base::Bind(&MessageChannel::DrainEarlyMessageQueue
,
438 weak_ptr_factory_
.GetWeakPtr()));
441 void MessageChannel::QueueJavaScriptMessages() {
442 if (early_message_queue_state_
== DRAIN_PENDING
)
443 early_message_queue_state_
= DRAIN_CANCELLED
;
445 early_message_queue_state_
= QUEUE_MESSAGES
;
448 void MessageChannel::NPVariantToPPVarComplete(
449 const std::list
<VarConversionResult
>::iterator
& result_iterator
,
450 const ppapi::ScopedPPVar
& result
,
452 *result_iterator
= VarConversionResult(result
, success
);
453 std::list
<VarConversionResult
>::iterator it
= converted_var_queue_
.begin();
454 while (it
!= converted_var_queue_
.end() && it
->conversion_completed
) {
456 PostMessageToNative(it
->result
.get());
458 PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(),
459 PP_LOGLEVEL_ERROR
, std::string(), kV8ToVarConversionError
);
462 converted_var_queue_
.erase(it
++);
466 void MessageChannel::DrainEarlyMessageQueue() {
467 // Take a reference on the PluginInstance. This is because JavaScript code
468 // may delete the plugin, which would destroy the PluginInstance and its
469 // corresponding MessageChannel.
470 scoped_refptr
<PepperPluginInstanceImpl
> instance_ref(instance_
);
472 if (early_message_queue_state_
== DRAIN_CANCELLED
) {
473 early_message_queue_state_
= QUEUE_MESSAGES
;
476 DCHECK(early_message_queue_state_
== DRAIN_PENDING
);
478 while (!early_message_queue_
.empty()) {
479 PostMessageToJavaScriptImpl(early_message_queue_
.front());
480 early_message_queue_
.pop_front();
482 early_message_queue_state_
= SEND_DIRECTLY
;
485 void MessageChannel::PostMessageToJavaScriptImpl(
486 const WebSerializedScriptValue
& message_data
) {
489 WebPluginContainer
* container
= instance_
->container();
490 // It's possible that container() is NULL if the plugin has been removed from
491 // the DOM (but the PluginInstance is not destroyed yet).
496 container
->element().document().createEvent("MessageEvent");
497 WebDOMMessageEvent msg_event
= event
.to
<WebDOMMessageEvent
>();
498 msg_event
.initMessageEvent("message", // type
501 message_data
, // data
505 // [*] Note that the |origin| is only specified for cross-document and server-
506 // sent messages, while |source| is only specified for cross-document
508 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
509 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
510 // at least, postMessage on Workers does not provide the origin or source.
511 // TODO(dmichael): Add origin if we change to a more iframe-like origin
512 // policy (see crbug.com/81537)
514 container
->element().dispatchEvent(msg_event
);
517 void MessageChannel::PostMessageToNative(PP_Var message_data
) {
518 if (instance_
->module()->IsProxied()) {
519 // In the proxied case, the copy will happen via serializiation, and the
520 // message is asynchronous. Therefore there's no need to copy the Var, nor
522 PostMessageToNativeImpl(message_data
);
524 // Make a copy of the message data for the Task we will run.
525 PP_Var
var_copy(CopyPPVar(message_data
));
527 base::MessageLoop::current()->PostTask(
529 base::Bind(&MessageChannel::PostMessageToNativeImpl
,
530 weak_ptr_factory_
.GetWeakPtr(),
535 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data
) {
536 instance_
->HandleMessage(message_data
);
539 MessageChannel::~MessageChannel() {
540 WebBindings::releaseObject(np_object_
);
541 if (passthrough_object_
)
542 WebBindings::releaseObject(passthrough_object_
);
545 void MessageChannel::SetPassthroughObject(NPObject
* passthrough
) {
546 // Retain the passthrough object; We need to ensure it lives as long as this
549 WebBindings::retainObject(passthrough
);
551 // If we had a passthrough set already, release it. Note that we retain the
552 // incoming passthrough object first, so that we behave correctly if anyone
554 // SetPassthroughObject(passthrough_object());
555 if (passthrough_object_
)
556 WebBindings::releaseObject(passthrough_object_
);
558 passthrough_object_
= passthrough
;
561 } // namespace content