Index: content/renderer/pepper/message_channel.cc |
diff --git a/content/renderer/pepper/message_channel.cc b/content/renderer/pepper/message_channel.cc |
index 7a04d37964ad5f107812b7a56fa7fc1104cbad23..2fd4410bda3b738f41bb435499282e81584ceb77 100644 |
--- a/content/renderer/pepper/message_channel.cc |
+++ b/content/renderer/pepper/message_channel.cc |
@@ -45,6 +45,7 @@ namespace content { |
namespace { |
const char kPostMessage[] = "postMessage"; |
+const char kPostMessageAndAwaitResponse[] = "postMessageAndAwaitResponse"; |
const char kV8ToVarConversionError[] = |
"Failed to convert a PostMessage " |
"argument from a JavaScript value to a PP_Var. It may have cycles or be of " |
@@ -71,6 +72,14 @@ bool IdentifierIs(NPIdentifier identifier, const char string[]) { |
return WebBindings::getStringIdentifier(string) == identifier; |
} |
+bool HasDevChannelPermission(NPObject* channel_object) { |
+ MessageChannel* channel = ToMessageChannel(channel_object); |
+ if (!channel) |
+ return false; |
+ return channel->instance()->module()->permissions().HasPermission( |
+ ppapi::PERMISSION_DEV_CHANNEL); |
+} |
+ |
//------------------------------------------------------------------------------ |
// Implementations of NPClass functions. These are here to: |
// - Implement postMessage behavior. |
@@ -93,7 +102,10 @@ bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) { |
if (IdentifierIs(name, kPostMessage)) |
return true; |
- |
+ if (IdentifierIs(name, kPostMessageAndAwaitResponse) && |
+ HasDevChannelPermission(np_obj)) { |
+ return true; |
+ } |
// Other method names we will pass to the passthrough object, if we have one. |
NPObject* passthrough = ToPassThroughObject(np_obj); |
if (passthrough) |
@@ -113,12 +125,17 @@ bool MessageChannelInvoke(NPObject* np_obj, |
if (!message_channel) |
return false; |
- // Check to see if we should handle this function ourselves. We only handle |
- // kPostMessage. |
+ // Check to see if we should handle this function ourselves. |
if (IdentifierIs(name, kPostMessage) && (arg_count == 1)) { |
message_channel->PostMessageToNative(&args[0]); |
return true; |
+ } else if (IdentifierIs(name, kPostMessageAndAwaitResponse) && |
+ (arg_count == 1) && |
+ HasDevChannelPermission(np_obj)) { |
+ message_channel->PostBlockingMessageToNative(&args[0], result); |
+ return true; |
} |
+ |
// Other method calls we will pass to the passthrough object, if we have one. |
NPObject* passthrough = ToPassThroughObject(np_obj); |
if (passthrough) { |
@@ -167,10 +184,13 @@ bool MessageChannelGetProperty(NPObject* np_obj, |
if (!np_obj) |
return false; |
- // Don't allow getting the postMessage function. |
+ // Don't allow getting the postMessage functions. |
if (IdentifierIs(name, kPostMessage)) |
return false; |
- |
+ if (IdentifierIs(name, kPostMessageAndAwaitResponse) && |
+ HasDevChannelPermission(np_obj)) { |
+ return false; |
+ } |
MessageChannel* message_channel = ToMessageChannel(np_obj); |
if (message_channel) { |
if (message_channel->GetReadOnlyProperty(name, result)) |
@@ -190,10 +210,13 @@ bool MessageChannelSetProperty(NPObject* np_obj, |
if (!np_obj) |
return false; |
- // Don't allow setting the postMessage function. |
+ // Don't allow setting the postMessage functions. |
if (IdentifierIs(name, kPostMessage)) |
return false; |
- |
+ if (IdentifierIs(name, kPostMessageAndAwaitResponse) && |
+ HasDevChannelPermission(np_obj)) { |
+ return false; |
+ } |
// Invoke on the passthrough object, if we have one. |
NPObject* passthrough = ToPassThroughObject(np_obj); |
if (passthrough) |
@@ -447,6 +470,80 @@ void MessageChannel::PostMessageToNative(const NPVariant* message_data) { |
DrainCompletedPluginMessages(); |
} |
+void MessageChannel::PostBlockingMessageToNative(const NPVariant* message_data, |
+ NPVariant* np_result) { |
+ if (early_message_queue_state_ == QUEUE_MESSAGES) { |
+ WebBindings::setException( |
+ np_object_, |
+ "Attempted to call a synchronous method on a plugin that was not " |
+ "yet loaded."); |
+ return; |
+ } |
+ |
+ // If the queue of messages to the plugin is non-empty, we're still waiting on |
+ // pending Var conversions. This means at some point in the past, JavaScript |
+ // called postMessage (the async one) and passed us something with a browser- |
+ // side host (e.g., FileSystem) and we haven't gotten a response from the |
+ // browser yet. We can't currently support sending a sync message if the |
+ // plugin does this, because it will break the ordering of the messages |
+ // arriving at the plugin. |
+ // TODO(dmichael): Fix this. |
+ // See https://code.google.com/p/chromium/issues/detail?id=367896#c4 |
+ if (!plugin_message_queue_.empty()) { |
+ WebBindings::setException( |
+ np_object_, |
+ "Failed to convert parameter synchronously, because a prior " |
+ "call to postMessage contained a type which required asynchronous " |
+ "transfer which has not completed. Not all types are supported yet by " |
+ "postMessageAndAwaitResponse. See crbug.com/367896."); |
+ return; |
+ } |
+ ScopedPPVar param; |
+ if (message_data->type == NPVariantType_Object) { |
+ // Convert NPVariantType_Object in to an appropriate PP_Var like Dictionary, |
+ // Array, etc. Note NPVariantToVar would convert to an "Object" PP_Var, |
+ // which we don't support for Messaging. |
+ v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(message_data); |
+ V8VarConverter v8_var_converter(instance_->pp_instance()); |
+ bool success = v8_var_converter.FromV8ValueSync( |
+ v8_value, |
+ v8::Isolate::GetCurrent()->GetCurrentContext(), |
+ ¶m); |
+ if (!success) { |
+ WebBindings::setException( |
+ np_object_, |
+ "Failed to convert the given parameter to a PP_Var to send to " |
+ "the plugin."); |
+ return; |
+ } |
+ } else { |
+ param = ScopedPPVar(ScopedPPVar::PassRef(), |
+ NPVariantToPPVar(instance(), message_data)); |
+ } |
+ ScopedPPVar pp_result; |
+ bool was_handled = instance_->HandleBlockingMessage(param, &pp_result); |
+ if (!was_handled) { |
+ WebBindings::setException( |
+ np_object_, |
+ "The plugin has not registered a handler for synchronous messages. " |
+ "See the documentation for PPB_Messaging::RegisterMessageHandler " |
+ "and PPP_MessageHandler."); |
+ return; |
+ } |
+ v8::Handle<v8::Value> v8_val; |
+ if (!V8VarConverter(instance_->pp_instance()).ToV8Value( |
+ pp_result.get(), |
+ v8::Isolate::GetCurrent()->GetCurrentContext(), |
+ &v8_val)) { |
+ WebBindings::setException( |
+ np_object_, |
+ "Failed to convert the plugin's result to a JavaScript type."); |
+ return; |
+ } |
+ // Success! Convert the result to an NPVariant. |
+ WebBindings::toNPVariant(v8_val, NULL, np_result); |
+} |
+ |
MessageChannel::~MessageChannel() { |
WebBindings::releaseObject(np_object_); |
if (passthrough_object_) |