- add sources.
[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/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 WebKit::WebBindings;
36 using WebKit::WebElement;
37 using WebKit::WebDOMEvent;
38 using WebKit::WebDOMMessageEvent;
39 using WebKit::WebPluginContainer;
40 using WebKit::WebSerializedScriptValue;
41
42 namespace content {
43
44 namespace {
45
46 const char kPostMessage[] = "postMessage";
47 const char kV8ToVarConversionError[] = "Failed to convert a PostMessage "
48     "argument from a JavaScript value to a PP_Var. It may have cycles or be of "
49     "an unsupported type.";
50 const char kVarToV8ConversionError[] = "Failed to convert a PostMessage "
51     "argument from a PP_Var to a Javascript value. It may have cycles or be of "
52     "an unsupported type.";
53
54 // Helper function to get the MessageChannel that is associated with an
55 // NPObject*.
56 MessageChannel* ToMessageChannel(NPObject* object) {
57   return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
58       message_channel.get();
59 }
60
61 NPObject* ToPassThroughObject(NPObject* object) {
62   MessageChannel* channel = ToMessageChannel(object);
63   return channel ? channel->passthrough_object() : NULL;
64 }
65
66 // Helper function to determine if a given identifier is equal to kPostMessage.
67 bool IdentifierIsPostMessage(NPIdentifier identifier) {
68   return WebBindings::getStringIdentifier(kPostMessage) == identifier;
69 }
70
71 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
72 // This currently just copies the value.  For a string Var, the result is a
73 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
74 PP_Var CopyPPVar(const PP_Var& var) {
75   switch (var.type) {
76     case PP_VARTYPE_UNDEFINED:
77     case PP_VARTYPE_NULL:
78     case PP_VARTYPE_BOOL:
79     case PP_VARTYPE_INT32:
80     case PP_VARTYPE_DOUBLE:
81       return var;
82     case PP_VARTYPE_STRING: {
83       StringVar* string = StringVar::FromPPVar(var);
84       if (!string)
85         return PP_MakeUndefined();
86       return StringVar::StringToPPVar(string->value());
87     }
88     case PP_VARTYPE_ARRAY_BUFFER: {
89       ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
90       if (!buffer)
91         return PP_MakeUndefined();
92       PP_Var new_buffer_var = PpapiGlobals::Get()->GetVarTracker()->
93           MakeArrayBufferPPVar(buffer->ByteLength());
94       DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER);
95       if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER)
96         return PP_MakeUndefined();
97       ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var);
98       DCHECK(new_buffer);
99       if (!new_buffer)
100         return PP_MakeUndefined();
101       memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength());
102       return new_buffer_var;
103     }
104     case PP_VARTYPE_OBJECT:
105     case PP_VARTYPE_ARRAY:
106     case PP_VARTYPE_DICTIONARY:
107     case PP_VARTYPE_RESOURCE:
108       // These types are not supported by PostMessage in-process.
109       NOTREACHED();
110       return PP_MakeUndefined();
111   }
112   NOTREACHED();
113   return PP_MakeUndefined();
114 }
115
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;
124 }
125
126 void MessageChannelDeallocate(NPObject* object) {
127   MessageChannel::MessageChannelNPObject* instance =
128       static_cast<MessageChannel::MessageChannelNPObject*>(object);
129   delete instance;
130 }
131
132 bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
133   if (!np_obj)
134     return false;
135
136   // We only handle a function called postMessage.
137   if (IdentifierIsPostMessage(name))
138     return true;
139
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;
145 }
146
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;
152
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;
161     }
162   }
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);
168   }
169   return false;
170 }
171
172 bool MessageChannelInvokeDefault(NPObject* np_obj,
173                                  const NPVariant* args,
174                                  uint32 arg_count,
175                                  NPVariant* result) {
176   if (!np_obj)
177     return false;
178
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);
184   }
185   return false;
186 }
187
188 bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
189   if (!np_obj)
190     return false;
191
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;
197 }
198
199 bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
200                                NPVariant* result) {
201   if (!np_obj)
202     return false;
203
204   // Don't allow getting the postMessage function.
205   if (IdentifierIsPostMessage(name))
206     return false;
207
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;
213 }
214
215 bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
216                                const NPVariant* variant) {
217   if (!np_obj)
218     return false;
219
220   // Don't allow setting the postMessage function.
221   if (IdentifierIsPostMessage(name))
222     return false;
223
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;
229 }
230
231 bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
232                              uint32_t *count) {
233   if (!np_obj)
234     return false;
235
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;
254     }
255   }
256
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;
262 }
263
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,
277 };
278
279 }  // namespace
280
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;
293 };
294
295 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
296 }
297
298 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
299
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();
313 }
314
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;
363     }
364   }
365   NPVariantToPPVarComplete(result_iterator,
366       ppapi::ScopedPPVar(PP_MakeUndefined()), false);
367 }
368
369 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
370   v8::HandleScope scope(v8::Isolate::GetCurrent());
371
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;
379
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);
386
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;
393   }
394
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();
405
406   WebSerializedScriptValue serialized_val =
407       WebSerializedScriptValue::serialize(v8_val);
408
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);
420     }
421   } else {
422     base::MessageLoop::current()->PostTask(
423         FROM_HERE,
424         base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
425                    weak_ptr_factory_.GetWeakPtr(),
426                    serialized_val));
427   }
428 }
429
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()));
439 }
440
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;
446 }
447
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);
460     }
461
462     converted_var_queue_.erase(it++);
463   }
464 }
465
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_);
471
472   if (early_message_queue_state_ == DRAIN_CANCELLED) {
473     early_message_queue_state_ = QUEUE_MESSAGES;
474     return;
475   }
476   DCHECK(early_message_queue_state_ == DRAIN_PENDING);
477
478   while (!early_message_queue_.empty()) {
479     PostMessageToJavaScriptImpl(early_message_queue_.front());
480     early_message_queue_.pop_front();
481   }
482   early_message_queue_state_ = SEND_DIRECTLY;
483 }
484
485 void MessageChannel::PostMessageToJavaScriptImpl(
486     const WebSerializedScriptValue& message_data) {
487   DCHECK(instance_);
488
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;
494
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)
513
514   container->element().dispatchEvent(msg_event);
515 }
516
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));
526
527     base::MessageLoop::current()->PostTask(
528         FROM_HERE,
529         base::Bind(&MessageChannel::PostMessageToNativeImpl,
530                    weak_ptr_factory_.GetWeakPtr(),
531                    var_copy));
532   }
533 }
534
535 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
536   instance_->HandleMessage(message_data);
537 }
538
539 MessageChannel::~MessageChannel() {
540   WebBindings::releaseObject(np_object_);
541   if (passthrough_object_)
542     WebBindings::releaseObject(passthrough_object_);
543 }
544
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);
550
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_);
557
558   passthrough_object_ = passthrough;
559 }
560
561 }  // namespace content