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