0

A proposal and implementation for an initial postMessage interface. These interfaces will allow JavaScript to send data asynchronously to a module instance, and the module instance to asynchronously send data to a JavaScript message handler.

Note, I did something differently from other per-instance interfaces.  While the C interface has 'PPB_Messaging' and 'PPP_Messaging' separate from the Instance interfaces, I stuck the per-instance messaging in to pp::Instance.  It seems more intuitive to me, and doesn't have the drawbacks of having too many functions in the C layer instance interfaces.  Happy to back off of that position, but it's worth a shot.

Overview:
  From JavaScript, you can invoke 'postMessage' on the embedded module.  That results in a call to 'PPP_Messaging::HandleMessage'.

  From Native Code, you can invoke 'PPB_Messaging::PostMessage', which results
  in a call to an 'onmessage' function on the DOM element for the module
  instance in the JavaScript code (if one has been registered).

  Please see the included example or the examples in the comments of
  PPB_Messaging and PPP_Messaging.

Restrictions:
- This implementation is synchronous. A later CL will make it asynchronous.
- This implementation supports only intrinsic values and strings (all types that PP_Var supports except for objects). Object & array support will come later.
- This implementation only allows for 1 channel per instance. You can not expose other 'channels' or 'ports'.  Future CLs will add support for MessagePorts.

BUG=None
TEST=test_post_message.h/.cc

