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