• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ppapi/tests/test_message_handler.h"
6 
7 #include <string.h>
8 #include <algorithm>
9 #include <map>
10 #include <sstream>
11 
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"
32 
33 // Windows defines 'PostMessage', so we have to undef it.
34 #ifdef PostMessage
35 #undef PostMessage
36 #endif
37 
38 REGISTER_TEST_CASE(MessageHandler);
39 
40 namespace {
41 
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 EchoingMessageHandler : public pp::MessageHandler {
46  public:
EchoingMessageHandler(TestingInstance * instance,const pp::MessageLoop & loop)47   explicit EchoingMessageHandler(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     AssertOnMainThread();
55   }
Register()56   void Register() {
57     AssertOnMainThread();
58     assert(!is_registered_);
59     int32_t result =
60         testing_instance_->RegisterMessageHandler(this, message_handler_loop_);
61     if (result == PP_OK) {
62       is_registered_ = true;
63     } else {
64       std::ostringstream stream;
65       stream << "Failed to register message handler; got error " << result;
66       AddError(stream.str());
67       test_finished_event_.Signal();
68     }
69     // Note, at this point, we can't safely read or write errors_ until we wait
70     // on destroy_event_.
71   }
Unregister()72   void Unregister() {
73     AssertOnMainThread();
74     assert(is_registered_);
75     testing_instance_->UnregisterMessageHandler();
76     is_registered_ = false;
77   }
WaitForTestFinishedMessage()78   void WaitForTestFinishedMessage() {
79     test_finished_event_.Wait();
80     test_finished_event_.Reset();
81   }
82   // Wait for Destroy() to be called on the MessageHandler thread. When it's
83   // done, return any errors that occurred during the time the MessageHandler
84   // was getting messages.
WaitForDestroy()85   std::string WaitForDestroy() {
86     AssertOnMainThread();
87     // If we haven't called Unregister, we'll be waiting forever.
88     assert(!is_registered_);
89     destroy_event_.Wait();
90     destroy_event_.Reset();
91     // Now that we know Destroy() has been called, we know errors_ isn't being
92     // written on the MessageHandler thread anymore. So we can safely read it
93     // here on the main thread (since destroy_event_ gave us a memory barrier).
94     std::string temp_errors;
95     errors_.swap(temp_errors);
96     return temp_errors;
97   }
98  private:
AssertOnMainThread()99   static void AssertOnMainThread() {
100     assert(pp::MessageLoop::GetForMainThread() ==
101            pp::MessageLoop::GetCurrent());
102   }
AddError(const std::string & error)103   void AddError(const std::string& error) {
104     if (!error.empty()) {
105       if (!errors_.empty())
106         errors_ += "<p>";
107       errors_ += error;
108     }
109   }
HandleMessage(pp::InstanceHandle instance,const pp::Var & var)110   virtual void HandleMessage(pp::InstanceHandle instance, const pp::Var& var) {
111     if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
112       AddError("HandleMessage was called on the wrong thread!");
113     if (instance.pp_instance() != testing_instance_->pp_instance())
114       AddError("HandleMessage was passed the wrong instance!");
115     if (var.is_string() && var.AsString() == "FINISHED_TEST")
116       test_finished_event_.Signal();
117     else
118       testing_instance_->PostMessage(var);
119   }
120 
HandleBlockingMessage(pp::InstanceHandle instance,const pp::Var & var)121   virtual pp::Var HandleBlockingMessage(pp::InstanceHandle instance,
122                                         const pp::Var& var) {
123     if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
124       AddError("HandleBlockingMessage was called on the wrong thread!");
125     if (instance.pp_instance() != testing_instance_->pp_instance())
126       AddError("HandleBlockingMessage was passed the wrong instance!");
127 
128     // Attempt a blocking operation; make sure it's disallowed.
129     pp::NetworkMonitor monitor(instance);
130     PP_Resource out_param = 0;
131     pp::CompletionCallbackWithOutput<pp::NetworkList> blocking_callback(
132         &out_param);
133     int32_t error = monitor.UpdateNetworkList(blocking_callback);
134     if (error != PP_ERROR_WOULD_BLOCK_THREAD) {
135       AddError("HandleBlockingMessage was allowed to do a blocking call!");
136       pp::Module::Get()->core()->ReleaseResource(out_param);
137     }
138 
139     return var;
140   }
141 
WasUnregistered(pp::InstanceHandle instance)142   virtual void WasUnregistered(pp::InstanceHandle instance) {
143     if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
144       AddError("Destroy was called on the wrong thread!");
145     if (instance.pp_instance() != testing_instance_->pp_instance())
146       AddError("Destroy was passed the wrong instance!");
147     destroy_event_.Signal();
148   }
149 
150   // These data members are initialized on the main thread, but don't change for
151   // the life of the object, so are safe to access on the background thread,
152   // because there will be a memory barrier before the the MessageHandler calls
153   // are invoked.
154   TestingInstance* const testing_instance_;
155   const pp::MessageLoop message_handler_loop_;
156   const pp::MessageLoop main_loop_;
157 
158   // is_registered_ is only read/written on the main thread.
159   bool is_registered_;
160 
161   // errors_ is written on the MessageHandler thread. When Destroy() is
162   // called, we stop writing to errors_ and signal destroy_event_. This causes
163   // a memory barrier, so it's safe to read errors_ after that.
164   std::string errors_;
165   NestedEvent test_finished_event_;
166   NestedEvent destroy_event_;
167 
168   // Undefined & private to disallow copy and assign.
169   EchoingMessageHandler(const EchoingMessageHandler&);
170   EchoingMessageHandler& operator=(const EchoingMessageHandler&);
171 };
172 
FakeHandleMessage(PP_Instance instance,void * user_data,const PP_Var * message_data)173 void FakeHandleMessage(PP_Instance instance,
174                        void* user_data,
175                        const PP_Var* message_data) {}
FakeHandleBlockingMessage(PP_Instance instance,void * user_data,const PP_Var * message_data,PP_Var * result)176 void FakeHandleBlockingMessage(PP_Instance instance,
177                                void* user_data,
178                                const PP_Var* message_data,
179                                PP_Var* result) {}
FakeDestroy(PP_Instance instance,void * user_data)180 void FakeDestroy(PP_Instance instance, void* user_data) {}
181 
182 }  // namespace
183 
TestMessageHandler(TestingInstance * instance)184 TestMessageHandler::TestMessageHandler(TestingInstance* instance)
185     : TestCase(instance),
186       ppb_messaging_if_(NULL),
187       handler_thread_(instance),
188       message_received_(instance->pp_instance()) {
189 }
190 
~TestMessageHandler()191 TestMessageHandler::~TestMessageHandler() {
192   handler_thread_.Join();
193 }
194 
Init()195 bool TestMessageHandler::Init() {
196   ppb_messaging_if_ = static_cast<const PPB_Messaging_1_2*>(
197       pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_2));
198   return ppb_messaging_if_ &&
199          CheckTestingInterface() &&
200          handler_thread_.Start();
201 }
202 
RunTests(const std::string & filter)203 void TestMessageHandler::RunTests(const std::string& filter) {
204   RUN_TEST(RegisterErrorConditions, filter);
205   RUN_TEST(PostMessageAndAwaitResponse, filter);
206   RUN_TEST(Exceptions, filter);
207 }
208 
HandleMessage(const pp::Var & message_data)209 void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
210   if (instance()->current_test_name() == "Exceptions") {
211     // For TestPostMessageAndAwaitResponse(), all messages should go to the
212     // background thread message handler.
213     assert(false);
214   } else {
215     if (message_data.is_string()) {
216       last_message_ = message_data.AsString();
217     } else {
218       last_message_ = "message_data was not a string!";
219     }
220     message_received_.Signal();
221   }
222 }
223 
TestRegisterErrorConditions()224 std::string TestMessageHandler::TestRegisterErrorConditions() {
225   {
226     // Test registering with the main thread as the message loop.
227     PPP_MessageHandler_0_2 fake_ppp_message_handler = {
228       &FakeHandleMessage, &FakeHandleBlockingMessage, &FakeDestroy
229     };
230     pp::MessageLoop main_loop = pp::MessageLoop::GetForMainThread();
231     int32_t result = ppb_messaging_if_->RegisterMessageHandler(
232         instance()->pp_instance(),
233         reinterpret_cast<void*>(0xdeadbeef),
234         &fake_ppp_message_handler,
235         main_loop.pp_resource());
236     ASSERT_EQ(PP_ERROR_WRONG_THREAD, result);
237   }
238   {
239     // Test registering with incomplete PPP_Messaging interface.
240     PPP_MessageHandler_0_2 bad_ppp_ifs[] = {
241         { NULL, &FakeHandleBlockingMessage, &FakeDestroy },
242         { &FakeHandleMessage, NULL, &FakeDestroy },
243         { &FakeHandleMessage, &FakeHandleBlockingMessage, NULL }};
244     for (size_t i = 0; i < sizeof(bad_ppp_ifs)/sizeof(bad_ppp_ifs[0]); ++i) {
245       int32_t result = ppb_messaging_if_->RegisterMessageHandler(
246           instance()->pp_instance(),
247           reinterpret_cast<void*>(0xdeadbeef),
248           &bad_ppp_ifs[i],
249           handler_thread_.message_loop().pp_resource());
250       ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
251     }
252   }
253   PASS();
254 }
255 
TestPostMessageAndAwaitResponse()256 std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
257   EchoingMessageHandler handler(instance(),
258                                 handler_thread_.message_loop());
259   // Test doing a sync call before the handler is registered.
260   handler.Register();
261   std::string js_code("var plugin = document.getElementById('plugin');\n");
262   js_code += "var result = undefined;\n";
263   const char* const values_to_test[] = {
264       "5",
265       "undefined",
266       "1.5",
267       "'hello'",
268       "{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
269       NULL
270   };
271   for (size_t i = 0; values_to_test[i]; ++i) {
272     js_code += "result = plugin.postMessageAndAwaitResponse(";
273     js_code +=     values_to_test[i];
274     js_code += ");\n";
275     js_code += "if (!deepCompare(result, ";
276     js_code +=     values_to_test[i];
277     js_code += "))\n";
278     js_code += "  InternalError(\" Failed postMessageAndAwaitResponse for: ";
279     js_code +=      values_to_test[i];
280     js_code +=    " result: \" + result);\n";
281   }
282   instance_->EvalScript(js_code);
283   instance_->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
284   handler.WaitForTestFinishedMessage();
285   handler.Unregister();
286   ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
287 
288   PASS();
289 }
290 
TestExceptions()291 std::string TestMessageHandler::TestExceptions() {
292   EchoingMessageHandler handler(instance(),
293                                 handler_thread_.message_loop());
294   {
295     // First, try sending a blocking message when there is no handler
296     // registered. It should throw an exception.
297     std::string js_code(
298         "var plugin = document.getElementById('plugin');\n"
299         "var caught_exception = false;\n"
300         "try {\n"
301         "  plugin.postMessageAndAwaitResponse('Hello!');\n"
302         "} catch (err) {\n"
303         "  caught_exception = true;\n"
304         "}\n"
305         "plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n");
306     instance_->EvalScript(js_code);
307     message_received_.Wait();
308     ASSERT_EQ("SUCCESS", last_message_);
309   }
310   handler.Register();
311   {
312     // Now that a handler is registered, try requesting and sending a
313     // FileSystem. It should throw an exception. The file system is opened
314     // asynchronously. What *should* happen is that it opens successfully, then
315     // we try to send it via postMessageAndAwaitResponse, which fails with an
316     // exception. The test could fail either because the filesystem doesn't
317     // open or because postMessageAndAwaitResponse doesn't throw an exception.
318     std::string js_code(
319         "var plugin = document.getElementById('plugin');\n"
320         "function gotFileSystem(fs) {\n"
321         "  var caught_exception = false;\n"
322         "  try {\n"
323         "    plugin.postMessageAndAwaitResponse(fs);\n"
324         "  } catch (err) {\n"
325         "    caught_exception = true;\n"
326         "  }\n"
327         "  plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n"
328         "}\n"
329         "function fileSystemError() {\n"
330         "  plugin.postMessage('Failed to open filesystem');\n"
331         "}\n"
332         "window.webkitRequestFileSystem(\n"
333         "    window.Temporary, 1024, gotFileSystem, fileSystemError)\n");
334     instance_->EvalScript(js_code);
335     message_received_.Wait();
336     ASSERT_EQ("SUCCESS", last_message_);
337   }
338   handler.Unregister();
339   ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
340 
341   PASS();
342 }
343 
344