(This CL replaces http://codereview.chromium.org/6538028/ )
Review URL: http://codereview.chromium.org/6716005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79178 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
dmichael@google.com
2011-03-23 21:07:15 +00:00
parent 2752bf6a31
commit 9888f134c6
24 changed files with 1212 additions and 19 deletions

@ -0,0 +1,82 @@
/* Copyright (c) 2011 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_C_DEV_PPB_MESSAGING_DEV_H_
#define PPAPI_C_DEV_PPB_MESSAGING_DEV_H_
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_var.h"
#define PPB_MESSAGING_DEV_INTERFACE "PPB_Messaging(Dev);0.1"
/**
* @file
* This file defines the PPB_Messaging_Dev interface implemented by the browser.
* The PPB_Messaging_Dev interface contains pointers to functions related to
* sending messages to the JavaScript onmessage handler on the DOM element
* associated with a specific module instance.
*
* @addtogroup Interfaces
* @{
*/
/**
* The PPB_Messaging_Dev interface contains pointers to functions related to
* sending messages to the JavaScript onmessage handler on the DOM element
* associated with a specific module instance.
*/
struct PPB_Messaging_Dev {
/**
* @a PostMessage is a pointer to a function which asynchronously invokes the
* onmessage handler on the DOM element for the given module instance, if one
* exists. This means that a call to @a PostMessage will not block while the
* message is processed.
*
* @param message is a PP_Var containing the data to be sent to JavaScript.
* Currently, it can have an int32_t, double, bool, or string value (objects
* are not supported.)
*
* The onmessage handler in JavaScript code will receive an object conforming
* to the MessageEvent interface. In particular, the value of @a message will
* be contained as a property called @a data in the received MessageEvent.
* This is analogous to listening for messages from Web Workers.
*
* See:
* http://www.whatwg.org/specs/web-workers/current-work/
*
* For example:
*
* @verbatim
*
* <body>
* <object id="plugin"
* type="application/x-ppapi-postMessage-example"/>
* <script type="text/javascript">
* document.getElementById('plugin').onmessage = function(message) {
* alert(message.data);
* }
* </script>
* </body>
*
* @endverbatim
*
* If the module instance then invokes @a PostMessage() as follows:
* <code>
* char hello_world[] = "Hello world!";
* PP_Var hello_var = ppb_var_if->VarFromUtf8(instance,
* hello_world,
* sizeof(hello_world));
* ppb_messaging_if->PostMessage(instance, hello_var);
* </code>
*
* The browser will pop-up an alert saying "Hello world!".
*/
void (*PostMessage)(PP_Instance instance, struct PP_Var message);
};
/**
* @}
*/
#endif /* PPAPI_C_DEV_PPB_MESSAGING_DEV_H_ */

@ -0,0 +1,63 @@
/* Copyright (c) 2011 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_C_DEV_PPP_MESSAGING_DEV_H_
#define PPAPI_C_DEV_PPP_MESSAGING_DEV_H_
#include "ppapi/c/pp_instance.h"
struct PP_Var;
#define PPP_MESSAGING_DEV_INTERFACE "PPP_Messaging_Dev;0.1"
/**
* @file
* This file defines the PPP_Messaging_Dev structure - a series of pointers to
* methods that you must implement if you wish to handle messages posted to the
* module instance via calls to postMessage on the associated DOM element.
*
*/
/** @addtogroup Interfaces
* @{
*/
/**
* The PPP_Messaging_Dev interface contains pointers to a series of functions
* that you must implement if you wish to handle messages posted to the module
* instance via calls to postMessage on the associated DOM element.
*/
struct PPP_Messaging_Dev {
/**
* HandleMessage is a pointer to a function that the browser will call when
* @a postMessage() is invoked on the DOM element for the module instance in
* JavaScript. Note that @a postMessage() in the JavaScript interface is
* asynchronous, meaning JavaScript execution will not be blocked while
* @a HandleMessage() is processing the given @a message.
*
* For example:
*
* @verbatim
*
* <body>
* <object id="plugin"
* type="application/x-ppapi-postMessage-example"/>
* <script type="text/javascript">
* document.getElementById('plugin').postMessage("Hello world!");
* </script>
* </body>
*
* @endverbatim
*
* This will result in @a HandleMessage being invoked, passing the module
* instance on which it was invoked, with @a message being a string PP_Var
* containing "Hello world!".
*/
void (*HandleMessage)(PP_Instance instance, struct PP_Var message);
};
/**
* @}
*/
#endif /* PPAPI_C_DEV_PPP_MESSAGING_DEV_H_ */

@ -4,6 +4,7 @@
#include "ppapi/cpp/instance.h"
#include "ppapi/c/dev/ppb_messaging_dev.h"
#include "ppapi/c/dev/ppp_printing_dev.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/cpp/common.h"
@ -25,6 +26,10 @@ template <> const char* interface_name<PPB_Instance>() {
return PPB_INSTANCE_INTERFACE;
}
template <> const char* interface_name<PPB_Messaging_Dev>() {
return PPB_MESSAGING_DEV_INTERFACE;
}
} // namespace
Instance::Instance(PP_Instance instance) : pp_instance_(instance) {
@ -61,6 +66,10 @@ bool Instance::HandleInputEvent(const PP_InputEvent& /*event*/) {
return false;
}
void Instance::HandleMessage(const Var& /*message_data*/) {
return;
}
Var Instance::GetInstanceObject() {
return Var();
}
@ -115,6 +124,13 @@ Var Instance::ExecuteScript(const Var& script, Var* exception) {
Var::OutException(exception).get()));
}
void Instance::PostMessage(const Var& message) {
if (!has_interface<PPB_Messaging_Dev>())
return;
get_interface<PPB_Messaging_Dev>()->PostMessage(pp_instance(),
message.pp_var());
}
void Instance::AddPerInstanceObject(const std::string& interface_name,
void* object) {
// Ensure we're not trying to register more than one object per interface

@ -96,6 +96,16 @@ class Instance {
/** See PPB_Instance.ExecuteScript. */
Var ExecuteScript(const Var& script, Var* exception = NULL);
// These functions use the PPP_Messaging and PPB_Messaging interfaces, so that
// messaging can be done conveniently for a pp::Instance without using a
// separate C++ class.
/** See PPP_Messaging.HandleMessage. */
virtual void HandleMessage(const Var& message_data);
/** See PPB_Messaging.PostMessage. */
void PostMessage(const Var& message);
// @}
/**

@ -25,6 +25,7 @@
#include <string.h>
#include "ppapi/c/dev/ppp_messaging_dev.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppp_instance.h"
@ -134,6 +135,20 @@ static PPP_Instance instance_interface = {
&Instance_GetInstanceObject
};
void Messaging_HandleMessage(PP_Instance pp_instance, PP_Var var) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return;
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
if (!instance)
return;
instance->HandleMessage(Var(Var::PassRef(), var));
}
static PPP_Messaging_Dev instance_messaging_interface = {
&Messaging_HandleMessage
};
// Module ----------------------------------------------------------------------
Module::Module() : pp_module_(0), get_browser_interface_(NULL), core_(NULL) {
@ -152,6 +167,9 @@ const void* Module::GetPluginInterface(const char* interface_name) {
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
return &instance_interface;
if (strcmp(interface_name, PPP_MESSAGING_DEV_INTERFACE) == 0)
return &instance_messaging_interface;
// Now see if anything was dynamically registered.
InterfaceMap::const_iterator found = additional_interfaces_.find(
std::string(interface_name));

@ -0,0 +1,57 @@
// Copyright (c) 2011 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 <algorithm>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
// This is a simple C++ Pepper plugin that demonstrates HandleMessage and
// PostMessage.
// This object represents one time the page says <embed>.
class MyInstance : public pp::Instance {
public:
explicit MyInstance(PP_Instance instance) : pp::Instance(instance) {}
virtual ~MyInstance() {}
virtual void HandleMessage(const pp::Var& message_data);
};
// HandleMessage gets invoked when postMessage is called on the DOM element
// associated with this plugin instance.
// In this case, if we are given a string, we'll post a message back to
// JavaScript indicating whether or not that string is a palindrome.
void MyInstance::HandleMessage(const pp::Var& message_data) {
if (message_data.is_string()) {
std::string string_copy(message_data.AsString());
std::reverse(string_copy.begin(), string_copy.end());
bool is_palindrome(message_data.AsString() == string_copy);
PostMessage(pp::Var(is_palindrome));
}
}
// This object is the global object representing this plugin library as long
// as it is loaded.
class MyModule : public pp::Module {
public:
MyModule() : pp::Module() {}
virtual ~MyModule() {}
// Override CreateInstance to create your customized Instance object.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new MyInstance(instance);
}
};
namespace pp {
// Factory function for your specialization of the Module object.
Module* CreateModule() {
return new MyModule();
}
} // namespace pp

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<!--
Copyright (c) 2011 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.
-->
<head>
<title>postMessage Example</title>
</head>
<body>
<script type="text/javascript">
function SendString() {
plugin = document.getElementById('plugin');
// If we haven't already done it, set up an 'onmessage' function. This will
// get invoked whenever the plugin calls Instance::PostMessage in C++ (or
// PPB_Messaging::PostMessage in C). In this case, we're expecting a bool to
// tell us whether the string we passed was a palindrome.
if (!plugin.onmessage) {
plugin.onmessage = function(message_event) {
if (message_event.data) {
alert("The string was a palindrome.");
} else {
alert("The string was not a palindrome.");
}
}
}
var inputBox = document.getElementById("inputBox");
// Send the string to the plugin using postMessage. This results in a call
// to Instance::HandleMessage in C++ (or PPP_Messaging::HandleMessage in C).
plugin.postMessage(inputBox.value);
}
</script>
<input type="text" id="inputBox" name="inputBox" value="ablewasiereisawelba"/>
<p>
<button onclick='SendString()'>Is Palindrome</button>
<object id="plugin" type="application/x-ppapi-post-message-example"
width="0" height="0"/>
<hr>
</body>
</html>

@ -0,0 +1,74 @@
// Copyright (c) 2011 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 <algorithm>
#include <sstream>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
// This is a simple C++ Pepper plugin that demonstrates HandleMessage and
// PostMessage.
// This object represents one time the page says <embed>.
class MyInstance : public pp::Instance {
public:
explicit MyInstance(PP_Instance instance) : pp::Instance(instance) {}
virtual ~MyInstance() {}
virtual void HandleMessage(const pp::Var& message_data);
};
// HandleMessage gets invoked when postMessage is called on the DOM element
// associated with this plugin instance.
// In this case, if we are given a string, we'll post a message back to
// JavaScript indicating whether or not that string is a palindrome.
void MyInstance::HandleMessage(const pp::Var& message_data) {
if (message_data.is_string()) {
std::string string_copy(message_data.AsString());
std::istringstream str_stream(string_copy);
std::string id;
std::string input_string;
// Tokenize the string to get the id and the input_string. If we find both,
// post a message back to JavaScript indicating whether the given string is
// a palindrome.
if (std::getline(str_stream, id, ',') &&
std::getline(str_stream, input_string, ',')) {
std::string reversed_string(input_string);
std::reverse(reversed_string.begin(), reversed_string.end());
bool is_palindrome(input_string == reversed_string);
// Create a result string of the form "<id>,<result>", where <id> is the
// id we were given, and <result> is true if the given string was a
// palindrome, false otherwise.
std::string result(id);
result += ",";
result += is_palindrome ? "true" : "false";
// Send this result back to JS.
PostMessage(pp::Var(result));
}
}
}
// This object is the global object representing this plugin library as long
// as it is loaded.
class MyModule : public pp::Module {
public:
MyModule() : pp::Module() {}
virtual ~MyModule() {}
// Override CreateInstance to create your customized Instance object.
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new MyInstance(instance);
}
};
namespace pp {
// Factory function for your specialization of the Module object.
Module* CreateModule() {
return new MyModule();
}
} // namespace pp

@ -0,0 +1,48 @@
<body>
<script type="text/javascript">
var id_string_dictionary = new Object;
var last_id = 0;
function SendString() {
plugin = document.getElementById('plugin');
// If we haven't already done it, set up an 'onmessage' function. This will
// get invoked whenever the plugin calls Instance::PostMessage in C++ (or
// PPB_Instance::PostMessage in C). In this case, we're expecting a bool to
// tell us whether the string we passed was a palindrome.
if (!plugin.onmessage) {
plugin.onmessage = function(message_event) {
var id_bool_pair = message_event.data.split(",");
var sent_string = id_string_dictionary[id_bool_pair[0]];
delete id_string_dictionary[id_bool_pair[0]];
if (id_bool_pair[1] == "true") {
alert(sent_string + " was a palindrome.");
} else {
alert(sent_string + " was not a palindrome.");
}
}
}
var inputBox = document.getElementById("inputBox");
// Send an id and a string to the plugin using postMessage. This results in a call
// to Instance::HandleMessage in C++ (or PPP_Instance::HandleMessage in C).
var id = ++last_id;
plugin.postMessage(id + "," + inputBox.value);
// Now put the string in our dictionary, so when we get a response, we know
// which request it goes with.
id_string_dictionary[id] = inputBox.value;
}
</script>
<input type="text" id="inputBox" name="inputBox" value="ablewasiereisawelba"/>
<p>
<button onclick='SendString()'>Is Palindrome</button>
<object id="plugin" type="application/x-ppapi-reentrant-example"
width="0" height="0"/>
<hr>
</body>

@ -63,6 +63,7 @@
'c/dev/ppb_font_list_dev.h',
'c/dev/ppb_fullscreen_dev.h',
'c/dev/ppb_graphics_3d_dev.h',
'c/dev/ppb_messaging_dev.h',
'c/dev/ppb_opengles_dev.h',
'c/dev/ppb_scrollbar_dev.h',
'c/dev/ppb_surface_3d_dev.h',
@ -75,6 +76,7 @@
'c/dev/ppp_cursor_control_dev.h',
'c/dev/ppp_find_dev.h',
'c/dev/ppp_graphics_3d_dev.h',
'c/dev/ppp_messaging_dev.h',
'c/dev/ppp_scrollbar_dev.h',
'c/dev/ppp_selection_dev.h',
'c/dev/ppp_printing_dev.h',

@ -144,6 +144,15 @@
# ],
# },
# {
# 'target_name': 'ppapi_example_post_message',
# 'dependencies': [
# 'ppapi_example_skeleton',
# ],
# 'sources': [
# 'examples/scripting/post_message.cc',
# ],
# },
# {
# 'target_name': 'ppapi_example_scroll',
# 'dependencies': [
# 'ppapi_example_skeleton',
@ -196,6 +205,8 @@
'tests/test_image_data.h',
'tests/test_paint_aggregator.cc',
'tests/test_paint_aggregator.h',
'tests/test_post_message.cc',
'tests/test_post_message.h',
'tests/test_scrollbar.cc',
'tests/test_scrollbar.h',
'tests/test_struct_sizes.c',

@ -34,6 +34,8 @@ pp::Var TestCase::GetTestObject() {
return test_object_;
}
void TestCase::HandleMessage(const pp::Var& message_data) {}
pp::deprecated::ScriptableObject* TestCase::CreateTestObject() {
return NULL;
}

@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PPAPI_TEST_TEST_CASE_H_
#define PPAPI_TEST_TEST_CASE_H_
#ifndef PPAPI_TESTS_TEST_CASE_H_
#define PPAPI_TESTS_TEST_CASE_H_
#include <cmath>
#include <limits>
#include <string>
#include "ppapi/c/pp_resource.h"
@ -22,7 +24,7 @@ class ScriptableObject;
// Individual classes of tests derive from this generic test case.
class TestCase {
public:
TestCase(TestingInstance* instance) : instance_(instance) {}
explicit TestCase(TestingInstance* instance) : instance_(instance) {}
virtual ~TestCase() {}
// Optionally override to do testcase specific initialization.
@ -38,6 +40,12 @@ class TestCase {
// Internally, this uses CreateTestObject which each test overrides.
pp::Var GetTestObject();
// A function that is invoked whenever HandleMessage is called on the
// associated TestingInstance. Default implementation does nothing. TestCases
// that want to handle incoming postMessage events should override this
// method.
virtual void HandleMessage(const pp::Var& message_data);
protected:
// Overridden by each test to supply a ScriptableObject corresponding to the
// test. There can only be one object created for all test in a given class
@ -115,6 +123,10 @@ class TestCaseFactory {
#define ASSERT_EQ(a, b) ASSERT_TRUE((a) == (b))
#define ASSERT_NE(a, b) ASSERT_TRUE((a) != (b))
#define ASSERT_DOUBLE_EQ(a, b) ASSERT_TRUE( \
std::fabs((a)-(b)) <= std::numeric_limits<double>::epsilon())
#define PASS() return std::string()
#endif // PPAPI_TEST_TEST_CASE_H_
#endif // PPAPI_TESTS_TEST_CASE_H_

@ -0,0 +1,166 @@
// Copyright (c) 2011 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_post_message.h"
#include "ppapi/c/dev/ppb_testing_dev.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/cpp/dev/scriptable_object_deprecated.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/var.h"
#include "ppapi/tests/testing_instance.h"
REGISTER_TEST_CASE(PostMessage);
namespace {
const PPB_Testing_Dev* GetTestingInterface() {
static const PPB_Testing_Dev* g_testing_interface =
reinterpret_cast<PPB_Testing_Dev const*>(
pp::Module::Get()->GetBrowserInterface(PPB_TESTING_DEV_INTERFACE));
return g_testing_interface;
}
const char kTestString[] = "Hello world!";
const bool kTestBool = true;
const int32_t kTestInt = 42;
const double kTestDouble = 42.0;
const int32_t kThreadsToRun = 10;
} // namespace
bool TestPostMessage::Init() {
testing_interface_ = reinterpret_cast<const PPB_Testing_Dev*>(
pp::Module::Get()->GetBrowserInterface(PPB_TESTING_DEV_INTERFACE));
if (!testing_interface_) {
// Give a more helpful error message for the testing interface being gone
// since that needs special enabling in Chrome.
instance_->AppendError("This test needs the testing interface, which is "
"not currently available. In Chrome, use --enable-pepper-testing when "
"launching.");
}
return (testing_interface_ != NULL);
}
void TestPostMessage::RunTest() {
RUN_TEST(SendingData);
RUN_TEST(MessageEvent);
RUN_TEST(NoHandler);
}
void TestPostMessage::HandleMessage(const pp::Var& message_data) {
message_data_.push_back(message_data);
}
bool TestPostMessage::MakeOnMessageEcho(const std::string& expression) {
std::string js_code(
"document.getElementById('plugin').onmessage = function(message_event) {"
" document.getElementById('plugin').postMessage(");
js_code += expression;
js_code += ");}";
pp::Var exception;
// TODO(dmichael): Move ExecuteScript to the testing interface.
instance_->ExecuteScript(js_code, &exception);
return(exception.is_undefined());
}
std::string TestPostMessage::TestSendingData() {
// Set up the JavaScript onmessage handler to echo the data part of the
// message event back to us.
ASSERT_TRUE(MakeOnMessageEcho("message_event.data"));
// Test sending a message to JavaScript for each supported type. The JS sends
// the data back to us, and we check that they match.
message_data_.clear();
instance_->PostMessage(pp::Var(kTestString));
// Note that the trusted in-process version is completely synchronous, so we
// do not need to use 'RunMessageLoop' to wait.
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_string());
ASSERT_EQ(message_data_.back().AsString(), kTestString);
message_data_.clear();
instance_->PostMessage(pp::Var(kTestBool));
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_bool());
ASSERT_EQ(message_data_.back().AsBool(), kTestBool);
message_data_.clear();
instance_->PostMessage(pp::Var(kTestInt));
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_number());
ASSERT_DOUBLE_EQ(message_data_.back().AsDouble(),
static_cast<double>(kTestInt));
message_data_.clear();
instance_->PostMessage(pp::Var(kTestDouble));
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_number());
ASSERT_DOUBLE_EQ(message_data_.back().AsDouble(), kTestDouble);
message_data_.clear();
instance_->PostMessage(pp::Var());
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_undefined());
message_data_.clear();
instance_->PostMessage(pp::Var(pp::Var::Null()));
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_null());
PASS();
}
std::string TestPostMessage::TestMessageEvent() {
// Set up the JavaScript onmessage handler to pass us some values from the
// MessageEvent and make sure they match our expectations.
// Have onmessage pass back the type of message_event and make sure it's
// "object".
ASSERT_TRUE(MakeOnMessageEcho("typeof(message_event)"));
message_data_.clear();
instance_->PostMessage(pp::Var(kTestInt));
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_string());
ASSERT_EQ(message_data_.back().AsString(), "object");
// Make sure all the non-data properties have the expected values.
bool success = MakeOnMessageEcho("((message_event.origin == '')"
" && (message_event.lastEventId == '')"
" && (message_event.source == null)"
" && (message_event.ports == null)"
" && (message_event.bubbles == false)"
" && (message_event.cancelable == false)"
")");
ASSERT_TRUE(success);
message_data_.clear();
instance_->PostMessage(pp::Var(kTestInt));
// Note that the trusted in-process version is completely synchronous, so we
// do not need to use 'RunMessageLoop' to wait.
ASSERT_EQ(message_data_.size(), 1);
ASSERT_TRUE(message_data_.back().is_bool());
ASSERT_TRUE(message_data_.back().AsBool());
PASS();
}
std::string TestPostMessage::TestNoHandler() {
// Delete the onmessage handler (if it exists)
std::string js_code(
"if (document.getElementById('plugin').onmessage) {"
" delete document.getElementById('plugin').onmessage;"
"}");
pp::Var exception;
instance_->ExecuteScript(js_code, &exception);
ASSERT_TRUE(exception.is_undefined());
// Now send a message and make sure we don't get anything back (and that we
// don't crash).
message_data_.clear();
instance_->PostMessage(pp::Var());
ASSERT_EQ(message_data_.size(), 0);
PASS();
}

@ -0,0 +1,53 @@
// Copyright (c) 2011 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_POST_MESSAGE_H_
#define PPAPI_TESTS_TEST_POST_MESSAGE_H_
#include <string>
#include <vector>
#include "ppapi/tests/test_case.h"
struct PPB_Testing_Dev;
class TestPostMessage : public TestCase {
public:
explicit TestPostMessage(TestingInstance* instance)
: TestCase(instance), testing_interface_(NULL) {}
private:
// TestCase implementation.
virtual bool Init();
virtual void RunTest();
// A handler for JS->Native calls to postMessage. Simply pushes
// the given value to the back of message_data_
virtual void HandleMessage(const pp::Var& message_data);
// Set the JavaScript onmessage handler to echo back some expression based on
// the message_event by passing it to postMessage. Returns true on success,
// false on failure.
bool MakeOnMessageEcho(const std::string& expression);
// Test some basic functionality; make sure we can send data successfully
// in both directions.
std::string TestSendingData();
// Test the MessageEvent object that JavaScript received to make sure it is
// of the right type and has all the expected fields.
std::string TestMessageEvent();
// Test sending a message when no handler exists, make sure nothing happens.
std::string TestNoHandler();
const PPB_Testing_Dev* testing_interface_;
// This is used to store pp::Var objects we receive via a call to
// HandleMessage.
std::vector<pp::Var> message_data_;
};
#endif // PPAPI_TESTS_TEST_POST_MESSAGE_H_

@ -5,7 +5,8 @@
#include "ppapi/tests/testing_instance.h"
#include <algorithm>
#include <string.h>
#include <cstring>
#include <vector>
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
@ -27,15 +28,15 @@ bool TestingInstance::Init(uint32_t argc,
const char* argn[],
const char* argv[]) {
for (uint32_t i = 0; i < argc; i++) {
if (strcmp(argn[i], "mode") == 0) {
if (strcmp(argv[i], "nacl") == 0)
if (std::strcmp(argn[i], "mode") == 0) {
if (std::strcmp(argv[i], "nacl") == 0)
nacl_mode_ = true;
break;
}
}
// Create the proper test case from the argument.
for (uint32_t i = 0; i < argc; i++) {
if (strcmp(argn[i], "testcase") == 0) {
if (std::strcmp(argn[i], "testcase") == 0) {
if (argv[i][0] == '\0')
break;
current_case_ = CaseForTestName(argv[i]);
@ -55,6 +56,10 @@ pp::Var TestingInstance::GetInstanceObject() {
return current_case_->GetTestObject();
}
void TestingInstance::HandleMessage(const pp::Var& message_data) {
current_case_->HandleMessage(message_data);
}
void TestingInstance::DidChangeView(const pp::Rect& position,
const pp::Rect& clip) {
if (!executed_tests_) {
@ -66,7 +71,7 @@ void TestingInstance::DidChangeView(const pp::Rect& position,
}
void TestingInstance::LogTest(const std::string& test_name,
const std::string& error_message) {
const std::string& error_message) {
std::string html;
html.append("<div class=\"test_line\"><span class=\"test_name\">");
html.append(test_name);
@ -120,7 +125,7 @@ void TestingInstance::ExecuteTests(int32_t unused) {
TestCase* TestingInstance::CaseForTestName(const char* name) {
TestCaseFactory* iter = TestCaseFactory::head_;
while (iter != NULL) {
if (strcmp(name, iter->name_) == 0)
if (std::strcmp(name, iter->name_) == 0)
return iter->method_(this);
iter = iter->next_;
}

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PPAPI_TEST_TESTING_INSTANCE_H_
#define PPAPI_TEST_TESTING_INSTANCE_H_
#ifndef PPAPI_TESTS_TESTING_INSTANCE_H_
#define PPAPI_TESTS_TESTING_INSTANCE_H_
#include <string>
@ -14,7 +14,7 @@ class TestCase;
class TestingInstance : public pp::Instance {
public:
TestingInstance(PP_Instance instance);
explicit TestingInstance(PP_Instance instance);
// pp::Instance override.
virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
@ -41,6 +41,10 @@ class TestingInstance : public pp::Instance {
// Appends an error message to the log.
void AppendError(const std::string& message);
// Passes the message_data through to the HandleMessage method on the
// TestClass object that's associated with this instance.
virtual void HandleMessage(const pp::Var& message_data);
private:
void ExecuteTests(int32_t unused);
@ -75,4 +79,5 @@ class TestingInstance : public pp::Instance {
bool nacl_mode_;
};
#endif // PPAPI_TEST_TESTING_INSTANCE_H_
#endif // PPAPI_TESTS_TESTING_INSTANCE_H_

@ -267,6 +267,8 @@
'../plugins/ppapi/file_path.cc',
'../plugins/ppapi/file_path.h',
'../plugins/ppapi/fullscreen_container.h',
'../plugins/ppapi/message_channel.cc',
'../plugins/ppapi/message_channel.h',
'../plugins/ppapi/npapi_glue.cc',
'../plugins/ppapi/npapi_glue.h',
'../plugins/ppapi/plugin_delegate.h',

@ -0,0 +1,350 @@
// Copyright (c) 2011 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 "webkit/plugins/ppapi/message_channel.h"
#include <cstdlib>
#include <string>
#include "base/logging.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
#include "webkit/plugins/ppapi/npapi_glue.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/var.h"
using WebKit::WebBindings;
namespace webkit {
namespace ppapi {
namespace {
const char kPostMessage[] = "postMessage";
// Helper function to get the MessageChannel that is associated with an
// NPObject*.
MessageChannel& ToMessageChannel(NPObject* object) {
return *(static_cast<MessageChannel::MessageChannelNPObject*>(object)->
message_channel);
}
// Helper function to determine if a given identifier is equal to kPostMessage.
bool IdentifierIsPostMessage(NPIdentifier identifier) {
return WebBindings::getStringIdentifier(kPostMessage) == identifier;
}
// Converts the given PP_Var to an NPVariant, returning true on success.
// False means that the given variant is invalid. In this case, the result
// NPVariant will be set to a void one.
//
// The contents of the PP_Var will NOT be copied, so you need to ensure that
// the PP_Var remains valid while the resultant NPVariant is in use.
//
// Note: This is largely copied from var.cc so that we don't depend on code
// which will be removed. TODO(dmichael) remove this comment when var
// is removed.
bool PPVarToNPVariantNoCopy(PP_Var var, NPVariant* result) {
switch (var.type) {
case PP_VARTYPE_UNDEFINED:
VOID_TO_NPVARIANT(*result);
break;
case PP_VARTYPE_NULL:
NULL_TO_NPVARIANT(*result);
break;
case PP_VARTYPE_BOOL:
BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result);
break;
case PP_VARTYPE_INT32:
INT32_TO_NPVARIANT(var.value.as_int, *result);
break;
case PP_VARTYPE_DOUBLE:
DOUBLE_TO_NPVARIANT(var.value.as_double, *result);
break;
case PP_VARTYPE_STRING: {
scoped_refptr<StringVar> string(StringVar::FromPPVar(var));
if (!string) {
VOID_TO_NPVARIANT(*result);
return false;
}
const std::string& value = string->value();
STRINGN_TO_NPVARIANT(value.c_str(), value.size(), *result);
break;
}
case PP_VARTYPE_OBJECT:
// Objects are not currently supported.
DCHECK(false);
VOID_TO_NPVARIANT(*result);
return false;
default:
VOID_TO_NPVARIANT(*result);
return false;
}
return true;
}
//------------------------------------------------------------------------------
// Implementations of NPClass functions. These are here to:
// - Implement postMessage behavior.
// - Forward calls to the 'passthrough' object to allow backwards-compatibility
// with GetInstanceObject() objects.
//------------------------------------------------------------------------------
NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) {
return new MessageChannel::MessageChannelNPObject;
}
void MessageChannelDeallocate(NPObject* object) {
MessageChannel::MessageChannelNPObject* instance =
static_cast<MessageChannel::MessageChannelNPObject*>(object);
delete instance;
}
bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) {
if (!np_obj)
return false;
// We only handle a function called postMessage.
if (IdentifierIsPostMessage(name))
return true;
// Other method names we will pass to the passthrough object, if we have one.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough)
return WebBindings::hasMethod(NULL, passthrough, name);
return false;
}
bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name,
const NPVariant* args, uint32 arg_count,
NPVariant* result) {
if (!np_obj)
return false;
// We only handle a function called postMessage.
if (IdentifierIsPostMessage(name) && (arg_count == 1)) {
MessageChannel& message_channel(ToMessageChannel(np_obj));
PP_Var argument(Var::NPVariantToPPVar(message_channel.instance(),
&args[0]));
message_channel.PostMessageToNative(argument);
return true;
}
// Other method calls we will pass to the passthrough object, if we have one.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough) {
return WebBindings::invoke(NULL, passthrough, name, args, arg_count,
result);
}
return false;
}
bool MessageChannelInvokeDefault(NPObject* np_obj,
const NPVariant* args,
uint32 arg_count,
NPVariant* result) {
if (!np_obj)
return false;
// Invoke on the passthrough object, if we have one.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough) {
return WebBindings::invokeDefault(NULL, passthrough, args, arg_count,
result);
}
return false;
}
bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) {
if (!np_obj)
return false;
// Invoke on the passthrough object, if we have one.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough)
return WebBindings::hasProperty(NULL, passthrough, name);
return false;
}
bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name,
NPVariant* result) {
if (!np_obj)
return false;
// Don't allow getting the postMessage function.
if (IdentifierIsPostMessage(name))
return false;
// Invoke on the passthrough object, if we have one.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough)
return WebBindings::getProperty(NULL, passthrough, name, result);
return false;
}
bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name,
const NPVariant* variant) {
if (!np_obj)
return false;
// Don't allow setting the postMessage function.
if (IdentifierIsPostMessage(name))
return false;
// Invoke on the passthrough object, if we have one.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough)
return WebBindings::setProperty(NULL, passthrough, name, variant);
return false;
}
bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value,
uint32_t *count) {
if (!np_obj)
return false;
// Invoke on the passthrough object, if we have one, to enumerate its
// properties.
NPObject* passthrough = ToMessageChannel(np_obj).passthrough_object();
if (passthrough) {
bool success = WebBindings::enumerate(NULL, passthrough, value, count);
if (success) {
// Add postMessage to the list and return it.
NPIdentifier* new_array = static_cast<NPIdentifier*>(
std::malloc(sizeof(NPIdentifier) * (*count + 1)));
std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count));
new_array[*count] = WebBindings::getStringIdentifier(kPostMessage);
std::free(*value);
*value = new_array;
++(*count);
return true;
}
}
// Otherwise, build an array that includes only postMessage.
*value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier)));
(*value)[0] = WebBindings::getStringIdentifier(kPostMessage);
*count = 1;
return true;
}
NPClass message_channel_class = {
NP_CLASS_STRUCT_VERSION,
&MessageChannelAllocate,
&MessageChannelDeallocate,
NULL,
&MessageChannelHasMethod,
&MessageChannelInvoke,
&MessageChannelInvokeDefault,
&MessageChannelHasProperty,
&MessageChannelGetProperty,
&MessageChannelSetProperty,
NULL,
&MessageChannelEnumerate,
};
} // namespace
// MessageChannel --------------------------------------------------------------
MessageChannel::MessageChannelNPObject::MessageChannelNPObject() {}
MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {}
MessageChannel::MessageChannel(PluginInstance* instance)
: instance_(instance),
passthrough_object_(NULL),
np_object_(NULL) {
VOID_TO_NPVARIANT(onmessage_invoker_);
// Now create an NPObject for receiving calls to postMessage.
NPObject* obj = WebBindings::createObject(NULL, &message_channel_class);
DCHECK(obj);
np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj);
np_object_->message_channel = this;
}
bool MessageChannel::EvaluateOnMessageInvoker() {
// If we've already evaluated the function, just return.
if (NPVARIANT_IS_OBJECT(onmessage_invoker_))
return true;
// This is the javascript code that we invoke. It checks to see if onmessage
// exists, and if so, it invokes it.
const char invoke_onmessage_js[] =
"(function(module_instance, message_data) {"
" if (module_instance &&" // Only invoke if the instance is valid and
" module_instance.onmessage &&" // has a function named onmessage.
" typeof(module_instance.onmessage) == 'function') {"
" var message_event = document.createEvent('MessageEvent');"
" message_event.initMessageEvent('message'," // type
" false," // canBubble
" false," // cancelable
" message_data," // data
" ''," // origin
" ''," // lastEventId
" module_instance," // source
" []);" // ports
" module_instance.onmessage(message_event);"
" }"
"})";
NPString function_string = { invoke_onmessage_js,
sizeof(invoke_onmessage_js)-1 };
// Get the current frame to pass to the evaluate function.
WebKit::WebFrame* frame =
instance_->container()->element().document().frame();
// Evaluate the function and obtain an NPVariant pointing to it.
if (!WebBindings::evaluate(NULL, frame->windowObject(), &function_string,
&onmessage_invoker_)) {
// If it fails, do nothing.
return false;
}
DCHECK(NPVARIANT_IS_OBJECT(onmessage_invoker_));
return true;
}
void MessageChannel::PostMessageToJavaScript(PP_Var message_data) {
// Make sure we have our function for invoking a JavaScript onmessage
// function.
bool success = EvaluateOnMessageInvoker();
DCHECK(success);
if (!success)
return;
DCHECK(instance_);
NPVariant result_var;
VOID_TO_NPVARIANT(result_var);
NPVariant npvariant_args[2];
OBJECT_TO_NPVARIANT(instance_->container()->scriptableObjectForElement(),
npvariant_args[0]);
// Convert message to an NPVariant without copying. Note this means that
// in-process plugins will not copy the data, so isn't really following the
// postMessage spec in spirit. Copying is handled in the proxy, and we don't
// want to re-copy unnecessarily.
//
// TODO(dmichael): We need to do structured clone eventually to copy a object
// structure. The details and PPAPI changes for this are TBD.
if (!PPVarToNPVariantNoCopy(message_data, &npvariant_args[1]))
return;
WebBindings::invokeDefault(NULL,
NPVARIANT_TO_OBJECT(onmessage_invoker_),
npvariant_args,
sizeof(npvariant_args)/sizeof(*npvariant_args),
&result_var);
}
void MessageChannel::PostMessageToNative(PP_Var message_data) {
instance_->HandleMessage(message_data);
}
MessageChannel::~MessageChannel() {
WebBindings::releaseVariantValue(&onmessage_invoker_);
}
} // namespace ppapi
} // namespace webkit

@ -0,0 +1,93 @@
// Copyright (c) 2011 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 WEBKIT_PLUGINS_PPAPI_MESSAGE_CHANNEL_H_
#define WEBKIT_PLUGINS_PPAPI_MESSAGE_CHANNEL_H_
#include "third_party/npapi/bindings/npruntime.h"
#include "webkit/plugins/ppapi/resource.h"
struct PP_Var;
namespace webkit {
namespace ppapi {
class PluginInstance;
// MessageChannel implements bidirectional postMessage functionality, allowing
// calls from JavaScript to plugins and vice-versa. See
// PPB_Instance::PostMessage and PPP_Instance::HandleMessage for more
// information.
//
// Currently, only 1 MessageChannel can exist, to implement postMessage
// functionality for the instance interfaces. In the future, when we create a
// MessagePort type in PPAPI, those may be implemented here as well with some
// refactoring.
// - Separate message ports won't require the passthrough object.
// - The message target won't be limited to instance, and should support
// either plugin-provided or JS objects.
// TODO(dmichael): Add support for separate MessagePorts.
class MessageChannel {
public:
// MessageChannelNPObject is a simple struct that adds a pointer back to a
// MessageChannel instance. This way, we can use an NPObject to allow
// JavaScript interactions without forcing MessageChannel to inherit from
// NPObject.
struct MessageChannelNPObject : public NPObject {
MessageChannelNPObject();
~MessageChannelNPObject();
MessageChannel* message_channel;
};
explicit MessageChannel(PluginInstance* instance);
~MessageChannel();
void PostMessageToJavaScript(PP_Var message_data);
void PostMessageToNative(PP_Var message_data);
// 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
// there is a scriptable 'InstanceObject' associated with this channel's
// instance.
NPObject* passthrough_object() {
return passthrough_object_;
}
void set_passthrough_object(NPObject* passthrough) {
passthrough_object_ = passthrough;
}
NPObject* np_object() { return np_object_; }
PluginInstance* instance() {
return instance_;
}
private:
PluginInstance* instance_;
// We pass all non-postMessage calls through to the passthrough_object_.
// This way, a plugin can use PPB_Class or PPP_Class_Deprecated and also
// postMessage. This is necessary to support backwards-compatibility, and
// also trusted plugins for which we will continue to support synchronous
// scripting.
NPObject* passthrough_object_;
// The NPObject we use to expose postMessage to JavaScript.
MessageChannelNPObject* np_object_;
// An NPVariant referring to the JavaScript function we use to send a message
// to a JavaScript target.
NPVariant onmessage_invoker_;
bool EvaluateOnMessageInvoker();
DISALLOW_COPY_AND_ASSIGN(MessageChannel);
};
} // namespace ppapi
} // namespace webkit
#endif // WEBKIT_PLUGINS_PPAPI_MESSAGE_CHANNEL_H_

@ -27,6 +27,7 @@
#include "ppapi/c/dev/ppb_fullscreen_dev.h"
#include "ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h"
#include "ppapi/c/dev/ppb_graphics_3d_dev.h"
#include "ppapi/c/dev/ppb_messaging_dev.h"
#include "ppapi/c/dev/ppb_opengles_dev.h"
#include "ppapi/c/dev/ppb_scrollbar_dev.h"
#include "ppapi/c/dev/ppb_testing_dev.h"
@ -272,6 +273,8 @@ const void* GetInterface(const char* name) {
return PPB_ImageData_Impl::GetTrustedInterface();
if (strcmp(name, PPB_INSTANCE_INTERFACE) == 0)
return PluginInstance::GetInterface();
if (strcmp(name, PPB_MESSAGING_DEV_INTERFACE) == 0)
return PluginInstance::GetMessagingInterface();
if (strcmp(name, PPB_PDF_INTERFACE) == 0)
return PPB_PDF_Impl::GetInterface();
if (strcmp(name, PPB_PROXY_PRIVATE_INTERFACE) == 0)

@ -11,8 +11,10 @@
#include "base/utf_string_conversions.h"
#include "ppapi/c/dev/ppb_find_dev.h"
#include "ppapi/c/dev/ppb_fullscreen_dev.h"
#include "ppapi/c/dev/ppb_messaging_dev.h"
#include "ppapi/c/dev/ppb_zoom_dev.h"
#include "ppapi/c/dev/ppp_find_dev.h"
#include "ppapi/c/dev/ppp_messaging_dev.h"
#include "ppapi/c/dev/ppp_selection_dev.h"
#include "ppapi/c/dev/ppp_zoom_dev.h"
#include "ppapi/c/pp_input_event.h"
@ -42,6 +44,7 @@
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/event_conversion.h"
#include "webkit/plugins/ppapi/fullscreen_container.h"
#include "webkit/plugins/ppapi/message_channel.h"
#include "webkit/plugins/ppapi/plugin_delegate.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/plugin_object.h"
@ -100,7 +103,8 @@ typedef bool (*RenderPDFPageToDCProc)(
namespace {
#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \
COMPILE_ASSERT(int(WebCursorInfo::webkit_name) == int(np_name), \
COMPILE_ASSERT(static_cast<int>(WebCursorInfo::webkit_name) \
== static_cast<int>(np_name), \
mismatching_enums)
COMPILE_ASSERT_MATCHING_ENUM(TypePointer, PP_CURSORTYPE_POINTER);
@ -262,13 +266,23 @@ PP_Bool GetScreenSize(PP_Instance instance_id, PP_Size* size) {
return PP_TRUE;
}
const PPB_Fullscreen_Dev ppb_fullscreen = {
&IsFullscreen,
&SetFullscreen,
&GetScreenSize
};
void PostMessage(PP_Instance instance_id, PP_Var message) {
PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
if (!instance)
return;
instance->PostMessage(message);
}
const PPB_Messaging_Dev ppb_messaging = {
&PostMessage
};
void ZoomChanged(PP_Instance instance_id, double factor) {
PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
if (!instance)
@ -326,9 +340,11 @@ PluginInstance::PluginInstance(PluginDelegate* delegate,
has_content_area_focus_(false),
find_identifier_(-1),
plugin_find_interface_(NULL),
plugin_messaging_interface_(NULL),
plugin_pdf_interface_(NULL),
plugin_selection_interface_(NULL),
plugin_zoom_interface_(NULL),
checked_for_plugin_messaging_interface_(false),
#if defined(OS_LINUX)
canvas_(NULL),
#endif // defined(OS_LINUX)
@ -337,6 +353,7 @@ PluginInstance::PluginInstance(PluginDelegate* delegate,
always_on_top_(false),
fullscreen_container_(NULL),
fullscreen_(false),
message_channel_(NULL),
sad_plugin_(NULL) {
pp_instance_ = ResourceTracker::Get()->AddInstance(this);
@ -344,6 +361,7 @@ PluginInstance::PluginInstance(PluginDelegate* delegate,
DCHECK(delegate);
module_->InstanceCreated(this);
delegate_->InstanceCreated(this);
message_channel_.reset(new MessageChannel(this));
}
PluginInstance::~PluginInstance() {
@ -382,6 +400,11 @@ const PPB_Fullscreen_Dev* PluginInstance::GetFullscreenInterface() {
return &ppb_fullscreen;
}
// static
const PPB_Messaging_Dev* PluginInstance::GetMessagingInterface() {
return &ppb_messaging;
}
// static
const PPB_Zoom_Dev* PluginInstance::GetZoomInterface() {
return &ppb_zoom;
@ -643,6 +666,10 @@ PP_Var PluginInstance::ExecuteScript(PP_Var script, PP_Var* exception) {
return ret;
}
void PluginInstance::PostMessage(PP_Var message) {
message_channel_->PostMessageToJavaScript(message);
}
void PluginInstance::Delete() {
// Keep a reference on the stack. See NOTE above.
scoped_refptr<PluginInstance> ref(this);
@ -702,6 +729,14 @@ bool PluginInstance::HandleInputEvent(const WebKit::WebInputEvent& event,
return rv;
}
void PluginInstance::HandleMessage(PP_Var message) {
// Keep a reference on the stack. See NOTE above.
scoped_refptr<PluginInstance> ref(this);
if (!LoadMessagingInterface())
return;
plugin_messaging_interface_->HandleMessage(pp_instance(), message);
}
PP_Var PluginInstance::GetInstanceObject() {
return instance_interface_->GetInstanceObject(pp_instance());
}
@ -878,6 +913,17 @@ bool PluginInstance::LoadFindInterface() {
return !!plugin_find_interface_;
}
bool PluginInstance::LoadMessagingInterface() {
if (!checked_for_plugin_messaging_interface_) {
checked_for_plugin_messaging_interface_ = true;
plugin_messaging_interface_ =
reinterpret_cast<const PPP_Messaging_Dev*>(module_->GetPluginInterface(
PPP_MESSAGING_DEV_INTERFACE));
}
return !!plugin_messaging_interface_;
}
bool PluginInstance::LoadPdfInterface() {
if (!plugin_pdf_interface_) {
plugin_pdf_interface_ =

@ -29,9 +29,11 @@ struct PP_Var;
struct PPB_Instance;
struct PPB_Find_Dev;
struct PPB_Fullscreen_Dev;
struct PPB_Messaging_Dev;
struct PPB_Zoom_Dev;
struct PPP_Find_Dev;
struct PPP_Instance;
struct PPP_Messaging_Dev;
struct PPP_Pdf;
struct PPP_Selection_Dev;
struct PPP_Zoom_Dev;
@ -53,6 +55,7 @@ namespace webkit {
namespace ppapi {
class FullscreenContainer;
class MessageChannel;
class ObjectVar;
class PluginDelegate;
class PluginModule;
@ -80,10 +83,12 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
// exposed to the plugin.
static const PPB_Find_Dev* GetFindInterface();
static const PPB_Fullscreen_Dev* GetFullscreenInterface();
static const PPB_Messaging_Dev* GetMessagingInterface();
static const PPB_Zoom_Dev* GetZoomInterface();
PluginDelegate* delegate() const { return delegate_; }
PluginModule* module() const { return module_.get(); }
MessageChannel& message_channel() { return *message_channel_; }
WebKit::WebPluginContainer* container() const { return container_; }
@ -214,6 +219,10 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
// Implementation of PPB_Flash.
bool NavigateToURL(const char* url, const char* target);
// Implementation of PPB_Messaging and PPP_Messaging.
void PostMessage(PP_Var message);
void HandleMessage(PP_Var message);
PluginDelegate::PlatformContext3D* CreateContext3D();
// Tracks all live ObjectVar. This is so we can map between PluginModule +
@ -234,6 +243,7 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
private:
bool LoadFindInterface();
bool LoadMessagingInterface();
bool LoadPdfInterface();
bool LoadSelectionInterface();
bool LoadZoomInterface();
@ -319,10 +329,15 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
// The plugin-provided interfaces.
const PPP_Find_Dev* plugin_find_interface_;
const PPP_Messaging_Dev* plugin_messaging_interface_;
const PPP_Pdf* plugin_pdf_interface_;
const PPP_Selection_Dev* plugin_selection_interface_;
const PPP_Zoom_Dev* plugin_zoom_interface_;
// A flag to indicate whether we have asked this plugin instance for its
// messaging interface, so that we can ask only once.
bool checked_for_plugin_messaging_interface_;
// This is only valid between a successful PrintBegin call and a PrintEnd
// call.
PP_PrintSettings_Dev current_print_settings_;
@ -367,6 +382,10 @@ class PluginInstance : public base::RefCounted<PluginInstance> {
// True if we are in fullscreen mode. Note: it is false during the transition.
bool fullscreen_;
// The MessageChannel used to implement bidirectional postMessage for the
// instance.
scoped_ptr<MessageChannel> message_channel_;
// Bitmap for crashed plugin. Lazily initialized, non-owning pointer.
SkBitmap* sad_plugin_;

@ -12,6 +12,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "webkit/plugins/ppapi/message_channel.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/ppb_url_loader_impl.h"
@ -90,9 +91,14 @@ void WebPluginImpl::destroy() {
NPObject* WebPluginImpl::scriptableObject() {
scoped_refptr<ObjectVar> object(
ObjectVar::FromPPVar(instance_->GetInstanceObject()));
if (object)
return object->np_object();
return NULL;
// If there's an InstanceObject, tell the Instance's MessageChannel to pass
// any non-postMessage calls to it.
if (object) {
instance_->message_channel().set_passthrough_object(
object->np_object());
}
// And return the instance's MessageChannel.
return instance_->message_channel().np_object();
}
void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& rect) {