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/WebLocalFrame.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 blink::WebBindings;
36 using blink::WebElement;
37 using blink::WebDOMEvent;
38 using blink::WebDOMMessageEvent;
39 using blink::WebPluginContainer;
40 using blink::WebSerializedScriptValue;
46 const char kPostMessage[] = "postMessage";
47 const char kV8ToVarConversionError[] =
48 "Failed to convert a PostMessage "
49 "argument from a JavaScript value to a PP_Var. It may have cycles or be of "
50 "an unsupported type.";
51 const char kVarToV8ConversionError[] =
52 "Failed to convert a PostMessage "
53 "argument from a PP_Var to a Javascript value. It may have cycles or be of "
54 "an unsupported type.";
56 // Helper function to get the MessageChannel that is associated with an
58 MessageChannel* ToMessageChannel(NPObject* object) {
59 return static_cast<MessageChannel::MessageChannelNPObject*>(object)
60 ->message_channel.get();
63 NPObject* ToPassThroughObject(NPObject* object) {
64 MessageChannel* channel = ToMessageChannel(object);
65 return channel ? channel->passthrough_object() : NULL;
68 // Helper function to determine if a given identifier is equal to kPostMessage.
69 bool IdentifierIsPostMessage(NPIdentifier identifier) {
70 return WebBindings::getStringIdentifier(kPostMessage) == identifier;
73 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
74 // This currently just copies the value. For a string Var, the result is a
75 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
76 PP_Var CopyPPVar(const PP_Var& var) {
78 case PP_VARTYPE_UNDEFINED:
81 case PP_VARTYPE_INT32:
82 case PP_VARTYPE_DOUBLE:
84 case PP_VARTYPE_STRING: {
85 StringVar* string = StringVar::FromPPVar(var);
87 return PP_MakeUndefined();
88 return StringVar::StringToPPVar(string->value());
90 case PP_VARTYPE_ARRAY_BUFFER: {
91 ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
93 return PP_MakeUndefined();
94 PP_Var new_buffer_var =
95 PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
96 buffer->ByteLength());
97 DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER);
98 if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER)
99 return PP_MakeUndefined();
100 ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var);
103 return PP_MakeUndefined();
104 memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength());
105 return new_buffer_var;
107 case PP_VARTYPE_OBJECT:
108 case PP_VARTYPE_ARRAY:
109 case PP_VARTYPE_DICTIONARY:
110 case PP_VARTYPE_RESOURCE:
111 // These types are not supported by PostMessage in-process. In some rare
112 // cases with the NaCl plugin, they may be sent but they will be dropped
113 // anyway (see crbug.com/318837 for details).
114 return PP_MakeUndefined();
117 return PP_MakeUndefined();
120 //------------------------------------------------------------------------------
121 // Implementations of NPClass functions. These are here to:
122 // - Implement postMessage behavior.
123 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
124 // with GetInstanceObject() objects.
125 //------------------------------------------------------------------------------
126 NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
127 return new MessageChannel::MessageChannelNPObject;
130 void MessageChannelDeallocate(NPObject* object) {
131 MessageChannel::MessageChannelNPObject* instance =
132 static_cast<MessageChannel::MessageChannelNPObject*>(object);
136 bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
140 // We only handle a function called postMessage.
141 if (IdentifierIsPostMessage(name))
144 // Other method names we will pass to the passthrough object, if we have one.
145 NPObject* passthrough = ToPassThroughObject(np_obj);
147 return WebBindings::hasMethod(NULL, passthrough, name);
151 bool MessageChannelInvoke(NPObject* np_obj,
153 const NPVariant* args,
159 // We only handle a function called postMessage.
160 if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
161 MessageChannel* message_channel = ToMessageChannel(np_obj);
162 if (message_channel) {
163 message_channel->NPVariantToPPVar(&args[0]);
169 // Other method calls we will pass to the passthrough object, if we have one.
170 NPObject* passthrough = ToPassThroughObject(np_obj);
172 return WebBindings::invoke(
173 NULL, passthrough, name, args, arg_count, result);
178 bool MessageChannelInvokeDefault(NPObject* np_obj,
179 const NPVariant* args,
185 // Invoke on the passthrough object, if we have one.
186 NPObject* passthrough = ToPassThroughObject(np_obj);
188 return WebBindings::invokeDefault(
189 NULL, passthrough, args, arg_count, result);
194 bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
198 MessageChannel* message_channel = ToMessageChannel(np_obj);
199 if (message_channel) {
200 if (message_channel->GetReadOnlyProperty(name, NULL))
204 // Invoke on the passthrough object, if we have one.
205 NPObject* passthrough = ToPassThroughObject(np_obj);
207 return WebBindings::hasProperty(NULL, passthrough, name);
211 bool MessageChannelGetProperty(NPObject* np_obj,
217 // Don't allow getting the postMessage function.
218 if (IdentifierIsPostMessage(name))
221 MessageChannel* message_channel = ToMessageChannel(np_obj);
222 if (message_channel) {
223 if (message_channel->GetReadOnlyProperty(name, result))
227 // Invoke on the passthrough object, if we have one.
228 NPObject* passthrough = ToPassThroughObject(np_obj);
230 return WebBindings::getProperty(NULL, passthrough, name, result);
234 bool MessageChannelSetProperty(NPObject* np_obj,
236 const NPVariant* variant) {
240 // Don't allow setting the postMessage function.
241 if (IdentifierIsPostMessage(name))
244 // Invoke on the passthrough object, if we have one.
245 NPObject* passthrough = ToPassThroughObject(np_obj);
247 return WebBindings::setProperty(NULL, passthrough, name, variant);
251 bool MessageChannelEnumerate(NPObject* np_obj,
252 NPIdentifier** value,
257 // Invoke on the passthrough object, if we have one, to enumerate its
259 NPObject* passthrough = ToPassThroughObject(np_obj);
261 bool success = WebBindings::enumerate(NULL, passthrough, value, count);
263 // Add postMessage to the list and return it.
264 if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <=
265 static_cast<size_t>(*count) + 1) // Else, "always false" x64 warning.
267 NPIdentifier* new_array = static_cast<NPIdentifier*>(
268 std::malloc(sizeof(NPIdentifier) * (*count + 1)));
269 std::memcpy(new_array, *value, sizeof(NPIdentifier) * (*count));
270 new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
278 // Otherwise, build an array that includes only postMessage.
279 *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
280 (*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
285 NPClass message_channel_class = {
286 NP_CLASS_STRUCT_VERSION, &MessageChannelAllocate,
287 &MessageChannelDeallocate, NULL,
288 &MessageChannelHasMethod, &MessageChannelInvoke,
289 &MessageChannelInvokeDefault, &MessageChannelHasProperty,
290 &MessageChannelGetProperty, &MessageChannelSetProperty,
291 NULL, &MessageChannelEnumerate, };
295 // MessageChannel --------------------------------------------------------------
296 struct MessageChannel::VarConversionResult {
297 VarConversionResult(const ppapi::ScopedPPVar& r, bool s)
298 : result(r), success(s), conversion_completed(true) {}
299 VarConversionResult() : success(false), conversion_completed(false) {}
300 ppapi::ScopedPPVar result;
302 bool conversion_completed;
305 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {}
307 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
309 MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance)
310 : instance_(instance),
311 passthrough_object_(NULL),
313 early_message_queue_state_(QUEUE_MESSAGES),
314 weak_ptr_factory_(this) {
315 // Now create an NPObject for receiving calls to postMessage. This sets the
316 // reference count to 1. We release it in the destructor.
317 NPObject* obj = WebBindings::createObject(instance_->instanceNPP(),
318 &message_channel_class);
320 np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
321 np_object_->message_channel = weak_ptr_factory_.GetWeakPtr();
324 void MessageChannel::NPVariantToPPVar(const NPVariant* variant) {
325 converted_var_queue_.push_back(VarConversionResult());
326 std::list<VarConversionResult>::iterator result_iterator =
327 --converted_var_queue_.end();
328 switch (variant->type) {
329 case NPVariantType_Void:
330 NPVariantToPPVarComplete(
331 result_iterator, ppapi::ScopedPPVar(PP_MakeUndefined()), true);
333 case NPVariantType_Null:
334 NPVariantToPPVarComplete(
335 result_iterator, ppapi::ScopedPPVar(PP_MakeNull()), true);
337 case NPVariantType_Bool:
338 NPVariantToPPVarComplete(result_iterator,
339 ppapi::ScopedPPVar(PP_MakeBool(PP_FromBool(
340 NPVARIANT_TO_BOOLEAN(*variant)))),
343 case NPVariantType_Int32:
344 NPVariantToPPVarComplete(
346 ppapi::ScopedPPVar(PP_MakeInt32(NPVARIANT_TO_INT32(*variant))),
349 case NPVariantType_Double:
350 NPVariantToPPVarComplete(
352 ppapi::ScopedPPVar(PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant))),
355 case NPVariantType_String:
356 NPVariantToPPVarComplete(
358 ppapi::ScopedPPVar(ppapi::ScopedPPVar::PassRef(),
359 StringVar::StringToPPVar(
360 NPVARIANT_TO_STRING(*variant).UTF8Characters,
361 NPVARIANT_TO_STRING(*variant).UTF8Length)),
364 case NPVariantType_Object: {
365 // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
366 // shouldn't result in a deep copy.
367 v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant);
368 V8VarConverter(instance_->pp_instance())
369 .FromV8Value(v8_value,
370 v8::Isolate::GetCurrent()->GetCurrentContext(),
371 base::Bind(&MessageChannel::NPVariantToPPVarComplete,
372 weak_ptr_factory_.GetWeakPtr(),
377 NPVariantToPPVarComplete(
378 result_iterator, ppapi::ScopedPPVar(PP_MakeUndefined()), false);
381 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
382 v8::HandleScope scope(v8::Isolate::GetCurrent());
384 // Because V8 is probably not on the stack for Native->JS calls, we need to
385 // enter the appropriate context for the plugin.
386 WebPluginContainer* container = instance_->container();
387 // It's possible that container() is NULL if the plugin has been removed from
388 // the DOM (but the PluginInstance is not destroyed yet).
392 v8::Local<v8::Context> context =
393 container->element().document().frame()->mainWorldScriptContext();
394 // If the page is being destroyed, the context may be empty.
395 if (context.IsEmpty())
397 v8::Context::Scope context_scope(context);
399 v8::Handle<v8::Value> v8_val;
400 if (!V8VarConverter(instance_->pp_instance())
401 .ToV8Value(message_data, context, &v8_val)) {
402 PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(),
405 kVarToV8ConversionError);
409 WebSerializedScriptValue serialized_val =
410 WebSerializedScriptValue::serialize(v8_val);
412 if (instance_->module()->IsProxied()) {
413 if (early_message_queue_state_ != SEND_DIRECTLY) {
414 // We can't just PostTask here; the messages would arrive out of
415 // order. Instead, we queue them up until we're ready to post
417 early_message_queue_.push_back(serialized_val);
419 // The proxy sent an asynchronous message, so the plugin is already
420 // unblocked. Therefore, there's no need to PostTask.
421 DCHECK(early_message_queue_.size() == 0);
422 PostMessageToJavaScriptImpl(serialized_val);
425 base::MessageLoop::current()->PostTask(
427 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
428 weak_ptr_factory_.GetWeakPtr(),
433 void MessageChannel::StopQueueingJavaScriptMessages() {
434 // We PostTask here instead of draining the message queue directly
435 // since we haven't finished initializing the PepperWebPluginImpl yet, so
436 // the plugin isn't available in the DOM.
437 early_message_queue_state_ = DRAIN_PENDING;
438 base::MessageLoop::current()->PostTask(
440 base::Bind(&MessageChannel::DrainEarlyMessageQueue,
441 weak_ptr_factory_.GetWeakPtr()));
444 void MessageChannel::QueueJavaScriptMessages() {
445 if (early_message_queue_state_ == DRAIN_PENDING)
446 early_message_queue_state_ = DRAIN_CANCELLED;
448 early_message_queue_state_ = QUEUE_MESSAGES;
451 void MessageChannel::NPVariantToPPVarComplete(
452 const std::list<VarConversionResult>::iterator& result_iterator,
453 const ppapi::ScopedPPVar& result,
455 *result_iterator = VarConversionResult(result, success);
456 std::list<VarConversionResult>::iterator it = converted_var_queue_.begin();
457 while (it != converted_var_queue_.end() && it->conversion_completed) {
459 PostMessageToNative(it->result.get());
461 PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(),
464 kV8ToVarConversionError);
467 converted_var_queue_.erase(it++);
471 void MessageChannel::DrainEarlyMessageQueue() {
472 // Take a reference on the PluginInstance. This is because JavaScript code
473 // may delete the plugin, which would destroy the PluginInstance and its
474 // corresponding MessageChannel.
475 scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_);
477 if (early_message_queue_state_ == DRAIN_CANCELLED) {
478 early_message_queue_state_ = QUEUE_MESSAGES;
481 DCHECK(early_message_queue_state_ == DRAIN_PENDING);
483 while (!early_message_queue_.empty()) {
484 PostMessageToJavaScriptImpl(early_message_queue_.front());
485 early_message_queue_.pop_front();
487 early_message_queue_state_ = SEND_DIRECTLY;
490 void MessageChannel::PostMessageToJavaScriptImpl(
491 const WebSerializedScriptValue& message_data) {
494 WebPluginContainer* container = instance_->container();
495 // It's possible that container() is NULL if the plugin has been removed from
496 // the DOM (but the PluginInstance is not destroyed yet).
501 container->element().document().createEvent("MessageEvent");
502 WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>();
503 msg_event.initMessageEvent("message", // type
506 message_data, // data
510 // [*] Note that the |origin| is only specified for cross-document and server-
511 // sent messages, while |source| is only specified for cross-document
513 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
514 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
515 // at least, postMessage on Workers does not provide the origin or source.
516 // TODO(dmichael): Add origin if we change to a more iframe-like origin
517 // policy (see crbug.com/81537)
519 container->element().dispatchEvent(msg_event);
522 void MessageChannel::PostMessageToNative(PP_Var message_data) {
523 if (instance_->module()->IsProxied()) {
524 // In the proxied case, the copy will happen via serializiation, and the
525 // message is asynchronous. Therefore there's no need to copy the Var, nor
527 PostMessageToNativeImpl(message_data);
529 // Make a copy of the message data for the Task we will run.
530 PP_Var var_copy(CopyPPVar(message_data));
532 base::MessageLoop::current()->PostTask(
534 base::Bind(&MessageChannel::PostMessageToNativeImpl,
535 weak_ptr_factory_.GetWeakPtr(),
540 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
541 instance_->HandleMessage(message_data);
544 MessageChannel::~MessageChannel() {
545 WebBindings::releaseObject(np_object_);
546 if (passthrough_object_)
547 WebBindings::releaseObject(passthrough_object_);
550 void MessageChannel::SetPassthroughObject(NPObject* passthrough) {
551 // Retain the passthrough object; We need to ensure it lives as long as this
554 WebBindings::retainObject(passthrough);
556 // If we had a passthrough set already, release it. Note that we retain the
557 // incoming passthrough object first, so that we behave correctly if anyone
559 // SetPassthroughObject(passthrough_object());
560 if (passthrough_object_)
561 WebBindings::releaseObject(passthrough_object_);
563 passthrough_object_ = passthrough;
566 bool MessageChannel::GetReadOnlyProperty(NPIdentifier key,
567 NPVariant* value) const {
568 std::map<NPIdentifier, ppapi::ScopedPPVar>::const_iterator it =
569 internal_properties_.find(key);
570 if (it != internal_properties_.end()) {
572 return PPVarToNPVariant(it->second.get(), value);
578 void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) {
579 internal_properties_[PPVarToNPIdentifier(key)] = ppapi::ScopedPPVar(value);
582 } // namespace content