summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-18 16:44:00 +0000
committerdmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-18 16:44:00 +0000
commite87640bdf9afb28737f94c6784b7723d27b9dfdd (patch)
treedba77d110fa9b84c2ba1547a35208a11629b2e76
parent2073de75d29457e2e865bdbb87148e42b3ccf454 (diff)
downloadchromium_src-e87640bdf9afb28737f94c6784b7723d27b9dfdd.zip
chromium_src-e87640bdf9afb28737f94c6784b7723d27b9dfdd.tar.gz
chromium_src-e87640bdf9afb28737f94c6784b7723d27b9dfdd.tar.bz2
PPAPI: Implement synchronous postMessage
BUG=367896 Review URL: https://codereview.chromium.org/264303002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278102 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/test/ppapi/ppapi_browsertest.cc18
-rw-r--r--content/renderer/pepper/message_channel.cc111
-rw-r--r--content/renderer/pepper/message_channel.h4
-rw-r--r--content/renderer/pepper/pepper_plugin_instance_impl.cc30
-rw-r--r--content/renderer/pepper/pepper_plugin_instance_impl.h6
-rw-r--r--content/renderer/pepper/v8_var_converter.cc12
-rw-r--r--content/renderer/pepper/v8_var_converter.h3
-rw-r--r--content/test/ppapi/ppapi_browsertest.cc2
-rw-r--r--ppapi/api/ppb_messaging.idl12
-rw-r--r--ppapi/api/ppp_message_handler.idl2
-rw-r--r--ppapi/c/ppb_messaging.h14
-rw-r--r--ppapi/c/ppp_message_handler.h4
-rw-r--r--ppapi/ppapi_proxy.gypi2
-rw-r--r--ppapi/ppapi_sources.gypi2
-rw-r--r--ppapi/proxy/message_handler.cc134
-rw-r--r--ppapi/proxy/message_handler.h75
-rw-r--r--ppapi/proxy/plugin_dispatcher.h5
-rw-r--r--ppapi/proxy/ppapi_messages.h7
-rw-r--r--ppapi/proxy/ppb_instance_proxy.cc21
-rw-r--r--ppapi/proxy/ppb_message_loop_proxy.h4
-rw-r--r--ppapi/proxy/ppp_messaging_proxy.cc85
-rw-r--r--ppapi/proxy/ppp_messaging_proxy.h3
-rw-r--r--ppapi/tests/test_case.html36
-rw-r--r--ppapi/tests/test_message_handler.cc297
-rw-r--r--ppapi/tests/test_message_handler.h34
-rw-r--r--ppapi/thunk/interfaces_ppb_public_dev_channel.h1
-rw-r--r--tools/metrics/histograms/histograms.xml1
27 files changed, 895 insertions, 30 deletions
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index d23323b..d153b06 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -62,11 +62,25 @@ using content::RenderViewHost;
#if defined(DISABLE_NACL)
#define TEST_PPAPI_NACL(test_name)
+#define TEST_PPAPI_NACL_NO_PNACL(test_name)
#define TEST_PPAPI_NACL_DISALLOWED_SOCKETS(test_name)
#define TEST_PPAPI_NACL_WITH_SSL_SERVER(test_name)
#else
+// TODO(dmichael): Remove this macro, crbug.com/384539
+#define TEST_PPAPI_NACL_NO_PNACL(test_name) \
+ IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, test_name) { \
+ RunTestViaHTTP(STRIP_PREFIXES(test_name)); \
+ } \
+ IN_PROC_BROWSER_TEST_F(PPAPINaClGLibcTest, MAYBE_GLIBC(test_name)) { \
+ RunTestViaHTTP(STRIP_PREFIXES(test_name)); \
+ } \
+ IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClNonSfiTest, \
+ MAYBE_PNACL_NONSFI(test_name)) { \
+ RunTestViaHTTP(STRIP_PREFIXES(test_name)); \
+ }
+
// NaCl based PPAPI tests
#define TEST_PPAPI_NACL(test_name) \
IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, test_name) { \
@@ -1219,6 +1233,10 @@ TEST_PPAPI_NACL(VideoSource)
// Printing doesn't work in content_browsertests.
TEST_PPAPI_OUT_OF_PROCESS(Printing)
+// TODO(dmichael): Make this work on PNaCl and remove the macro.
+// crbug.com/384539
+TEST_PPAPI_NACL_NO_PNACL(MessageHandler)
+
TEST_PPAPI_NACL(MessageLoop_Basics)
TEST_PPAPI_NACL(MessageLoop_Post)
diff --git a/content/renderer/pepper/message_channel.cc b/content/renderer/pepper/message_channel.cc
index 7a04d37..2fd4410 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(),
+ &param);
+ 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_)
diff --git a/content/renderer/pepper/message_channel.h b/content/renderer/pepper/message_channel.h
index 077a68d..8c744a0 100644
--- a/content/renderer/pepper/message_channel.h
+++ b/content/renderer/pepper/message_channel.h
@@ -60,6 +60,10 @@ class MessageChannel {
// Post a message to the plugin's HandleMessage function for this channel's
// instance.
void PostMessageToNative(const NPVariant* message_data);
+ // Post a message to the plugin's HandleBlocking Message function for this
+ // channel's instance synchronously, and return a result.
+ void PostBlockingMessageToNative(const NPVariant* message_data,
+ NPVariant* np_result);
// Return the NPObject* to which we should forward any calls which aren't
// related to postMessage. Note that this can be NULL; it only gets set if
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 6e7ff9f..a26b13b 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1168,8 +1168,8 @@ void PepperPluginInstanceImpl::HandleMessage(ScopedPPVar message) {
ppapi::proxy::HostDispatcher* dispatcher =
ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
if (!dispatcher || (message.get().type == PP_VARTYPE_OBJECT)) {
- // The dispatcher should always be valid, and the browser should never send
- // an 'object' var over PPP_Messaging.
+ // The dispatcher should always be valid, and MessageChannel should never
+ // send an 'object' var over PPP_Messaging.
NOTREACHED();
return;
}
@@ -1180,6 +1180,32 @@ void PepperPluginInstanceImpl::HandleMessage(ScopedPPVar message) {
pp_instance())));
}
+bool PepperPluginInstanceImpl::HandleBlockingMessage(ScopedPPVar message,
+ ScopedPPVar* result) {
+ TRACE_EVENT0("ppapi", "PepperPluginInstanceImpl::HandleBlockingMessage");
+ ppapi::proxy::HostDispatcher* dispatcher =
+ ppapi::proxy::HostDispatcher::GetForInstance(pp_instance());
+ if (!dispatcher || (message.get().type == PP_VARTYPE_OBJECT)) {
+ // The dispatcher should always be valid, and MessageChannel should never
+ // send an 'object' var over PPP_Messaging.
+ NOTREACHED();
+ return false;
+ }
+ ppapi::proxy::ReceiveSerializedVarReturnValue msg_reply;
+ bool was_handled = false;
+ dispatcher->Send(new PpapiMsg_PPPMessageHandler_HandleBlockingMessage(
+ ppapi::API_ID_PPP_MESSAGING,
+ pp_instance(),
+ ppapi::proxy::SerializedVarSendInputShmem(dispatcher, message.get(),
+ pp_instance()),
+ &msg_reply,
+ &was_handled));
+ *result = ScopedPPVar(ScopedPPVar::PassRef(), msg_reply.Return(dispatcher));
+ TRACE_EVENT0("ppapi",
+ "PepperPluginInstanceImpl::HandleBlockingMessage return.");
+ return was_handled;
+}
+
PP_Var PepperPluginInstanceImpl::GetInstanceObject() {
// Keep a reference on the stack. See NOTE above.
scoped_refptr<PepperPluginInstanceImpl> ref(this);
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index 5fb61d8..ea21418 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -303,6 +303,12 @@ class CONTENT_EXPORT PepperPluginInstanceImpl
// Send the message on to the plugin.
void HandleMessage(ppapi::ScopedPPVar message);
+ // Send the message synchronously to the plugin, and get a result. Returns
+ // true if the plugin handled the message, false if it didn't. The plugin
+ // won't handle the message if it has not registered a PPP_MessageHandler.
+ bool HandleBlockingMessage(ppapi::ScopedPPVar message,
+ ppapi::ScopedPPVar* result);
+
// Returns true if the plugin is processing a user gesture.
bool IsProcessingUserGesture();
diff --git a/content/renderer/pepper/v8_var_converter.cc b/content/renderer/pepper/v8_var_converter.cc
index b64fe8e..b09fccd 100644
--- a/content/renderer/pepper/v8_var_converter.cc
+++ b/content/renderer/pepper/v8_var_converter.cc
@@ -411,6 +411,18 @@ V8VarConverter::VarResult V8VarConverter::FromV8Value(
return result;
}
+bool V8VarConverter::FromV8ValueSync(
+ v8::Handle<v8::Value> val,
+ v8::Handle<v8::Context> context,
+ ppapi::ScopedPPVar* result_var) {
+ bool success = FromV8ValueInternal(val, context, result_var);
+ if (!success || resource_converter_->NeedsFlush()) {
+ resource_converter_->Reset();
+ return false;
+ }
+ return true;
+}
+
bool V8VarConverter::FromV8ValueInternal(
v8::Handle<v8::Value> val,
v8::Handle<v8::Context> context,
diff --git a/content/renderer/pepper/v8_var_converter.h b/content/renderer/pepper/v8_var_converter.h
index 97adc33..42ef7a6 100644
--- a/content/renderer/pepper/v8_var_converter.h
+++ b/content/renderer/pepper/v8_var_converter.h
@@ -62,6 +62,9 @@ class CONTENT_EXPORT V8VarConverter {
v8::Handle<v8::Value> val,
v8::Handle<v8::Context> context,
const base::Callback<void(const ppapi::ScopedPPVar&, bool)>& callback);
+ bool FromV8ValueSync(v8::Handle<v8::Value> val,
+ v8::Handle<v8::Context> context,
+ ppapi::ScopedPPVar* result_var);
private:
// Returns true on success, false on failure.
bool FromV8ValueInternal(v8::Handle<v8::Value> val,
diff --git a/content/test/ppapi/ppapi_browsertest.cc b/content/test/ppapi/ppapi_browsertest.cc
index 95947be..704e704 100644
--- a/content/test/ppapi/ppapi_browsertest.cc
+++ b/content/test/ppapi/ppapi_browsertest.cc
@@ -108,6 +108,8 @@ TEST_PPAPI_OUT_OF_PROCESS(MediaStreamVideoTrack)
TEST_PPAPI_IN_PROCESS(Memory)
TEST_PPAPI_OUT_OF_PROCESS(Memory)
+TEST_PPAPI_OUT_OF_PROCESS(MessageHandler)
+
TEST_PPAPI_OUT_OF_PROCESS(MessageLoop_Basics)
TEST_PPAPI_OUT_OF_PROCESS(MessageLoop_Post)
diff --git a/ppapi/api/ppb_messaging.idl b/ppapi/api/ppb_messaging.idl
index dc50788..5100f5f 100644
--- a/ppapi/api/ppb_messaging.idl
+++ b/ppapi/api/ppb_messaging.idl
@@ -86,9 +86,6 @@ interface PPB_Messaging {
void PostMessage([in] PP_Instance instance, [in] PP_Var message);
/**
- * <strong>Note:</strong> This function is not yet implemented. Please use
- * PPB_Messaging_1_0.
- *
* Registers a handler for receiving messages from JavaScript. If a handler
* is registered this way, it will replace PPP_Messaging, and all messages
* sent from JavaScript via postMessage and postMessageAndAwaitResponse will
@@ -99,6 +96,12 @@ interface PPB_Messaging {
* <code>message_loop</code> is attached, when <code>message_loop</code> is
* run. It is illegal to pass the main thread message loop;
* RegisterMessageHandler will return PP_ERROR_WRONG_THREAD in that case.
+ * If you quit <code>message_loop</code> before calling Unregister(),
+ * the browser will not be able to call functions in the plugin's message
+ * handler any more. That could mean missing some messages or could cause a
+ * leak if you depend on Destroy() to free hander data. So you should,
+ * whenever possible, Unregister() the handler prior to quitting its event
+ * loop.
*
* Attempting to register a message handler when one is already registered
* will cause the current MessageHandler to be unregistered and replaced. In
@@ -123,9 +126,6 @@ interface PPB_Messaging {
[in] PPP_MessageHandler handler,
[in] PP_Resource message_loop);
/**
- * <strong>Note:</strong> This function is not yet implemented. Please use
- * PPB_Messaging_1_0.
- *
* Unregisters the current message handler for <code>instance</code> if one
* is registered. After this call, the message handler (if one was
* registered) will have "Destroy" called on it and will receive no further
diff --git a/ppapi/api/ppp_message_handler.idl b/ppapi/api/ppp_message_handler.idl
index d08afcf..675b1a3 100644
--- a/ppapi/api/ppp_message_handler.idl
+++ b/ppapi/api/ppp_message_handler.idl
@@ -37,7 +37,7 @@ interface PPP_MessageHandler {
* postMessage().
*/
void HandleMessage([in] PP_Instance instance,
- [in] mem_t user_data,
+ [inout] mem_t user_data,
[in] PP_Var message);
/**
* Invoked as a result of JavaScript invoking postMessageAndAwaitResponse()
diff --git a/ppapi/c/ppb_messaging.h b/ppapi/c/ppb_messaging.h
index 21c8467..c361d14 100644
--- a/ppapi/c/ppb_messaging.h
+++ b/ppapi/c/ppb_messaging.h
@@ -3,7 +3,7 @@
* found in the LICENSE file.
*/
-/* From ppb_messaging.idl modified Mon Jun 2 11:00:28 2014. */
+/* From ppb_messaging.idl modified Fri Jun 13 15:28:26 2014. */
#ifndef PPAPI_C_PPB_MESSAGING_H_
#define PPAPI_C_PPB_MESSAGING_H_
@@ -100,9 +100,6 @@ struct PPB_Messaging_1_1 { /* dev */
*/
void (*PostMessage)(PP_Instance instance, struct PP_Var message);
/**
- * <strong>Note:</strong> This function is not yet implemented. Please use
- * PPB_Messaging_1_0.
- *
* Registers a handler for receiving messages from JavaScript. If a handler
* is registered this way, it will replace PPP_Messaging, and all messages
* sent from JavaScript via postMessage and postMessageAndAwaitResponse will
@@ -113,6 +110,12 @@ struct PPB_Messaging_1_1 { /* dev */
* <code>message_loop</code> is attached, when <code>message_loop</code> is
* run. It is illegal to pass the main thread message loop;
* RegisterMessageHandler will return PP_ERROR_WRONG_THREAD in that case.
+ * If you quit <code>message_loop</code> before calling Unregister(),
+ * the browser will not be able to call functions in the plugin's message
+ * handler any more. That could mean missing some messages or could cause a
+ * leak if you depend on Destroy() to free hander data. So you should,
+ * whenever possible, Unregister() the handler prior to quitting its event
+ * loop.
*
* Attempting to register a message handler when one is already registered
* will cause the current MessageHandler to be unregistered and replaced. In
@@ -137,9 +140,6 @@ struct PPB_Messaging_1_1 { /* dev */
const struct PPP_MessageHandler_0_1* handler,
PP_Resource message_loop);
/**
- * <strong>Note:</strong> This function is not yet implemented. Please use
- * PPB_Messaging_1_0.
- *
* Unregisters the current message handler for <code>instance</code> if one
* is registered. After this call, the message handler (if one was
* registered) will have "Destroy" called on it and will receive no further
diff --git a/ppapi/c/ppp_message_handler.h b/ppapi/c/ppp_message_handler.h
index 26cd5e0..8032378 100644
--- a/ppapi/c/ppp_message_handler.h
+++ b/ppapi/c/ppp_message_handler.h
@@ -3,7 +3,7 @@
* found in the LICENSE file.
*/
-/* From ppp_message_handler.idl modified Fri May 30 15:49:17 2014. */
+/* From ppp_message_handler.idl modified Tue Jun 3 16:50:26 2014. */
#ifndef PPAPI_C_PPP_MESSAGE_HANDLER_H_
#define PPAPI_C_PPP_MESSAGE_HANDLER_H_
@@ -49,7 +49,7 @@ struct PPP_MessageHandler_0_1 {
* postMessage().
*/
void (*HandleMessage)(PP_Instance instance,
- const void* user_data,
+ void* user_data,
struct PP_Var message);
/**
* Invoked as a result of JavaScript invoking postMessageAndAwaitResponse()
diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi
index 117ede6..c775386 100644
--- a/ppapi/ppapi_proxy.gypi
+++ b/ppapi/ppapi_proxy.gypi
@@ -94,6 +94,8 @@
'proxy/media_stream_track_resource_base.h',
'proxy/media_stream_video_track_resource.cc',
'proxy/media_stream_video_track_resource.h',
+ 'proxy/message_handler.cc',
+ 'proxy/message_handler.h',
'proxy/net_address_resource.cc',
'proxy/net_address_resource.h',
'proxy/network_list_resource.cc',
diff --git a/ppapi/ppapi_sources.gypi b/ppapi/ppapi_sources.gypi
index 50e84c3..8c0c531 100644
--- a/ppapi/ppapi_sources.gypi
+++ b/ppapi/ppapi_sources.gypi
@@ -446,6 +446,8 @@
'tests/test_media_stream_video_track.h',
'tests/test_memory.cc',
'tests/test_memory.h',
+ 'tests/test_message_handler.cc',
+ 'tests/test_message_handler.h',
'tests/test_message_loop.cc',
'tests/test_message_loop.h',
'tests/test_mouse_cursor.cc',
diff --git a/ppapi/proxy/message_handler.cc b/ppapi/proxy/message_handler.cc
new file mode 100644
index 0000000..6e44d0e
--- /dev/null
+++ b/ppapi/proxy/message_handler.cc
@@ -0,0 +1,134 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ppapi/proxy/message_handler.h"
+
+#include "ipc/ipc_message.h"
+#include "ppapi/proxy/plugin_dispatcher.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/ppb_message_loop_proxy.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/scoped_pp_var.h"
+#include "ppapi/thunk/enter.h"
+
+namespace ppapi {
+namespace proxy {
+namespace {
+
+typedef void (*HandleMessageFunc)(PP_Instance, void*, PP_Var);
+typedef PP_Var (*HandleBlockingMessageFunc)(PP_Instance, void*, PP_Var);
+
+void HandleMessageWrapper(HandleMessageFunc function,
+ PP_Instance instance,
+ void* user_data,
+ ScopedPPVar message_data) {
+ CallWhileUnlocked(function, instance, user_data, message_data.get());
+}
+
+void HandleBlockingMessageWrapper(HandleBlockingMessageFunc function,
+ PP_Instance instance,
+ void* user_data,
+ ScopedPPVar message_data,
+ scoped_ptr<IPC::Message> reply_msg) {
+ PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
+ if (!dispatcher)
+ return;
+ PP_Var return_value = CallWhileUnlocked(function,
+ instance,
+ user_data,
+ message_data.get());
+ PpapiMsg_PPPMessageHandler_HandleBlockingMessage::WriteReplyParams(
+ reply_msg.get(),
+ SerializedVarReturnValue::Convert(dispatcher, return_value),
+ true /* was_handled */);
+ dispatcher->Send(reply_msg.release());
+}
+
+} // namespace
+
+// static
+scoped_ptr<MessageHandler> MessageHandler::Create(
+ PP_Instance instance,
+ const PPP_MessageHandler_0_1* handler_if,
+ void* user_data,
+ PP_Resource message_loop,
+ int32_t* error) {
+ scoped_ptr<MessageHandler> result;
+ // The interface and all function pointers must be valid.
+ if (!handler_if ||
+ !handler_if->HandleMessage ||
+ !handler_if->HandleBlockingMessage ||
+ !handler_if->Destroy) {
+ *error = PP_ERROR_BADARGUMENT;
+ return result.Pass();
+ }
+ thunk::EnterResourceNoLock<thunk::PPB_MessageLoop_API>
+ enter_loop(message_loop, true);
+ if (enter_loop.failed()) {
+ *error = PP_ERROR_BADRESOURCE;
+ return result.Pass();
+ }
+ scoped_refptr<MessageLoopResource> message_loop_resource(
+ static_cast<MessageLoopResource*>(enter_loop.object()));
+ if (message_loop_resource->is_main_thread_loop()) {
+ *error = PP_ERROR_WRONG_THREAD;
+ return result.Pass();
+ }
+
+ result.reset(new MessageHandler(
+ instance, handler_if, user_data, message_loop_resource));
+ *error = PP_OK;
+ return result.Pass();
+}
+
+MessageHandler::~MessageHandler() {
+ // It's possible the message_loop_proxy is NULL if that loop has been quit.
+ // In that case, we unfortunately just can't call Destroy.
+ if (message_loop_->message_loop_proxy()) {
+ // The posted task won't have the proxy lock, but that's OK, it doesn't
+ // touch any internal state; it's a direct call on the plugin's function.
+ message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
+ base::Bind(handler_if_->Destroy,
+ instance_,
+ user_data_));
+ }
+}
+
+bool MessageHandler::LoopIsValid() const {
+ return !!message_loop_->message_loop_proxy();
+}
+
+void MessageHandler::HandleMessage(ScopedPPVar var) {
+ message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
+ RunWhileLocked(base::Bind(&HandleMessageWrapper,
+ handler_if_->HandleMessage,
+ instance_,
+ user_data_,
+ var)));
+}
+
+void MessageHandler::HandleBlockingMessage(ScopedPPVar var,
+ scoped_ptr<IPC::Message> reply_msg) {
+ message_loop_->message_loop_proxy()->PostTask(FROM_HERE,
+ RunWhileLocked(base::Bind(&HandleBlockingMessageWrapper,
+ handler_if_->HandleBlockingMessage,
+ instance_,
+ user_data_,
+ var,
+ base::Passed(reply_msg.Pass()))));
+}
+
+MessageHandler::MessageHandler(
+ PP_Instance instance,
+ const PPP_MessageHandler_0_1* handler_if,
+ void* user_data,
+ scoped_refptr<MessageLoopResource> message_loop)
+ : instance_(instance),
+ handler_if_(handler_if),
+ user_data_(user_data),
+ message_loop_(message_loop) {
+}
+
+} // namespace proxy
+} // namespace ppapi
diff --git a/ppapi/proxy/message_handler.h b/ppapi/proxy/message_handler.h
new file mode 100644
index 0000000..61ee639
--- /dev/null
+++ b/ppapi/proxy/message_handler.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PPAPI_PROXY_MESSAGE_HANDLER_H_
+#define PPAPI_PROXY_MESSAGE_HANDLER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "ppapi/c/pp_resource.h"
+#include "ppapi/c/ppp_message_handler.h"
+#include "ppapi/proxy/ppapi_proxy_export.h"
+
+namespace IPC {
+class Message;
+}
+
+namespace ppapi {
+
+class ScopedPPVar;
+
+namespace proxy {
+
+class MessageLoopResource;
+
+// MessageHandler wraps a PPP_MessageHandler to encapsulate calling methods
+// on the right thread and calling the Destroy function when this
+// MessageHandler is destroyed.
+class PPAPI_PROXY_EXPORT MessageHandler {
+ public:
+ // Create a MessageHandler. If any parameters are invalid, it will return a
+ // null scoped_ptr and set |*error| appropriately.
+ // |handler_if| is the struct of function pointers we will invoke. All of
+ // the function pointers within must be valid, or we fail
+ // with PP_ERROR_BADARGUMENT.
+ // |user_data| is a pointer provided by the plugin that we pass back when we
+ // call functions in |handler_if|.
+ // |message_loop| is the message loop where we will invoke functions in
+ // |handler_if|. Must not be the main thread message loop,
+ // to try to force the plugin to not over-subscribe the main
+ // thread. If it's the main thread loop, |error| will be set
+ // to PP_ERROR_WRONGTHREAD.
+ // |error| is an out-param that will be set on failure.
+ static scoped_ptr<MessageHandler> Create(
+ PP_Instance instance,
+ const PPP_MessageHandler_0_1* handler_if,
+ void* user_data,
+ PP_Resource message_loop,
+ int32_t* error);
+ ~MessageHandler();
+
+ bool LoopIsValid() const;
+
+ void HandleMessage(ScopedPPVar var);
+ void HandleBlockingMessage(ScopedPPVar var,
+ scoped_ptr<IPC::Message> reply_msg);
+
+ private:
+ MessageHandler(PP_Instance instance,
+ const PPP_MessageHandler_0_1* handler_if,
+ void* user_data,
+ scoped_refptr<MessageLoopResource> message_loop);
+
+ PP_Instance instance_;
+ const PPP_MessageHandler_0_1* handler_if_;
+ void* user_data_;
+ scoped_refptr<MessageLoopResource> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageHandler);
+};
+
+} // namespace proxy
+} // namespace ppapi
+
+#endif // PPAPI_PROXY_MESSAGE_HANDLER_H_
diff --git a/ppapi/proxy/plugin_dispatcher.h b/ppapi/proxy/plugin_dispatcher.h
index d071e61..f8e22f6 100644
--- a/ppapi/proxy/plugin_dispatcher.h
+++ b/ppapi/proxy/plugin_dispatcher.h
@@ -20,6 +20,7 @@
#include "ppapi/c/pp_rect.h"
#include "ppapi/c/ppb_console.h"
#include "ppapi/proxy/dispatcher.h"
+#include "ppapi/proxy/message_handler.h"
#include "ppapi/shared_impl/ppapi_preferences.h"
#include "ppapi/shared_impl/ppb_view_shared.h"
#include "ppapi/shared_impl/singleton_resource_id.h"
@@ -62,6 +63,10 @@ struct InstanceData {
// calling when we shouldn't).
bool is_request_surrounding_text_pending;
bool should_do_request_surrounding_text;
+
+ // The message handler which should handle JavaScript->Plugin messages, if
+ // one has been registered, otherwise NULL.
+ scoped_ptr<MessageHandler> message_handler;
};
class PPAPI_PROXY_EXPORT PluginDispatcher
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 255cc9c..24e993a 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -676,10 +676,15 @@ IPC_MESSAGE_ROUTED3(PpapiMsg_PPPInstance_HandleDocumentLoad,
int /* pending_loader_host_id */,
ppapi::URLResponseInfoData /* response */)
-// PPP_Messaging.
+// PPP_Messaging and PPP_MessageHandler.
IPC_MESSAGE_ROUTED2(PpapiMsg_PPPMessaging_HandleMessage,
PP_Instance /* instance */,
ppapi::proxy::SerializedVar /* message */)
+IPC_SYNC_MESSAGE_ROUTED2_2(PpapiMsg_PPPMessageHandler_HandleBlockingMessage,
+ PP_Instance /* instance */,
+ ppapi::proxy::SerializedVar /* message */,
+ ppapi::proxy::SerializedVar /* result */,
+ bool /* was_handled */);
// PPP_MouseLock.
IPC_MESSAGE_ROUTED1(PpapiMsg_PPPMouseLock_MouseLockLost,
diff --git a/ppapi/proxy/ppb_instance_proxy.cc b/ppapi/proxy/ppb_instance_proxy.cc
index 2b1a988..4f64e91 100644
--- a/ppapi/proxy/ppb_instance_proxy.cc
+++ b/ppapi/proxy/ppb_instance_proxy.cc
@@ -26,6 +26,7 @@
#include "ppapi/proxy/gamepad_resource.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/isolated_file_system_private_resource.h"
+#include "ppapi/proxy/message_handler.h"
#include "ppapi/proxy/network_proxy_resource.h"
#include "ppapi/proxy/pdf_resource.h"
#include "ppapi/proxy/plugin_dispatcher.h"
@@ -773,17 +774,31 @@ void PPB_Instance_Proxy::PostMessage(PP_Instance instance,
instance, SerializedVarSendInputShmem(dispatcher(), message,
instance)));
}
+
int32_t PPB_Instance_Proxy::RegisterMessageHandler(
PP_Instance instance,
void* user_data,
const PPP_MessageHandler_0_1* handler,
PP_Resource message_loop) {
- // Not yet implemented. See crbug.com/367896
- return PP_ERROR_NOTSUPPORTED;
+ InstanceData* data =
+ static_cast<PluginDispatcher*>(dispatcher())->GetInstanceData(instance);
+ if (!data)
+ return PP_ERROR_BADARGUMENT;
+
+ int32_t result = PP_ERROR_FAILED;
+ scoped_ptr<MessageHandler> message_handler = MessageHandler::Create(
+ instance, handler, user_data, message_loop, &result);
+ if (message_handler)
+ data->message_handler = message_handler.Pass();
+ return result;
}
void PPB_Instance_Proxy::UnregisterMessageHandler(PP_Instance instance) {
- // Not yet implemented. See crbug.com/367896
+ InstanceData* data =
+ static_cast<PluginDispatcher*>(dispatcher())->GetInstanceData(instance);
+ if (!data)
+ return;
+ data->message_handler.reset();
}
PP_Bool PPB_Instance_Proxy::SetCursor(PP_Instance instance,
diff --git a/ppapi/proxy/ppb_message_loop_proxy.h b/ppapi/proxy/ppb_message_loop_proxy.h
index d8bfc4c..f6cc252 100644
--- a/ppapi/proxy/ppb_message_loop_proxy.h
+++ b/ppapi/proxy/ppb_message_loop_proxy.h
@@ -44,6 +44,10 @@ class PPAPI_PROXY_EXPORT MessageLoopResource : public MessageLoopShared {
return is_main_thread_loop_;
}
+ const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy() {
+ return loop_proxy_;
+ }
+
private:
struct TaskInfo {
tracked_objects::Location from_here;
diff --git a/ppapi/proxy/ppp_messaging_proxy.cc b/ppapi/proxy/ppp_messaging_proxy.cc
index ba83ca7..75614ae 100644
--- a/ppapi/proxy/ppp_messaging_proxy.cc
+++ b/ppapi/proxy/ppp_messaging_proxy.cc
@@ -8,17 +8,53 @@
#include "ppapi/c/ppp_messaging.h"
#include "ppapi/proxy/host_dispatcher.h"
+#include "ppapi/proxy/message_handler.h"
+#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_resource_tracker.h"
#include "ppapi/proxy/plugin_var_tracker.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/serialized_var.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/scoped_pp_var.h"
#include "ppapi/shared_impl/var_tracker.h"
namespace ppapi {
namespace proxy {
+namespace {
+
+MessageHandler* GetMessageHandler(Dispatcher* dispatcher,
+ PP_Instance instance) {
+ if (!dispatcher || !dispatcher->IsPlugin()) {
+ NOTREACHED();
+ return NULL;
+ }
+ PluginDispatcher* plugin_dispatcher =
+ static_cast<PluginDispatcher*>(dispatcher);
+ InstanceData* instance_data = plugin_dispatcher->GetInstanceData(instance);
+ if (!instance_data)
+ return NULL;
+
+ return instance_data->message_handler.get();
+}
+
+void ResetMessageHandler(Dispatcher* dispatcher, PP_Instance instance) {
+ if (!dispatcher || !dispatcher->IsPlugin()) {
+ NOTREACHED();
+ return;
+ }
+ PluginDispatcher* plugin_dispatcher =
+ static_cast<PluginDispatcher*>(dispatcher);
+ InstanceData* instance_data = plugin_dispatcher->GetInstanceData(instance);
+ if (!instance_data)
+ return;
+
+ instance_data->message_handler.reset();
+}
+
+} // namespace
+
PPP_Messaging_Proxy::PPP_Messaging_Proxy(Dispatcher* dispatcher)
: InterfaceProxy(dispatcher),
ppp_messaging_impl_(NULL) {
@@ -39,6 +75,9 @@ bool PPP_Messaging_Proxy::OnMessageReceived(const IPC::Message& msg) {
IPC_BEGIN_MESSAGE_MAP(PPP_Messaging_Proxy, msg)
IPC_MESSAGE_HANDLER(PpapiMsg_PPPMessaging_HandleMessage,
OnMsgHandleMessage)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(
+ PpapiMsg_PPPMessageHandler_HandleBlockingMessage,
+ OnMsgHandleBlockingMessage)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -47,13 +86,57 @@ bool PPP_Messaging_Proxy::OnMessageReceived(const IPC::Message& msg) {
void PPP_Messaging_Proxy::OnMsgHandleMessage(
PP_Instance instance, SerializedVarReceiveInput message_data) {
PP_Var received_var(message_data.GetForInstance(dispatcher(), instance));
+ MessageHandler* message_handler = GetMessageHandler(dispatcher(), instance);
+ if (message_handler) {
+ if (message_handler->LoopIsValid()) {
+ message_handler->HandleMessage(ScopedPPVar(received_var));
+ return;
+ } else {
+ // If the MessageHandler's loop has been quit, then we should treat it as
+ // though it has been unregistered and start sending messages to the
+ // default handler. This might mean the plugin has lost messages, but
+ // there's not really anything sane we can do about it. They should have
+ // used UnregisterMessageHandler.
+ ResetMessageHandler(dispatcher(), instance);
+ }
+ }
+ // If we reach this point, then there's no message handler registered, so
+ // we send to the default PPP_Messaging one for the instance.
+
// SerializedVarReceiveInput will decrement the reference count, but we want
- // to give the recipient a reference.
+ // to give the recipient a reference in the legacy API.
PpapiGlobals::Get()->GetVarTracker()->AddRefVar(received_var);
CallWhileUnlocked(ppp_messaging_impl_->HandleMessage,
instance,
received_var);
}
+void PPP_Messaging_Proxy::OnMsgHandleBlockingMessage(
+ PP_Instance instance,
+ SerializedVarReceiveInput message_data,
+ IPC::Message* reply_msg) {
+ ScopedPPVar received_var(message_data.GetForInstance(dispatcher(), instance));
+ MessageHandler* message_handler = GetMessageHandler(dispatcher(), instance);
+ if (message_handler) {
+ if (message_handler->LoopIsValid()) {
+ message_handler->HandleBlockingMessage(
+ received_var, scoped_ptr<IPC::Message>(reply_msg));
+ return;
+ } else {
+ // If the MessageHandler's loop has been quit, then we should treat it as
+ // though it has been unregistered. Also see the note for PostMessage.
+ ResetMessageHandler(dispatcher(), instance);
+ }
+ }
+ // We have no handler, but we still need to respond to unblock the renderer
+ // and inform the JavaScript caller.
+ PpapiMsg_PPPMessageHandler_HandleBlockingMessage::WriteReplyParams(
+ reply_msg,
+ SerializedVarReturnValue::Convert(dispatcher(), PP_MakeUndefined()),
+ false /* was_handled */);
+ dispatcher()->Send(reply_msg);
+}
+
+
} // namespace proxy
} // namespace ppapi
diff --git a/ppapi/proxy/ppp_messaging_proxy.h b/ppapi/proxy/ppp_messaging_proxy.h
index 210574a..d9dde3c 100644
--- a/ppapi/proxy/ppp_messaging_proxy.h
+++ b/ppapi/proxy/ppp_messaging_proxy.h
@@ -27,6 +27,9 @@ class PPP_Messaging_Proxy : public InterfaceProxy {
// Message handlers.
void OnMsgHandleMessage(PP_Instance instance,
SerializedVarReceiveInput data);
+ void OnMsgHandleBlockingMessage(PP_Instance instance,
+ SerializedVarReceiveInput data,
+ IPC::Message* reply);
// When this proxy is in the plugin side, this value caches the interface
// pointer so we don't have to retrieve it from the dispatcher each time.
diff --git a/ppapi/tests/test_case.html b/ppapi/tests/test_case.html
index 987c102..f0992ec 100644
--- a/ppapi/tests/test_case.html
+++ b/ppapi/tests/test_case.html
@@ -3,6 +3,42 @@
<meta http-equiv="Expires" content="-1" />
<link rel="stylesheet" href="test_page.css">
<script>
+// Do a deep comparison of two values. Return true if their values are
+// identical, false otherwise.
+function deepCompare(left, right) {
+ if (typeof(left) !== typeof(right))
+ return false;
+ // If their identity is the same or they're basic types with the same value,
+ // they are equal.
+ if (left === right)
+ return true;
+ // If it's a basic type and we got here, we know they're not equal.
+ if (["undefined", "boolean", "number", "string", "function"].indexOf(
+ typeof(left)) > -1) {
+ return false;
+ }
+ // Use right_keys as a set containing all keys from |right| which we haven't
+ // yet compared.
+ var right_keys = {};
+ for (var key in right)
+ right_keys[key] = true;
+ for (var key in left) {
+ if (key in right_keys) {
+ if (!deepCompare(left[key], right[key]))
+ return false;
+ } else {
+ // |left| had a key that |right| didn't.
+ return false;
+ }
+ delete right_keys[key];
+ }
+ // If there are keys left in |right_keys|, it means they didn't exist in
+ // |left|, so the objects aren't equal.
+ if (Object.keys(right_keys).length > 0)
+ return false;
+ return true;
+}
+
function AdjustHeight(frameWin) {
var div = frameWin.document.getElementsByTagName("div")[0];
var height = frameWin.getComputedStyle(div).height;
diff --git a/ppapi/tests/test_message_handler.cc b/ppapi/tests/test_message_handler.cc
new file mode 100644
index 0000000..54474f7
--- /dev/null
+++ b/ppapi/tests/test_message_handler.cc
@@ -0,0 +1,297 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ppapi/tests/test_message_handler.h"
+
+#include <string.h>
+#include <algorithm>
+#include <map>
+#include <sstream>
+
+#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppb_file_io.h"
+#include "ppapi/c/ppp_message_handler.h"
+#include "ppapi/cpp/file_io.h"
+#include "ppapi/cpp/file_ref.h"
+#include "ppapi/cpp/file_system.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module_impl.h"
+#include "ppapi/cpp/var.h"
+#include "ppapi/cpp/var_array.h"
+#include "ppapi/cpp/var_array_buffer.h"
+#include "ppapi/cpp/var_dictionary.h"
+#include "ppapi/tests/pp_thread.h"
+#include "ppapi/tests/test_utils.h"
+#include "ppapi/tests/testing_instance.h"
+
+// Windows defines 'PostMessage', so we have to undef it.
+#ifdef PostMessage
+#undef PostMessage
+#endif
+
+REGISTER_TEST_CASE(MessageHandler);
+
+namespace {
+
+// Created and destroyed on the main thread. All public methods should be called
+// on the main thread. Most data members are only accessed on the main thread.
+// (Though it handles messages on the background thread).
+class EchoingMessageHandler {
+ public:
+ explicit EchoingMessageHandler(PP_Instance instance,
+ const pp::MessageLoop& loop)
+ : pp_instance_(instance),
+ message_handler_loop_(loop),
+ ppb_messaging_if_(static_cast<const PPB_Messaging_1_1*>(
+ pp::Module::Get()->GetBrowserInterface(
+ PPB_MESSAGING_INTERFACE_1_1))),
+ ppp_message_handler_if_(),
+ is_registered_(false),
+ test_finished_event_(instance),
+ destroy_event_(instance) {
+ AssertOnMainThread();
+ ppp_message_handler_if_.HandleMessage = &HandleMessage;
+ ppp_message_handler_if_.HandleBlockingMessage = &HandleBlockingMessage;
+ ppp_message_handler_if_.Destroy = &Destroy;
+ }
+ void Register() {
+ AssertOnMainThread();
+ assert(!is_registered_);
+ int32_t result = ppb_messaging_if_->RegisterMessageHandler(
+ pp_instance_,
+ this,
+ &ppp_message_handler_if_,
+ message_handler_loop_.pp_resource());
+ if (result == PP_OK) {
+ is_registered_ = true;
+ } else {
+ std::ostringstream stream;
+ stream << "Failed to register message handler; got error " << result;
+ AddError(stream.str());
+ test_finished_event_.Signal();
+ }
+ // Note, at this point, we can't safely read or write errors_ until we wait
+ // on destroy_event_.
+ }
+ void Unregister() {
+ AssertOnMainThread();
+ assert(is_registered_);
+ ppb_messaging_if_->UnregisterMessageHandler(pp_instance_);
+ is_registered_ = false;
+ }
+ void WaitForTestFinishedMessage() {
+ test_finished_event_.Wait();
+ test_finished_event_.Reset();
+ }
+ // Wait for Destroy() to be called on the MessageHandler thread. When it's
+ // done, return any errors that occurred during the time the MessageHandler
+ // was getting messages.
+ std::string WaitForDestroy() {
+ AssertOnMainThread();
+ // If we haven't called Unregister, we'll be waiting forever.
+ assert(!is_registered_);
+ destroy_event_.Wait();
+ destroy_event_.Reset();
+ // Now that we know Destroy() has been called, we know errors_ isn't being
+ // written on the MessageHandler thread anymore. So we can safely read it
+ // here on the main thread (since destroy_event_ gave us a memory barrier).
+ std::string temp_errors;
+ errors_.swap(temp_errors);
+ return temp_errors;
+ }
+ private:
+ static void AssertOnMainThread() {
+ assert(pp::MessageLoop::GetForMainThread() ==
+ pp::MessageLoop::GetCurrent());
+ }
+ void AddError(const std::string& error) {
+ if (!error.empty()) {
+ if (!errors_.empty())
+ errors_ += "<p>";
+ errors_ += error;
+ }
+ }
+ static void HandleMessage(PP_Instance instance,
+ void* user_data,
+ struct PP_Var message_data) {
+ EchoingMessageHandler* thiz =
+ static_cast<EchoingMessageHandler*>(user_data);
+ if (pp::MessageLoop::GetCurrent() != thiz->message_handler_loop_)
+ thiz->AddError("HandleMessage was called on the wrong thread!");
+ if (instance != thiz->pp_instance_)
+ thiz->AddError("HandleMessage was passed the wrong instance!");
+ pp::Var var(message_data);
+ if (var.is_string() && var.AsString() == "FINISHED_TEST")
+ thiz->test_finished_event_.Signal();
+ else
+ thiz->ppb_messaging_if_->PostMessage(instance, message_data);
+ }
+
+ static PP_Var HandleBlockingMessage(PP_Instance instance,
+ void* user_data,
+ struct PP_Var message_data) {
+ EchoingMessageHandler* thiz =
+ static_cast<EchoingMessageHandler*>(user_data);
+ if (pp::MessageLoop::GetCurrent() != thiz->message_handler_loop_)
+ thiz->AddError("HandleBlockingMessage was called on the wrong thread!");
+ if (instance != thiz->pp_instance_)
+ thiz->AddError("HandleBlockingMessage was passed the wrong instance!");
+
+ // The PP_Var we are passed is an in-parameter, so the browser is not
+ // giving us a ref-count. The ref-count it has will be decremented after we
+ // return. But we need to add a ref when returning a PP_Var, to pass to the
+ // caller.
+ pp::Var take_ref(message_data);
+ take_ref.Detach();
+ return message_data;
+ }
+
+ static void Destroy(PP_Instance instance, void* user_data) {
+ EchoingMessageHandler* thiz =
+ static_cast<EchoingMessageHandler*>(user_data);
+ if (pp::MessageLoop::GetCurrent() != thiz->message_handler_loop_)
+ thiz->AddError("Destroy was called on the wrong thread!");
+ if (instance != thiz->pp_instance_)
+ thiz->AddError("Destroy was passed the wrong instance!");
+ thiz->destroy_event_.Signal();
+ }
+
+ // These data members are initialized on the main thread, but don't change for
+ // the life of the object, so are safe to access on the background thread,
+ // because there will be a memory barrier before the the MessageHandler calls
+ // are invoked.
+ const PP_Instance pp_instance_;
+ const pp::MessageLoop message_handler_loop_;
+ const pp::MessageLoop main_loop_;
+ const PPB_Messaging_1_1* const ppb_messaging_if_;
+ // Spiritually, this member is const, but we can't initialize it in C++03,
+ // so it has to be non-const to be set in the constructor body.
+ PPP_MessageHandler_0_1 ppp_message_handler_if_;
+
+ // is_registered_ is only read/written on the main thread.
+ bool is_registered_;
+
+ // errors_ is written on the MessageHandler thread. When Destroy() is
+ // called, we stop writing to errors_ and signal destroy_event_. This causes
+ // a memory barrier, so it's safe to read errors_ after that.
+ std::string errors_;
+ NestedEvent test_finished_event_;
+ NestedEvent destroy_event_;
+
+ // Undefined & private to disallow copy and assign.
+ EchoingMessageHandler(const EchoingMessageHandler&);
+ EchoingMessageHandler& operator=(const EchoingMessageHandler&);
+};
+
+void FakeHandleMessage(PP_Instance instance,
+ void* user_data,
+ struct PP_Var message_data) {}
+PP_Var FakeHandleBlockingMessage(PP_Instance instance,
+ void* user_data,
+ struct PP_Var message_data) {
+ return PP_MakeUndefined();
+}
+void FakeDestroy(PP_Instance instance, void* user_data) {}
+
+} // namespace
+
+TestMessageHandler::TestMessageHandler(TestingInstance* instance)
+ : TestCase(instance),
+ ppb_messaging_if_(NULL),
+ handler_thread_(instance) {
+}
+
+TestMessageHandler::~TestMessageHandler() {
+ handler_thread_.Join();
+}
+
+bool TestMessageHandler::Init() {
+ ppb_messaging_if_ = static_cast<const PPB_Messaging_1_1*>(
+ pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_1));
+ return ppb_messaging_if_ &&
+ CheckTestingInterface() &&
+ handler_thread_.Start();
+}
+
+void TestMessageHandler::RunTests(const std::string& filter) {
+ RUN_TEST(RegisterErrorConditions, filter);
+ RUN_TEST(PostMessageAndAwaitResponse, filter);
+}
+
+void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
+ // All messages should go to the background thread message handler.
+ assert(false);
+}
+
+std::string TestMessageHandler::TestRegisterErrorConditions() {
+ {
+ // Test registering with the main thread as the message loop.
+ PPP_MessageHandler_0_1 fake_ppp_message_handler = {
+ &FakeHandleMessage, &FakeHandleBlockingMessage, &FakeDestroy
+ };
+ pp::MessageLoop main_loop = pp::MessageLoop::GetForMainThread();
+ int32_t result = ppb_messaging_if_->RegisterMessageHandler(
+ instance()->pp_instance(),
+ reinterpret_cast<void*>(0xdeadbeef),
+ &fake_ppp_message_handler,
+ main_loop.pp_resource());
+ ASSERT_EQ(PP_ERROR_WRONG_THREAD, result);
+ }
+ {
+ // Test registering with incomplete PPP_Messaging interface.
+ PPP_MessageHandler_0_1 bad_ppp_ifs[] = {
+ { NULL, &FakeHandleBlockingMessage, &FakeDestroy },
+ { &FakeHandleMessage, NULL, &FakeDestroy },
+ { &FakeHandleMessage, &FakeHandleBlockingMessage, NULL }};
+ for (size_t i = 0; i < sizeof(bad_ppp_ifs)/sizeof(bad_ppp_ifs[0]); ++i) {
+ int32_t result = ppb_messaging_if_->RegisterMessageHandler(
+ instance()->pp_instance(),
+ reinterpret_cast<void*>(0xdeadbeef),
+ &bad_ppp_ifs[i],
+ handler_thread_.message_loop().pp_resource());
+ ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
+ }
+ }
+ PASS();
+}
+
+std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
+ EchoingMessageHandler handler(instance()->pp_instance(),
+ handler_thread_.message_loop());
+ handler.Register();
+ std::string js_code("var plugin = document.getElementById('plugin');\n");
+ js_code += "var result = undefined;\n";
+ const char* const values_to_test[] = {
+ "5",
+ "undefined",
+ "1.5",
+ "'hello'",
+ "{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
+ NULL
+ };
+ for (size_t i = 0; values_to_test[i]; ++i) {
+ js_code += "result = plugin.postMessageAndAwaitResponse(";
+ js_code += values_to_test[i];
+ js_code += ");\n";
+ js_code += "if (!deepCompare(result, ";
+ js_code += values_to_test[i];
+ js_code += "))\n";
+ js_code += " InternalError(\" Failed postMessageAndAwaitResponse for: ";
+ js_code += values_to_test[i];
+ js_code += " result: \" + result);\n";
+ }
+ // TODO(dmichael): Setting a property uses GetInstanceObject, which sends sync
+ // message, which can get interrupted with message to eval script, etc.
+ // FINISHED_WAITING message can therefore jump ahead. This test is
+ // currently carefully crafted to avoid races by doing all the JS in one call.
+ // That should be fixed before this API goes to stable. See crbug.com/384528
+ js_code += "plugin.postMessage('FINISHED_TEST');\n";
+ instance_->EvalScript(js_code);
+ handler.WaitForTestFinishedMessage();
+ handler.Unregister();
+ ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
+
+ PASS();
+}
+
diff --git a/ppapi/tests/test_message_handler.h b/ppapi/tests/test_message_handler.h
new file mode 100644
index 0000000..560a1ff
--- /dev/null
+++ b/ppapi/tests/test_message_handler.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PPAPI_TESTS_TEST_MESSAGE_HANDLER_H_
+#define PPAPI_TESTS_TEST_MESSAGE_HANDLER_H_
+
+#include <string>
+#include <vector>
+
+#include "ppapi/c/ppb_messaging.h"
+#include "ppapi/tests/test_case.h"
+#include "ppapi/utility/threading/simple_thread.h"
+
+class TestMessageHandler : public TestCase {
+ public:
+ explicit TestMessageHandler(TestingInstance* instance);
+ virtual ~TestMessageHandler();
+
+ private:
+ // TestCase implementation.
+ virtual bool Init();
+ virtual void RunTests(const std::string& filter);
+ virtual void HandleMessage(const pp::Var& message_data);
+
+ std::string TestRegisterErrorConditions();
+ std::string TestPostMessageAndAwaitResponse();
+
+ const PPB_Messaging_1_1* ppb_messaging_if_;
+ pp::SimpleThread handler_thread_;
+};
+
+#endif // PPAPI_TESTS_TEST_MESSAGE_HANDLER_H_
+
diff --git a/ppapi/thunk/interfaces_ppb_public_dev_channel.h b/ppapi/thunk/interfaces_ppb_public_dev_channel.h
index f3ee665..f08470e 100644
--- a/ppapi/thunk/interfaces_ppb_public_dev_channel.h
+++ b/ppapi/thunk/interfaces_ppb_public_dev_channel.h
@@ -11,6 +11,7 @@
PROXIED_IFACE(PPB_COMPOSITOR_INTERFACE_0_1, PPB_Compositor_0_1)
PROXIED_IFACE(PPB_COMPOSITORLAYER_INTERFACE_0_1, PPB_CompositorLayer_0_1)
PROXIED_IFACE(PPB_FILEMAPPING_INTERFACE_0_1, PPB_FileMapping_0_1)
+PROXIED_IFACE(PPB_MESSAGING_INTERFACE_1_1, PPB_Messaging_1_1)
PROXIED_IFACE(PPB_VIDEODECODER_INTERFACE_0_1, PPB_VideoDecoder_0_1)
#include "ppapi/thunk/interfaces_postamble.h"
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2a77de3..bba0069 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -41970,6 +41970,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="1981643755" label="PPB_FileMapping;0.1"/>
<int value="1994108724" label="PPB_Flash_File_FileRef;2"/>
<int value="1998274350" label="PPB_Font(Dev);0.6"/>
+ <int value="2001322203" label="PPB_Messaging;1.1"/>
<int value="2003778556" label="PPB_MouseInputEvent;1.1"/>
<int value="2005291722" label="PPB_NetAddress_Private;1.1"/>
<int value="2012645499" label="PPB_Find(Dev);0.3"/>