Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1084)

Side by Side Diff: webkit/plugins/ppapi/message_channel.cc

Issue 20165002: Move webkit/plugins/ppapi to content/renderer/pepper. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: more more clang fun Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « webkit/plugins/ppapi/message_channel.h ('k') | webkit/plugins/ppapi/mock_platform_image_2d.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "webkit/plugins/ppapi/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 "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/public/web/WebBindings.h"
17 #include "third_party/WebKit/public/web/WebDocument.h"
18 #include "third_party/WebKit/public/web/WebDOMMessageEvent.h"
19 #include "third_party/WebKit/public/web/WebElement.h"
20 #include "third_party/WebKit/public/web/WebFrame.h"
21 #include "third_party/WebKit/public/web/WebNode.h"
22 #include "third_party/WebKit/public/web/WebPluginContainer.h"
23 #include "third_party/WebKit/public/web/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_impl.h"
29 #include "webkit/plugins/ppapi/v8_var_converter.h"
30
31 using ppapi::ArrayBufferVar;
32 using ppapi::PpapiGlobals;
33 using ppapi::StringVar;
34 using WebKit::WebBindings;
35 using WebKit::WebElement;
36 using WebKit::WebDOMEvent;
37 using WebKit::WebDOMMessageEvent;
38 using WebKit::WebPluginContainer;
39 using WebKit::WebSerializedScriptValue;
40
41 namespace webkit {
42
43 namespace ppapi {
44
45 namespace {
46
47 const char kPostMessage[] = "postMessage";
48 const char kV8ToVarConversionError[] = "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[] = "Failed to convert a PostMessage "
52 "argument from a PP_Var to a Javascript value. It may have cycles or be of "
53 "an unsupported type.";
54
55 // Helper function to get the MessageChannel that is associated with an
56 // NPObject*.
57 MessageChannel* ToMessageChannel(NPObject* object) {
58 return static_cast<MessageChannel::MessageChannelNPObject*>(object)->
59 message_channel.get();
60 }
61
62 NPObject* ToPassThroughObject(NPObject* object) {
63 MessageChannel* channel = ToMessageChannel(object);
64 return channel ? channel->passthrough_object() : NULL;
65 }
66
67 // Helper function to determine if a given identifier is equal to kPostMessage.
68 bool IdentifierIsPostMessage(NPIdentifier identifier) {
69 return WebBindings::getStringIdentifier(kPostMessage) == identifier;
70 }
71
72 bool NPVariantToPPVar(const NPVariant* variant, PP_Var* result) {
73 switch (variant->type) {
74 case NPVariantType_Void:
75 *result = PP_MakeUndefined();
76 return true;
77 case NPVariantType_Null:
78 *result = PP_MakeNull();
79 return true;
80 case NPVariantType_Bool:
81 *result = PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant)));
82 return true;
83 case NPVariantType_Int32:
84 *result = PP_MakeInt32(NPVARIANT_TO_INT32(*variant));
85 return true;
86 case NPVariantType_Double:
87 *result = PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant));
88 return true;
89 case NPVariantType_String:
90 *result = StringVar::StringToPPVar(
91 NPVARIANT_TO_STRING(*variant).UTF8Characters,
92 NPVARIANT_TO_STRING(*variant).UTF8Length);
93 return true;
94 case NPVariantType_Object: {
95 // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
96 // shouldn't result in a deep copy.
97 v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant);
98 if (!V8VarConverter::FromV8Value(v8_value, v8::Context::GetCurrent(),
99 result)) {
100 return false;
101 }
102 return true;
103 }
104 }
105 return false;
106 }
107
108 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage.
109 // This currently just copies the value. For a string Var, the result is a
110 // PP_Var with the a copy of |var|'s string contents and a reference count of 1.
111 PP_Var CopyPPVar(const PP_Var& var) {
112 switch (var.type) {
113 case PP_VARTYPE_UNDEFINED:
114 case PP_VARTYPE_NULL:
115 case PP_VARTYPE_BOOL:
116 case PP_VARTYPE_INT32:
117 case PP_VARTYPE_DOUBLE:
118 return var;
119 case PP_VARTYPE_STRING: {
120 StringVar* string = StringVar::FromPPVar(var);
121 if (!string)
122 return PP_MakeUndefined();
123 return StringVar::StringToPPVar(string->value());
124 }
125 case PP_VARTYPE_ARRAY_BUFFER: {
126 ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
127 if (!buffer)
128 return PP_MakeUndefined();
129 PP_Var new_buffer_var = PpapiGlobals::Get()->GetVarTracker()->
130 MakeArrayBufferPPVar(buffer->ByteLength());
131 DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER);
132 if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER)
133 return PP_MakeUndefined();
134 ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var);
135 DCHECK(new_buffer);
136 if (!new_buffer)
137 return PP_MakeUndefined();
138 memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength());
139 return new_buffer_var;
140 }
141 case PP_VARTYPE_OBJECT:
142 case PP_VARTYPE_ARRAY:
143 case PP_VARTYPE_DICTIONARY:
144 // Objects/Arrays/Dictionaries not supported by PostMessage in-process.
145 NOTREACHED();
146 return PP_MakeUndefined();
147 }
148 NOTREACHED();
149 return PP_MakeUndefined();
150 }
151
152 //------------------------------------------------------------------------------
153 // Implementations of NPClass functions. These are here to:
154 // - Implement postMessage behavior.
155 // - Forward calls to the 'passthrough' object to allow backwards-compatibility
156 // with GetInstanceObject() objects.
157 //------------------------------------------------------------------------------
158 NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
159 return new MessageChannel::MessageChannelNPObject;
160 }
161
162 void MessageChannelDeallocate(NPObject* object) {
163 MessageChannel::MessageChannelNPObject* instance =
164 static_cast<MessageChannel::MessageChannelNPObject*>(object);
165 delete instance;
166 }
167
168 bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
169 if (!np_obj)
170 return false;
171
172 // We only handle a function called postMessage.
173 if (IdentifierIsPostMessage(name))
174 return true;
175
176 // Other method names we will pass to the passthrough object, if we have one.
177 NPObject* passthrough = ToPassThroughObject(np_obj);
178 if (passthrough)
179 return WebBindings::hasMethod(NULL, passthrough, name);
180 return false;
181 }
182
183 bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name,
184 const NPVariant* args, uint32 arg_count,
185 NPVariant* result) {
186 if (!np_obj)
187 return false;
188
189 // We only handle a function called postMessage.
190 if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
191 MessageChannel* message_channel = ToMessageChannel(np_obj);
192 if (message_channel) {
193 PP_Var argument = PP_MakeUndefined();
194 if (!NPVariantToPPVar(&args[0], &argument)) {
195 PpapiGlobals::Get()->LogWithSource(
196 message_channel->instance()->pp_instance(),
197 PP_LOGLEVEL_ERROR, std::string(), kV8ToVarConversionError);
198 return false;
199 }
200 message_channel->PostMessageToNative(argument);
201 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(argument);
202 return true;
203 } else {
204 return false;
205 }
206 }
207 // Other method calls we will pass to the passthrough object, if we have one.
208 NPObject* passthrough = ToPassThroughObject(np_obj);
209 if (passthrough) {
210 return WebBindings::invoke(NULL, passthrough, name, args, arg_count,
211 result);
212 }
213 return false;
214 }
215
216 bool MessageChannelInvokeDefault(NPObject* np_obj,
217 const NPVariant* args,
218 uint32 arg_count,
219 NPVariant* result) {
220 if (!np_obj)
221 return false;
222
223 // Invoke on the passthrough object, if we have one.
224 NPObject* passthrough = ToPassThroughObject(np_obj);
225 if (passthrough) {
226 return WebBindings::invokeDefault(NULL, passthrough, args, arg_count,
227 result);
228 }
229 return false;
230 }
231
232 bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
233 if (!np_obj)
234 return false;
235
236 // Invoke on the passthrough object, if we have one.
237 NPObject* passthrough = ToPassThroughObject(np_obj);
238 if (passthrough)
239 return WebBindings::hasProperty(NULL, passthrough, name);
240 return false;
241 }
242
243 bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
244 NPVariant* result) {
245 if (!np_obj)
246 return false;
247
248 // Don't allow getting the postMessage function.
249 if (IdentifierIsPostMessage(name))
250 return false;
251
252 // Invoke on the passthrough object, if we have one.
253 NPObject* passthrough = ToPassThroughObject(np_obj);
254 if (passthrough)
255 return WebBindings::getProperty(NULL, passthrough, name, result);
256 return false;
257 }
258
259 bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
260 const NPVariant* variant) {
261 if (!np_obj)
262 return false;
263
264 // Don't allow setting the postMessage function.
265 if (IdentifierIsPostMessage(name))
266 return false;
267
268 // Invoke on the passthrough object, if we have one.
269 NPObject* passthrough = ToPassThroughObject(np_obj);
270 if (passthrough)
271 return WebBindings::setProperty(NULL, passthrough, name, variant);
272 return false;
273 }
274
275 bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
276 uint32_t *count) {
277 if (!np_obj)
278 return false;
279
280 // Invoke on the passthrough object, if we have one, to enumerate its
281 // properties.
282 NPObject* passthrough = ToPassThroughObject(np_obj);
283 if (passthrough) {
284 bool success = WebBindings::enumerate(NULL, passthrough, value, count);
285 if (success) {
286 // Add postMessage to the list and return it.
287 if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <=
288 static_cast<size_t>(*count) + 1) // Else, "always false" x64 warning.
289 return false;
290 NPIdentifier* new_array = static_cast<NPIdentifier*>(
291 std::malloc(sizeof(NPIdentifier) * (*count + 1)));
292 std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count));
293 new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
294 std::free(*value);
295 *value = new_array;
296 ++(*count);
297 return true;
298 }
299 }
300
301 // Otherwise, build an array that includes only postMessage.
302 *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
303 (*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
304 *count = 1;
305 return true;
306 }
307
308 NPClass message_channel_class = {
309 NP_CLASS_STRUCT_VERSION,
310 &MessageChannelAllocate,
311 &MessageChannelDeallocate,
312 NULL,
313 &MessageChannelHasMethod,
314 &MessageChannelInvoke,
315 &MessageChannelInvokeDefault,
316 &MessageChannelHasProperty,
317 &MessageChannelGetProperty,
318 &MessageChannelSetProperty,
319 NULL,
320 &MessageChannelEnumerate,
321 };
322
323 } // namespace
324
325 // MessageChannel --------------------------------------------------------------
326 MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {
327 }
328
329 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
330
331 MessageChannel::MessageChannel(PluginInstanceImpl* instance)
332 : instance_(instance),
333 passthrough_object_(NULL),
334 np_object_(NULL),
335 weak_ptr_factory_(this),
336 early_message_queue_state_(QUEUE_MESSAGES) {
337 // Now create an NPObject for receiving calls to postMessage. This sets the
338 // reference count to 1. We release it in the destructor.
339 NPObject* obj = WebBindings::createObject(instance_->instanceNPP(),
340 &message_channel_class);
341 DCHECK(obj);
342 np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
343 np_object_->message_channel = weak_ptr_factory_.GetWeakPtr();
344 }
345
346 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
347 v8::HandleScope scope;
348
349 // Because V8 is probably not on the stack for Native->JS calls, we need to
350 // enter the appropriate context for the plugin.
351 WebPluginContainer* container = instance_->container();
352 // It's possible that container() is NULL if the plugin has been removed from
353 // the DOM (but the PluginInstance is not destroyed yet).
354 if (!container)
355 return;
356
357 v8::Local<v8::Context> context =
358 container->element().document().frame()->mainWorldScriptContext();
359 // If the page is being destroyed, the context may be empty.
360 if (context.IsEmpty())
361 return;
362 v8::Context::Scope context_scope(context);
363
364 v8::Handle<v8::Value> v8_val;
365 if (!V8VarConverter::ToV8Value(message_data, context, &v8_val)) {
366 PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(),
367 PP_LOGLEVEL_ERROR, std::string(), kVarToV8ConversionError);
368 return;
369 }
370
371 // This is for backward compatibility. It usually makes sense for us to return
372 // a string object rather than a string primitive because it allows multiple
373 // references to the same string (as with PP_Var strings). However, prior to
374 // implementing dictionary and array, vars we would return a string primitive
375 // here. Changing it to an object now will break existing code that uses
376 // strict comparisons for strings returned from PostMessage. e.g. x === "123"
377 // will no longer return true. So if the only value to return is a string
378 // object, just return the string primitive.
379 if (v8_val->IsStringObject())
380 v8_val = v8_val->ToString();
381
382 WebSerializedScriptValue serialized_val =
383 WebSerializedScriptValue::serialize(v8_val);
384
385 if (instance_->module()->IsProxied()) {
386 if (early_message_queue_state_ != SEND_DIRECTLY) {
387 // We can't just PostTask here; the messages would arrive out of
388 // order. Instead, we queue them up until we're ready to post
389 // them.
390 early_message_queue_.push_back(serialized_val);
391 } else {
392 // The proxy sent an asynchronous message, so the plugin is already
393 // unblocked. Therefore, there's no need to PostTask.
394 DCHECK(early_message_queue_.size() == 0);
395 PostMessageToJavaScriptImpl(serialized_val);
396 }
397 } else {
398 base::MessageLoop::current()->PostTask(
399 FROM_HERE,
400 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl,
401 weak_ptr_factory_.GetWeakPtr(),
402 serialized_val));
403 }
404 }
405
406 void MessageChannel::StopQueueingJavaScriptMessages() {
407 // We PostTask here instead of draining the message queue directly
408 // since we haven't finished initializing the WebPluginImpl yet, so
409 // the plugin isn't available in the DOM.
410 early_message_queue_state_ = DRAIN_PENDING;
411 base::MessageLoop::current()->PostTask(
412 FROM_HERE,
413 base::Bind(&MessageChannel::DrainEarlyMessageQueue,
414 weak_ptr_factory_.GetWeakPtr()));
415 }
416
417 void MessageChannel::QueueJavaScriptMessages() {
418 if (early_message_queue_state_ == DRAIN_PENDING)
419 early_message_queue_state_ = DRAIN_CANCELLED;
420 else
421 early_message_queue_state_ = QUEUE_MESSAGES;
422 }
423
424 void MessageChannel::DrainEarlyMessageQueue() {
425 // Take a reference on the PluginInstance. This is because JavaScript code
426 // may delete the plugin, which would destroy the PluginInstance and its
427 // corresponding MessageChannel.
428 scoped_refptr<PluginInstanceImpl> instance_ref(instance_);
429
430 if (early_message_queue_state_ == DRAIN_CANCELLED) {
431 early_message_queue_state_ = QUEUE_MESSAGES;
432 return;
433 }
434 DCHECK(early_message_queue_state_ == DRAIN_PENDING);
435
436 while (!early_message_queue_.empty()) {
437 PostMessageToJavaScriptImpl(early_message_queue_.front());
438 early_message_queue_.pop_front();
439 }
440 early_message_queue_state_ = SEND_DIRECTLY;
441 }
442
443 void MessageChannel::PostMessageToJavaScriptImpl(
444 const WebSerializedScriptValue& message_data) {
445 DCHECK(instance_);
446
447 WebPluginContainer* container = instance_->container();
448 // It's possible that container() is NULL if the plugin has been removed from
449 // the DOM (but the PluginInstance is not destroyed yet).
450 if (!container)
451 return;
452
453 WebDOMEvent event =
454 container->element().document().createEvent("MessageEvent");
455 WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>();
456 msg_event.initMessageEvent("message", // type
457 false, // canBubble
458 false, // cancelable
459 message_data, // data
460 "", // origin [*]
461 NULL, // source [*]
462 ""); // lastEventId
463 // [*] Note that the |origin| is only specified for cross-document and server-
464 // sent messages, while |source| is only specified for cross-document
465 // messages:
466 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
467 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari
468 // at least, postMessage on Workers does not provide the origin or source.
469 // TODO(dmichael): Add origin if we change to a more iframe-like origin
470 // policy (see crbug.com/81537)
471
472 container->element().dispatchEvent(msg_event);
473 }
474
475 void MessageChannel::PostMessageToNative(PP_Var message_data) {
476 if (instance_->module()->IsProxied()) {
477 // In the proxied case, the copy will happen via serializiation, and the
478 // message is asynchronous. Therefore there's no need to copy the Var, nor
479 // to PostTask.
480 PostMessageToNativeImpl(message_data);
481 } else {
482 // Make a copy of the message data for the Task we will run.
483 PP_Var var_copy(CopyPPVar(message_data));
484
485 base::MessageLoop::current()->PostTask(
486 FROM_HERE,
487 base::Bind(&MessageChannel::PostMessageToNativeImpl,
488 weak_ptr_factory_.GetWeakPtr(),
489 var_copy));
490 }
491 }
492
493 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) {
494 instance_->HandleMessage(message_data);
495 }
496
497 MessageChannel::~MessageChannel() {
498 WebBindings::releaseObject(np_object_);
499 if (passthrough_object_)
500 WebBindings::releaseObject(passthrough_object_);
501 }
502
503 void MessageChannel::SetPassthroughObject(NPObject* passthrough) {
504 // Retain the passthrough object; We need to ensure it lives as long as this
505 // MessageChannel.
506 if (passthrough)
507 WebBindings::retainObject(passthrough);
508
509 // If we had a passthrough set already, release it. Note that we retain the
510 // incoming passthrough object first, so that we behave correctly if anyone
511 // invokes:
512 // SetPassthroughObject(passthrough_object());
513 if (passthrough_object_)
514 WebBindings::releaseObject(passthrough_object_);
515
516 passthrough_object_ = passthrough;
517 }
518
519 } // namespace ppapi
520 } // namespace webkit
OLDNEW
« no previous file with comments | « webkit/plugins/ppapi/message_channel.h ('k') | webkit/plugins/ppapi/mock_platform_image_2d.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698