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