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
,
200 void TestImeInputEvent::GetFocusBySimulatingMouseClick() {
201 // For receiving IME events, the plugin DOM node needs to be focused.
202 // The following code is for achieving that by simulating a mouse click event.
203 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
204 PP_INPUTEVENT_CLASS_MOUSE
);
205 SimulateInputEvent(pp::MouseInputEvent(
207 PP_INPUTEVENT_TYPE_MOUSEDOWN
,
210 PP_INPUTEVENT_MOUSEBUTTON_LEFT
,
212 view_rect_
.x() + view_rect_
.width() / 2,
213 view_rect_
.y() + view_rect_
.height() / 2),
215 pp::Point())); // movement
218 // Simulates the input event and calls PostMessage to let us know when
219 // we have received all resulting events from the browser.
220 bool TestImeInputEvent::SimulateInputEvent(const pp::InputEvent
& input_event
) {
221 received_unexpected_event_
= false;
222 received_finish_message_
= false;
223 testing_interface_
->SimulateInputEvent(instance_
->pp_instance(),
224 input_event
.pp_resource());
225 instance_
->PostMessage(pp::Var(FINISHED_WAITING_MESSAGE
));
226 testing_interface_
->RunMessageLoop(instance_
->pp_instance());
227 return received_finish_message_
&& !received_unexpected_event_
;
230 bool TestImeInputEvent::AreEquivalentEvents(PP_Resource received
,
231 PP_Resource expected
) {
232 if (!input_event_interface_
->IsInputEvent(received
) ||
233 !input_event_interface_
->IsInputEvent(expected
)) {
237 // Test common fields, except modifiers and time stamp, which may be changed
239 int32_t received_type
= input_event_interface_
->GetType(received
);
240 int32_t expected_type
= input_event_interface_
->GetType(expected
);
241 if (received_type
!= expected_type
)
244 // Test event type-specific fields.
245 switch (received_type
) {
246 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START
:
247 // COMPOSITION_START does not convey further information.
250 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END
:
251 case PP_INPUTEVENT_TYPE_IME_TEXT
:
252 // For COMPOSITION_END and TEXT, GetText() has meaning.
253 return pp::Var(pp::PASS_REF
,
254 ime_input_event_interface_
->GetText(received
)) ==
255 pp::Var(pp::PASS_REF
,
256 ime_input_event_interface_
->GetText(expected
));
258 case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE
:
259 // For COMPOSITION_UPDATE, all fields must be checked.
261 uint32_t received_segment_number
=
262 ime_input_event_interface_
->GetSegmentNumber(received
);
263 uint32_t expected_segment_number
=
264 ime_input_event_interface_
->GetSegmentNumber(expected
);
265 if (received_segment_number
!= expected_segment_number
)
268 // The "<=" is not a bug. i-th segment is represented as the pair of
269 // i-th and (i+1)-th offsets in Pepper IME API.
270 for (uint32_t i
= 0; i
<= received_segment_number
; ++i
) {
271 if (ime_input_event_interface_
->GetSegmentOffset(received
, i
) !=
272 ime_input_event_interface_
->GetSegmentOffset(expected
, i
))
276 uint32_t received_selection_start
= 0;
277 uint32_t received_selection_end
= 0;
278 uint32_t expected_selection_start
= 0;
279 uint32_t expected_selection_end
= 0;
280 ime_input_event_interface_
->GetSelection(
281 received
, &received_selection_start
, &received_selection_end
);
282 ime_input_event_interface_
->GetSelection(
283 expected
, &expected_selection_start
, &expected_selection_end
);
284 if (received_selection_start
!= expected_selection_start
||
285 received_selection_end
!= expected_selection_end
) {
289 return pp::Var(pp::PASS_REF
,
290 ime_input_event_interface_
->GetText(received
)) ==
291 pp::Var(pp::PASS_REF
,
292 ime_input_event_interface_
->GetText(expected
)) &&
293 ime_input_event_interface_
->GetTargetSegment(received
) ==
294 ime_input_event_interface_
->GetTargetSegment(expected
);
297 case PP_INPUTEVENT_TYPE_CHAR
:
299 keyboard_input_event_interface_
->GetKeyCode(received
) ==
300 keyboard_input_event_interface_
->GetKeyCode(expected
) &&
301 pp::Var(pp::PASS_REF
,
302 keyboard_input_event_interface_
->GetCharacterText(received
)) ==
303 pp::Var(pp::PASS_REF
,
304 keyboard_input_event_interface_
->GetCharacterText(expected
));
312 std::string
TestImeInputEvent::TestImeCommit() {
313 GetFocusBySimulatingMouseClick();
315 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
316 PP_INPUTEVENT_CLASS_KEYBOARD
|
317 PP_INPUTEVENT_CLASS_IME
);
319 std::vector
<uint32_t> segments
;
320 segments
.push_back(0U);
321 segments
.push_back(3U);
322 segments
.push_back(7U);
323 segments
.push_back(11U);
324 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
325 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
327 expected_events_
.clear();
328 expected_events_
.push_back(CreateImeCompositionStartEvent());
329 expected_events_
.push_back(update_event
);
330 expected_events_
.push_back(CreateImeCompositionEndEvent(kCompositionText
));
331 expected_events_
.push_back(CreateImeTextEvent(kCompositionText
));
333 // Simulate the case when IME successfully committed some text.
334 ASSERT_TRUE(SimulateInputEvent(update_event
));
335 ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText
)));
337 ASSERT_TRUE(expected_events_
.empty());
341 std::string
TestImeInputEvent::TestImeCancel() {
342 GetFocusBySimulatingMouseClick();
344 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
345 PP_INPUTEVENT_CLASS_KEYBOARD
|
346 PP_INPUTEVENT_CLASS_IME
);
348 std::vector
<uint32_t> segments
;
349 segments
.push_back(0U);
350 segments
.push_back(3U);
351 segments
.push_back(7U);
352 segments
.push_back(11U);
353 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
354 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
356 expected_events_
.clear();
357 expected_events_
.push_back(CreateImeCompositionStartEvent());
358 expected_events_
.push_back(update_event
);
359 expected_events_
.push_back(CreateImeCompositionEndEvent(std::string()));
361 // Simulate the case when IME canceled composition.
362 ASSERT_TRUE(SimulateInputEvent(update_event
));
363 ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(std::string())));
365 ASSERT_TRUE(expected_events_
.empty());
369 std::string
TestImeInputEvent::TestImeUnawareCommit() {
370 GetFocusBySimulatingMouseClick();
372 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
373 PP_INPUTEVENT_CLASS_IME
);
374 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
375 PP_INPUTEVENT_CLASS_KEYBOARD
);
377 std::vector
<uint32_t> segments
;
378 segments
.push_back(0U);
379 segments
.push_back(3U);
380 segments
.push_back(7U);
381 segments
.push_back(11U);
382 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
383 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
385 expected_events_
.clear();
386 expected_events_
.push_back(CreateCharEvent(kCompositionChar
[0]));
387 expected_events_
.push_back(CreateCharEvent(kCompositionChar
[1]));
388 expected_events_
.push_back(CreateCharEvent(kCompositionChar
[2]));
390 // Test for IME-unaware plugins. Commit event is translated to char events.
391 ASSERT_TRUE(SimulateInputEvent(update_event
));
392 ASSERT_TRUE(SimulateInputEvent(CreateImeTextEvent(kCompositionText
)));
394 ASSERT_TRUE(expected_events_
.empty());
399 std::string
TestImeInputEvent::TestImeUnawareCancel() {
400 GetFocusBySimulatingMouseClick();
402 input_event_interface_
->ClearInputEventRequest(instance_
->pp_instance(),
403 PP_INPUTEVENT_CLASS_IME
);
404 input_event_interface_
->RequestInputEvents(instance_
->pp_instance(),
405 PP_INPUTEVENT_CLASS_KEYBOARD
);
407 std::vector
<uint32_t> segments
;
408 segments
.push_back(0U);
409 segments
.push_back(3U);
410 segments
.push_back(7U);
411 segments
.push_back(11U);
412 pp::InputEvent update_event
= CreateImeCompositionUpdateEvent(
413 kCompositionText
, segments
, 1, std::make_pair(3U, 7U));
415 expected_events_
.clear();
417 // Test for IME-unaware plugins. Cancel won't issue any events.
418 ASSERT_TRUE(SimulateInputEvent(update_event
));
419 ASSERT_TRUE(SimulateInputEvent(CreateImeCompositionEndEvent(std::string())));
421 ASSERT_TRUE(expected_events_
.empty());