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