OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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_ime_input_event.h" | |
6 | |
7 #include "ppapi/c/dev/ppb_ime_input_event_dev.h" | |
8 #include "ppapi/c/dev/ppb_testing_dev.h" | |
9 #include "ppapi/c/pp_errors.h" | |
10 #include "ppapi/c/ppb_input_event.h" | |
11 #include "ppapi/cpp/dev/ime_input_event_dev.h" | |
12 #include "ppapi/cpp/input_event.h" | |
13 #include "ppapi/cpp/module.h" | |
14 #include "ppapi/tests/test_utils.h" | |
15 #include "ppapi/tests/testing_instance.h" | |
16 | |
17 REGISTER_TEST_CASE(ImeInputEvent); | |
18 | |
19 namespace { | |
20 | |
21 const char *(kCompositionChar[]) = { | |
kochi
2012/05/18 10:18:50
I don't think you don't need parentheses and you s
kinaba
2012/05/21 02:40:15
Done.
| |
22 "\xE6\x96\x87", "\xE5\xAD\x97", "\xE5\x88\x97" | |
kochi
2012/05/18 10:18:50
Can you add a comment that describes what is the s
kinaba
2012/05/21 02:40:15
Done.
| |
23 }; | |
24 | |
25 const char kCompositionText[] = "\xE6\x96\x87\xE5\xAD\x97\xE5\x88\x97"; | |
26 | |
27 #define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING" | |
28 | |
29 } // namespace | |
30 | |
31 TestImeInputEvent::TestImeInputEvent(TestingInstance* instance) | |
32 : TestCase(instance), | |
33 input_event_interface_(NULL), | |
34 keyboard_input_event_interface_(NULL), | |
35 ime_input_event_interface_(NULL), | |
36 received_unexpected_event_(true), | |
37 received_finish_message_(false) { | |
38 } | |
39 | |
40 TestImeInputEvent::~TestImeInputEvent() { | |
41 // Remove the special listener that only responds to a | |
42 // FINISHED_WAITING_MESSAGE string. See Init for where it gets added. | |
43 std::string js_code; | |
44 js_code = "var plugin = document.getElementById('plugin');" | |
45 "plugin.removeEventListener('message'," | |
46 " plugin.wait_for_messages_handler);" | |
47 "delete plugin.wait_for_messages_handler;"; | |
48 instance_->EvalScript(js_code); | |
49 } | |
50 | |
51 void TestImeInputEvent::RunTests(const std::string& filter) { | |
52 RUN_TEST(ImeCommit, filter); | |
53 RUN_TEST(ImeCancel, filter); | |
54 RUN_TEST(ImeUnawareCommit, filter); | |
55 RUN_TEST(ImeUnawareCancel, filter); | |
56 } | |
57 | |
58 bool TestImeInputEvent::Init() { | |
59 input_event_interface_ = static_cast<const PPB_InputEvent*>( | |
60 pp::Module::Get()->GetBrowserInterface(PPB_INPUT_EVENT_INTERFACE)); | |
61 keyboard_input_event_interface_ = | |
62 static_cast<const PPB_KeyboardInputEvent*>( | |
63 pp::Module::Get()->GetBrowserInterface( | |
64 PPB_KEYBOARD_INPUT_EVENT_INTERFACE)); | |
65 ime_input_event_interface_ = static_cast<const PPB_IMEInputEvent_Dev*>( | |
66 pp::Module::Get()->GetBrowserInterface( | |
67 PPB_IME_INPUT_EVENT_DEV_INTERFACE)); | |
68 | |
69 bool success = | |
70 input_event_interface_ && | |
71 keyboard_input_event_interface_ && | |
72 ime_input_event_interface_ && | |
73 CheckTestingInterface(); | |
74 | |
75 // Set up a listener for our message that signals that all input events have | |
76 // been received. | |
77 std::string js_code; | |
kochi
2012/05/18 10:18:50
nit: why don't you just declare and substitute at
kinaba
2012/05/21 02:40:15
Done.
| |
78 // Note the following code is dependent on some features of test_case.html. | |
79 // E.g., it is assumed that the DOM element where the plugin is embedded has | |
80 // an id of 'plugin', and there is a function 'IsTestingMessage' that allows | |
81 // us to ignore the messages that are intended for use by the testing | |
82 // framework itself. | |
83 js_code = "var plugin = document.getElementById('plugin');" | |
84 "var wait_for_messages_handler = function(message_event) {" | |
85 " if (!IsTestingMessage(message_event.data) &&" | |
86 " message_event.data === '" FINISHED_WAITING_MESSAGE "') {" | |
87 " plugin.postMessage('" FINISHED_WAITING_MESSAGE "');" | |
88 " }" | |
89 "};" | |
90 "plugin.addEventListener('message', wait_for_messages_handler);" | |
91 // Stash it on the plugin so we can remove it in the destructor. | |
92 "plugin.wait_for_messages_handler = wait_for_messages_handler;"; | |
93 instance_->EvalScript(js_code); | |
94 | |
95 return success; | |
96 } | |
97 | |
98 bool TestImeInputEvent::HandleInputEvent(const pp::InputEvent& input_event) { | |
99 // Check whether the IME related events comes in the expected order. | |
100 switch (input_event.GetType()) { | |
101 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: | |
102 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: | |
103 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: | |
104 case PP_INPUTEVENT_TYPE_IME_TEXT: | |
105 case PP_INPUTEVENT_TYPE_CHAR: | |
106 if (expected_events_.empty()) { | |
107 received_unexpected_event_ = true; | |
108 } else { | |
109 received_unexpected_event_ = | |
110 !AreEquivalentEvents(input_event.pp_resource(), | |
111 expected_events_.front().pp_resource()); | |
112 expected_events_.erase(expected_events_.begin()); | |
113 } | |
114 break; | |
115 | |
116 default: | |
kochi
2012/05/18 10:18:50
Could you add a comment for ignoring other events?
kinaba
2012/05/21 02:40:15
Done.
| |
117 break; | |
118 } | |
119 | |
120 // Handle all input events. | |
121 return true; | |
122 } | |
123 | |
124 void TestImeInputEvent::HandleMessage(const pp::Var& message_data) { | |
125 if (message_data.is_string() && | |
126 (message_data.AsString() == FINISHED_WAITING_MESSAGE)) { | |
127 testing_interface_->QuitMessageLoop(instance_->pp_instance()); | |
128 received_finish_message_ = true; | |
129 } | |
130 } | |
131 | |
132 void TestImeInputEvent::DidChangeView(const pp::View& view) { | |
133 view_rect_ = view.GetRect(); | |
134 } | |
135 | |
136 pp::InputEvent TestImeInputEvent::CreateImeCompositionStartEvent() { | |
137 return pp::IMEInputEvent_Dev( | |
138 instance_, | |
139 PP_INPUTEVENT_TYPE_IME_COMPOSITION_START, | |
140 100, // time_stamp | |
141 pp::Var(""), | |
142 std::vector<uint32_t>(), | |
143 -1, // target_segment | |
144 std::make_pair(0U, 0U) // selection | |
145 ); | |
146 } | |
147 | |
148 pp::InputEvent TestImeInputEvent::CreateImeCompositionUpdateEvent( | |
149 const std::string& text, | |
150 const std::vector<uint32_t>& segments, | |
151 int32_t target_segment, | |
152 const std::pair<uint32_t, uint32_t>& selection) { | |
153 return pp::IMEInputEvent_Dev( | |
154 instance_, | |
155 PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE, | |
156 100, // time_stamp | |
157 text, | |
158 segments, | |
159 target_segment, | |
160 selection | |
161 ); | |
162 } | |
163 | |
164 pp::InputEvent TestImeInputEvent::CreateImeCompositionEndEvent( | |
165 const std::string& text) { | |
166 return pp::IMEInputEvent_Dev( | |
167 instance_, | |
168 PP_INPUTEVENT_TYPE_IME_COMPOSITION_END, | |
169 100, // time_stamp | |
170 pp::Var(text), | |
171 std::vector<uint32_t>(), | |
172 -1, // target_segment | |
173 std::make_pair(0U, 0U) // selection | |
174 ); | |
175 } | |
176 | |
177 pp::InputEvent TestImeInputEvent::CreateImeTextEvent(const std::string& text) { | |
178 return pp::IMEInputEvent_Dev( | |
179 instance_, | |
180 PP_INPUTEVENT_TYPE_IME_TEXT, | |
181 100, // time_stamp | |
182 pp::Var(text), | |
183 std::vector<uint32_t>(), | |
184 -1, // target_segment | |
185 std::make_pair(0U, 0U) // selection | |
186 ); | |
187 } | |
188 | |
189 pp::InputEvent TestImeInputEvent::CreateCharEvent(const std::string& text) { | |
190 return pp::KeyboardInputEvent( | |
191 instance_, | |
192 PP_INPUTEVENT_TYPE_CHAR, | |
193 100, // time_stamp | |
194 0, // modifiers | |
195 0, // keycode | |
196 pp::Var(text)); | |
197 } | |
198 | |
199 void TestImeInputEvent::GetFocusBySimulatingMouseClick() { | |
200 // For receiving IME events, the plugin DOM node needs to be focused. | |
201 // The following code is for achieving that by simulating a mouse click event. | |
202 input_event_interface_->RequestInputEvents(instance_->pp_instance(), | |
203 PP_INPUTEVENT_CLASS_MOUSE); | |
204 SimulateInputEvent(pp::MouseInputEvent( | |
205 instance_, | |
206 PP_INPUTEVENT_TYPE_MOUSEDOWN, | |
207 100, // time_stamp | |
208 0, // modifiers | |
209 PP_INPUTEVENT_MOUSEBUTTON_LEFT, | |
210 pp::Point( | |
211 view_rect_.x() + view_rect_.width() / 2, | |
212 view_rect_.y() + view_rect_.height() / 2), | |
213 1, // click count | |
214 pp::Point())); // movement | |
215 } | |
216 | |
217 // Simulates the input event and calls PostMessage to let us know when | |
218 // we have received all resulting events from the browser. | |
219 bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent& input_event) { | |
220 received_unexpected_event_ = false; | |
221 received_finish_message_ = false; | |
222 testing_interface_->SimulateInputEvent(instance_->pp_instance(), | |
223 input_event.pp_resource()); | |
224 instance_->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE)); | |
225 testing_interface_->RunMessageLoop(instance_->pp_instance()); | |
226 return received_finish_message_ && !received_unexpected_event_; | |
227 } | |
228 | |
229 bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received, | |
230 PP_Resource expected) { | |
231 if (!input_event_interface_->IsInputEvent(received) || | |
232 !input_event_interface_->IsInputEvent(expected)) { | |
233 return false; | |
234 } | |
235 | |
236 // Test common fields, except modifiers and time stamp, which may be changed | |
237 // by the browser. | |
238 int32_t received_type = input_event_interface_->GetType(received); | |
239 int32_t expected_type = input_event_interface_->GetType(expected); | |
240 if (received_type != expected_type) | |
241 return false; | |
242 | |
243 // Test event type-specific fields. | |
244 switch (received_type) { | |
245 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: | |
246 // COMPOSITION_START does not convey further information. | |
247 break; | |
248 | |
249 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: | |
250 case PP_INPUTEVENT_TYPE_IME_TEXT: | |
251 // For COMPOSITION_END and TEXT, GetText() has meaning. | |
252 return pp::Var(pp::PASS_REF, | |
253 ime_input_event_interface_->GetText(received)) == | |
254 pp::Var(pp::PASS_REF, | |
255 ime_input_event_interface_->GetText(expected)); | |
256 | |
257 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: | |
258 // For COMPOSITION_UPDATE, all fields must be checked. | |
259 { | |
260 uint32_t received_segment_number = | |
261 ime_input_event_interface_->GetSegmentNumber(received); | |
262 uint32_t expected_segment_number = | |
263 ime_input_event_interface_->GetSegmentNumber(expected); | |
264 if (received_segment_number != expected_segment_number) | |
265 return false; | |
266 | |
267 // The "<=" is not a bug. i-th segment is represented as the pair of | |
268 // i-th and (i+1)-th offsets in Pepper IME API. | |
269 for (uint32_t i = 0; i <= received_segment_number; ++i) { | |
270 if (ime_input_event_interface_->GetSegmentOffset(received, i) != | |
271 ime_input_event_interface_->GetSegmentOffset(expected, i)) | |
272 return false; | |
273 } | |
274 | |
275 uint32_t received_selection_start; | |
276 uint32_t received_selection_end; | |
277 uint32_t expected_selection_start; | |
278 uint32_t expected_selection_end; | |
kochi
2012/05/18 10:18:50
Usually initialization should be done for local va
kinaba
2012/05/21 02:40:15
Done.
| |
279 ime_input_event_interface_->GetSelection( | |
280 received, &received_selection_start, &received_selection_end); | |
281 ime_input_event_interface_->GetSelection( | |
282 expected, &expected_selection_start, &expected_selection_end); | |
283 if (received_selection_start != expected_selection_start || | |
284 received_selection_end != expected_selection_end) { | |
285 return true; | |
286 } | |
287 | |
288 return pp::Var(pp::PASS_REF, | |
289 ime_input_event_interface_->GetText(received)) == | |
290 pp::Var(pp::PASS_REF, | |
291 ime_input_event_interface_->GetText(expected)) && | |
292 ime_input_event_interface_->GetTargetSegment(received) == | |
293 ime_input_event_interface_->GetTargetSegment(expected); | |
294 } | |
295 | |
296 case PP_INPUTEVENT_TYPE_CHAR: | |
297 return | |
298 keyboard_input_event_interface_->GetKeyCode(received) == | |
299 keyboard_input_event_interface_->GetKeyCode(expected) && | |
300 pp::Var(pp::PASS_REF, | |
301 keyboard_input_event_interface_->GetCharacterText(received)) == | |
302 pp::Var(pp::PASS_REF, | |
303 keyboard_input_event_interface_->GetCharacterText(expected)); | |
304 | |
305 default: | |
306 break; | |
307 } | |
308 return true; | |
309 } | |
310 | |
311 std::string TestImeInputEvent::TestImeCommit() { | |
312 GetFocusBySimulatingMouseClick(); | |
313 | |
314 input_event_interface_->RequestInputEvents(instance_->pp_instance(), | |
315 PP_INPUTEVENT_CLASS_KEYBOARD | | |
316 PP_INPUTEVENT_CLASS_IME); | |
317 | |
318 std::vector<uint32_t> segments; | |
319 segments.push_back(0U); | |
320 segments.push_back(3U); | |
321 segments.push_back(6U); | |
322 segments.push_back(9U); | |
323 pp::InputEvent update_event = CreateImeCompositionUpdateEvent( | |
324 kCompositionText, segments, 1, std::make_pair(3U, 6U)); | |
325 | |
326 expected_events_.clear(); | |
327 expected_events_.push_back(CreateImeCompositionStartEvent()); | |
328 expected_events_.push_back(update_event); | |
329 expected_events_.push_back(CreateImeCompositionEndEvent(kCompositionText)); | |
330 expected_events_.push_back(CreateImeTextEvent(kCompositionText)); | |
331 | |
332 // Simulate the case when IME successfully committed some text. | |
333 ASSERT_TRUE( | |
kochi
2012/05/18 10:18:50
nit: new line unnecessary.
kinaba
2012/05/21 02:40:15
Done.
| |
334 SimulateInputEvent(update_event)); | |
335 ASSERT_TRUE( | |
kochi
2012/05/18 10:18:50
This line also should fit in one line.
kinaba
2012/05/21 02:40:15
Done.
| |
336 SimulateInputEvent(CreateImeTextEvent(kCompositionText))); | |
337 | |
338 ASSERT_TRUE(expected_events_.empty()); | |
339 PASS(); | |
340 } | |
341 | |
342 std::string TestImeInputEvent::TestImeCancel() { | |
343 GetFocusBySimulatingMouseClick(); | |
344 | |
345 input_event_interface_->RequestInputEvents(instance_->pp_instance(), | |
346 PP_INPUTEVENT_CLASS_KEYBOARD | | |
347 PP_INPUTEVENT_CLASS_IME); | |
348 | |
349 std::vector<uint32_t> segments; | |
350 segments.push_back(0U); | |
351 segments.push_back(3U); | |
352 segments.push_back(6U); | |
353 segments.push_back(9U); | |
354 pp::InputEvent update_event = CreateImeCompositionUpdateEvent( | |
355 kCompositionText, segments, 1, std::make_pair(3U, 6U)); | |
356 | |
357 expected_events_.clear(); | |
358 expected_events_.push_back(CreateImeCompositionStartEvent()); | |
359 expected_events_.push_back(update_event); | |
360 expected_events_.push_back(CreateImeCompositionEndEvent("")); | |
361 | |
362 // Simulate the case when IME canceled composition. | |
363 ASSERT_TRUE( | |
kochi
2012/05/18 10:18:50
same as line 333
kinaba
2012/05/21 02:40:15
Done.
| |
364 SimulateInputEvent(update_event)); | |
365 ASSERT_TRUE( | |
kochi
2012/05/18 10:18:50
same as line 335
kinaba
2012/05/21 02:40:15
Done. Thanks for catching them up!
| |
366 SimulateInputEvent(CreateImeCompositionEndEvent(""))); | |
367 | |
368 ASSERT_TRUE(expected_events_.empty()); | |
369 PASS(); | |
370 } | |
371 | |
372 std::string TestImeInputEvent::TestImeUnawareCommit() { | |
373 GetFocusBySimulatingMouseClick(); | |
374 | |
375 input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), | |
376 PP_INPUTEVENT_CLASS_IME); | |
377 input_event_interface_->RequestInputEvents(instance_->pp_instance(), | |
378 PP_INPUTEVENT_CLASS_KEYBOARD); | |
379 | |
380 std::vector<uint32_t> segments; | |
381 segments.push_back(0U); | |
382 segments.push_back(3U); | |
383 segments.push_back(6U); | |
384 segments.push_back(9U); | |
385 pp::InputEvent update_event = CreateImeCompositionUpdateEvent( | |
386 kCompositionText, segments, 1, std::make_pair(3U, 6U)); | |
387 | |
388 expected_events_.clear(); | |
389 expected_events_.push_back(CreateCharEvent(kCompositionChar[0])); | |
390 expected_events_.push_back(CreateCharEvent(kCompositionChar[1])); | |
391 expected_events_.push_back(CreateCharEvent(kCompositionChar[2])); | |
392 | |
393 // Test for IME-unaware plugins. Commit event is translated to char events. | |
394 ASSERT_TRUE(SimulateInputEvent(update_event)); | |
395 ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText))); | |
396 | |
397 ASSERT_TRUE(expected_events_.empty()); | |
398 PASS(); | |
399 } | |
400 | |
401 | |
402 std::string TestImeInputEvent::TestImeUnawareCancel() { | |
403 GetFocusBySimulatingMouseClick(); | |
404 | |
405 input_event_interface_->ClearInputEventRequest(instance_->pp_instance(), | |
406 PP_INPUTEVENT_CLASS_IME); | |
407 input_event_interface_->RequestInputEvents(instance_->pp_instance(), | |
408 PP_INPUTEVENT_CLASS_KEYBOARD); | |
409 | |
410 std::vector<uint32_t> segments; | |
411 segments.push_back(0U); | |
412 segments.push_back(3U); | |
413 segments.push_back(6U); | |
414 segments.push_back(9U); | |
415 pp::InputEvent update_event = CreateImeCompositionUpdateEvent( | |
416 kCompositionText, segments, 1, std::make_pair(3U, 6U)); | |
417 | |
418 expected_events_.clear(); | |
419 | |
420 // Test for IME-unaware plugins. Cancel won't issue any events. | |
421 ASSERT_TRUE(SimulateInputEvent(update_event)); | |
422 ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(""))); | |
423 | |
424 ASSERT_TRUE(expected_events_.empty()); | |
425 PASS(); | |
426 } | |
427 | |
OLD | NEW |