1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "ppapi/tests/test_message_handler.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <sstream>
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb_file_io.h"
#include "ppapi/c/ppb_messaging.h"
#include "ppapi/c/ppp_message_handler.h"
#include "ppapi/cpp/completion_callback.h"
#include "ppapi/cpp/file_io.h"
#include "ppapi/cpp/file_ref.h"
#include "ppapi/cpp/file_system.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/message_handler.h"
#include "ppapi/cpp/module_impl.h"
#include "ppapi/cpp/network_list.h"
#include "ppapi/cpp/network_monitor.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array.h"
#include "ppapi/cpp/var_array_buffer.h"
#include "ppapi/cpp/var_dictionary.h"
#include "ppapi/tests/pp_thread.h"
#include "ppapi/tests/test_utils.h"
#include "ppapi/tests/testing_instance.h"
// Windows defines 'PostMessage', so we have to undef it.
#ifdef PostMessage
#undef PostMessage
#endif
REGISTER_TEST_CASE(MessageHandler);
namespace {
// Created and destroyed on the main thread. All public methods should be called
// on the main thread. Most data members are only accessed on the main thread.
// (Though it handles messages on the background thread).
class MyMessageHandler : public pp::MessageHandler {
public:
explicit MyMessageHandler(TestingInstance* instance,
const pp::MessageLoop& loop)
: testing_instance_(instance),
message_handler_loop_(loop),
is_registered_(false),
test_finished_event_(instance->pp_instance()),
destroy_event_(instance->pp_instance()),
async_message_received_(instance->pp_instance()) {
AssertOnMainThread();
}
void Register() {
AssertOnMainThread();
assert(!is_registered_);
int32_t result =
testing_instance_->RegisterMessageHandler(this, message_handler_loop_);
if (result == PP_OK) {
is_registered_ = true;
} else {
std::ostringstream stream;
stream << "Failed to register message handler; got error " << result;
AddError(stream.str());
test_finished_event_.Signal();
}
// Note, at this point, we can't safely read or write errors_ until we wait
// on destroy_event_.
}
void Unregister() {
AssertOnMainThread();
assert(is_registered_);
testing_instance_->UnregisterMessageHandler();
is_registered_ = false;
}
void WaitForTestFinishedMessage() {
test_finished_event_.Wait();
test_finished_event_.Reset();
}
// Wait for Destroy() to be called on the MessageHandler thread. When it's
// done, return any errors that occurred during the time the MessageHandler
// was getting messages.
std::string WaitForDestroy() {
AssertOnMainThread();
// If we haven't called Unregister, we'll be waiting forever.
assert(!is_registered_);
destroy_event_.Wait();
destroy_event_.Reset();
// Now that we know Destroy() has been called, we know errors_ isn't being
// written on the MessageHandler thread anymore. So we can safely read it
// here on the main thread (since destroy_event_ gave us a memory barrier).
std::string temp_errors;
errors_.swap(temp_errors);
return temp_errors;
}
pp::Var WaitForAsyncMessage() {
async_message_received_.Wait();
pp::Var var_to_return = last_async_message_received_;
last_async_message_received_ = pp::Var();
async_message_received_.Reset();
return var_to_return;
}
private:
static void AssertOnMainThread() {
assert(pp::MessageLoop::GetForMainThread() ==
pp::MessageLoop::GetCurrent());
}
void AddError(const std::string& error) {
if (!error.empty()) {
if (!errors_.empty())
errors_ += "<p>";
errors_ += error;
}
}
virtual void HandleMessage(pp::InstanceHandle instance, const pp::Var& var) {
if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
AddError("HandleMessage was called on the wrong thread!");
if (instance.pp_instance() != testing_instance_->pp_instance())
AddError("HandleMessage was passed the wrong instance!");
if (var.is_string() && var.AsString() == "FINISHED_TEST") {
test_finished_event_.Signal();
} else {
// Any client causing a message to arrive must wait for the message
// before continuing. See WaitForAsyncMessage().
assert(last_async_message_received_.is_undefined());
last_async_message_received_ = var;
async_message_received_.Signal();
}
}
virtual pp::Var HandleBlockingMessage(pp::InstanceHandle instance,
const pp::Var& var) {
if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
AddError("HandleBlockingMessage was called on the wrong thread!");
if (instance.pp_instance() != testing_instance_->pp_instance())
AddError("HandleBlockingMessage was passed the wrong instance!");
// Attempt a blocking operation; make sure it's disallowed.
pp::NetworkMonitor monitor(instance);
PP_Resource out_param = 0;
pp::CompletionCallbackWithOutput<pp::NetworkList> blocking_callback(
&out_param);
int32_t error = monitor.UpdateNetworkList(blocking_callback);
if (error != PP_ERROR_WOULD_BLOCK_THREAD) {
AddError("HandleBlockingMessage was allowed to do a blocking call!");
pp::Module::Get()->core()->ReleaseResource(out_param);
}
return var;
}
virtual void WasUnregistered(pp::InstanceHandle instance) {
if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
AddError("Destroy was called on the wrong thread!");
if (instance.pp_instance() != testing_instance_->pp_instance())
AddError("Destroy was passed the wrong instance!");
destroy_event_.Signal();
}
// These data members are initialized on the main thread, but don't change for
// the life of the object, so are safe to access on the background thread,
// because there will be a memory barrier before the the MessageHandler calls
// are invoked.
TestingInstance* const testing_instance_;
const pp::MessageLoop message_handler_loop_;
const pp::MessageLoop main_loop_;
// is_registered_ is only read/written on the main thread.
bool is_registered_;
// errors_ is written on the MessageHandler thread. When Destroy() is
// called, we stop writing to errors_ and signal destroy_event_. This causes
// a memory barrier, so it's safe to read errors_ after that.
std::string errors_;
NestedEvent test_finished_event_;
NestedEvent destroy_event_;
pp::Var last_async_message_received_;
NestedEvent async_message_received_;
// Undefined & private to disallow copy and assign.
MyMessageHandler(const MyMessageHandler&);
MyMessageHandler& operator=(const MyMessageHandler&);
};
void FakeHandleMessage(PP_Instance instance,
void* user_data,
const PP_Var* message_data) {}
void FakeHandleBlockingMessage(PP_Instance instance,
void* user_data,
const PP_Var* message_data,
PP_Var* result) {}
void FakeDestroy(PP_Instance instance, void* user_data) {}
} // namespace
TestMessageHandler::TestMessageHandler(TestingInstance* instance)
: TestCase(instance),
message_received_(instance->pp_instance()),
ppb_messaging_if_(NULL),
handler_thread_(instance) {
}
TestMessageHandler::~TestMessageHandler() {
handler_thread_.Join();
}
bool TestMessageHandler::Init() {
ppb_messaging_if_ = static_cast<const PPB_Messaging_1_2*>(
pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_2));
return ppb_messaging_if_ &&
CheckTestingInterface() &&
handler_thread_.Start();
}
void TestMessageHandler::RunTests(const std::string& filter) {
RUN_TEST(RegisterErrorConditions, filter);
RUN_TEST(PostMessageAndAwaitResponse, filter);
RUN_TEST(ArrayBuffer, filter);
RUN_TEST(Exceptions, filter);
}
void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
if (instance()->current_test_name() == "Exceptions") {
// For TestPostMessageAndAwaitResponse(), all messages should go to the
// background thread message handler.
assert(false);
} else {
// Any subtest causing a message to arrive here must wait for it before
// continuing. See WaitForMessage().
assert(last_message_.is_undefined());
last_message_ = message_data;
message_received_.Signal();
}
}
std::string TestMessageHandler::TestRegisterErrorConditions() {
{
// Test registering with the main thread as the message loop.
PPP_MessageHandler_0_2 fake_ppp_message_handler = {
&FakeHandleMessage, &FakeHandleBlockingMessage, &FakeDestroy
};
pp::MessageLoop main_loop = pp::MessageLoop::GetForMainThread();
int32_t result = ppb_messaging_if_->RegisterMessageHandler(
instance()->pp_instance(),
reinterpret_cast<void*>(0xdeadbeef),
&fake_ppp_message_handler,
main_loop.pp_resource());
ASSERT_EQ(PP_ERROR_WRONG_THREAD, result);
}
{
// Test registering with incomplete PPP_Messaging interface.
PPP_MessageHandler_0_2 bad_ppp_ifs[] = {
{ NULL, &FakeHandleBlockingMessage, &FakeDestroy },
{ &FakeHandleMessage, NULL, &FakeDestroy },
{ &FakeHandleMessage, &FakeHandleBlockingMessage, NULL }};
for (size_t i = 0; i < sizeof(bad_ppp_ifs)/sizeof(bad_ppp_ifs[0]); ++i) {
int32_t result = ppb_messaging_if_->RegisterMessageHandler(
instance()->pp_instance(),
reinterpret_cast<void*>(0xdeadbeef),
&bad_ppp_ifs[i],
handler_thread_.message_loop().pp_resource());
ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
}
}
PASS();
}
std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
MyMessageHandler handler(instance(),
handler_thread_.message_loop());
handler.Register();
std::string js_code("var plugin = document.getElementById('plugin');\n");
js_code += "var result = undefined;\n";
const char* const values_to_test[] = {
"5",
"undefined",
"1.5",
"'hello'",
"{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
NULL
};
for (size_t i = 0; values_to_test[i]; ++i) {
js_code += "result = plugin.postMessageAndAwaitResponse(";
js_code += values_to_test[i];
js_code += ");\n";
js_code += "if (!deepCompare(result, ";
js_code += values_to_test[i];
js_code += "))\n";
js_code += " InternalError(\" Failed postMessageAndAwaitResponse for: ";
js_code += values_to_test[i];
js_code += " result: \" + result);\n";
}
instance_->EvalScript(js_code);
instance_->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
handler.WaitForTestFinishedMessage();
handler.Unregister();
ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
PASS();
}
std::string TestMessageHandler::TestArrayBuffer() {
// Set the array buffer shared memory threshold so that some of the
// ArrayBuffer values will be sent as shared memory.
ScopedArrayBufferSizeSetter setter(testing_interface_,
instance_->pp_instance(),
200);
const char* const sizes[] = { "0", "128", "1024", "4096", NULL };
MyMessageHandler handler(instance(),
handler_thread_.message_loop());
handler.Register();
std::string js_code("var plugin = document.getElementById('plugin');\n");
js_code += "var result = undefined;\n";
js_code += "var param = undefined;\n";
for (size_t i = 0; sizes[i]; ++i) {
js_code += "param = new ArrayBuffer(";
js_code += sizes[i];
js_code += ");";
// TODO(dmichael): It would be better to set specific values in param.
js_code += "result = plugin.postMessageAndAwaitResponse(param);";
js_code += "if (!deepCompare(result, param))\n";
js_code += " InternalError(\" Failed postMessageAndAwaitResponse for ";
js_code += "ArrayBuffer of size: ";
js_code += sizes[i];
js_code += " result: \" + result);\n";
}
instance_->EvalScript(js_code);
instance_->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
handler.WaitForTestFinishedMessage();
handler.Unregister();
ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
PASS();
}
std::string TestMessageHandler::TestExceptions() {
MyMessageHandler handler(instance(),
handler_thread_.message_loop());
{
// First, try sending a blocking message when there is no handler
// registered. It should throw an exception.
std::string js_code(
"var plugin = document.getElementById('plugin');\n"
"var caught_exception = false;\n"
"try {\n"
" plugin.postMessageAndAwaitResponse('Hello!');\n"
"} catch (err) {\n"
" caught_exception = true;\n"
"}\n"
"plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n");
instance_->EvalScript(js_code);
// Note that we want to wait for the Instance to get the SUCCESS/FAIL
// message here. |message_handler| is not yet registered, so the message
// goes to the instance instead.
pp::Var msg = WaitForMessage();
ASSERT_TRUE(msg.is_string());
ASSERT_EQ("SUCCESS", msg.AsString());
}
handler.Register();
{
// Now that a handler is registered, try requesting and sending a
// FileSystem. It should throw an exception. The file system is opened
// asynchronously. What *should* happen is that it opens successfully, then
// we try to send it via postMessageAndAwaitResponse, which fails with an
// exception. The test could fail either because the filesystem doesn't
// open or because postMessageAndAwaitResponse doesn't throw an exception.
std::string js_code(
"var plugin = document.getElementById('plugin');\n"
"function gotFileSystem(fs) {\n"
" var caught_exception = false;\n"
" try {\n"
" plugin.postMessageAndAwaitResponse(fs);\n"
" } catch (err) {\n"
" caught_exception = true;\n"
" }\n"
" plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n"
"}\n"
"function fileSystemError() {\n"
" plugin.postMessage('Failed to open filesystem');\n"
"}\n"
"window.webkitRequestFileSystem(\n"
" window.Temporary, 1024, gotFileSystem, fileSystemError)\n");
instance_->EvalScript(js_code);
pp::Var msg = handler.WaitForAsyncMessage();
ASSERT_EQ(PP_VARTYPE_STRING, msg.pp_var().type);
ASSERT_EQ("SUCCESS", msg.AsString());
}
handler.Unregister();
ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
PASS();
}
pp::Var TestMessageHandler::WaitForMessage() {
message_received_.Wait();
pp::Var var_to_return = last_message_;
last_message_ = pp::Var();
message_received_.Reset();
return var_to_return;
}
|