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
32using ppapi::ArrayBufferVar;
33using ppapi::PpapiGlobals;
34using ppapi::StringVar;
35using blink::WebBindings;
36using blink::WebElement;
37using blink::WebDOMEvent;
38using blink::WebDOMMessageEvent;
39using blink::WebPluginContainer;
40using blink::WebSerializedScriptValue;
41
42namespace content {
43
44namespace {
45
46const char kPostMessage[] = "postMessage";
47const 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.";
50const 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*.
56MessageChannel* ToMessageChannel(NPObject* object) {
57  return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
58      message_channel.get();
59}
60
61NPObject* 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.
67bool 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.
74PP_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//------------------------------------------------------------------------------
123NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
124  return new MessageChannel::MessageChannelNPObject;
125}
126
127void MessageChannelDeallocate(NPObject* object) {
128  MessageChannel::MessageChannelNPObject* instance =
129      static_cast<MessageChannel::MessageChannelNPObject*>(object);
130  delete instance;
131}
132
133bool 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
148bool 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
173bool 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
189bool 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
206bool 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
228bool 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
244bool 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
277NPClass 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 --------------------------------------------------------------
295struct MessageChannel::VarConversionResult {
296  VarConversionResult(const ppapi::ScopedPPVar& r, bool s)
297      : result(r),
298        success(s),
299        conversion_completed(true) {}
300  VarConversionResult()
301      : success(false),
302        conversion_completed(false) {}
303  ppapi::ScopedPPVar result;
304  bool success;
305  bool conversion_completed;
306};
307
308MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
309}
310
311MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
312
313MessageChannel::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
328void 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
382void 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
443void 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
454void 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
461void 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
479void 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
498void 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
530void 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
548void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
549  instance_->HandleMessage(message_data);
550}
551
552MessageChannel::~MessageChannel() {
553  WebBindings::releaseObject(np_object_);
554  if (passthrough_object_)
555    WebBindings::releaseObject(passthrough_object_);
556}
557
558void 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
574bool 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
586void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) {
587  internal_properties_[PPVarToNPIdentifier(key)] = ppapi::ScopedPPVar(value);
588}
589
590}  // namespace content
591