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