• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*.
ToMessageChannel(NPObject * object)56 MessageChannel* ToMessageChannel(NPObject* object) {
57   return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
58       message_channel.get();
59 }
60 
ToPassThroughObject(NPObject * object)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.
IdentifierIsPostMessage(NPIdentifier identifier)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.
CopyPPVar(const PP_Var & var)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 //------------------------------------------------------------------------------
MessageChannelAllocate(NPP npp,NPClass * the_class)123 NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
124   return new MessageChannel::MessageChannelNPObject;
125 }
126 
MessageChannelDeallocate(NPObject * object)127 void MessageChannelDeallocate(NPObject* object) {
128   MessageChannel::MessageChannelNPObject* instance =
129       static_cast<MessageChannel::MessageChannelNPObject*>(object);
130   delete instance;
131 }
132 
MessageChannelHasMethod(NPObject * np_obj,NPIdentifier name)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 
MessageChannelInvoke(NPObject * np_obj,NPIdentifier name,const NPVariant * args,uint32 arg_count,NPVariant * result)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 
MessageChannelInvokeDefault(NPObject * np_obj,const NPVariant * args,uint32 arg_count,NPVariant * result)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 
MessageChannelHasProperty(NPObject * np_obj,NPIdentifier name)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 
MessageChannelGetProperty(NPObject * np_obj,NPIdentifier name,NPVariant * result)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 
MessageChannelSetProperty(NPObject * np_obj,NPIdentifier name,const NPVariant * variant)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 
MessageChannelEnumerate(NPObject * np_obj,NPIdentifier ** value,uint32_t * count)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 {
VarConversionResultcontent::MessageChannel::VarConversionResult296   VarConversionResult(const ppapi::ScopedPPVar& r, bool s)
297       : result(r),
298         success(s),
299         conversion_completed(true) {}
VarConversionResultcontent::MessageChannel::VarConversionResult300   VarConversionResult()
301       : success(false),
302         conversion_completed(false) {}
303   ppapi::ScopedPPVar result;
304   bool success;
305   bool conversion_completed;
306 };
307 
MessageChannelNPObject()308 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
309 }
310 
~MessageChannelNPObject()311 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
312 
MessageChannel(PepperPluginInstanceImpl * instance)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 
NPVariantToPPVar(const NPVariant * variant)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 
PostMessageToJavaScript(PP_Var message_data)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 
StopQueueingJavaScriptMessages()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 
QueueJavaScriptMessages()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 
NPVariantToPPVarComplete(const std::list<VarConversionResult>::iterator & result_iterator,const ppapi::ScopedPPVar & result,bool success)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 
DrainEarlyMessageQueue()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 
PostMessageToJavaScriptImpl(const WebSerializedScriptValue & message_data)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 
PostMessageToNative(PP_Var message_data)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 
PostMessageToNativeImpl(PP_Var message_data)548 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
549   instance_->HandleMessage(message_data);
550 }
551 
~MessageChannel()552 MessageChannel::~MessageChannel() {
553   WebBindings::releaseObject(np_object_);
554   if (passthrough_object_)
555     WebBindings::releaseObject(passthrough_object_);
556 }
557 
SetPassthroughObject(NPObject * passthrough)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 
GetReadOnlyProperty(NPIdentifier key,NPVariant * value) const574 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 
SetReadOnlyProperty(PP_Var key,PP_Var value)586 void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) {
587   internal_properties_[PPVarToNPIdentifier(key)] = ppapi::ScopedPPVar(value);
588 }
589 
590 }  // namespace content
591