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.
5 #include "ppapi/tests/test_ime_input_event.h"
7 #include "ppapi/c/pp_errors.h"
8 #include "ppapi/c/ppb_input_event.h"
9 #include "ppapi/cpp/input_event.h"
10 #include "ppapi/cpp/module.h"
11 #include "ppapi/tests/test_utils.h"
12 #include "ppapi/tests/testing_instance.h"
14 REGISTER_TEST_CASE(ImeInputEvent
);
18 // Japanese Kanji letters
19 const char* kCompositionChar
[] = {
20 "\xE6\x96\x87", // An example character of normal unicode.
21 "\xF0\xA0\xAE\x9F", // An example character of surrogate pair.
22 "\xF0\x9F\x98\x81" // An example character of surrogate pair(emoji).
25 const char kCompositionText
[] = "\xE6\x96\x87\xF0\xA0\xAE\x9F\xF0\x9F\x98\x81";
27 #define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING"
31 TestImeInputEvent::TestImeInputEvent(TestingInstance
* 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) {
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.
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
);
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
);
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
*>(
66 pp::Module::Get()->GetBrowserInterface(
67 PPB_IME_INPUT_EVENT_INTERFACE
));
70 input_event_interface_
&&
71 keyboard_input_event_interface_
&&
72 ime_input_event_interface_
&&
73 CheckTestingInterface();
75 // Set up a listener for our message that signals that all input events have
77 // Note the following code is dependent on some features of test_case.html.
78 // E.g., it is assumed that the DOM element where the plugin is embedded has
79 // an id of 'plugin', and there is a function 'IsTestingMessage' that allows
80 // us to ignore the messages that are intended for use by the testing
83 "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
"');"
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
);
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;
109 received_unexpected_event_
=
110 !AreEquivalentEvents(input_event
.pp_resource(),
111 expected_events_
.front().pp_resource());
112 expected_events_
.erase(expected_events_
.begin());
117 // Don't care for any other input event types for this test.
121 // Handle all input events.
125 void TestImeInputEvent::HandleMessage(const pp::Var
& message_data
) {
126 if (message_data
.is_string() &&
127 (message_data
.AsString() == FINISHED_WAITING_MESSAGE
)) {
128 testing_interface_
->QuitMessageLoop(instance_
->pp_instance());
129 received_finish_message_
= true;
133 void TestImeInputEvent::DidChangeView(const pp::View
& view
) {
134 view_rect_
= view
.GetRect();
137 pp::InputEvent
TestImeInputEvent::CreateImeCompositionStartEvent() {
138 return pp::IMEInputEvent(
140 PP_INPUTEVENT_TYPE_IME_COMPOSITION_START
,
143 std::vector
<uint32_t>(),
144 -1, // target_segment
145 std::make_pair(0U, 0U) // selection
149 pp::InputEvent
TestImeInputEvent::CreateImeCompositionUpdateEvent(
150 const std::string
& text
,
151 const std::vector
<uint32_t>& segments
,
152 int32_t target_segment
,
153 const std::pair
<uint32_t, uint32_t>& selection
) {
154 return pp::IMEInputEvent(
156 PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE
,
165 pp::InputEvent
TestImeInputEvent::CreateImeCompositionEndEvent(
166 const std::string
& text
) {
167 return pp::IMEInputEvent(
169 PP_INPUTEVENT_TYPE_IME_COMPOSITION_END
,
172 std::vector
<uint32_t>(),
173 -1, // target_segment
174 std::make_pair(0U, 0U) // selection
178 pp::InputEvent
TestImeInputEvent::CreateImeTextEvent(const std::string
& text
) {
179 return pp::IMEInputEvent(
181 PP_INPUTEVENT_TYPE_IME_TEXT
,
184 std::vector
<uint32_t>(),
185 -1, // target_segment
186 std::make_pair(0U, 0U) // selection
190 pp::InputEvent
TestImeInputEvent::CreateCharEvent(const std::string
& text
) {
191 return pp::KeyboardInputEvent(
193 PP_INPUTEVENT_TYPE_CHAR
,
201 void TestImeInputEvent::GetFocusBySimulatingMouseClick() {
202 // For receiving IME events, the plugin DOM node needs to be focused.
203 // The following code is for achieving that by simulating a mouse click event.
204 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
205 PP_INPUTEVENT_CLASS_MOUSE
);
206 SimulateInputEvent(pp::MouseInputEvent(
208 PP_INPUTEVENT_TYPE_MOUSEDOWN
,
211 PP_INPUTEVENT_MOUSEBUTTON_LEFT
,
213 view_rect_
.x() + view_rect_
.width() / 2,
214 view_rect_
.y() + view_rect_
.height() / 2),
216 pp::Point())); // movement
219 // Simulates the input event and calls PostMessage to let us know when
220 // we have received all resulting events from the browser.
221 bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent
& input_event
) {
222 received_unexpected_event_
= false;
223 received_finish_message_
= false;
224 testing_interface_
->SimulateInputEvent(instance_
->pp_instance(),
225 input_event
.pp_resource());
226 instance_
->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE
));
227 testing_interface_
->RunMessageLoop(instance_
->pp_instance());
228 return received_finish_message_
&& !received_unexpected_event_
;
231 bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received
,
232 PP_Resource expected
) {
233 if (!input_event_interface_
->IsInputEvent(received
) ||
234 !input_event_interface_
->IsInputEvent(expected
)) {
238 // Test common fields, except modifiers and time stamp, which may be changed
240 int32_t received_type
= input_event_interface_
->GetType(received
);
241 int32_t expected_type
= input_event_interface_
->GetType(expected
);
242 if (received_type
!= expected_type
)
245 // Test event type-specific fields.
246 switch (received_type
) {
247 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START
:
248 // COMPOSITION_START does not convey further information.
251 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END
:
252 case PP_INPUTEVENT_TYPE_IME_TEXT
:
253 // For COMPOSITION_END and TEXT, GetText() has meaning.
254 return pp::Var(pp::PASS_REF
,
255 ime_input_event_interface_
->GetText(received
)) ==
256 pp::Var(pp::PASS_REF
,
257 ime_input_event_interface_
->GetText(expected
));
259 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE
:
260 // For COMPOSITION_UPDATE, all fields must be checked.
262 uint32_t received_segment_number
=
263 ime_input_event_interface_
->GetSegmentNumber(received
);
264 uint32_t expected_segment_number
=
265 ime_input_event_interface_
->GetSegmentNumber(expected
);
266 if (received_segment_number
!= expected_segment_number
)
269 // The "<=" is not a bug. i-th segment is represented as the pair of
270 // i-th and (i+1)-th offsets in Pepper IME API.
271 for (uint32_t i
= 0; i
<= received_segment_number
; ++i
) {
272 if (ime_input_event_interface_
->GetSegmentOffset(received
, i
) !=
273 ime_input_event_interface_
->GetSegmentOffset(expected
, i
))
277 uint32_t received_selection_start
= 0;
278 uint32_t received_selection_end
= 0;
279 uint32_t expected_selection_start
= 0;
280 uint32_t expected_selection_end
= 0;
281 ime_input_event_interface_
->GetSelection(
282 received
, &received_selection_start
, &received_selection_end
);
283 ime_input_event_interface_
->GetSelection(
284 expected
, &expected_selection_start
, &expected_selection_end
);
285 if (received_selection_start
!= expected_selection_start
||
286 received_selection_end
!= expected_selection_end
) {
290 return pp::Var(pp::PASS_REF
,
291 ime_input_event_interface_
->GetText(received
)) ==
292 pp::Var(pp::PASS_REF
,
293 ime_input_event_interface_
->GetText(expected
)) &&
294 ime_input_event_interface_
->GetTargetSegment(received
) ==
295 ime_input_event_interface_
->GetTargetSegment(expected
);
298 case PP_INPUTEVENT_TYPE_CHAR
:
300 keyboard_input_event_interface_
->GetKeyCode(received
) ==
301 keyboard_input_event_interface_
->GetKeyCode(expected
) &&
302 pp::Var(pp::PASS_REF
,
303 keyboard_input_event_interface_
->GetCharacterText(received
)) ==
304 pp::Var(pp::PASS_REF
,
305 keyboard_input_event_interface_
->GetCharacterText(expected
));
313 std::string
TestImeInputEvent::TestImeCommit() {
314 GetFocusBySimulatingMouseClick();
316 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
317 PP_INPUTEVENT_CLASS_KEYBOARD
|
318 PP_INPUTEVENT_CLASS_IME
);
320 std::vector
<uint32_t> segments
;
321 segments
.push_back(0U);
322 segments
.push_back(3U);
323 segments
.push_back(7U);
324 segments
.push_back(11U);
325 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
326 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
328 expected_events_
.clear();
329 expected_events_
.push_back(CreateImeCompositionStartEvent());
330 expected_events_
.push_back(update_event
);
331 expected_events_
.push_back(CreateImeCompositionEndEvent(kCompositionText
));
332 expected_events_
.push_back(CreateImeTextEvent(kCompositionText
));
334 // Simulate the case when IME successfully committed some text.
335 ASSERT_TRUE(SimulateInputEvent(update_event
));
336 ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText
)));
338 ASSERT_TRUE(expected_events_
.empty());
342 std::string
TestImeInputEvent::TestImeCancel() {
343 GetFocusBySimulatingMouseClick();
345 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
346 PP_INPUTEVENT_CLASS_KEYBOARD
|
347 PP_INPUTEVENT_CLASS_IME
);
349 std::vector
<uint32_t> segments
;
350 segments
.push_back(0U);
351 segments
.push_back(3U);
352 segments
.push_back(7U);
353 segments
.push_back(11U);
354 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
355 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
357 expected_events_
.clear();
358 expected_events_
.push_back(CreateImeCompositionStartEvent());
359 expected_events_
.push_back(update_event
);
360 expected_events_
.push_back(CreateImeCompositionEndEvent(std::string()));
362 // Simulate the case when IME canceled composition.
363 ASSERT_TRUE(SimulateInputEvent(update_event
));
364 ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(std::string())));
366 ASSERT_TRUE(expected_events_
.empty());
370 std::string
TestImeInputEvent::TestImeUnawareCommit() {
371 GetFocusBySimulatingMouseClick();
373 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
374 PP_INPUTEVENT_CLASS_IME
);
375 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
376 PP_INPUTEVENT_CLASS_KEYBOARD
);
378 std::vector
<uint32_t> segments
;
379 segments
.push_back(0U);
380 segments
.push_back(3U);
381 segments
.push_back(7U);
382 segments
.push_back(11U);
383 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
384 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
386 expected_events_
.clear();
387 expected_events_
.push_back(CreateCharEvent(kCompositionChar
[0]));
388 expected_events_
.push_back(CreateCharEvent(kCompositionChar
[1]));
389 expected_events_
.push_back(CreateCharEvent(kCompositionChar
[2]));
391 // Test for IME-unaware plugins. Commit event is translated to char events.
392 ASSERT_TRUE(SimulateInputEvent(update_event
));
393 ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText
)));
395 ASSERT_TRUE(expected_events_
.empty());
400 std::string
TestImeInputEvent::TestImeUnawareCancel() {
401 GetFocusBySimulatingMouseClick();
403 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
404 PP_INPUTEVENT_CLASS_IME
);
405 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
406 PP_INPUTEVENT_CLASS_KEYBOARD
);
408 std::vector
<uint32_t> segments
;
409 segments
.push_back(0U);
410 segments
.push_back(3U);
411 segments
.push_back(7U);
412 segments
.push_back(11U);
413 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
414 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
416 expected_events_
.clear();
418 // Test for IME-unaware plugins. Cancel won't issue any events.
419 ASSERT_TRUE(SimulateInputEvent(update_event
));
420 ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(std::string())));
422 ASSERT_TRUE(expected_events_
.empty());