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