Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / webkit / plugins / ppapi / message_channel.cc
blob4a0165fd49ae20e2c9836deeb42326602530b029
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 "webkit/plugins/ppapi/message_channel.h"
7 #include <cstdlib>
8 #include <string>
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "ppapi/shared_impl/ppapi_globals.h"
14 #include "ppapi/shared_impl/var.h"
15 #include "ppapi/shared_impl/var_tracker.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMMessageEvent.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSerializedScriptValue.h"
24 #include "v8/include/v8.h"
25 #include "webkit/plugins/ppapi/host_array_buffer_var.h"
26 #include "webkit/plugins/ppapi/npapi_glue.h"
27 #include "webkit/plugins/ppapi/plugin_module.h"
28 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
30 using ppapi::ArrayBufferVar;
31 using ppapi::PpapiGlobals;
32 using ppapi::StringVar;
33 using WebKit::WebBindings;
34 using WebKit::WebElement;
35 using WebKit::WebDOMEvent;
36 using WebKit::WebDOMMessageEvent;
37 using WebKit::WebPluginContainer;
38 using WebKit::WebSerializedScriptValue;
40 namespace webkit {
42 namespace ppapi {
44 namespace {
46 const char kPostMessage[] = "postMessage";
48 // Helper function to get the MessageChannel that is associated with an
49 // NPObject*.
50 MessageChannel* ToMessageChannel(NPObject* object) {
51 return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
52 message_channel;
55 NPObject* ToPassThroughObject(NPObject* object) {
56 MessageChannel* channel = ToMessageChannel(object);
57 return channel ? channel->passthrough_object() : NULL;
60 // Helper function to determine if a given identifier is equal to kPostMessage.
61 bool IdentifierIsPostMessage(NPIdentifier identifier) {
62 return WebBindings::getStringIdentifier(kPostMessage) == identifier;
65 // Converts the given PP_Var to a v8::Value, returning true on success.
66 // False means that the given variant is invalid. In this case, |result| will
67 // be set to an empty handle.
68 bool PPVarToV8Value(PP_Var var, v8::Handle<v8::Value>* result) {
69 switch (var.type) {
70 case PP_VARTYPE_UNDEFINED:
71 *result = v8::Undefined();
72 break;
73 case PP_VARTYPE_NULL:
74 *result = v8::Null();
75 break;
76 case PP_VARTYPE_BOOL:
77 *result = (var.value.as_bool == PP_TRUE) ? v8::True() : v8::False();
78 break;
79 case PP_VARTYPE_INT32:
80 *result = v8::Integer::New(var.value.as_int);
81 break;
82 case PP_VARTYPE_DOUBLE:
83 *result = v8::Number::New(var.value.as_double);
84 break;
85 case PP_VARTYPE_STRING: {
86 StringVar* string = StringVar::FromPPVar(var);
87 if (!string) {
88 result->Clear();
89 return false;
91 const std::string& value = string->value();
92 // TODO(dmichael): We should consider caching the V8 string in the host-
93 // side StringVar, so that we only have to convert/copy once if a
94 // string is sent more than once.
95 *result = v8::String::New(value.c_str(), value.size());
96 break;
98 case PP_VARTYPE_ARRAY_BUFFER: {
99 ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
100 if (!buffer) {
101 result->Clear();
102 return false;
104 HostArrayBufferVar* host_buffer =
105 static_cast<HostArrayBufferVar*>(buffer);
106 *result =
107 v8::Local<v8::Value>::New(host_buffer->webkit_buffer().toV8Value());
108 break;
110 case PP_VARTYPE_OBJECT:
111 case PP_VARTYPE_ARRAY:
112 case PP_VARTYPE_DICTIONARY:
113 // These are not currently supported.
114 NOTIMPLEMENTED();
115 result->Clear();
116 return false;
118 return true;
121 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
122 // This currently just copies the value. For a string Var, the result is a
123 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
125 // TODO(dmichael): Bypass this step for out-of-process plugins, since a copy
126 // happens already when the Var is serialized.
127 PP_Var CopyPPVar(const PP_Var& var) {
128 if (var.type == PP_VARTYPE_OBJECT) {
129 // Objects are not currently supported.
130 NOTIMPLEMENTED();
131 return PP_MakeUndefined();
132 } else if (var.type == PP_VARTYPE_STRING) {
133 StringVar* string = StringVar::FromPPVar(var);
134 if (!string)
135 return PP_MakeUndefined();
136 return StringVar::StringToPPVar(string->value());
137 } else if (var.type == PP_VARTYPE_ARRAY_BUFFER) {
138 ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
139 if (!buffer)
140 return PP_MakeUndefined();
141 PP_Var new_buffer_var = PpapiGlobals::Get()->GetVarTracker()->
142 MakeArrayBufferPPVar(buffer->ByteLength());
143 DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER);
144 if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER)
145 return PP_MakeUndefined();
146 ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var);
147 DCHECK(new_buffer);
148 if (!new_buffer)
149 return PP_MakeUndefined();
150 memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength());
151 return new_buffer_var;
152 } else {
153 return var;
157 //------------------------------------------------------------------------------
158 // Implementations of NPClass functions. These are here to:
159 // - Implement postMessage behavior.
160 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
161 // with GetInstanceObject() objects.
162 //------------------------------------------------------------------------------
163 NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
164 return new MessageChannel::MessageChannelNPObject;
167 void MessageChannelDeallocate(NPObject* object) {
168 MessageChannel::MessageChannelNPObject* instance =
169 static_cast<MessageChannel::MessageChannelNPObject*>(object);
170 delete instance;
173 bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
174 if (!np_obj)
175 return false;
177 // We only handle a function called postMessage.
178 if (IdentifierIsPostMessage(name))
179 return true;
181 // Other method names we will pass to the passthrough object, if we have one.
182 NPObject* passthrough = ToPassThroughObject(np_obj);
183 if (passthrough)
184 return WebBindings::hasMethod(NULL, passthrough, name);
185 return false;
188 bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name,
189 const NPVariant* args, uint32 arg_count,
190 NPVariant* result) {
191 if (!np_obj)
192 return false;
194 // We only handle a function called postMessage.
195 if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
196 MessageChannel* message_channel = ToMessageChannel(np_obj);
197 if (message_channel) {
198 PP_Var argument(NPVariantToPPVar(message_channel->instance(), &args[0]));
199 message_channel->PostMessageToNative(argument);
200 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(argument);
201 return true;
202 } else {
203 return false;
206 // Other method calls we will pass to the passthrough object, if we have one.
207 NPObject* passthrough = ToPassThroughObject(np_obj);
208 if (passthrough) {
209 return WebBindings::invoke(NULL, passthrough, name, args, arg_count,
210 result);
212 return false;
215 bool MessageChannelInvokeDefault(NPObject* np_obj,
216 const NPVariant* args,
217 uint32 arg_count,
218 NPVariant* result) {
219 if (!np_obj)
220 return false;
222 // Invoke on the passthrough object, if we have one.
223 NPObject* passthrough = ToPassThroughObject(np_obj);
224 if (passthrough) {
225 return WebBindings::invokeDefault(NULL, passthrough, args, arg_count,
226 result);
228 return false;
231 bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
232 if (!np_obj)
233 return false;
235 // Invoke on the passthrough object, if we have one.
236 NPObject* passthrough = ToPassThroughObject(np_obj);
237 if (passthrough)
238 return WebBindings::hasProperty(NULL, passthrough, name);
239 return false;
242 bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
243 NPVariant* result) {
244 if (!np_obj)
245 return false;
247 // Don't allow getting the postMessage function.
248 if (IdentifierIsPostMessage(name))
249 return false;
251 // Invoke on the passthrough object, if we have one.
252 NPObject* passthrough = ToPassThroughObject(np_obj);
253 if (passthrough)
254 return WebBindings::getProperty(NULL, passthrough, name, result);
255 return false;
258 bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
259 const NPVariant* variant) {
260 if (!np_obj)
261 return false;
263 // Don't allow setting the postMessage function.
264 if (IdentifierIsPostMessage(name))
265 return false;
267 // Invoke on the passthrough object, if we have one.
268 NPObject* passthrough = ToPassThroughObject(np_obj);
269 if (passthrough)
270 return WebBindings::setProperty(NULL, passthrough, name, variant);
271 return false;
274 bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
275 uint32_t *count) {
276 if (!np_obj)
277 return false;
279 // Invoke on the passthrough object, if we have one, to enumerate its
280 // properties.
281 NPObject* passthrough = ToPassThroughObject(np_obj);
282 if (passthrough) {
283 bool success = WebBindings::enumerate(NULL, passthrough, value, count);
284 if (success) {
285 // Add postMessage to the list and return it.
286 if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <=
287 (*count + 1))
288 return false;
289 NPIdentifier* new_array = static_cast<NPIdentifier*>(
290 std::malloc(sizeof(NPIdentifier) * (*count + 1)));
291 std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count));
292 new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
293 std::free(*value);
294 *value = new_array;
295 ++(*count);
296 return true;
300 // Otherwise, build an array that includes only postMessage.
301 *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
302 (*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
303 *count = 1;
304 return true;
307 NPClass message_channel_class = {
308 NP_CLASS_STRUCT_VERSION,
309 &MessageChannelAllocate,
310 &MessageChannelDeallocate,
311 NULL,
312 &MessageChannelHasMethod,
313 &MessageChannelInvoke,
314 &MessageChannelInvokeDefault,
315 &MessageChannelHasProperty,
316 &MessageChannelGetProperty,
317 &MessageChannelSetProperty,
318 NULL,
319 &MessageChannelEnumerate,
322 } // namespace
324 // MessageChannel --------------------------------------------------------------
325 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
328 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
330 MessageChannel::MessageChannel(PluginInstance* instance)
331 : instance_(instance),
332 passthrough_object_(NULL),
333 np_object_(NULL),
334 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
335 early_message_queue_state_(QUEUE_MESSAGES) {
336 // Now create an NPObject for receiving calls to postMessage. This sets the
337 // reference count to 1. We release it in the destructor.
338 NPObject* obj = WebBindings::createObject(NULL, &message_channel_class);
339 DCHECK(obj);
340 np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
341 np_object_->message_channel = weak_ptr_factory_.GetWeakPtr();
344 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
345 // Serialize the message data.
346 v8::HandleScope scope;
347 // Because V8 is probably not on the stack for Native->JS calls, we need to
348 // enter the appropriate context for the plugin.
349 v8::Local<v8::Context> context =
350 instance_->container()->element().document().frame()->
351 mainWorldScriptContext();
352 v8::Context::Scope context_scope(context);
354 v8::Local<v8::Value> v8_val;
355 if (!PPVarToV8Value(message_data, &v8_val)) {
356 NOTREACHED();
357 return;
360 WebSerializedScriptValue serialized_val =
361 WebSerializedScriptValue::serialize(v8_val);
363 if (instance_->module()->IsProxied()) {
364 if (early_message_queue_state_ != SEND_DIRECTLY) {
365 // We can't just PostTask here; the messages would arrive out of
366 // order. Instead, we queue them up until we're ready to post
367 // them.
368 early_message_queue_.push_back(serialized_val);
369 } else {
370 // The proxy sent an asynchronous message, so the plugin is already
371 // unblocked. Therefore, there's no need to PostTask.
372 DCHECK(early_message_queue_.size() == 0);
373 PostMessageToJavaScriptImpl(serialized_val);
375 } else {
376 MessageLoop::current()->PostTask(
377 FROM_HERE,
378 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
379 weak_ptr_factory_.GetWeakPtr(),
380 serialized_val));
384 void MessageChannel::StopQueueingJavaScriptMessages() {
385 // We PostTask here instead of draining the message queue directly
386 // since we haven't finished initializing the WebPluginImpl yet, so
387 // the plugin isn't available in the DOM.
388 early_message_queue_state_ = DRAIN_PENDING;
389 MessageLoop::current()->PostTask(
390 FROM_HERE,
391 base::Bind(&MessageChannel::DrainEarlyMessageQueue,
392 weak_ptr_factory_.GetWeakPtr()));
395 void MessageChannel::QueueJavaScriptMessages() {
396 if (early_message_queue_state_ == DRAIN_PENDING)
397 early_message_queue_state_ = DRAIN_CANCELLED;
398 else
399 early_message_queue_state_ = QUEUE_MESSAGES;
402 void MessageChannel::DrainEarlyMessageQueue() {
403 // Take a reference on the PluginInstance. This is because JavaScript code
404 // may delete the plugin, which would destroy the PluginInstance and its
405 // corresponding MessageChannel.
406 scoped_refptr<PluginInstance> instance_ref(instance_);
408 if (early_message_queue_state_ == DRAIN_CANCELLED) {
409 early_message_queue_state_ = QUEUE_MESSAGES;
410 return;
412 DCHECK(early_message_queue_state_ == DRAIN_PENDING);
414 while (!early_message_queue_.empty()) {
415 PostMessageToJavaScriptImpl(early_message_queue_.front());
416 early_message_queue_.pop_front();
418 early_message_queue_state_ = SEND_DIRECTLY;
421 void MessageChannel::PostMessageToJavaScriptImpl(
422 const WebSerializedScriptValue& message_data) {
423 DCHECK(instance_);
425 WebPluginContainer* container = instance_->container();
426 // It's possible that container() is NULL if the plugin has been removed from
427 // the DOM (but the PluginInstance is not destroyed yet).
428 if (!container)
429 return;
431 WebDOMEvent event =
432 container->element().document().createEvent("MessageEvent");
433 WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>();
434 msg_event.initMessageEvent("message", // type
435 false, // canBubble
436 false, // cancelable
437 message_data, // data
438 "", // origin [*]
439 NULL, // source [*]
440 ""); // lastEventId
441 // [*] Note that the |origin| is only specified for cross-document and server-
442 // sent messages, while |source| is only specified for cross-document
443 // messages:
444 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
445 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
446 // at least, postMessage on Workers does not provide the origin or source.
447 // TODO(dmichael): Add origin if we change to a more iframe-like origin
448 // policy (see crbug.com/81537)
450 container->element().dispatchEvent(msg_event);
453 void MessageChannel::PostMessageToNative(PP_Var message_data) {
454 if (instance_->module()->IsProxied()) {
455 // In the proxied case, the copy will happen via serializiation, and the
456 // message is asynchronous. Therefore there's no need to copy the Var, nor
457 // to PostTask.
458 PostMessageToNativeImpl(message_data);
459 } else {
460 // Make a copy of the message data for the Task we will run.
461 PP_Var var_copy(CopyPPVar(message_data));
463 MessageLoop::current()->PostTask(FROM_HERE,
464 base::Bind(&MessageChannel::PostMessageToNativeImpl,
465 weak_ptr_factory_.GetWeakPtr(),
466 var_copy));
470 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
471 instance_->HandleMessage(message_data);
474 MessageChannel::~MessageChannel() {
475 WebBindings::releaseObject(np_object_);
476 if (passthrough_object_)
477 WebBindings::releaseObject(passthrough_object_);
480 void MessageChannel::SetPassthroughObject(NPObject* passthrough) {
481 // Retain the passthrough object; We need to ensure it lives as long as this
482 // MessageChannel.
483 if (passthrough)
484 WebBindings::retainObject(passthrough);
486 // If we had a passthrough set already, release it. Note that we retain the
487 // incoming passthrough object first, so that we behave correctly if anyone
488 // invokes:
489 // SetPassthroughObject(passthrough_object());
490 if (passthrough_object_)
491 WebBindings::releaseObject(passthrough_object_);
493 passthrough_object_ = passthrough;
496 } // namespace ppapi
497 } // namespace webkit