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
38REGISTER_TEST_CASE(MessageHandler);
39
40namespace {
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).
45class EchoingMessageHandler : public pp::MessageHandler {
46 public:
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  }
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  }
72  void Unregister() {
73    AssertOnMainThread();
74    assert(is_registered_);
75    testing_instance_->UnregisterMessageHandler();
76    is_registered_ = false;
77  }
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.
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:
99  static void AssertOnMainThread() {
100    assert(pp::MessageLoop::GetForMainThread() ==
101           pp::MessageLoop::GetCurrent());
102  }
103  void AddError(const std::string& error) {
104    if (!error.empty()) {
105      if (!errors_.empty())
106        errors_ += "<p>";
107      errors_ += error;
108    }
109  }
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
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
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
173void FakeHandleMessage(PP_Instance instance,
174                       void* user_data,
175                       const PP_Var* message_data) {}
176void FakeHandleBlockingMessage(PP_Instance instance,
177                               void* user_data,
178                               const PP_Var* message_data,
179                               PP_Var* result) {}
180void FakeDestroy(PP_Instance instance, void* user_data) {}
181
182}  // namespace
183
184TestMessageHandler::TestMessageHandler(TestingInstance* instance)
185    : TestCase(instance),
186      ppb_messaging_if_(NULL),
187      handler_thread_(instance),
188      message_received_(instance->pp_instance()) {
189}
190
191TestMessageHandler::~TestMessageHandler() {
192  handler_thread_.Join();
193}
194
195bool 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
203void TestMessageHandler::RunTests(const std::string& filter) {
204  RUN_TEST(RegisterErrorConditions, filter);
205  RUN_TEST(PostMessageAndAwaitResponse, filter);
206  RUN_TEST(Exceptions, filter);
207}
208
209void 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
224std::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
256std::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
291std::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