• Home
  • 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/ppp_message_handler.h"
15 #include "ppapi/cpp/file_io.h"
16 #include "ppapi/cpp/file_ref.h"
17 #include "ppapi/cpp/file_system.h"
18 #include "ppapi/cpp/instance.h"
19 #include "ppapi/cpp/module_impl.h"
20 #include "ppapi/cpp/var.h"
21 #include "ppapi/cpp/var_array.h"
22 #include "ppapi/cpp/var_array_buffer.h"
23 #include "ppapi/cpp/var_dictionary.h"
24 #include "ppapi/tests/pp_thread.h"
25 #include "ppapi/tests/test_utils.h"
26 #include "ppapi/tests/testing_instance.h"
27 
28 // Windows defines 'PostMessage', so we have to undef it.
29 #ifdef PostMessage
30 #undef PostMessage
31 #endif
32 
33 REGISTER_TEST_CASE(MessageHandler);
34 
35 namespace {
36 
37 // Created and destroyed on the main thread. All public methods should be called
38 // on the main thread. Most data members are only accessed on the main thread.
39 // (Though it handles messages on the background thread).
40 class EchoingMessageHandler {
41  public:
EchoingMessageHandler(PP_Instance instance,const pp::MessageLoop & loop)42   explicit EchoingMessageHandler(PP_Instance instance,
43                                  const pp::MessageLoop& loop)
44       : pp_instance_(instance),
45         message_handler_loop_(loop),
46         ppb_messaging_if_(static_cast<const PPB_Messaging_1_1*>(
47             pp::Module::Get()->GetBrowserInterface(
48                 PPB_MESSAGING_INTERFACE_1_1))),
49         ppp_message_handler_if_(),
50         is_registered_(false),
51         test_finished_event_(instance),
52         destroy_event_(instance) {
53     AssertOnMainThread();
54     ppp_message_handler_if_.HandleMessage = &HandleMessage;
55     ppp_message_handler_if_.HandleBlockingMessage = &HandleBlockingMessage;
56     ppp_message_handler_if_.Destroy = &Destroy;
57   }
Register()58   void Register() {
59     AssertOnMainThread();
60     assert(!is_registered_);
61     int32_t result = ppb_messaging_if_->RegisterMessageHandler(
62         pp_instance_,
63         this,
64         &ppp_message_handler_if_,
65         message_handler_loop_.pp_resource());
66     if (result == PP_OK) {
67       is_registered_ = true;
68     } else {
69       std::ostringstream stream;
70       stream << "Failed to register message handler; got error " << result;
71       AddError(stream.str());
72       test_finished_event_.Signal();
73     }
74     // Note, at this point, we can't safely read or write errors_ until we wait
75     // on destroy_event_.
76   }
Unregister()77   void Unregister() {
78     AssertOnMainThread();
79     assert(is_registered_);
80     ppb_messaging_if_->UnregisterMessageHandler(pp_instance_);
81     is_registered_ = false;
82   }
WaitForTestFinishedMessage()83   void WaitForTestFinishedMessage() {
84     test_finished_event_.Wait();
85     test_finished_event_.Reset();
86   }
87   // Wait for Destroy() to be called on the MessageHandler thread. When it's
88   // done, return any errors that occurred during the time the MessageHandler
89   // was getting messages.
WaitForDestroy()90   std::string WaitForDestroy() {
91     AssertOnMainThread();
92     // If we haven't called Unregister, we'll be waiting forever.
93     assert(!is_registered_);
94     destroy_event_.Wait();
95     destroy_event_.Reset();
96     // Now that we know Destroy() has been called, we know errors_ isn't being
97     // written on the MessageHandler thread anymore. So we can safely read it
98     // here on the main thread (since destroy_event_ gave us a memory barrier).
99     std::string temp_errors;
100     errors_.swap(temp_errors);
101     return temp_errors;
102   }
103  private:
AssertOnMainThread()104   static void AssertOnMainThread() {
105     assert(pp::MessageLoop::GetForMainThread() ==
106            pp::MessageLoop::GetCurrent());
107   }
AddError(const std::string & error)108   void AddError(const std::string& error) {
109     if (!error.empty()) {
110       if (!errors_.empty())
111         errors_ += "<p>";
112       errors_ += error;
113     }
114   }
HandleMessage(PP_Instance instance,void * user_data,struct PP_Var message_data)115   static void HandleMessage(PP_Instance instance,
116                             void* user_data,
117                             struct PP_Var message_data) {
118     EchoingMessageHandler* thiz =
119         static_cast<EchoingMessageHandler*>(user_data);
120     if (pp::MessageLoop::GetCurrent() != thiz->message_handler_loop_)
121       thiz->AddError("HandleMessage was called on the wrong thread!");
122     if (instance != thiz->pp_instance_)
123       thiz->AddError("HandleMessage was passed the wrong instance!");
124     pp::Var var(message_data);
125     if (var.is_string() && var.AsString() == "FINISHED_TEST")
126       thiz->test_finished_event_.Signal();
127     else
128       thiz->ppb_messaging_if_->PostMessage(instance, message_data);
129   }
130 
HandleBlockingMessage(PP_Instance instance,void * user_data,struct PP_Var message_data)131   static PP_Var HandleBlockingMessage(PP_Instance instance,
132                                       void* user_data,
133                                       struct PP_Var message_data) {
134     EchoingMessageHandler* thiz =
135         static_cast<EchoingMessageHandler*>(user_data);
136     if (pp::MessageLoop::GetCurrent() != thiz->message_handler_loop_)
137       thiz->AddError("HandleBlockingMessage was called on the wrong thread!");
138     if (instance != thiz->pp_instance_)
139       thiz->AddError("HandleBlockingMessage was passed the wrong instance!");
140 
141     // The PP_Var we are passed is an in-parameter, so the browser is not
142     // giving us a ref-count. The ref-count it has will be decremented after we
143     // return. But we need to add a ref when returning a PP_Var, to pass to the
144     // caller.
145     pp::Var take_ref(message_data);
146     take_ref.Detach();
147     return message_data;
148   }
149 
Destroy(PP_Instance instance,void * user_data)150   static void Destroy(PP_Instance instance, void* user_data) {
151     EchoingMessageHandler* thiz =
152         static_cast<EchoingMessageHandler*>(user_data);
153     if (pp::MessageLoop::GetCurrent() != thiz->message_handler_loop_)
154       thiz->AddError("Destroy was called on the wrong thread!");
155     if (instance != thiz->pp_instance_)
156       thiz->AddError("Destroy was passed the wrong instance!");
157     thiz->destroy_event_.Signal();
158   }
159 
160   // These data members are initialized on the main thread, but don't change for
161   // the life of the object, so are safe to access on the background thread,
162   // because there will be a memory barrier before the the MessageHandler calls
163   // are invoked.
164   const PP_Instance pp_instance_;
165   const pp::MessageLoop message_handler_loop_;
166   const pp::MessageLoop main_loop_;
167   const PPB_Messaging_1_1* const ppb_messaging_if_;
168   // Spiritually, this member is const, but we can't initialize it in C++03,
169   // so it has to be non-const to be set in the constructor body.
170   PPP_MessageHandler_0_1 ppp_message_handler_if_;
171 
172   // is_registered_ is only read/written on the main thread.
173   bool is_registered_;
174 
175   // errors_ is written on the MessageHandler thread. When Destroy() is
176   // called, we stop writing to errors_ and signal destroy_event_. This causes
177   // a memory barrier, so it's safe to read errors_ after that.
178   std::string errors_;
179   NestedEvent test_finished_event_;
180   NestedEvent destroy_event_;
181 
182   // Undefined & private to disallow copy and assign.
183   EchoingMessageHandler(const EchoingMessageHandler&);
184   EchoingMessageHandler& operator=(const EchoingMessageHandler&);
185 };
186 
FakeHandleMessage(PP_Instance instance,void * user_data,struct PP_Var message_data)187 void FakeHandleMessage(PP_Instance instance,
188                        void* user_data,
189                        struct PP_Var message_data) {}
FakeHandleBlockingMessage(PP_Instance instance,void * user_data,struct PP_Var message_data)190 PP_Var FakeHandleBlockingMessage(PP_Instance instance,
191                                  void* user_data,
192                                  struct PP_Var message_data) {
193   return PP_MakeUndefined();
194 }
FakeDestroy(PP_Instance instance,void * user_data)195 void FakeDestroy(PP_Instance instance, void* user_data) {}
196 
197 }  // namespace
198 
TestMessageHandler(TestingInstance * instance)199 TestMessageHandler::TestMessageHandler(TestingInstance* instance)
200     : TestCase(instance),
201       ppb_messaging_if_(NULL),
202       handler_thread_(instance) {
203 }
204 
~TestMessageHandler()205 TestMessageHandler::~TestMessageHandler() {
206   handler_thread_.Join();
207 }
208 
Init()209 bool TestMessageHandler::Init() {
210   ppb_messaging_if_ = static_cast<const PPB_Messaging_1_1*>(
211       pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_1));
212   return ppb_messaging_if_ &&
213          CheckTestingInterface() &&
214          handler_thread_.Start();
215 }
216 
RunTests(const std::string & filter)217 void TestMessageHandler::RunTests(const std::string& filter) {
218   RUN_TEST(RegisterErrorConditions, filter);
219   RUN_TEST(PostMessageAndAwaitResponse, filter);
220 }
221 
HandleMessage(const pp::Var & message_data)222 void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
223   // All messages should go to the background thread message handler.
224   assert(false);
225 }
226 
TestRegisterErrorConditions()227 std::string TestMessageHandler::TestRegisterErrorConditions() {
228   {
229     // Test registering with the main thread as the message loop.
230     PPP_MessageHandler_0_1 fake_ppp_message_handler = {
231       &FakeHandleMessage, &FakeHandleBlockingMessage, &FakeDestroy
232     };
233     pp::MessageLoop main_loop = pp::MessageLoop::GetForMainThread();
234     int32_t result = ppb_messaging_if_->RegisterMessageHandler(
235         instance()->pp_instance(),
236         reinterpret_cast<void*>(0xdeadbeef),
237         &fake_ppp_message_handler,
238         main_loop.pp_resource());
239     ASSERT_EQ(PP_ERROR_WRONG_THREAD, result);
240   }
241   {
242     // Test registering with incomplete PPP_Messaging interface.
243     PPP_MessageHandler_0_1 bad_ppp_ifs[] = {
244         { NULL, &FakeHandleBlockingMessage, &FakeDestroy },
245         { &FakeHandleMessage, NULL, &FakeDestroy },
246         { &FakeHandleMessage, &FakeHandleBlockingMessage, NULL }};
247     for (size_t i = 0; i < sizeof(bad_ppp_ifs)/sizeof(bad_ppp_ifs[0]); ++i) {
248       int32_t result = ppb_messaging_if_->RegisterMessageHandler(
249           instance()->pp_instance(),
250           reinterpret_cast<void*>(0xdeadbeef),
251           &bad_ppp_ifs[i],
252           handler_thread_.message_loop().pp_resource());
253       ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
254     }
255   }
256   PASS();
257 }
258 
TestPostMessageAndAwaitResponse()259 std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
260   EchoingMessageHandler handler(instance()->pp_instance(),
261                                 handler_thread_.message_loop());
262   handler.Register();
263   std::string js_code("var plugin = document.getElementById('plugin');\n");
264   js_code += "var result = undefined;\n";
265   const char* const values_to_test[] = {
266       "5",
267       "undefined",
268       "1.5",
269       "'hello'",
270       "{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
271       NULL
272   };
273   for (size_t i = 0; values_to_test[i]; ++i) {
274     js_code += "result = plugin.postMessageAndAwaitResponse(";
275     js_code +=     values_to_test[i];
276     js_code += ");\n";
277     js_code += "if (!deepCompare(result, ";
278     js_code +=     values_to_test[i];
279     js_code += "))\n";
280     js_code += "  InternalError(\" Failed postMessageAndAwaitResponse for: ";
281     js_code +=      values_to_test[i];
282     js_code +=    " result: \" + result);\n";
283   }
284   // TODO(dmichael): Setting a property uses GetInstanceObject, which sends sync
285   // message, which can get interrupted with message to eval script, etc.
286   // FINISHED_WAITING message can therefore jump ahead. This test is
287   // currently carefully crafted to avoid races by doing all the JS in one call.
288   // That should be fixed before this API goes to stable. See crbug.com/384528
289   js_code += "plugin.postMessage('FINISHED_TEST');\n";
290   instance_->EvalScript(js_code);
291   handler.WaitForTestFinishedMessage();
292   handler.Unregister();
293   ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
294 
295   PASS();
296 }
297 
298