OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/plugins/ppapi/message_channel.h" | 5 #include "webkit/plugins/ppapi/message_channel.h" |
6 | 6 |
7 #include <cstdlib> | 7 #include <cstdlib> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
13 #include "ppapi/shared_impl/var.h" | |
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" | 14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" |
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | 15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMMessageEvent.h" | |
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | 17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" |
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" | |
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" | 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" |
18 #include "ppapi/shared_impl/var.h" | 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSerializedScriptVa lue.h" |
22 #include "v8/include/v8.h" | |
19 #include "webkit/plugins/ppapi/npapi_glue.h" | 23 #include "webkit/plugins/ppapi/npapi_glue.h" |
20 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | 24 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" |
21 | 25 |
22 using ppapi::StringVar; | 26 using ppapi::StringVar; |
23 using WebKit::WebBindings; | 27 using WebKit::WebBindings; |
28 using WebKit::WebDOMMessageEvent; | |
29 using WebKit::WebSerializedScriptValue; | |
24 | 30 |
25 namespace webkit { | 31 namespace webkit { |
26 | 32 |
27 namespace ppapi { | 33 namespace ppapi { |
28 | 34 |
29 namespace { | 35 namespace { |
30 | 36 |
31 const char kPostMessage[] = "postMessage"; | 37 const char kPostMessage[] = "postMessage"; |
32 | 38 |
33 // Helper function to get the MessageChannel that is associated with an | 39 // Helper function to get the MessageChannel that is associated with an |
34 // NPObject*. | 40 // NPObject*. |
35 MessageChannel& ToMessageChannel(NPObject* object) { | 41 MessageChannel& ToMessageChannel(NPObject* object) { |
36 return *(static_cast<MessageChannel::MessageChannelNPObject*>(object)-> | 42 return *(static_cast<MessageChannel::MessageChannelNPObject*>(object)-> |
37 message_channel); | 43 message_channel); |
38 } | 44 } |
39 | 45 |
40 // Helper function to determine if a given identifier is equal to kPostMessage. | 46 // Helper function to determine if a given identifier is equal to kPostMessage. |
41 bool IdentifierIsPostMessage(NPIdentifier identifier) { | 47 bool IdentifierIsPostMessage(NPIdentifier identifier) { |
42 return WebBindings::getStringIdentifier(kPostMessage) == identifier; | 48 return WebBindings::getStringIdentifier(kPostMessage) == identifier; |
43 } | 49 } |
44 | 50 |
45 // Converts the given PP_Var to an NPVariant, returning true on success. | 51 // Converts the given PP_Var to a v8::Value, returning true on success. |
46 // False means that the given variant is invalid. In this case, the result | 52 // False means that the given variant is invalid. In this case, |result| will |
47 // NPVariant will be set to a void one. | 53 // be set to an empty handle. |
48 // | 54 bool PPVarToV8Value(PP_Var var, v8::Handle<v8::Value>* result) { |
49 // The contents of the PP_Var will NOT be copied, so you need to ensure that | |
50 // the PP_Var remains valid while the resultant NPVariant is in use. | |
51 // | |
52 // Note: This is largely copied from var.cc so that we don't depend on code | |
53 // which will be removed. TODO(dmichael) remove this comment when var | |
54 // is removed. | |
55 bool PPVarToNPVariantNoCopy(PP_Var var, NPVariant* result) { | |
56 switch (var.type) { | 55 switch (var.type) { |
57 case PP_VARTYPE_UNDEFINED: | 56 case PP_VARTYPE_UNDEFINED: |
58 VOID_TO_NPVARIANT(*result); | 57 *result = v8::Undefined(); |
59 break; | 58 break; |
60 case PP_VARTYPE_NULL: | 59 case PP_VARTYPE_NULL: |
61 NULL_TO_NPVARIANT(*result); | 60 *result = v8::Null(); |
62 break; | 61 break; |
63 case PP_VARTYPE_BOOL: | 62 case PP_VARTYPE_BOOL: |
64 BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result); | 63 *result = (var.value.as_bool == PP_TRUE) ? v8::True() : v8::False(); |
65 break; | 64 break; |
66 case PP_VARTYPE_INT32: | 65 case PP_VARTYPE_INT32: |
67 INT32_TO_NPVARIANT(var.value.as_int, *result); | 66 *result = v8::Integer::New(var.value.as_int); |
68 break; | 67 break; |
69 case PP_VARTYPE_DOUBLE: | 68 case PP_VARTYPE_DOUBLE: |
70 DOUBLE_TO_NPVARIANT(var.value.as_double, *result); | 69 *result = v8::Number::New(var.value.as_double); |
71 break; | 70 break; |
72 case PP_VARTYPE_STRING: { | 71 case PP_VARTYPE_STRING: { |
73 StringVar* string = StringVar::FromPPVar(var); | 72 StringVar* string = StringVar::FromPPVar(var); |
74 if (!string) { | 73 if (!string) { |
75 VOID_TO_NPVARIANT(*result); | 74 result->Clear(); |
76 return false; | 75 return false; |
77 } | 76 } |
78 const std::string& value = string->value(); | 77 const std::string& value = string->value(); |
79 STRINGN_TO_NPVARIANT(value.c_str(), value.size(), *result); | 78 // TODO(dmichael): We should consider caching the V8 string in the host- |
79 // side StringVar, so that we only have to convert/copy once if a | |
80 // string is sent more than once. | |
81 *result = v8::String::New(value.c_str(), value.size()); | |
80 break; | 82 break; |
81 } | 83 } |
82 case PP_VARTYPE_OBJECT: | 84 case PP_VARTYPE_OBJECT: |
83 // Objects are not currently supported. | 85 // Objects are not currently supported. |
84 NOTIMPLEMENTED(); | 86 NOTIMPLEMENTED(); |
85 VOID_TO_NPVARIANT(*result); | 87 result->Clear(); |
86 return false; | 88 return false; |
87 default: | 89 default: |
88 VOID_TO_NPVARIANT(*result); | 90 result->Clear(); |
89 return false; | 91 return false; |
90 } | 92 } |
91 return true; | 93 return true; |
92 } | 94 } |
93 | 95 |
94 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage. | 96 // Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage. |
95 // This currently just copies the value. For a string Var, the result is a | 97 // This currently just copies the value. For a string Var, the result is a |
96 // PP_Var with the a copy of |var|'s string contents and a reference count of 1. | 98 // PP_Var with the a copy of |var|'s string contents and a reference count of 1. |
97 // | 99 // |
98 // TODO(dmichael): We need to do structured clone eventually to copy a object | 100 // TODO(dmichael): We need to do structured clone eventually to copy a object |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
279 : message_channel(NULL) { | 281 : message_channel(NULL) { |
280 } | 282 } |
281 | 283 |
282 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {} | 284 MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {} |
283 | 285 |
284 MessageChannel::MessageChannel(PluginInstance* instance) | 286 MessageChannel::MessageChannel(PluginInstance* instance) |
285 : instance_(instance), | 287 : instance_(instance), |
286 passthrough_object_(NULL), | 288 passthrough_object_(NULL), |
287 np_object_(NULL), | 289 np_object_(NULL), |
288 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | 290 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
289 VOID_TO_NPVARIANT(onmessage_invoker_); | |
290 | |
291 // Now create an NPObject for receiving calls to postMessage. This sets the | 291 // Now create an NPObject for receiving calls to postMessage. This sets the |
292 // reference count to 1. We release it in the destructor. | 292 // reference count to 1. We release it in the destructor. |
293 NPObject* obj = WebBindings::createObject(NULL, &message_channel_class); | 293 NPObject* obj = WebBindings::createObject(NULL, &message_channel_class); |
294 DCHECK(obj); | 294 DCHECK(obj); |
295 np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj); | 295 np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj); |
296 np_object_->message_channel = this; | 296 np_object_->message_channel = this; |
297 } | 297 } |
298 | 298 |
299 bool MessageChannel::EvaluateOnMessageInvoker() { | 299 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { |
300 // If we've already evaluated the function, just return. | 300 // Serialize the message data. |
301 if (NPVARIANT_IS_OBJECT(onmessage_invoker_)) | 301 v8::HandleScope scope; |
302 return true; | 302 v8::Handle<v8::Value> v8_val; |
303 if (!PPVarToV8Value(message_data, &v8_val)) { | |
304 NOTREACHED(); | |
305 return; | |
306 } | |
303 | 307 |
304 // This is the javascript code that we invoke to create and dispatch a | 308 WebSerializedScriptValue serialized_val = |
305 // message event. | 309 WebSerializedScriptValue::serialize(v8_val); |
306 const char invoke_onmessage_js[] = | |
307 "(function(window, module_instance, message_data) {" | |
308 " if (module_instance) {" | |
309 " var message_event = new MessageEvent('message', " | |
310 " { data: message_data });" | |
311 " module_instance.dispatchEvent(message_event);" | |
312 " }" | |
313 "})"; | |
314 // Note that we purposely omit |origin| and |source|. The |origin| is only | |
315 // specified for cross-document and server-sent messages, while |source| is | |
316 // only specified for cross-document messages: | |
317 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html | |
318 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari | |
319 // at least, postMessage on Workers does not provide the origin or source. | |
320 // TODO(dmichael): Add origin if we change to a more iframe-like origin | |
321 // policy (see crbug.com/81537) | |
322 | |
323 NPString function_string = { invoke_onmessage_js, | |
324 sizeof(invoke_onmessage_js)-1 }; | |
325 // Get the current frame to pass to the evaluate function. | |
326 WebKit::WebFrame* frame = | |
327 instance_->container()->element().document().frame(); | |
328 // Evaluate the function and obtain an NPVariant pointing to it. | |
329 if (!WebBindings::evaluate(NULL, frame->windowObject(), &function_string, | |
330 &onmessage_invoker_)) { | |
331 // If it fails, do nothing. | |
332 return false; | |
333 } | |
334 DCHECK(NPVARIANT_IS_OBJECT(onmessage_invoker_)); | |
335 return true; | |
336 } | |
337 | |
338 void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { | |
339 // Make a copy of the message data for the Task we will run. | |
340 PP_Var var_copy(CopyPPVar(message_data)); | |
341 | 310 |
342 MessageLoop::current()->PostTask( | 311 MessageLoop::current()->PostTask( |
343 FROM_HERE, | 312 FROM_HERE, |
344 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl, | 313 base::Bind(&MessageChannel::PostMessageToJavaScriptImpl, |
345 weak_ptr_factory_.GetWeakPtr(), | 314 weak_ptr_factory_.GetWeakPtr(), |
346 var_copy)); | 315 serialized_val)); |
347 } | 316 } |
348 | 317 |
349 void MessageChannel::PostMessageToJavaScriptImpl(PP_Var message_data) { | 318 void MessageChannel::PostMessageToJavaScriptImpl( |
350 // Make sure we have our function for invoking onmessage on JavaScript. | 319 const WebSerializedScriptValue& message_data) { |
351 bool success = EvaluateOnMessageInvoker(); | |
352 DCHECK(success); | |
353 if (!success) | |
354 return; | |
355 | |
356 DCHECK(instance_); | 320 DCHECK(instance_); |
357 | 321 |
358 NPVariant result_var; | 322 const WebKit::WebElement& element = instance_->container()->element(); |
359 VOID_TO_NPVARIANT(result_var); | |
360 NPVariant npvariant_args[3]; | |
361 // Get the frame so we can get the window object. | |
362 WebKit::WebFrame* frame = | |
363 instance_->container()->element().document().frame(); | |
364 if (!frame) | |
365 return; | |
366 | 323 |
367 OBJECT_TO_NPVARIANT(frame->windowObject(), npvariant_args[0]); | 324 WebKit::WebDOMEvent event = element.document().createEvent("MessageEvent"); |
darin (slow to review)
2011/11/16 22:14:11
nit: add a 'using WebKit::WebDOMEvent' to the top
dmichael (off chromium)
2011/11/16 23:17:31
Done.
| |
368 OBJECT_TO_NPVARIANT(instance_->container()->scriptableObjectForElement(), | 325 WebDOMMessageEvent& msg_event(static_cast<WebDOMMessageEvent&>(event)); |
369 npvariant_args[1]); | 326 msg_event.initMessageEvent("message", // type |
370 // Convert message to an NPVariant without copying. At this point, the data | 327 false, // canBubble |
371 // has already been copied. | 328 false, // cancelable |
372 if (!PPVarToNPVariantNoCopy(message_data, &npvariant_args[2])) { | 329 message_data, // data |
373 // We couldn't create an NPVariant, so we can't invoke the method. Thus, | 330 "", // origin [*] |
374 // WebBindings::invokeDefault does not take ownership of these variants, so | 331 NULL, // source [*] |
375 // we must release our references to them explicitly. | 332 ""); // lastEventId |
376 WebBindings::releaseVariantValue(&npvariant_args[0]); | 333 // [*] Note that the |origin| is only specified for cross-document and server- |
377 WebBindings::releaseVariantValue(&npvariant_args[1]); | 334 // sent messages, while |source| is only specified for cross-document |
378 return; | 335 // messages: |
379 } | 336 // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html |
337 // This currently behaves like Web Workers. On Firefox, Chrome, and Safari | |
338 // at least, postMessage on Workers does not provide the origin or source. | |
339 // TODO(dmichael): Add origin if we change to a more iframe-like origin | |
340 // policy (see crbug.com/81537) | |
380 | 341 |
381 WebBindings::invokeDefault(NULL, | 342 instance_->container()->element().dispatchEvent(msg_event); |
382 NPVARIANT_TO_OBJECT(onmessage_invoker_), | |
383 npvariant_args, | |
384 sizeof(npvariant_args)/sizeof(*npvariant_args), | |
385 &result_var); | |
386 } | 343 } |
387 | 344 |
388 void MessageChannel::PostMessageToNative(PP_Var message_data) { | 345 void MessageChannel::PostMessageToNative(PP_Var message_data) { |
389 // Make a copy of the message data for the Task we will run. | 346 // Make a copy of the message data for the Task we will run. |
390 PP_Var var_copy(CopyPPVar(message_data)); | 347 PP_Var var_copy(CopyPPVar(message_data)); |
391 | 348 |
392 MessageLoop::current()->PostTask(FROM_HERE, | 349 MessageLoop::current()->PostTask(FROM_HERE, |
393 base::Bind(&MessageChannel::PostMessageToNativeImpl, | 350 base::Bind(&MessageChannel::PostMessageToNativeImpl, |
394 weak_ptr_factory_.GetWeakPtr(), | 351 weak_ptr_factory_.GetWeakPtr(), |
395 var_copy)); | 352 var_copy)); |
396 } | 353 } |
397 | 354 |
398 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) { | 355 void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) { |
399 instance_->HandleMessage(message_data); | 356 instance_->HandleMessage(message_data); |
400 } | 357 } |
401 | 358 |
402 MessageChannel::~MessageChannel() { | 359 MessageChannel::~MessageChannel() { |
403 WebBindings::releaseObject(np_object_); | 360 WebBindings::releaseObject(np_object_); |
404 if (passthrough_object_) | 361 if (passthrough_object_) |
405 WebBindings::releaseObject(passthrough_object_); | 362 WebBindings::releaseObject(passthrough_object_); |
406 WebBindings::releaseVariantValue(&onmessage_invoker_); | |
407 } | 363 } |
408 | 364 |
409 void MessageChannel::SetPassthroughObject(NPObject* passthrough) { | 365 void MessageChannel::SetPassthroughObject(NPObject* passthrough) { |
410 // Retain the passthrough object; We need to ensure it lives as long as this | 366 // Retain the passthrough object; We need to ensure it lives as long as this |
411 // MessageChannel. | 367 // MessageChannel. |
412 WebBindings::retainObject(passthrough); | 368 WebBindings::retainObject(passthrough); |
413 | 369 |
414 // If we had a passthrough set already, release it. Note that we retain the | 370 // If we had a passthrough set already, release it. Note that we retain the |
415 // incoming passthrough object first, so that we behave correctly if anyone | 371 // incoming passthrough object first, so that we behave correctly if anyone |
416 // invokes: | 372 // invokes: |
417 // SetPassthroughObject(passthrough_object()); | 373 // SetPassthroughObject(passthrough_object()); |
418 if (passthrough_object_) | 374 if (passthrough_object_) |
419 WebBindings::releaseObject(passthrough_object_); | 375 WebBindings::releaseObject(passthrough_object_); |
420 | 376 |
421 passthrough_object_ = passthrough; | 377 passthrough_object_ = passthrough; |
422 } | 378 } |
423 | 379 |
424 } // namespace ppapi | 380 } // namespace ppapi |
425 } // namespace webkit | 381 } // namespace webkit |
426 | 382 |
OLD | NEW |