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 |