Refactor management of overview window copy lifetime into a separate class.
[chromium-blink-merge.git] / content / renderer / pepper / message_channel.cc
blob32f21a40727341fa673eb8cc3f0dd9f15dad9683
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/pepper/message_channel.h"
7 #include <cstdlib>
8 #include <string>
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"
32 using ppapi::ArrayBufferVar;
33 using ppapi::PpapiGlobals;
34 using ppapi::StringVar;
35 using WebKit::WebBindings;
36 using WebKit::WebElement;
37 using WebKit::WebDOMEvent;
38 using WebKit::WebDOMMessageEvent;
39 using WebKit::WebPluginContainer;
40 using WebKit::WebSerializedScriptValue;
42 namespace content {
44 namespace {
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.";
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();
61 NPObject* ToPassThroughObject(NPObject* object) {
62 MessageChannel* channel = ToMessageChannel(object);
63 return channel ? channel->passthrough_object() : NULL;
66 // Helper function to determine if a given identifier is equal to kPostMessage.
67 bool IdentifierIsPostMessage(NPIdentifier identifier) {
68 return WebBindings::getStringIdentifier(kPostMessage) == identifier;
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());
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;
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.
109 NOTREACHED();
110 return PP_MakeUndefined();
112 NOTREACHED();
113 return PP_MakeUndefined();
116 //------------------------------------------------------------------------------
117 // Implementations of NPClass functions. These are here to:
118 // - Implement postMessage behavior.
119 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
120 // with GetInstanceObject() objects.
121 //------------------------------------------------------------------------------
122 NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
123 return new MessageChannel::MessageChannelNPObject;
126 void MessageChannelDeallocate(NPObject* object) {
127 MessageChannel::MessageChannelNPObject* instance =
128 static_cast<MessageChannel::MessageChannelNPObject*>(object);
129 delete instance;
132 bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
133 if (!np_obj)
134 return false;
136 // We only handle a function called postMessage.
137 if (IdentifierIsPostMessage(name))
138 return true;
140 // Other method names we will pass to the passthrough object, if we have one.
141 NPObject* passthrough = ToPassThroughObject(np_obj);
142 if (passthrough)
143 return WebBindings::hasMethod(NULL, passthrough, name);
144 return false;
147 bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name,
148 const NPVariant* args, uint32 arg_count,
149 NPVariant* result) {
150 if (!np_obj)
151 return false;
153 // We only handle a function called postMessage.
154 if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
155 MessageChannel* message_channel = ToMessageChannel(np_obj);
156 if (message_channel) {
157 message_channel->NPVariantToPPVar(&args[0]);
158 return true;
159 } else {
160 return false;
163 // Other method calls we will pass to the passthrough object, if we have one.
164 NPObject* passthrough = ToPassThroughObject(np_obj);
165 if (passthrough) {
166 return WebBindings::invoke(NULL, passthrough, name, args, arg_count,
167 result);
169 return false;
172 bool MessageChannelInvokeDefault(NPObject* np_obj,
173 const NPVariant* args,
174 uint32 arg_count,
175 NPVariant* result) {
176 if (!np_obj)
177 return false;
179 // Invoke on the passthrough object, if we have one.
180 NPObject* passthrough = ToPassThroughObject(np_obj);
181 if (passthrough) {
182 return WebBindings::invokeDefault(NULL, passthrough, args, arg_count,
183 result);
185 return false;
188 bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
189 if (!np_obj)
190 return false;
192 // Invoke on the passthrough object, if we have one.
193 NPObject* passthrough = ToPassThroughObject(np_obj);
194 if (passthrough)
195 return WebBindings::hasProperty(NULL, passthrough, name);
196 return false;
199 bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
200 NPVariant* result) {
201 if (!np_obj)
202 return false;
204 // Don't allow getting the postMessage function.
205 if (IdentifierIsPostMessage(name))
206 return false;
208 // Invoke on the passthrough object, if we have one.
209 NPObject* passthrough = ToPassThroughObject(np_obj);
210 if (passthrough)
211 return WebBindings::getProperty(NULL, passthrough, name, result);
212 return false;
215 bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
216 const NPVariant* variant) {
217 if (!np_obj)
218 return false;
220 // Don't allow setting the postMessage function.
221 if (IdentifierIsPostMessage(name))
222 return false;
224 // Invoke on the passthrough object, if we have one.
225 NPObject* passthrough = ToPassThroughObject(np_obj);
226 if (passthrough)
227 return WebBindings::setProperty(NULL, passthrough, name, variant);
228 return false;
231 bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
232 uint32_t *count) {
233 if (!np_obj)
234 return false;
236 // Invoke on the passthrough object, if we have one, to enumerate its
237 // properties.
238 NPObject* passthrough = ToPassThroughObject(np_obj);
239 if (passthrough) {
240 bool success = WebBindings::enumerate(NULL, passthrough, value, count);
241 if (success) {
242 // Add postMessage to the list and return it.
243 if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <=
244 static_cast<size_t>(*count) + 1) // Else, "always false" x64 warning.
245 return false;
246 NPIdentifier* new_array = static_cast<NPIdentifier*>(
247 std::malloc(sizeof(NPIdentifier) * (*count + 1)));
248 std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count));
249 new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
250 std::free(*value);
251 *value = new_array;
252 ++(*count);
253 return true;
257 // Otherwise, build an array that includes only postMessage.
258 *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
259 (*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
260 *count = 1;
261 return true;
264 NPClass message_channel_class = {
265 NP_CLASS_STRUCT_VERSION,
266 &MessageChannelAllocate,
267 &MessageChannelDeallocate,
268 NULL,
269 &MessageChannelHasMethod,
270 &MessageChannelInvoke,
271 &MessageChannelInvokeDefault,
272 &MessageChannelHasProperty,
273 &MessageChannelGetProperty,
274 &MessageChannelSetProperty,
275 NULL,
276 &MessageChannelEnumerate,
279 } // namespace
281 // MessageChannel --------------------------------------------------------------
282 struct MessageChannel::VarConversionResult {
283 VarConversionResult(const ppapi::ScopedPPVar& r, bool s)
284 : result(r),
285 success(s),
286 conversion_completed(true) {}
287 VarConversionResult()
288 : success(false),
289 conversion_completed(false) {}
290 ppapi::ScopedPPVar result;
291 bool success;
292 bool conversion_completed;
295 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
298 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
300 MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance)
301 : instance_(instance),
302 passthrough_object_(NULL),
303 np_object_(NULL),
304 early_message_queue_state_(QUEUE_MESSAGES),
305 weak_ptr_factory_(this) {
306 // Now create an NPObject for receiving calls to postMessage. This sets the
307 // reference count to 1. We release it in the destructor.
308 NPObject* obj = WebBindings::createObject(instance_->instanceNPP(),
309 &message_channel_class);
310 DCHECK(obj);
311 np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
312 np_object_->message_channel = weak_ptr_factory_.GetWeakPtr();
315 void MessageChannel::NPVariantToPPVar(const NPVariant* variant) {
316 converted_var_queue_.push_back(VarConversionResult());
317 std::list<VarConversionResult>::iterator result_iterator =
318 --converted_var_queue_.end();
319 switch (variant->type) {
320 case NPVariantType_Void:
321 NPVariantToPPVarComplete(result_iterator,
322 ppapi::ScopedPPVar(PP_MakeUndefined()), true);
323 return;
324 case NPVariantType_Null:
325 NPVariantToPPVarComplete(result_iterator,
326 ppapi::ScopedPPVar(PP_MakeNull()), true);
327 return;
328 case NPVariantType_Bool:
329 NPVariantToPPVarComplete(result_iterator,
330 ppapi::ScopedPPVar(
331 PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant)))),
332 true);
333 return;
334 case NPVariantType_Int32:
335 NPVariantToPPVarComplete(result_iterator,
336 ppapi::ScopedPPVar(
337 PP_MakeInt32(NPVARIANT_TO_INT32(*variant))),
338 true);
339 return;
340 case NPVariantType_Double:
341 NPVariantToPPVarComplete(result_iterator,
342 ppapi::ScopedPPVar(
343 PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant))),
344 true);
345 return;
346 case NPVariantType_String:
347 NPVariantToPPVarComplete(result_iterator,
348 ppapi::ScopedPPVar(ppapi::ScopedPPVar::PassRef(),
349 StringVar::StringToPPVar(
350 NPVARIANT_TO_STRING(*variant).UTF8Characters,
351 NPVARIANT_TO_STRING(*variant).UTF8Length)),
352 true);
353 return;
354 case NPVariantType_Object: {
355 // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
356 // shouldn't result in a deep copy.
357 v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant);
358 V8VarConverter(instance_->pp_instance()).FromV8Value(
359 v8_value, v8::Context::GetCurrent(),
360 base::Bind(&MessageChannel::NPVariantToPPVarComplete,
361 weak_ptr_factory_.GetWeakPtr(), result_iterator));
362 return;
365 NPVariantToPPVarComplete(result_iterator,
366 ppapi::ScopedPPVar(PP_MakeUndefined()), false);
369 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
370 v8::HandleScope scope(v8::Isolate::GetCurrent());
372 // Because V8 is probably not on the stack for Native->JS calls, we need to
373 // enter the appropriate context for the plugin.
374 WebPluginContainer* container = instance_->container();
375 // It's possible that container() is NULL if the plugin has been removed from
376 // the DOM (but the PluginInstance is not destroyed yet).
377 if (!container)
378 return;
380 v8::Local<v8::Context> context =
381 container->element().document().frame()->mainWorldScriptContext();
382 // If the page is being destroyed, the context may be empty.
383 if (context.IsEmpty())
384 return;
385 v8::Context::Scope context_scope(context);
387 v8::Handle<v8::Value> v8_val;
388 if (!V8VarConverter(instance_->pp_instance()).ToV8Value(
389 message_data, context, &v8_val)) {
390 PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(),
391 PP_LOGLEVEL_ERROR, std::string(), kVarToV8ConversionError);
392 return;
395 // This is for backward compatibility. It usually makes sense for us to return
396 // a string object rather than a string primitive because it allows multiple
397 // references to the same string (as with PP_Var strings). However, prior to
398 // implementing dictionary and array, vars we would return a string primitive
399 // here. Changing it to an object now will break existing code that uses
400 // strict comparisons for strings returned from PostMessage. e.g. x === "123"
401 // will no longer return true. So if the only value to return is a string
402 // object, just return the string primitive.
403 if (v8_val->IsStringObject())
404 v8_val = v8_val->ToString();
406 WebSerializedScriptValue serialized_val =
407 WebSerializedScriptValue::serialize(v8_val);
409 if (instance_->module()->IsProxied()) {
410 if (early_message_queue_state_ != SEND_DIRECTLY) {
411 // We can't just PostTask here; the messages would arrive out of
412 // order. Instead, we queue them up until we're ready to post
413 // them.
414 early_message_queue_.push_back(serialized_val);
415 } else {
416 // The proxy sent an asynchronous message, so the plugin is already
417 // unblocked. Therefore, there's no need to PostTask.
418 DCHECK(early_message_queue_.size() == 0);
419 PostMessageToJavaScriptImpl(serialized_val);
421 } else {
422 base::MessageLoop::current()->PostTask(
423 FROM_HERE,
424 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
425 weak_ptr_factory_.GetWeakPtr(),
426 serialized_val));
430 void MessageChannel::StopQueueingJavaScriptMessages() {
431 // We PostTask here instead of draining the message queue directly
432 // since we haven't finished initializing the PepperWebPluginImpl yet, so
433 // the plugin isn't available in the DOM.
434 early_message_queue_state_ = DRAIN_PENDING;
435 base::MessageLoop::current()->PostTask(
436 FROM_HERE,
437 base::Bind(&MessageChannel::DrainEarlyMessageQueue,
438 weak_ptr_factory_.GetWeakPtr()));
441 void MessageChannel::QueueJavaScriptMessages() {
442 if (early_message_queue_state_ == DRAIN_PENDING)
443 early_message_queue_state_ = DRAIN_CANCELLED;
444 else
445 early_message_queue_state_ = QUEUE_MESSAGES;
448 void MessageChannel::NPVariantToPPVarComplete(
449 const std::list<VarConversionResult>::iterator& result_iterator,
450 const ppapi::ScopedPPVar& result,
451 bool success) {
452 *result_iterator = VarConversionResult(result, success);
453 std::list<VarConversionResult>::iterator it = converted_var_queue_.begin();
454 while (it != converted_var_queue_.end() && it->conversion_completed) {
455 if (it->success) {
456 PostMessageToNative(it->result.get());
457 } else {
458 PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(),
459 PP_LOGLEVEL_ERROR, std::string(), kV8ToVarConversionError);
462 converted_var_queue_.erase(it++);
466 void MessageChannel::DrainEarlyMessageQueue() {
467 // Take a reference on the PluginInstance. This is because JavaScript code
468 // may delete the plugin, which would destroy the PluginInstance and its
469 // corresponding MessageChannel.
470 scoped_refptr<PepperPluginInstanceImpl> instance_ref(instance_);
472 if (early_message_queue_state_ == DRAIN_CANCELLED) {
473 early_message_queue_state_ = QUEUE_MESSAGES;
474 return;
476 DCHECK(early_message_queue_state_ == DRAIN_PENDING);
478 while (!early_message_queue_.empty()) {
479 PostMessageToJavaScriptImpl(early_message_queue_.front());
480 early_message_queue_.pop_front();
482 early_message_queue_state_ = SEND_DIRECTLY;
485 void MessageChannel::PostMessageToJavaScriptImpl(
486 const WebSerializedScriptValue& message_data) {
487 DCHECK(instance_);
489 WebPluginContainer* container = instance_->container();
490 // It's possible that container() is NULL if the plugin has been removed from
491 // the DOM (but the PluginInstance is not destroyed yet).
492 if (!container)
493 return;
495 WebDOMEvent event =
496 container->element().document().createEvent("MessageEvent");
497 WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>();
498 msg_event.initMessageEvent("message", // type
499 false, // canBubble
500 false, // cancelable
501 message_data, // data
502 "", // origin [*]
503 NULL, // source [*]
504 ""); // lastEventId
505 // [*] Note that the |origin| is only specified for cross-document and server-
506 // sent messages, while |source| is only specified for cross-document
507 // messages:
508 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
509 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
510 // at least, postMessage on Workers does not provide the origin or source.
511 // TODO(dmichael): Add origin if we change to a more iframe-like origin
512 // policy (see crbug.com/81537)
514 container->element().dispatchEvent(msg_event);
517 void MessageChannel::PostMessageToNative(PP_Var message_data) {
518 if (instance_->module()->IsProxied()) {
519 // In the proxied case, the copy will happen via serializiation, and the
520 // message is asynchronous. Therefore there's no need to copy the Var, nor
521 // to PostTask.
522 PostMessageToNativeImpl(message_data);
523 } else {
524 // Make a copy of the message data for the Task we will run.
525 PP_Var var_copy(CopyPPVar(message_data));
527 base::MessageLoop::current()->PostTask(
528 FROM_HERE,
529 base::Bind(&MessageChannel::PostMessageToNativeImpl,
530 weak_ptr_factory_.GetWeakPtr(),
531 var_copy));
535 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
536 instance_->HandleMessage(message_data);
539 MessageChannel::~MessageChannel() {
540 WebBindings::releaseObject(np_object_);
541 if (passthrough_object_)
542 WebBindings::releaseObject(passthrough_object_);
545 void MessageChannel::SetPassthroughObject(NPObject* passthrough) {
546 // Retain the passthrough object; We need to ensure it lives as long as this
547 // MessageChannel.
548 if (passthrough)
549 WebBindings::retainObject(passthrough);
551 // If we had a passthrough set already, release it. Note that we retain the
552 // incoming passthrough object first, so that we behave correctly if anyone
553 // invokes:
554 // SetPassthroughObject(passthrough_object());
555 if (passthrough_object_)
556 WebBindings::releaseObject(passthrough_object_);
558 passthrough_object_ = passthrough;
561 } // namespace content