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