1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ppapi/tests/test_message_handler.h"
12 #include "ppapi/c/pp_var.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/c/ppb_messaging.h"
15 #include "ppapi/c/ppp_message_handler.h"
16 #include "ppapi/cpp/completion_callback.h"
17 #include "ppapi/cpp/file_io.h"
18 #include "ppapi/cpp/file_ref.h"
19 #include "ppapi/cpp/file_system.h"
20 #include "ppapi/cpp/instance.h"
21 #include "ppapi/cpp/message_handler.h"
22 #include "ppapi/cpp/module_impl.h"
23 #include "ppapi/cpp/network_list.h"
24 #include "ppapi/cpp/network_monitor.h"
25 #include "ppapi/cpp/var.h"
26 #include "ppapi/cpp/var_array.h"
27 #include "ppapi/cpp/var_array_buffer.h"
28 #include "ppapi/cpp/var_dictionary.h"
29 #include "ppapi/tests/pp_thread.h"
30 #include "ppapi/tests/test_utils.h"
31 #include "ppapi/tests/testing_instance.h"
33 // Windows defines 'PostMessage', so we have to undef it.
38 REGISTER_TEST_CASE(MessageHandler
);
42 // Created and destroyed on the main thread. All public methods should be called
43 // on the main thread. Most data members are only accessed on the main thread.
44 // (Though it handles messages on the background thread).
45 class MyMessageHandler
: public pp::MessageHandler
{
47 explicit MyMessageHandler(TestingInstance
* instance
,
48 const pp::MessageLoop
& loop
)
49 : testing_instance_(instance
),
50 message_handler_loop_(loop
),
51 is_registered_(false),
52 test_finished_event_(instance
->pp_instance()),
53 destroy_event_(instance
->pp_instance()),
54 async_message_received_(instance
->pp_instance()) {
59 assert(!is_registered_
);
61 testing_instance_
->RegisterMessageHandler(this, message_handler_loop_
);
62 if (result
== PP_OK
) {
63 is_registered_
= true;
65 std::ostringstream stream
;
66 stream
<< "Failed to register message handler; got error " << result
;
67 AddError(stream
.str());
68 test_finished_event_
.Signal();
70 // Note, at this point, we can't safely read or write errors_ until we wait
75 assert(is_registered_
);
76 testing_instance_
->UnregisterMessageHandler();
77 is_registered_
= false;
79 void WaitForTestFinishedMessage() {
80 test_finished_event_
.Wait();
81 test_finished_event_
.Reset();
83 // Wait for Destroy() to be called on the MessageHandler thread. When it's
84 // done, return any errors that occurred during the time the MessageHandler
85 // was getting messages.
86 std::string
WaitForDestroy() {
88 // If we haven't called Unregister, we'll be waiting forever.
89 assert(!is_registered_
);
90 destroy_event_
.Wait();
91 destroy_event_
.Reset();
92 // Now that we know Destroy() has been called, we know errors_ isn't being
93 // written on the MessageHandler thread anymore. So we can safely read it
94 // here on the main thread (since destroy_event_ gave us a memory barrier).
95 std::string temp_errors
;
96 errors_
.swap(temp_errors
);
99 pp::Var
WaitForAsyncMessage() {
100 async_message_received_
.Wait();
101 pp::Var var_to_return
= last_async_message_received_
;
102 last_async_message_received_
= pp::Var();
103 async_message_received_
.Reset();
104 return var_to_return
;
107 static void AssertOnMainThread() {
108 assert(pp::MessageLoop::GetForMainThread() ==
109 pp::MessageLoop::GetCurrent());
111 void AddError(const std::string
& error
) {
112 if (!error
.empty()) {
113 if (!errors_
.empty())
118 virtual void HandleMessage(pp::InstanceHandle instance
, const pp::Var
& var
) {
119 if (pp::MessageLoop::GetCurrent() != message_handler_loop_
)
120 AddError("HandleMessage was called on the wrong thread!");
121 if (instance
.pp_instance() != testing_instance_
->pp_instance())
122 AddError("HandleMessage was passed the wrong instance!");
123 if (var
.is_string() && var
.AsString() == "FINISHED_TEST") {
124 test_finished_event_
.Signal();
126 // Any client causing a message to arrive must wait for the message
127 // before continuing. See WaitForAsyncMessage().
128 assert(last_async_message_received_
.is_undefined());
129 last_async_message_received_
= var
;
130 async_message_received_
.Signal();
134 virtual pp::Var
HandleBlockingMessage(pp::InstanceHandle instance
,
135 const pp::Var
& var
) {
136 if (pp::MessageLoop::GetCurrent() != message_handler_loop_
)
137 AddError("HandleBlockingMessage was called on the wrong thread!");
138 if (instance
.pp_instance() != testing_instance_
->pp_instance())
139 AddError("HandleBlockingMessage was passed the wrong instance!");
141 // Attempt a blocking operation; make sure it's disallowed.
142 pp::NetworkMonitor
monitor(instance
);
143 PP_Resource out_param
= 0;
144 pp::CompletionCallbackWithOutput
<pp::NetworkList
> blocking_callback(
146 int32_t error
= monitor
.UpdateNetworkList(blocking_callback
);
147 if (error
!= PP_ERROR_WOULD_BLOCK_THREAD
) {
148 AddError("HandleBlockingMessage was allowed to do a blocking call!");
149 pp::Module::Get()->core()->ReleaseResource(out_param
);
155 virtual void WasUnregistered(pp::InstanceHandle instance
) {
156 if (pp::MessageLoop::GetCurrent() != message_handler_loop_
)
157 AddError("Destroy was called on the wrong thread!");
158 if (instance
.pp_instance() != testing_instance_
->pp_instance())
159 AddError("Destroy was passed the wrong instance!");
160 destroy_event_
.Signal();
163 // These data members are initialized on the main thread, but don't change for
164 // the life of the object, so are safe to access on the background thread,
165 // because there will be a memory barrier before the the MessageHandler calls
167 TestingInstance
* const testing_instance_
;
168 const pp::MessageLoop message_handler_loop_
;
169 const pp::MessageLoop main_loop_
;
171 // is_registered_ is only read/written on the main thread.
174 // errors_ is written on the MessageHandler thread. When Destroy() is
175 // called, we stop writing to errors_ and signal destroy_event_. This causes
176 // a memory barrier, so it's safe to read errors_ after that.
178 NestedEvent test_finished_event_
;
179 NestedEvent destroy_event_
;
181 pp::Var last_async_message_received_
;
182 NestedEvent async_message_received_
;
184 // Undefined & private to disallow copy and assign.
185 MyMessageHandler(const MyMessageHandler
&);
186 MyMessageHandler
& operator=(const MyMessageHandler
&);
189 void FakeHandleMessage(PP_Instance instance
,
191 const PP_Var
* message_data
) {}
192 void FakeHandleBlockingMessage(PP_Instance instance
,
194 const PP_Var
* message_data
,
196 void FakeDestroy(PP_Instance instance
, void* user_data
) {}
200 TestMessageHandler::TestMessageHandler(TestingInstance
* instance
)
201 : TestCase(instance
),
202 message_received_(instance
->pp_instance()),
203 ppb_messaging_if_(NULL
),
204 handler_thread_(instance
) {
207 TestMessageHandler::~TestMessageHandler() {
208 handler_thread_
.Join();
211 bool TestMessageHandler::Init() {
212 ppb_messaging_if_
= static_cast<const PPB_Messaging_1_2
*>(
213 pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_2
));
214 return ppb_messaging_if_
&&
215 CheckTestingInterface() &&
216 handler_thread_
.Start();
219 void TestMessageHandler::RunTests(const std::string
& filter
) {
220 RUN_TEST(RegisterErrorConditions
, filter
);
221 RUN_TEST(PostMessageAndAwaitResponse
, filter
);
222 RUN_TEST(Exceptions
, filter
);
225 void TestMessageHandler::HandleMessage(const pp::Var
& message_data
) {
226 if (instance()->current_test_name() == "Exceptions") {
227 // For TestPostMessageAndAwaitResponse(), all messages should go to the
228 // background thread message handler.
231 // Any subtest causing a message to arrive here must wait for it before
232 // continuing. See WaitForMessage().
233 assert(last_message_
.is_undefined());
234 last_message_
= message_data
;
235 message_received_
.Signal();
239 std::string
TestMessageHandler::TestRegisterErrorConditions() {
241 // Test registering with the main thread as the message loop.
242 PPP_MessageHandler_0_2 fake_ppp_message_handler
= {
243 &FakeHandleMessage
, &FakeHandleBlockingMessage
, &FakeDestroy
245 pp::MessageLoop main_loop
= pp::MessageLoop::GetForMainThread();
246 int32_t result
= ppb_messaging_if_
->RegisterMessageHandler(
247 instance()->pp_instance(),
248 reinterpret_cast<void*>(0xdeadbeef),
249 &fake_ppp_message_handler
,
250 main_loop
.pp_resource());
251 ASSERT_EQ(PP_ERROR_WRONG_THREAD
, result
);
254 // Test registering with incomplete PPP_Messaging interface.
255 PPP_MessageHandler_0_2 bad_ppp_ifs
[] = {
256 { NULL
, &FakeHandleBlockingMessage
, &FakeDestroy
},
257 { &FakeHandleMessage
, NULL
, &FakeDestroy
},
258 { &FakeHandleMessage
, &FakeHandleBlockingMessage
, NULL
}};
259 for (size_t i
= 0; i
< sizeof(bad_ppp_ifs
)/sizeof(bad_ppp_ifs
[0]); ++i
) {
260 int32_t result
= ppb_messaging_if_
->RegisterMessageHandler(
261 instance()->pp_instance(),
262 reinterpret_cast<void*>(0xdeadbeef),
264 handler_thread_
.message_loop().pp_resource());
265 ASSERT_EQ(PP_ERROR_BADARGUMENT
, result
);
271 std::string
TestMessageHandler::TestPostMessageAndAwaitResponse() {
272 MyMessageHandler
handler(instance(),
273 handler_thread_
.message_loop());
275 std::string
js_code("var plugin = document.getElementById('plugin');\n");
276 js_code
+= "var result = undefined;\n";
277 const char* const values_to_test
[] = {
282 "{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
285 for (size_t i
= 0; values_to_test
[i
]; ++i
) {
286 js_code
+= "result = plugin.postMessageAndAwaitResponse(";
287 js_code
+= values_to_test
[i
];
289 js_code
+= "if (!deepCompare(result, ";
290 js_code
+= values_to_test
[i
];
292 js_code
+= " InternalError(\" Failed postMessageAndAwaitResponse for: ";
293 js_code
+= values_to_test
[i
];
294 js_code
+= " result: \" + result);\n";
296 instance_
->EvalScript(js_code
);
297 instance_
->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
298 handler
.WaitForTestFinishedMessage();
299 handler
.Unregister();
300 ASSERT_SUBTEST_SUCCESS(handler
.WaitForDestroy());
305 std::string
TestMessageHandler::TestExceptions() {
306 MyMessageHandler
handler(instance(),
307 handler_thread_
.message_loop());
309 // First, try sending a blocking message when there is no handler
310 // registered. It should throw an exception.
312 "var plugin = document.getElementById('plugin');\n"
313 "var caught_exception = false;\n"
315 " plugin.postMessageAndAwaitResponse('Hello!');\n"
317 " caught_exception = true;\n"
319 "plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n");
320 instance_
->EvalScript(js_code
);
321 // Note that we want to wait for the Instance to get the SUCCESS/FAIL
322 // message here. |message_handler| is not yet registered, so the message
323 // goes to the instance instead.
324 pp::Var msg
= WaitForMessage();
325 ASSERT_TRUE(msg
.is_string());
326 ASSERT_EQ("SUCCESS", msg
.AsString());
330 // Now that a handler is registered, try requesting and sending a
331 // FileSystem. It should throw an exception. The file system is opened
332 // asynchronously. What *should* happen is that it opens successfully, then
333 // we try to send it via postMessageAndAwaitResponse, which fails with an
334 // exception. The test could fail either because the filesystem doesn't
335 // open or because postMessageAndAwaitResponse doesn't throw an exception.
337 "var plugin = document.getElementById('plugin');\n"
338 "function gotFileSystem(fs) {\n"
339 " var caught_exception = false;\n"
341 " plugin.postMessageAndAwaitResponse(fs);\n"
343 " caught_exception = true;\n"
345 " plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n"
347 "function fileSystemError() {\n"
348 " plugin.postMessage('Failed to open filesystem');\n"
350 "window.webkitRequestFileSystem(\n"
351 " window.Temporary, 1024, gotFileSystem, fileSystemError)\n");
352 instance_
->EvalScript(js_code
);
353 pp::Var msg
= handler
.WaitForAsyncMessage();
354 ASSERT_EQ(PP_VARTYPE_STRING
, msg
.pp_var().type
);
355 ASSERT_EQ("SUCCESS", msg
.AsString());
357 handler
.Unregister();
358 ASSERT_SUBTEST_SUCCESS(handler
.WaitForDestroy());
363 pp::Var
TestMessageHandler::WaitForMessage() {
364 message_received_
.Wait();
365 pp::Var var_to_return
= last_message_
;
366 last_message_
= pp::Var();
367 message_received_
.Reset();
368 return var_to_return
;