diff options
author | dmichael@google.com <dmichael@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-23 21:07:15 +0000 |
---|---|---|
committer | dmichael@google.com <dmichael@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-23 21:07:15 +0000 |
commit | 9888f134c655a95c5922c980eb59d3337341d653 (patch) | |
tree | 8ec7b6849cd94196df9bf3ee7810f6a838242e8d /ppapi/tests | |
parent | 2752bf6a3120e172d216ab7bde42222a3ae4b5cb (diff) | |
download | chromium_src-9888f134c655a95c5922c980eb59d3337341d653.zip chromium_src-9888f134c655a95c5922c980eb59d3337341d653.tar.gz chromium_src-9888f134c655a95c5922c980eb59d3337341d653.tar.bz2 |
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
Diffstat (limited to 'ppapi/tests')
-rw-r--r-- | ppapi/tests/test_case.cc | 2 | ||||
-rw-r--r-- | ppapi/tests/test_case.h | 20 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.cc | 166 | ||||
-rw-r--r-- | ppapi/tests/test_post_message.h | 53 | ||||
-rw-r--r-- | ppapi/tests/testing_instance.cc | 17 | ||||
-rw-r--r-- | ppapi/tests/testing_instance.h | 13 |
6 files changed, 257 insertions, 14 deletions
diff --git a/ppapi/tests/test_case.cc b/ppapi/tests/test_case.cc index 20e45d1..ab54d51 100644 --- a/ppapi/tests/test_case.cc +++ b/ppapi/tests/test_case.cc @@ -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; } diff --git a/ppapi/tests/test_case.h b/ppapi/tests/test_case.h index 35026fe..b06ecb8 100644 --- a/ppapi/tests/test_case.h +++ b/ppapi/tests/test_case.h @@ -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_ + diff --git a/ppapi/tests/test_post_message.cc b/ppapi/tests/test_post_message.cc new file mode 100644 index 0000000..b22fb05 --- /dev/null +++ b/ppapi/tests/test_post_message.cc @@ -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(); +} + diff --git a/ppapi/tests/test_post_message.h b/ppapi/tests/test_post_message.h new file mode 100644 index 0000000..1b841c8 --- /dev/null +++ b/ppapi/tests/test_post_message.h @@ -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_ + diff --git a/ppapi/tests/testing_instance.cc b/ppapi/tests/testing_instance.cc index 34ec749..ca82b5c 100644 --- a/ppapi/tests/testing_instance.cc +++ b/ppapi/tests/testing_instance.cc @@ -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_; } diff --git a/ppapi/tests/testing_instance.h b/ppapi/tests/testing_instance.h index 5eedf24..5bb95ae 100644 --- a/ppapi/tests/testing_instance.h +++ b/ppapi/tests/testing_instance.h @@ -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_ + |