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