Home | History | Annotate | Download | only in pepper
      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.
      4 
      5 #include "content/renderer/pepper/message_channel.h"
      6 
      7 #include <cstdlib>
      8 #include <string>
      9 
     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/var.h"
     20 #include "ppapi/shared_impl/var_tracker.h"
     21 #include "third_party/WebKit/public/web/WebBindings.h"
     22 #include "third_party/WebKit/public/web/WebDocument.h"
     23 #include "third_party/WebKit/public/web/WebDOMMessageEvent.h"
     24 #include "third_party/WebKit/public/web/WebElement.h"
     25 #include "third_party/WebKit/public/web/WebFrame.h"
     26 #include "third_party/WebKit/public/web/WebNode.h"
     27 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     28 #include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
     29 #include "v8/include/v8.h"
     30 
     31 using ppapi::ArrayBufferVar;
     32 using ppapi::PpapiGlobals;
     33 using ppapi::StringVar;
     34 using WebKit::WebBindings;
     35 using WebKit::WebElement;
     36 using WebKit::WebDOMEvent;
     37 using WebKit::WebDOMMessageEvent;
     38 using WebKit::WebPluginContainer;
     39 using WebKit::WebSerializedScriptValue;
     40 
     41 namespace content {
     42 
     43 namespace {
     44 
     45 const char kPostMessage[] = "postMessage";
     46 const char kV8ToVarConversionError[] = "Failed to convert a PostMessage "
     47     "argument from a JavaScript value to a PP_Var. It may have cycles or be of "
     48     "an unsupported type.";
     49 const char kVarToV8ConversionError[] = "Failed to convert a PostMessage "
     50     "argument from a PP_Var to a Javascript value. It may have cycles or be of "
     51     "an unsupported type.";
     52 
     53 // Helper function to get the MessageChannel that is associated with an
     54 // NPObject*.
     55 MessageChannel* ToMessageChannel(NPObject* object) {
     56   return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
     57       message_channel.get();
     58 }
     59 
     60 NPObject* ToPassThroughObject(NPObject* object) {
     61   MessageChannel* channel = ToMessageChannel(object);
     62   return channel ? channel->passthrough_object() : NULL;
     63 }
     64 
     65 // Helper function to determine if a given identifier is equal to kPostMessage.
     66 bool IdentifierIsPostMessage(NPIdentifier identifier) {
     67   return WebBindings::getStringIdentifier(kPostMessage) == identifier;
     68 }
     69 
     70 bool NPVariantToPPVar(const NPVariant* variant, PP_Var* result) {
     71   switch (variant->type) {
     72     case NPVariantType_Void:
     73       *result = PP_MakeUndefined();
     74       return true;
     75     case NPVariantType_Null:
     76       *result = PP_MakeNull();
     77       return true;
     78     case NPVariantType_Bool:
     79       *result = PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant)));
     80       return true;
     81     case NPVariantType_Int32:
     82       *result = PP_MakeInt32(NPVARIANT_TO_INT32(*variant));
     83       return true;
     84     case NPVariantType_Double:
     85       *result = PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant));
     86       return true;
     87     case NPVariantType_String:
     88       *result = StringVar::StringToPPVar(
     89           NPVARIANT_TO_STRING(*variant).UTF8Characters,
     90           NPVARIANT_TO_STRING(*variant).UTF8Length);
     91       return true;
     92     case NPVariantType_Object: {
     93       // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
     94       // shouldn't result in a deep copy.
     95       v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant);
     96       if (!V8VarConverter::FromV8Value(v8_value, v8::Context::GetCurrent(),
     97                                        result)) {
     98         return false;
     99       }
    100       return true;
    101     }
    102   }
    103   return false;
    104 }
    105 
    106 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
    107 // This currently just copies the value.  For a string Var, the result is a
    108 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
    109 PP_Var CopyPPVar(const PP_Var& var) {
    110   switch (var.type) {
    111     case PP_VARTYPE_UNDEFINED:
    112     case PP_VARTYPE_NULL:
    113     case PP_VARTYPE_BOOL:
    114     case PP_VARTYPE_INT32:
    115     case PP_VARTYPE_DOUBLE:
    116       return var;
    117     case PP_VARTYPE_STRING: {
    118       StringVar* string = StringVar::FromPPVar(var);
    119       if (!string)
    120         return PP_MakeUndefined();
    121       return StringVar::StringToPPVar(string->value());
    122     }
    123     case PP_VARTYPE_ARRAY_BUFFER: {
    124       ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
    125       if (!buffer)
    126         return PP_MakeUndefined();
    127       PP_Var new_buffer_var = PpapiGlobals::Get()->GetVarTracker()->
    128           MakeArrayBufferPPVar(buffer->ByteLength());
    129       DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER);
    130       if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER)
    131         return PP_MakeUndefined();
    132       ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var);
    133       DCHECK(new_buffer);
    134       if (!new_buffer)
    135         return PP_MakeUndefined();
    136       memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength());
    137       return new_buffer_var;
    138     }
    139     case PP_VARTYPE_OBJECT:
    140     case PP_VARTYPE_ARRAY:
    141     case PP_VARTYPE_DICTIONARY:
    142       // Objects/Arrays/Dictionaries not supported by PostMessage in-process.
    143       NOTREACHED();
    144       return PP_MakeUndefined();
    145   }
    146   NOTREACHED();
    147   return PP_MakeUndefined();
    148 }
    149 
    150 //------------------------------------------------------------------------------
    151 // Implementations of NPClass functions.  These are here to:
    152 // - Implement postMessage behavior.
    153 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
    154 //   with GetInstanceObject() objects.
    155 //------------------------------------------------------------------------------
    156 NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
    157   return new MessageChannel::MessageChannelNPObject;
    158 }
    159 
    160 void MessageChannelDeallocate(NPObject* object) {
    161   MessageChannel::MessageChannelNPObject* instance =
    162       static_cast<MessageChannel::MessageChannelNPObject*>(object);
    163   delete instance;
    164 }
    165 
    166 bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
    167   if (!np_obj)
    168     return false;
    169 
    170   // We only handle a function called postMessage.
    171   if (IdentifierIsPostMessage(name))
    172     return true;
    173 
    174   // Other method names we will pass to the passthrough object, if we have one.
    175   NPObject* passthrough = ToPassThroughObject(np_obj);
    176   if (passthrough)
    177     return WebBindings::hasMethod(NULL, passthrough, name);
    178   return false;
    179 }
    180 
    181 bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name,
    182                           const NPVariant* args, uint32 arg_count,
    183                           NPVariant* result) {
    184   if (!np_obj)
    185     return false;
    186 
    187   // We only handle a function called postMessage.
    188   if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
    189     MessageChannel* message_channel = ToMessageChannel(np_obj);
    190     if (message_channel) {
    191       PP_Var argument = PP_MakeUndefined();
    192       if (!NPVariantToPPVar(&args[0], &argument)) {
    193         PpapiGlobals::Get()->LogWithSource(
    194             message_channel->instance()->pp_instance(),
    195             PP_LOGLEVEL_ERROR, std::string(), kV8ToVarConversionError);
    196         return false;
    197       }
    198       message_channel->PostMessageToNative(argument);
    199       PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(argument);
    200       return true;
    201     } else {
    202       return false;
    203     }
    204   }
    205   // Other method calls we will pass to the passthrough object, if we have one.
    206   NPObject* passthrough = ToPassThroughObject(np_obj);
    207   if (passthrough) {
    208     return WebBindings::invoke(NULL, passthrough, name, args, arg_count,
    209                                result);
    210   }
    211   return false;
    212 }
    213 
    214 bool MessageChannelInvokeDefault(NPObject* np_obj,
    215                                  const NPVariant* args,
    216                                  uint32 arg_count,
    217                                  NPVariant* result) {
    218   if (!np_obj)
    219     return false;
    220 
    221   // Invoke on the passthrough object, if we have one.
    222   NPObject* passthrough = ToPassThroughObject(np_obj);
    223   if (passthrough) {
    224     return WebBindings::invokeDefault(NULL, passthrough, args, arg_count,
    225                                       result);
    226   }
    227   return false;
    228 }
    229 
    230 bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
    231   if (!np_obj)
    232     return false;
    233 
    234   // Invoke on the passthrough object, if we have one.
    235   NPObject* passthrough = ToPassThroughObject(np_obj);
    236   if (passthrough)
    237     return WebBindings::hasProperty(NULL, passthrough, name);
    238   return false;
    239 }
    240 
    241 bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
    242                                NPVariant* result) {
    243   if (!np_obj)
    244     return false;
    245 
    246   // Don't allow getting the postMessage function.
    247   if (IdentifierIsPostMessage(name))
    248     return false;
    249 
    250   // Invoke on the passthrough object, if we have one.
    251   NPObject* passthrough = ToPassThroughObject(np_obj);
    252   if (passthrough)
    253     return WebBindings::getProperty(NULL, passthrough, name, result);
    254   return false;
    255 }
    256 
    257 bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
    258                                const NPVariant* variant) {
    259   if (!np_obj)
    260     return false;
    261 
    262   // Don't allow setting the postMessage function.
    263   if (IdentifierIsPostMessage(name))
    264     return false;
    265 
    266   // Invoke on the passthrough object, if we have one.
    267   NPObject* passthrough = ToPassThroughObject(np_obj);
    268   if (passthrough)
    269     return WebBindings::setProperty(NULL, passthrough, name, variant);
    270   return false;
    271 }
    272 
    273 bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
    274                              uint32_t *count) {
    275   if (!np_obj)
    276     return false;
    277 
    278   // Invoke on the passthrough object, if we have one, to enumerate its
    279   // properties.
    280   NPObject* passthrough = ToPassThroughObject(np_obj);
    281   if (passthrough) {
    282     bool success = WebBindings::enumerate(NULL, passthrough, value, count);
    283     if (success) {
    284       // Add postMessage to the list and return it.
    285       if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <=
    286           static_cast<size_t>(*count) + 1)  // Else, "always false" x64 warning.
    287         return false;
    288       NPIdentifier* new_array = static_cast<NPIdentifier*>(
    289           std::malloc(sizeof(NPIdentifier) * (*count + 1)));
    290       std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count));
    291       new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
    292       std::free(*value);
    293       *value = new_array;
    294       ++(*count);
    295       return true;
    296     }
    297   }
    298 
    299   // Otherwise, build an array that includes only postMessage.
    300   *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
    301   (*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
    302   *count = 1;
    303   return true;
    304 }
    305 
    306 NPClass message_channel_class = {
    307   NP_CLASS_STRUCT_VERSION,
    308   &MessageChannelAllocate,
    309   &MessageChannelDeallocate,
    310   NULL,
    311   &MessageChannelHasMethod,
    312   &MessageChannelInvoke,
    313   &MessageChannelInvokeDefault,
    314   &MessageChannelHasProperty,
    315   &MessageChannelGetProperty,
    316   &MessageChannelSetProperty,
    317   NULL,
    318   &MessageChannelEnumerate,
    319 };
    320 
    321 }  // namespace
    322 
    323 // MessageChannel --------------------------------------------------------------
    324 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
    325 }
    326 
    327 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
    328 
    329 MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance)
    330     : instance_(instance),
    331       passthrough_object_(NULL),
    332       np_object_(NULL),
    333       weak_ptr_factory_(this),
    334       early_message_queue_state_(QUEUE_MESSAGES) {
    335   // Now create an NPObject for receiving calls to postMessage. This sets the
    336   // reference count to 1.  We release it in the destructor.
    337   NPObject* obj = WebBindings::createObject(instance_->instanceNPP(),
    338                                             &message_channel_class);
    339   DCHECK(obj);
    340   np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
    341   np_object_->message_channel = weak_ptr_factory_.GetWeakPtr();
    342 }
    343 
    344 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
    345   v8::HandleScope scope;
    346 
    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   WebPluginContainer* container = instance_->container();
    350   // It's possible that container() is NULL if the plugin has been removed from
    351   // the DOM (but the PluginInstance is not destroyed yet).
    352   if (!container)
    353     return;
    354 
    355   v8::Local<v8::Context> context =
    356       container->element().document().frame()->mainWorldScriptContext();
    357   // If the page is being destroyed, the context may be empty.
    358   if (context.IsEmpty())
    359     return;
    360   v8::Context::Scope context_scope(context);
    361 
    362   v8::Handle<v8::Value> v8_val;
    363   if (!V8VarConverter::ToV8Value(message_data, context, &v8_val)) {
    364     PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(),
    365         PP_LOGLEVEL_ERROR, std::string(), kVarToV8ConversionError);
    366     return;
    367   }
    368 
    369   // This is for backward compatibility. It usually makes sense for us to return
    370   // a string object rather than a string primitive because it allows multiple
    371   // references to the same string (as with PP_Var strings). However, prior to
    372   // implementing dictionary and array, vars we would return a string primitive
    373   // here. Changing it to an object now will break existing code that uses
    374   // strict comparisons for strings returned from PostMessage. e.g. x === "123"
    375   // will no longer return true. So if the only value to return is a string
    376   // object, just return the string primitive.
    377   if (v8_val->IsStringObject())
    378     v8_val = v8_val->ToString();
    379 
    380   WebSerializedScriptValue serialized_val =
    381       WebSerializedScriptValue::serialize(v8_val);
    382 
    383   if (instance_->module()->IsProxied()) {
    384     if (early_message_queue_state_ != SEND_DIRECTLY) {
    385       // We can't just PostTask here; the messages would arrive out of
    386       // order. Instead, we queue them up until we're ready to post
    387       // them.
    388       early_message_queue_.push_back(serialized_val);
    389     } else {
    390       // The proxy sent an asynchronous message, so the plugin is already
    391       // unblocked. Therefore, there's no need to PostTask.
    392       DCHECK(early_message_queue_.size() == 0);
    393       PostMessageToJavaScriptImpl(serialized_val);
    394     }
    395   } else {
    396     base::MessageLoop::current()->PostTask(
    397         FROM_HERE,
    398         base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
    399                    weak_ptr_factory_.GetWeakPtr(),
    400                    serialized_val));
    401   }
    402 }
    403 
    404 void MessageChannel::StopQueueingJavaScriptMessages() {
    405   // We PostTask here instead of draining the message queue directly
    406   // since we haven't finished initializing the PepperWebPluginImpl yet, so
    407   // the plugin isn't available in the DOM.
    408   early_message_queue_state_ = DRAIN_PENDING;
    409   base::MessageLoop::current()->PostTask(
    410       FROM_HERE,
    411       base::Bind(&MessageChannel::DrainEarlyMessageQueue,
    412                  weak_ptr_factory_.GetWeakPtr()));
    413 }
    414 
    415 void MessageChannel::QueueJavaScriptMessages() {
    416   if (early_message_queue_state_ == DRAIN_PENDING)
    417     early_message_queue_state_ = DRAIN_CANCELLED;
    418   else
    419     early_message_queue_state_ = QUEUE_MESSAGES;
    420 }
    421 
    422 void MessageChannel::DrainEarlyMessageQueue() {
    423   // Take a reference on the PluginInstance. This is because JavaScript code
    424   // may delete the plugin, which would destroy the PluginInstance and its
    425   // corresponding MessageChannel.
    426   scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_);
    427 
    428   if (early_message_queue_state_ == DRAIN_CANCELLED) {
    429     early_message_queue_state_ = QUEUE_MESSAGES;
    430     return;
    431   }
    432   DCHECK(early_message_queue_state_ == DRAIN_PENDING);
    433 
    434   while (!early_message_queue_.empty()) {
    435     PostMessageToJavaScriptImpl(early_message_queue_.front());
    436     early_message_queue_.pop_front();
    437   }
    438   early_message_queue_state_ = SEND_DIRECTLY;
    439 }
    440 
    441 void MessageChannel::PostMessageToJavaScriptImpl(
    442     const WebSerializedScriptValue& message_data) {
    443   DCHECK(instance_);
    444 
    445   WebPluginContainer* container = instance_->container();
    446   // It's possible that container() is NULL if the plugin has been removed from
    447   // the DOM (but the PluginInstance is not destroyed yet).
    448   if (!container)
    449     return;
    450 
    451   WebDOMEvent event =
    452       container->element().document().createEvent("MessageEvent");
    453   WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>();
    454   msg_event.initMessageEvent("message",  // type
    455                              false,  // canBubble
    456                              false,  // cancelable
    457                              message_data,  // data
    458                              "",  // origin [*]
    459                              NULL,  // source [*]
    460                              "");  // lastEventId
    461   // [*] Note that the |origin| is only specified for cross-document and server-
    462   //     sent messages, while |source| is only specified for cross-document
    463   //     messages:
    464   //      http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
    465   //     This currently behaves like Web Workers. On Firefox, Chrome, and Safari
    466   //     at least, postMessage on Workers does not provide the origin or source.
    467   //     TODO(dmichael):  Add origin if we change to a more iframe-like origin
    468   //                      policy (see crbug.com/81537)
    469 
    470   container->element().dispatchEvent(msg_event);
    471 }
    472 
    473 void MessageChannel::PostMessageToNative(PP_Var message_data) {
    474   if (instance_->module()->IsProxied()) {
    475     // In the proxied case, the copy will happen via serializiation, and the
    476     // message is asynchronous. Therefore there's no need to copy the Var, nor
    477     // to PostTask.
    478     PostMessageToNativeImpl(message_data);
    479   } else {
    480     // Make a copy of the message data for the Task we will run.
    481     PP_Var var_copy(CopyPPVar(message_data));
    482 
    483     base::MessageLoop::current()->PostTask(
    484         FROM_HERE,
    485         base::Bind(&MessageChannel::PostMessageToNativeImpl,
    486                    weak_ptr_factory_.GetWeakPtr(),
    487                    var_copy));
    488   }
    489 }
    490 
    491 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
    492   instance_->HandleMessage(message_data);
    493 }
    494 
    495 MessageChannel::~MessageChannel() {
    496   WebBindings::releaseObject(np_object_);
    497   if (passthrough_object_)
    498     WebBindings::releaseObject(passthrough_object_);
    499 }
    500 
    501 void MessageChannel::SetPassthroughObject(NPObject* passthrough) {
    502   // Retain the passthrough object; We need to ensure it lives as long as this
    503   // MessageChannel.
    504   if (passthrough)
    505     WebBindings::retainObject(passthrough);
    506 
    507   // If we had a passthrough set already, release it. Note that we retain the
    508   // incoming passthrough object first, so that we behave correctly if anyone
    509   // invokes:
    510   //   SetPassthroughObject(passthrough_object());
    511   if (passthrough_object_)
    512     WebBindings::releaseObject(passthrough_object_);
    513 
    514   passthrough_object_ = passthrough;
    515 }
    516 
    517 }  // namespace content
    518