| Index: webkit/plugins/ppapi/message_channel.cc
|
| ===================================================================
|
| --- webkit/plugins/ppapi/message_channel.cc (revision 213561)
|
| +++ webkit/plugins/ppapi/message_channel.cc (working copy)
|
| @@ -1,520 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "webkit/plugins/ppapi/message_channel.h"
|
| -
|
| -#include <cstdlib>
|
| -#include <string>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/logging.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "ppapi/shared_impl/ppapi_globals.h"
|
| -#include "ppapi/shared_impl/var.h"
|
| -#include "ppapi/shared_impl/var_tracker.h"
|
| -#include "third_party/WebKit/public/web/WebBindings.h"
|
| -#include "third_party/WebKit/public/web/WebDocument.h"
|
| -#include "third_party/WebKit/public/web/WebDOMMessageEvent.h"
|
| -#include "third_party/WebKit/public/web/WebElement.h"
|
| -#include "third_party/WebKit/public/web/WebFrame.h"
|
| -#include "third_party/WebKit/public/web/WebNode.h"
|
| -#include "third_party/WebKit/public/web/WebPluginContainer.h"
|
| -#include "third_party/WebKit/public/web/WebSerializedScriptValue.h"
|
| -#include "v8/include/v8.h"
|
| -#include "webkit/plugins/ppapi/host_array_buffer_var.h"
|
| -#include "webkit/plugins/ppapi/npapi_glue.h"
|
| -#include "webkit/plugins/ppapi/plugin_module.h"
|
| -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h"
|
| -#include "webkit/plugins/ppapi/v8_var_converter.h"
|
| -
|
| -using ppapi::ArrayBufferVar;
|
| -using ppapi::PpapiGlobals;
|
| -using ppapi::StringVar;
|
| -using WebKit::WebBindings;
|
| -using WebKit::WebElement;
|
| -using WebKit::WebDOMEvent;
|
| -using WebKit::WebDOMMessageEvent;
|
| -using WebKit::WebPluginContainer;
|
| -using WebKit::WebSerializedScriptValue;
|
| -
|
| -namespace webkit {
|
| -
|
| -namespace ppapi {
|
| -
|
| -namespace {
|
| -
|
| -const char kPostMessage[] = "postMessage";
|
| -const char kV8ToVarConversionError[] = "Failed to convert a PostMessage "
|
| - "argument from a JavaScript value to a PP_Var. It may have cycles or be of "
|
| - "an unsupported type.";
|
| -const char kVarToV8ConversionError[] = "Failed to convert a PostMessage "
|
| - "argument from a PP_Var to a Javascript value. It may have cycles or be of "
|
| - "an unsupported type.";
|
| -
|
| -// Helper function to get the MessageChannel that is associated with an
|
| -// NPObject*.
|
| -MessageChannel* ToMessageChannel(NPObject* object) {
|
| - return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
|
| - message_channel.get();
|
| -}
|
| -
|
| -NPObject* ToPassThroughObject(NPObject* object) {
|
| - MessageChannel* channel = ToMessageChannel(object);
|
| - return channel ? channel->passthrough_object() : NULL;
|
| -}
|
| -
|
| -// Helper function to determine if a given identifier is equal to kPostMessage.
|
| -bool IdentifierIsPostMessage(NPIdentifier identifier) {
|
| - return WebBindings::getStringIdentifier(kPostMessage) == identifier;
|
| -}
|
| -
|
| -bool NPVariantToPPVar(const NPVariant* variant, PP_Var* result) {
|
| - switch (variant->type) {
|
| - case NPVariantType_Void:
|
| - *result = PP_MakeUndefined();
|
| - return true;
|
| - case NPVariantType_Null:
|
| - *result = PP_MakeNull();
|
| - return true;
|
| - case NPVariantType_Bool:
|
| - *result = PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant)));
|
| - return true;
|
| - case NPVariantType_Int32:
|
| - *result = PP_MakeInt32(NPVARIANT_TO_INT32(*variant));
|
| - return true;
|
| - case NPVariantType_Double:
|
| - *result = PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant));
|
| - return true;
|
| - case NPVariantType_String:
|
| - *result = StringVar::StringToPPVar(
|
| - NPVARIANT_TO_STRING(*variant).UTF8Characters,
|
| - NPVARIANT_TO_STRING(*variant).UTF8Length);
|
| - return true;
|
| - case NPVariantType_Object: {
|
| - // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
|
| - // shouldn't result in a deep copy.
|
| - v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant);
|
| - if (!V8VarConverter::FromV8Value(v8_value, v8::Context::GetCurrent(),
|
| - result)) {
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -// Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
|
| -// This currently just copies the value. For a string Var, the result is a
|
| -// PP_Var with the a copy of |var|'s string contents and a reference count of 1.
|
| -PP_Var CopyPPVar(const PP_Var& var) {
|
| - switch (var.type) {
|
| - case PP_VARTYPE_UNDEFINED:
|
| - case PP_VARTYPE_NULL:
|
| - case PP_VARTYPE_BOOL:
|
| - case PP_VARTYPE_INT32:
|
| - case PP_VARTYPE_DOUBLE:
|
| - return var;
|
| - case PP_VARTYPE_STRING: {
|
| - StringVar* string = StringVar::FromPPVar(var);
|
| - if (!string)
|
| - return PP_MakeUndefined();
|
| - return StringVar::StringToPPVar(string->value());
|
| - }
|
| - case PP_VARTYPE_ARRAY_BUFFER: {
|
| - ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
|
| - if (!buffer)
|
| - return PP_MakeUndefined();
|
| - PP_Var new_buffer_var = PpapiGlobals::Get()->GetVarTracker()->
|
| - MakeArrayBufferPPVar(buffer->ByteLength());
|
| - DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER);
|
| - if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER)
|
| - return PP_MakeUndefined();
|
| - ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var);
|
| - DCHECK(new_buffer);
|
| - if (!new_buffer)
|
| - return PP_MakeUndefined();
|
| - memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength());
|
| - return new_buffer_var;
|
| - }
|
| - case PP_VARTYPE_OBJECT:
|
| - case PP_VARTYPE_ARRAY:
|
| - case PP_VARTYPE_DICTIONARY:
|
| - // Objects/Arrays/Dictionaries not supported by PostMessage in-process.
|
| - NOTREACHED();
|
| - return PP_MakeUndefined();
|
| - }
|
| - NOTREACHED();
|
| - return PP_MakeUndefined();
|
| -}
|
| -
|
| -//------------------------------------------------------------------------------
|
| -// Implementations of NPClass functions. These are here to:
|
| -// - Implement postMessage behavior.
|
| -// - Forward calls to the 'passthrough' object to allow backwards-compatibility
|
| -// with GetInstanceObject() objects.
|
| -//------------------------------------------------------------------------------
|
| -NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
|
| - return new MessageChannel::MessageChannelNPObject;
|
| -}
|
| -
|
| -void MessageChannelDeallocate(NPObject* object) {
|
| - MessageChannel::MessageChannelNPObject* instance =
|
| - static_cast<MessageChannel::MessageChannelNPObject*>(object);
|
| - delete instance;
|
| -}
|
| -
|
| -bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // We only handle a function called postMessage.
|
| - if (IdentifierIsPostMessage(name))
|
| - return true;
|
| -
|
| - // Other method names we will pass to the passthrough object, if we have one.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough)
|
| - return WebBindings::hasMethod(NULL, passthrough, name);
|
| - return false;
|
| -}
|
| -
|
| -bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name,
|
| - const NPVariant* args, uint32 arg_count,
|
| - NPVariant* result) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // We only handle a function called postMessage.
|
| - if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
|
| - MessageChannel* message_channel = ToMessageChannel(np_obj);
|
| - if (message_channel) {
|
| - PP_Var argument = PP_MakeUndefined();
|
| - if (!NPVariantToPPVar(&args[0], &argument)) {
|
| - PpapiGlobals::Get()->LogWithSource(
|
| - message_channel->instance()->pp_instance(),
|
| - PP_LOGLEVEL_ERROR, std::string(), kV8ToVarConversionError);
|
| - return false;
|
| - }
|
| - message_channel->PostMessageToNative(argument);
|
| - PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(argument);
|
| - return true;
|
| - } else {
|
| - return false;
|
| - }
|
| - }
|
| - // Other method calls we will pass to the passthrough object, if we have one.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough) {
|
| - return WebBindings::invoke(NULL, passthrough, name, args, arg_count,
|
| - result);
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -bool MessageChannelInvokeDefault(NPObject* np_obj,
|
| - const NPVariant* args,
|
| - uint32 arg_count,
|
| - NPVariant* result) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // Invoke on the passthrough object, if we have one.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough) {
|
| - return WebBindings::invokeDefault(NULL, passthrough, args, arg_count,
|
| - result);
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // Invoke on the passthrough object, if we have one.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough)
|
| - return WebBindings::hasProperty(NULL, passthrough, name);
|
| - return false;
|
| -}
|
| -
|
| -bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
|
| - NPVariant* result) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // Don't allow getting the postMessage function.
|
| - if (IdentifierIsPostMessage(name))
|
| - return false;
|
| -
|
| - // Invoke on the passthrough object, if we have one.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough)
|
| - return WebBindings::getProperty(NULL, passthrough, name, result);
|
| - return false;
|
| -}
|
| -
|
| -bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
|
| - const NPVariant* variant) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // Don't allow setting the postMessage function.
|
| - if (IdentifierIsPostMessage(name))
|
| - return false;
|
| -
|
| - // Invoke on the passthrough object, if we have one.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough)
|
| - return WebBindings::setProperty(NULL, passthrough, name, variant);
|
| - return false;
|
| -}
|
| -
|
| -bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
|
| - uint32_t *count) {
|
| - if (!np_obj)
|
| - return false;
|
| -
|
| - // Invoke on the passthrough object, if we have one, to enumerate its
|
| - // properties.
|
| - NPObject* passthrough = ToPassThroughObject(np_obj);
|
| - if (passthrough) {
|
| - bool success = WebBindings::enumerate(NULL, passthrough, value, count);
|
| - if (success) {
|
| - // Add postMessage to the list and return it.
|
| - if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <=
|
| - static_cast<size_t>(*count) + 1) // Else, "always false" x64 warning.
|
| - return false;
|
| - NPIdentifier* new_array = static_cast<NPIdentifier*>(
|
| - std::malloc(sizeof(NPIdentifier) * (*count + 1)));
|
| - std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count));
|
| - new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
|
| - std::free(*value);
|
| - *value = new_array;
|
| - ++(*count);
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - // Otherwise, build an array that includes only postMessage.
|
| - *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
|
| - (*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
|
| - *count = 1;
|
| - return true;
|
| -}
|
| -
|
| -NPClass message_channel_class = {
|
| - NP_CLASS_STRUCT_VERSION,
|
| - &MessageChannelAllocate,
|
| - &MessageChannelDeallocate,
|
| - NULL,
|
| - &MessageChannelHasMethod,
|
| - &MessageChannelInvoke,
|
| - &MessageChannelInvokeDefault,
|
| - &MessageChannelHasProperty,
|
| - &MessageChannelGetProperty,
|
| - &MessageChannelSetProperty,
|
| - NULL,
|
| - &MessageChannelEnumerate,
|
| -};
|
| -
|
| -} // namespace
|
| -
|
| -// MessageChannel --------------------------------------------------------------
|
| -MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
|
| -}
|
| -
|
| -MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
|
| -
|
| -MessageChannel::MessageChannel(PluginInstanceImpl* instance)
|
| - : instance_(instance),
|
| - passthrough_object_(NULL),
|
| - np_object_(NULL),
|
| - weak_ptr_factory_(this),
|
| - early_message_queue_state_(QUEUE_MESSAGES) {
|
| - // Now create an NPObject for receiving calls to postMessage. This sets the
|
| - // reference count to 1. We release it in the destructor.
|
| - NPObject* obj = WebBindings::createObject(instance_->instanceNPP(),
|
| - &message_channel_class);
|
| - DCHECK(obj);
|
| - np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
|
| - np_object_->message_channel = weak_ptr_factory_.GetWeakPtr();
|
| -}
|
| -
|
| -void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
|
| - v8::HandleScope scope;
|
| -
|
| - // Because V8 is probably not on the stack for Native->JS calls, we need to
|
| - // enter the appropriate context for the plugin.
|
| - WebPluginContainer* container = instance_->container();
|
| - // It's possible that container() is NULL if the plugin has been removed from
|
| - // the DOM (but the PluginInstance is not destroyed yet).
|
| - if (!container)
|
| - return;
|
| -
|
| - v8::Local<v8::Context> context =
|
| - container->element().document().frame()->mainWorldScriptContext();
|
| - // If the page is being destroyed, the context may be empty.
|
| - if (context.IsEmpty())
|
| - return;
|
| - v8::Context::Scope context_scope(context);
|
| -
|
| - v8::Handle<v8::Value> v8_val;
|
| - if (!V8VarConverter::ToV8Value(message_data, context, &v8_val)) {
|
| - PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(),
|
| - PP_LOGLEVEL_ERROR, std::string(), kVarToV8ConversionError);
|
| - return;
|
| - }
|
| -
|
| - // This is for backward compatibility. It usually makes sense for us to return
|
| - // a string object rather than a string primitive because it allows multiple
|
| - // references to the same string (as with PP_Var strings). However, prior to
|
| - // implementing dictionary and array, vars we would return a string primitive
|
| - // here. Changing it to an object now will break existing code that uses
|
| - // strict comparisons for strings returned from PostMessage. e.g. x === "123"
|
| - // will no longer return true. So if the only value to return is a string
|
| - // object, just return the string primitive.
|
| - if (v8_val->IsStringObject())
|
| - v8_val = v8_val->ToString();
|
| -
|
| - WebSerializedScriptValue serialized_val =
|
| - WebSerializedScriptValue::serialize(v8_val);
|
| -
|
| - if (instance_->module()->IsProxied()) {
|
| - if (early_message_queue_state_ != SEND_DIRECTLY) {
|
| - // We can't just PostTask here; the messages would arrive out of
|
| - // order. Instead, we queue them up until we're ready to post
|
| - // them.
|
| - early_message_queue_.push_back(serialized_val);
|
| - } else {
|
| - // The proxy sent an asynchronous message, so the plugin is already
|
| - // unblocked. Therefore, there's no need to PostTask.
|
| - DCHECK(early_message_queue_.size() == 0);
|
| - PostMessageToJavaScriptImpl(serialized_val);
|
| - }
|
| - } else {
|
| - base::MessageLoop::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - serialized_val));
|
| - }
|
| -}
|
| -
|
| -void MessageChannel::StopQueueingJavaScriptMessages() {
|
| - // We PostTask here instead of draining the message queue directly
|
| - // since we haven't finished initializing the WebPluginImpl yet, so
|
| - // the plugin isn't available in the DOM.
|
| - early_message_queue_state_ = DRAIN_PENDING;
|
| - base::MessageLoop::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MessageChannel::DrainEarlyMessageQueue,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -void MessageChannel::QueueJavaScriptMessages() {
|
| - if (early_message_queue_state_ == DRAIN_PENDING)
|
| - early_message_queue_state_ = DRAIN_CANCELLED;
|
| - else
|
| - early_message_queue_state_ = QUEUE_MESSAGES;
|
| -}
|
| -
|
| -void MessageChannel::DrainEarlyMessageQueue() {
|
| - // Take a reference on the PluginInstance. This is because JavaScript code
|
| - // may delete the plugin, which would destroy the PluginInstance and its
|
| - // corresponding MessageChannel.
|
| - scoped_refptr<PluginInstanceImpl> instance_ref(instance_);
|
| -
|
| - if (early_message_queue_state_ == DRAIN_CANCELLED) {
|
| - early_message_queue_state_ = QUEUE_MESSAGES;
|
| - return;
|
| - }
|
| - DCHECK(early_message_queue_state_ == DRAIN_PENDING);
|
| -
|
| - while (!early_message_queue_.empty()) {
|
| - PostMessageToJavaScriptImpl(early_message_queue_.front());
|
| - early_message_queue_.pop_front();
|
| - }
|
| - early_message_queue_state_ = SEND_DIRECTLY;
|
| -}
|
| -
|
| -void MessageChannel::PostMessageToJavaScriptImpl(
|
| - const WebSerializedScriptValue& message_data) {
|
| - DCHECK(instance_);
|
| -
|
| - WebPluginContainer* container = instance_->container();
|
| - // It's possible that container() is NULL if the plugin has been removed from
|
| - // the DOM (but the PluginInstance is not destroyed yet).
|
| - if (!container)
|
| - return;
|
| -
|
| - WebDOMEvent event =
|
| - container->element().document().createEvent("MessageEvent");
|
| - WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>();
|
| - msg_event.initMessageEvent("message", // type
|
| - false, // canBubble
|
| - false, // cancelable
|
| - message_data, // data
|
| - "", // origin [*]
|
| - NULL, // source [*]
|
| - ""); // lastEventId
|
| - // [*] Note that the |origin| is only specified for cross-document and server-
|
| - // sent messages, while |source| is only specified for cross-document
|
| - // messages:
|
| - // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
|
| - // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
|
| - // at least, postMessage on Workers does not provide the origin or source.
|
| - // TODO(dmichael): Add origin if we change to a more iframe-like origin
|
| - // policy (see crbug.com/81537)
|
| -
|
| - container->element().dispatchEvent(msg_event);
|
| -}
|
| -
|
| -void MessageChannel::PostMessageToNative(PP_Var message_data) {
|
| - if (instance_->module()->IsProxied()) {
|
| - // In the proxied case, the copy will happen via serializiation, and the
|
| - // message is asynchronous. Therefore there's no need to copy the Var, nor
|
| - // to PostTask.
|
| - PostMessageToNativeImpl(message_data);
|
| - } else {
|
| - // Make a copy of the message data for the Task we will run.
|
| - PP_Var var_copy(CopyPPVar(message_data));
|
| -
|
| - base::MessageLoop::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MessageChannel::PostMessageToNativeImpl,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - var_copy));
|
| - }
|
| -}
|
| -
|
| -void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
|
| - instance_->HandleMessage(message_data);
|
| -}
|
| -
|
| -MessageChannel::~MessageChannel() {
|
| - WebBindings::releaseObject(np_object_);
|
| - if (passthrough_object_)
|
| - WebBindings::releaseObject(passthrough_object_);
|
| -}
|
| -
|
| -void MessageChannel::SetPassthroughObject(NPObject* passthrough) {
|
| - // Retain the passthrough object; We need to ensure it lives as long as this
|
| - // MessageChannel.
|
| - if (passthrough)
|
| - WebBindings::retainObject(passthrough);
|
| -
|
| - // If we had a passthrough set already, release it. Note that we retain the
|
| - // incoming passthrough object first, so that we behave correctly if anyone
|
| - // invokes:
|
| - // SetPassthroughObject(passthrough_object());
|
| - if (passthrough_object_)
|
| - WebBindings::releaseObject(passthrough_object_);
|
| -
|
| - passthrough_object_ = passthrough;
|
| -}
|
| -
|
| -} // namespace ppapi
|
| -} // namespace webkit
|
|
|