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
33REGISTER_TEST_CASE(MessageHandler);
34
35namespace {
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).
40class EchoingMessageHandler {
41 public:
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  }
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  }
77  void Unregister() {
78    AssertOnMainThread();
79    assert(is_registered_);
80    ppb_messaging_if_->UnregisterMessageHandler(pp_instance_);
81    is_registered_ = false;
82  }
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.
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:
104  static void AssertOnMainThread() {
105    assert(pp::MessageLoop::GetForMainThread() ==
106           pp::MessageLoop::GetCurrent());
107  }
108  void AddError(const std::string& error) {
109    if (!error.empty()) {
110      if (!errors_.empty())
111        errors_ += "<p>";
112      errors_ += error;
113    }
114  }
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
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
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
187void FakeHandleMessage(PP_Instance instance,
188                       void* user_data,
189                       struct PP_Var message_data) {}
190PP_Var FakeHandleBlockingMessage(PP_Instance instance,
191                                 void* user_data,
192                                 struct PP_Var message_data) {
193  return PP_MakeUndefined();
194}
195void FakeDestroy(PP_Instance instance, void* user_data) {}
196
197}  // namespace
198
199TestMessageHandler::TestMessageHandler(TestingInstance* instance)
200    : TestCase(instance),
201      ppb_messaging_if_(NULL),
202      handler_thread_(instance) {
203}
204
205TestMessageHandler::~TestMessageHandler() {
206  handler_thread_.Join();
207}
208
209bool 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
217void TestMessageHandler::RunTests(const std::string& filter) {
218  RUN_TEST(RegisterErrorConditions, filter);
219  RUN_TEST(PostMessageAndAwaitResponse, filter);
220}
221
222void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
223  // All messages should go to the background thread message handler.
224  assert(false);
225}
226
227std::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
259std::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