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.
5 #include "ui/base/ime/input_method_chromeos.h"
15 #include "base/i18n/char_iterator.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/base/ime/chromeos/composition_text.h"
20 #include "ui/base/ime/chromeos/ime_bridge.h"
21 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
22 #include "ui/base/ime/chromeos/mock_ime_engine_handler.h"
23 #include "ui/base/ime/dummy_text_input_client.h"
24 #include "ui/base/ime/input_method_delegate.h"
25 #include "ui/base/ime/text_input_client.h"
26 #include "ui/base/ime/text_input_focus_manager.h"
27 #include "ui/base/ui_base_switches_util.h"
28 #include "ui/events/event.h"
29 #include "ui/events/keycodes/dom3/dom_code.h"
30 #include "ui/events/test/events_test_utils_x11.h"
31 #include "ui/gfx/geometry/rect.h"
33 using base::UTF8ToUTF16
;
34 using base::UTF16ToUTF8
;
39 const base::string16 kSampleText
= base::UTF8ToUTF16(
40 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
42 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
45 uint32
GetOffsetInUTF16(
46 const base::string16
& utf16_string
, uint32 utf8_offset
) {
47 DCHECK_LT(utf8_offset
, utf16_string
.size());
48 base::i18n::UTF16CharIterator
char_iterator(&utf16_string
);
49 for (size_t i
= 0; i
< utf8_offset
; ++i
)
50 char_iterator
.Advance();
51 return char_iterator
.array_pos();
54 bool IsEqualXKeyEvent(const XEvent
& e1
, const XEvent
& e2
) {
55 if ((e1
.type
== KeyPress
&& e2
.type
== KeyPress
) ||
56 (e1
.type
== KeyRelease
&& e2
.type
== KeyRelease
)) {
57 return !std::memcmp(&e1
.xkey
, &e2
.xkey
, sizeof(XKeyEvent
));
62 enum KeyEventHandlerBehavior
{
70 class TestableInputMethodChromeOS
: public InputMethodChromeOS
{
72 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate
* delegate
)
73 : InputMethodChromeOS(delegate
),
74 process_key_event_post_ime_call_count_(0) {
77 struct ProcessKeyEventPostIMEArgs
{
78 ProcessKeyEventPostIMEArgs() : event(NULL
), handled(false) {}
79 const ui::KeyEvent
* event
;
83 // Overridden from InputMethodChromeOS:
84 void ProcessKeyEventPostIME(const ui::KeyEvent
& key_event
,
85 bool handled
) override
{
86 process_key_event_post_ime_args_
.event
= &key_event
;
87 process_key_event_post_ime_args_
.handled
= handled
;
88 ++process_key_event_post_ime_call_count_
;
91 void ResetCallCount() {
92 process_key_event_post_ime_call_count_
= 0;
95 const ProcessKeyEventPostIMEArgs
& process_key_event_post_ime_args() const {
96 return process_key_event_post_ime_args_
;
99 int process_key_event_post_ime_call_count() const {
100 return process_key_event_post_ime_call_count_
;
103 // Change access rights for testing.
104 using InputMethodChromeOS::ExtractCompositionText
;
105 using InputMethodChromeOS::ResetContext
;
108 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_
;
109 int process_key_event_post_ime_call_count_
;
112 class SynchronousKeyEventHandler
{
114 SynchronousKeyEventHandler(uint32 expected_keyval
,
115 uint32 expected_keycode
,
116 uint32 expected_state
,
117 KeyEventHandlerBehavior behavior
)
118 : expected_keyval_(expected_keyval
),
119 expected_keycode_(expected_keycode
),
120 expected_state_(expected_state
),
121 behavior_(behavior
) {}
123 virtual ~SynchronousKeyEventHandler() {}
125 void Run(uint32 keyval
,
128 const KeyEventCallback
& callback
) {
129 EXPECT_EQ(expected_keyval_
, keyval
);
130 EXPECT_EQ(expected_keycode_
, keycode
);
131 EXPECT_EQ(expected_state_
, state
);
132 callback
.Run(behavior_
== KEYEVENT_CONSUME
);
136 const uint32 expected_keyval_
;
137 const uint32 expected_keycode_
;
138 const uint32 expected_state_
;
139 const KeyEventHandlerBehavior behavior_
;
141 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler
);
144 class AsynchronousKeyEventHandler
{
146 AsynchronousKeyEventHandler(uint32 expected_keyval
,
147 uint32 expected_keycode
,
148 uint32 expected_state
)
149 : expected_keyval_(expected_keyval
),
150 expected_keycode_(expected_keycode
),
151 expected_state_(expected_state
) {}
153 virtual ~AsynchronousKeyEventHandler() {}
155 void Run(uint32 keyval
,
158 const KeyEventCallback
& callback
) {
159 EXPECT_EQ(expected_keyval_
, keyval
);
160 EXPECT_EQ(expected_keycode_
, keycode
);
161 EXPECT_EQ(expected_state_
, state
);
162 callback_
= callback
;
165 void RunCallback(KeyEventHandlerBehavior behavior
) {
166 callback_
.Run(behavior
== KEYEVENT_CONSUME
);
170 const uint32 expected_keyval_
;
171 const uint32 expected_keycode_
;
172 const uint32 expected_state_
;
173 KeyEventCallback callback_
;
175 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler
);
178 class SetSurroundingTextVerifier
{
180 SetSurroundingTextVerifier(const std::string
& expected_surrounding_text
,
181 uint32 expected_cursor_position
,
182 uint32 expected_anchor_position
)
183 : expected_surrounding_text_(expected_surrounding_text
),
184 expected_cursor_position_(expected_cursor_position
),
185 expected_anchor_position_(expected_anchor_position
) {}
187 void Verify(const std::string
& text
,
190 EXPECT_EQ(expected_surrounding_text_
, text
);
191 EXPECT_EQ(expected_cursor_position_
, cursor_pos
);
192 EXPECT_EQ(expected_anchor_position_
, anchor_pos
);
196 const std::string expected_surrounding_text_
;
197 const uint32 expected_cursor_position_
;
198 const uint32 expected_anchor_position_
;
200 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier
);
203 class InputMethodChromeOSTest
: public internal::InputMethodDelegate
,
204 public testing::Test
,
205 public DummyTextInputClient
{
207 InputMethodChromeOSTest()
208 : dispatched_key_event_(ui::ET_UNKNOWN
, ui::VKEY_UNKNOWN
, ui::EF_NONE
) {
212 ~InputMethodChromeOSTest() override
{}
214 void SetUp() override
{
215 chromeos::IMEBridge::Initialize();
217 mock_ime_engine_handler_
.reset(
218 new chromeos::MockIMEEngineHandler());
219 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
220 mock_ime_engine_handler_
.get());
222 mock_ime_candidate_window_handler_
.reset(
223 new chromeos::MockIMECandidateWindowHandler());
224 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
225 mock_ime_candidate_window_handler_
.get());
227 ime_
.reset(new TestableInputMethodChromeOS(this));
228 if (switches::IsTextInputFocusManagerEnabled())
229 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
231 ime_
->SetFocusedTextInputClient(this);
234 void TearDown() override
{
236 if (switches::IsTextInputFocusManagerEnabled())
237 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
239 ime_
->SetFocusedTextInputClient(NULL
);
242 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
243 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
244 mock_ime_engine_handler_
.reset();
245 mock_ime_candidate_window_handler_
.reset();
246 chromeos::IMEBridge::Shutdown();
249 // Overridden from ui::internal::InputMethodDelegate:
250 bool DispatchKeyEventPostIME(const ui::KeyEvent
& event
) override
{
251 dispatched_key_event_
= event
;
255 // Overridden from ui::TextInputClient:
256 void SetCompositionText(const CompositionText
& composition
) override
{
257 composition_text_
= composition
;
259 void ConfirmCompositionText() override
{
260 confirmed_text_
= composition_text_
;
261 composition_text_
.Clear();
263 void ClearCompositionText() override
{ composition_text_
.Clear(); }
264 void InsertText(const base::string16
& text
) override
{
265 inserted_text_
= text
;
267 void InsertChar(base::char16 ch
, int flags
) override
{
269 inserted_char_flags_
= flags
;
271 TextInputType
GetTextInputType() const override
{ return input_type_
; }
272 TextInputMode
GetTextInputMode() const override
{ return input_mode_
; }
273 bool CanComposeInline() const override
{ return can_compose_inline_
; }
274 gfx::Rect
GetCaretBounds() const override
{ return caret_bounds_
; }
275 bool HasCompositionText() const override
{
276 CompositionText empty
;
277 return composition_text_
!= empty
;
279 bool GetTextRange(gfx::Range
* range
) const override
{
280 *range
= text_range_
;
283 bool GetSelectionRange(gfx::Range
* range
) const override
{
284 *range
= selection_range_
;
287 bool GetTextFromRange(const gfx::Range
& range
,
288 base::string16
* text
) const override
{
289 *text
= surrounding_text_
.substr(range
.GetMin(), range
.length());
292 void OnInputMethodChanged() override
{
293 ++on_input_method_changed_call_count_
;
296 bool HasNativeEvent() const {
297 return dispatched_key_event_
.HasNativeEvent();
301 dispatched_key_event_
= ui::KeyEvent(ui::ET_UNKNOWN
, ui::VKEY_UNKNOWN
,
304 composition_text_
.Clear();
305 confirmed_text_
.Clear();
306 inserted_text_
.clear();
308 inserted_char_flags_
= 0;
309 on_input_method_changed_call_count_
= 0;
311 input_type_
= TEXT_INPUT_TYPE_NONE
;
312 input_mode_
= TEXT_INPUT_MODE_DEFAULT
;
313 can_compose_inline_
= true;
314 caret_bounds_
= gfx::Rect();
317 scoped_ptr
<TestableInputMethodChromeOS
> ime_
;
319 // Copy of the dispatched key event.
320 ui::KeyEvent dispatched_key_event_
;
322 // Variables for remembering the parameters that are passed to
323 // ui::TextInputClient functions.
324 CompositionText composition_text_
;
325 CompositionText confirmed_text_
;
326 base::string16 inserted_text_
;
327 base::char16 inserted_char_
;
328 unsigned int on_input_method_changed_call_count_
;
329 int inserted_char_flags_
;
331 // Variables that will be returned from the ui::TextInputClient functions.
332 TextInputType input_type_
;
333 TextInputMode input_mode_
;
334 bool can_compose_inline_
;
335 gfx::Rect caret_bounds_
;
336 gfx::Range text_range_
;
337 gfx::Range selection_range_
;
338 base::string16 surrounding_text_
;
340 scoped_ptr
<chromeos::MockIMEEngineHandler
> mock_ime_engine_handler_
;
341 scoped_ptr
<chromeos::MockIMECandidateWindowHandler
>
342 mock_ime_candidate_window_handler_
;
344 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest
);
347 // Tests public APIs in ui::InputMethod first.
349 TEST_F(InputMethodChromeOSTest
, GetInputLocale
) {
350 // ui::InputMethodChromeOS does not support the API.
352 EXPECT_EQ("", ime_
->GetInputLocale());
355 TEST_F(InputMethodChromeOSTest
, IsActive
) {
357 // ui::InputMethodChromeOS always returns true.
358 EXPECT_TRUE(ime_
->IsActive());
361 TEST_F(InputMethodChromeOSTest
, GetInputTextType
) {
363 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
364 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
365 ime_
->OnTextInputTypeChanged(this);
366 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
367 input_type_
= TEXT_INPUT_TYPE_TEXT
;
368 ime_
->OnTextInputTypeChanged(this);
369 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
, ime_
->GetTextInputType());
372 TEST_F(InputMethodChromeOSTest
, CanComposeInline
) {
374 EXPECT_TRUE(ime_
->CanComposeInline());
375 can_compose_inline_
= false;
376 ime_
->OnTextInputTypeChanged(this);
377 EXPECT_FALSE(ime_
->CanComposeInline());
380 TEST_F(InputMethodChromeOSTest
, GetTextInputClient
) {
382 EXPECT_EQ(this, ime_
->GetTextInputClient());
383 if (switches::IsTextInputFocusManagerEnabled())
384 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
386 ime_
->SetFocusedTextInputClient(NULL
);
387 EXPECT_EQ(NULL
, ime_
->GetTextInputClient());
390 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedClient
) {
392 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
393 if (switches::IsTextInputFocusManagerEnabled())
394 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
396 ime_
->SetFocusedTextInputClient(NULL
);
397 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
398 ime_
->OnTextInputTypeChanged(this);
399 // The OnTextInputTypeChanged() call above should be ignored since |this| is
400 // not the current focused client.
401 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
403 if (switches::IsTextInputFocusManagerEnabled())
404 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
406 ime_
->SetFocusedTextInputClient(this);
407 ime_
->OnTextInputTypeChanged(this);
408 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
411 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedWindow
) {
413 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
414 if (switches::IsTextInputFocusManagerEnabled())
415 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
418 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
419 ime_
->OnTextInputTypeChanged(this);
420 // The OnTextInputTypeChanged() call above should be ignored since the top-
421 // level window which the ime_ is attached to is not currently focused.
422 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
424 if (switches::IsTextInputFocusManagerEnabled())
425 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
428 ime_
->OnTextInputTypeChanged(this);
429 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
432 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedWindow2
) {
433 // We no longer support the case that |ime_->Init(false)| because no one
435 if (switches::IsTextInputFocusManagerEnabled())
438 ime_
->Init(false); // the top-level is initially unfocused.
439 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
440 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
441 ime_
->OnTextInputTypeChanged(this);
442 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
445 ime_
->OnTextInputTypeChanged(this);
446 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
449 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
451 TEST_F(InputMethodChromeOSTest
, FocusIn_Text
) {
453 // A context shouldn't be created since the daemon is not running.
454 EXPECT_EQ(0U, on_input_method_changed_call_count_
);
455 // Click a text input form.
456 input_type_
= TEXT_INPUT_TYPE_TEXT
;
457 ime_
->OnTextInputTypeChanged(this);
458 // Since a form has focus, IBusClient::FocusIn() should be called.
459 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
462 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
463 // ui::TextInputClient::OnInputMethodChanged() should be called when
464 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
465 // current text input type is not NONE.
466 EXPECT_EQ(1U, on_input_method_changed_call_count_
);
469 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
470 // input_type_ is PASSWORD.
471 TEST_F(InputMethodChromeOSTest
, FocusIn_Password
) {
473 EXPECT_EQ(0U, on_input_method_changed_call_count_
);
474 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
475 ime_
->OnTextInputTypeChanged(this);
476 // InputMethodEngine::FocusIn() should be called even for password field.
477 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
478 EXPECT_EQ(1U, on_input_method_changed_call_count_
);
481 // Confirm that IBusClient::FocusOut is called as expected.
482 TEST_F(InputMethodChromeOSTest
, FocusOut_None
) {
483 input_type_
= TEXT_INPUT_TYPE_TEXT
;
485 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
486 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
487 input_type_
= TEXT_INPUT_TYPE_NONE
;
488 ime_
->OnTextInputTypeChanged(this);
489 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
490 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
493 // Confirm that IBusClient::FocusOut is called as expected.
494 TEST_F(InputMethodChromeOSTest
, FocusOut_Password
) {
495 input_type_
= TEXT_INPUT_TYPE_TEXT
;
497 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
498 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
499 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
500 ime_
->OnTextInputTypeChanged(this);
501 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_in_call_count());
502 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
505 // FocusIn/FocusOut scenario test
506 TEST_F(InputMethodChromeOSTest
, Focus_Scenario
) {
508 // Confirm that both FocusIn and FocusOut are NOT called.
509 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_in_call_count());
510 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
511 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
,
512 mock_ime_engine_handler_
->last_text_input_context().type
);
513 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT
,
514 mock_ime_engine_handler_
->last_text_input_context().mode
);
516 input_type_
= TEXT_INPUT_TYPE_TEXT
;
517 input_mode_
= TEXT_INPUT_MODE_LATIN
;
518 ime_
->OnTextInputTypeChanged(this);
519 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
520 // TextInputMode is LATIN..
521 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
522 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
523 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
,
524 mock_ime_engine_handler_
->last_text_input_context().type
);
525 EXPECT_EQ(TEXT_INPUT_MODE_LATIN
,
526 mock_ime_engine_handler_
->last_text_input_context().mode
);
528 input_mode_
= TEXT_INPUT_MODE_KANA
;
529 ime_
->OnTextInputTypeChanged(this);
530 // Confirm that both FocusIn and FocusOut are called for mode change.
531 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_in_call_count());
532 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
533 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
,
534 mock_ime_engine_handler_
->last_text_input_context().type
);
535 EXPECT_EQ(TEXT_INPUT_MODE_KANA
,
536 mock_ime_engine_handler_
->last_text_input_context().mode
);
538 input_type_
= TEXT_INPUT_TYPE_URL
;
539 ime_
->OnTextInputTypeChanged(this);
540 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
542 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_in_call_count());
543 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_out_call_count());
544 EXPECT_EQ(TEXT_INPUT_TYPE_URL
,
545 mock_ime_engine_handler_
->last_text_input_context().type
);
546 EXPECT_EQ(TEXT_INPUT_MODE_KANA
,
547 mock_ime_engine_handler_
->last_text_input_context().mode
);
549 // When IsTextInputFocusManagerEnabled, InputMethod::SetFocusedTextInputClient
550 // is not supported and it's no-op.
551 if (switches::IsTextInputFocusManagerEnabled())
553 // Confirm that FocusOut is called when set focus to NULL client.
554 ime_
->SetFocusedTextInputClient(NULL
);
555 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_in_call_count());
556 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_out_call_count());
557 // Confirm that FocusIn is called when set focus to this client.
558 ime_
->SetFocusedTextInputClient(this);
559 EXPECT_EQ(4, mock_ime_engine_handler_
->focus_in_call_count());
560 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_out_call_count());
563 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
564 TEST_F(InputMethodChromeOSTest
, OnCaretBoundsChanged
) {
565 input_type_
= TEXT_INPUT_TYPE_TEXT
;
569 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
570 caret_bounds_
= gfx::Rect(1, 2, 3, 4);
571 ime_
->OnCaretBoundsChanged(this);
574 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
575 caret_bounds_
= gfx::Rect(0, 2, 3, 4);
576 ime_
->OnCaretBoundsChanged(this);
579 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
580 caret_bounds_
= gfx::Rect(0, 2, 3, 4); // unchanged
581 ime_
->OnCaretBoundsChanged(this);
582 // Current InputMethodChromeOS implementation performs the IPC
583 // regardless of the bounds are changed or not.
586 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
589 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_NoAttribute
) {
590 const base::string16 kSampleAsciiText
= UTF8ToUTF16("Sample Text");
591 const uint32 kCursorPos
= 2UL;
593 chromeos::CompositionText chromeos_composition_text
;
594 chromeos_composition_text
.set_text(kSampleAsciiText
);
596 CompositionText composition_text
;
597 ime_
->ExtractCompositionText(
598 chromeos_composition_text
, kCursorPos
, &composition_text
);
599 EXPECT_EQ(kSampleAsciiText
, composition_text
.text
);
600 // If there is no selection, |selection| represents cursor position.
601 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
602 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
603 // If there is no underline, |underlines| contains one underline and it is
604 // whole text underline.
605 ASSERT_EQ(1UL, composition_text
.underlines
.size());
606 EXPECT_EQ(0UL, composition_text
.underlines
[0].start_offset
);
607 EXPECT_EQ(kSampleAsciiText
.size(), composition_text
.underlines
[0].end_offset
);
608 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
611 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_SingleUnderline
) {
612 const uint32 kCursorPos
= 2UL;
614 // Set up chromeos composition text with one underline attribute.
615 chromeos::CompositionText chromeos_composition_text
;
616 chromeos_composition_text
.set_text(kSampleText
);
617 chromeos::CompositionText::UnderlineAttribute underline
;
618 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE
;
619 underline
.start_index
= 1UL;
620 underline
.end_index
= 4UL;
621 chromeos_composition_text
.mutable_underline_attributes()->push_back(
624 CompositionText composition_text
;
625 ime_
->ExtractCompositionText(
626 chromeos_composition_text
, kCursorPos
, &composition_text
);
627 EXPECT_EQ(kSampleText
, composition_text
.text
);
628 // If there is no selection, |selection| represents cursor position.
629 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
630 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
631 ASSERT_EQ(1UL, composition_text
.underlines
.size());
632 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
633 composition_text
.underlines
[0].start_offset
);
634 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
635 composition_text
.underlines
[0].end_offset
);
636 // Single underline represents as black thin line.
637 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
638 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
639 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
640 composition_text
.underlines
[0].background_color
);
643 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_DoubleUnderline
) {
644 const uint32 kCursorPos
= 2UL;
646 // Set up chromeos composition text with one underline attribute.
647 chromeos::CompositionText chromeos_composition_text
;
648 chromeos_composition_text
.set_text(kSampleText
);
649 chromeos::CompositionText::UnderlineAttribute underline
;
650 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE
;
651 underline
.start_index
= 1UL;
652 underline
.end_index
= 4UL;
653 chromeos_composition_text
.mutable_underline_attributes()->push_back(
656 CompositionText composition_text
;
657 ime_
->ExtractCompositionText(
658 chromeos_composition_text
, kCursorPos
, &composition_text
);
659 EXPECT_EQ(kSampleText
, composition_text
.text
);
660 // If there is no selection, |selection| represents cursor position.
661 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
662 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
663 ASSERT_EQ(1UL, composition_text
.underlines
.size());
664 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
665 composition_text
.underlines
[0].start_offset
);
666 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
667 composition_text
.underlines
[0].end_offset
);
668 // Double underline represents as black thick line.
669 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
670 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
671 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
672 composition_text
.underlines
[0].background_color
);
675 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_ErrorUnderline
) {
676 const uint32 kCursorPos
= 2UL;
678 // Set up chromeos composition text with one underline attribute.
679 chromeos::CompositionText chromeos_composition_text
;
680 chromeos_composition_text
.set_text(kSampleText
);
681 chromeos::CompositionText::UnderlineAttribute underline
;
682 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR
;
683 underline
.start_index
= 1UL;
684 underline
.end_index
= 4UL;
685 chromeos_composition_text
.mutable_underline_attributes()->push_back(
688 CompositionText composition_text
;
689 ime_
->ExtractCompositionText(
690 chromeos_composition_text
, kCursorPos
, &composition_text
);
691 EXPECT_EQ(kSampleText
, composition_text
.text
);
692 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
693 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
694 ASSERT_EQ(1UL, composition_text
.underlines
.size());
695 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
696 composition_text
.underlines
[0].start_offset
);
697 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
698 composition_text
.underlines
[0].end_offset
);
699 // Error underline represents as red thin line.
700 EXPECT_EQ(SK_ColorRED
, composition_text
.underlines
[0].color
);
701 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
704 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_Selection
) {
705 const uint32 kCursorPos
= 2UL;
707 // Set up chromeos composition text with one underline attribute.
708 chromeos::CompositionText chromeos_composition_text
;
709 chromeos_composition_text
.set_text(kSampleText
);
710 chromeos_composition_text
.set_selection_start(1UL);
711 chromeos_composition_text
.set_selection_end(4UL);
713 CompositionText composition_text
;
714 ime_
->ExtractCompositionText(
715 chromeos_composition_text
, kCursorPos
, &composition_text
);
716 EXPECT_EQ(kSampleText
, composition_text
.text
);
717 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
718 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
719 ASSERT_EQ(1UL, composition_text
.underlines
.size());
720 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
721 chromeos_composition_text
.selection_start()),
722 composition_text
.underlines
[0].start_offset
);
723 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
724 chromeos_composition_text
.selection_end()),
725 composition_text
.underlines
[0].end_offset
);
726 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
727 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
728 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
729 composition_text
.underlines
[0].background_color
);
732 TEST_F(InputMethodChromeOSTest
,
733 ExtractCompositionTextTest_SelectionStartWithCursor
) {
734 const uint32 kCursorPos
= 1UL;
736 // Set up chromeos composition text with one underline attribute.
737 chromeos::CompositionText chromeos_composition_text
;
738 chromeos_composition_text
.set_text(kSampleText
);
739 chromeos_composition_text
.set_selection_start(kCursorPos
);
740 chromeos_composition_text
.set_selection_end(4UL);
742 CompositionText composition_text
;
743 ime_
->ExtractCompositionText(
744 chromeos_composition_text
, kCursorPos
, &composition_text
);
745 EXPECT_EQ(kSampleText
, composition_text
.text
);
746 // If the cursor position is same as selection bounds, selection start
747 // position become opposit side of selection from cursor.
748 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
749 chromeos_composition_text
.selection_end()),
750 composition_text
.selection
.start());
751 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, kCursorPos
),
752 composition_text
.selection
.end());
753 ASSERT_EQ(1UL, composition_text
.underlines
.size());
754 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
755 chromeos_composition_text
.selection_start()),
756 composition_text
.underlines
[0].start_offset
);
757 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
758 chromeos_composition_text
.selection_end()),
759 composition_text
.underlines
[0].end_offset
);
760 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
761 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
762 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
763 composition_text
.underlines
[0].background_color
);
766 TEST_F(InputMethodChromeOSTest
,
767 ExtractCompositionTextTest_SelectionEndWithCursor
) {
768 const uint32 kCursorPos
= 4UL;
770 // Set up chromeos composition text with one underline attribute.
771 chromeos::CompositionText chromeos_composition_text
;
772 chromeos_composition_text
.set_text(kSampleText
);
773 chromeos_composition_text
.set_selection_start(1UL);
774 chromeos_composition_text
.set_selection_end(kCursorPos
);
776 CompositionText composition_text
;
777 ime_
->ExtractCompositionText(
778 chromeos_composition_text
, kCursorPos
, &composition_text
);
779 EXPECT_EQ(kSampleText
, composition_text
.text
);
780 // If the cursor position is same as selection bounds, selection start
781 // position become opposit side of selection from cursor.
782 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
783 chromeos_composition_text
.selection_start()),
784 composition_text
.selection
.start());
785 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, kCursorPos
),
786 composition_text
.selection
.end());
787 ASSERT_EQ(1UL, composition_text
.underlines
.size());
788 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
789 chromeos_composition_text
.selection_start()),
790 composition_text
.underlines
[0].start_offset
);
791 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
792 chromeos_composition_text
.selection_end()),
793 composition_text
.underlines
[0].end_offset
);
794 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
795 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
796 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
797 composition_text
.underlines
[0].background_color
);
800 TEST_F(InputMethodChromeOSTest
, SurroundingText_NoSelectionTest
) {
802 // Click a text input form.
803 input_type_
= TEXT_INPUT_TYPE_TEXT
;
804 ime_
->OnTextInputTypeChanged(this);
806 // Set the TextInputClient behaviors.
807 surrounding_text_
= UTF8ToUTF16("abcdef");
808 text_range_
= gfx::Range(0, 6);
809 selection_range_
= gfx::Range(3, 3);
811 // Set the verifier for SetSurroundingText mock call.
812 SetSurroundingTextVerifier
verifier(UTF16ToUTF8(surrounding_text_
), 3, 3);
815 ime_
->OnCaretBoundsChanged(this);
817 // Check the call count.
819 mock_ime_engine_handler_
->set_surrounding_text_call_count());
820 EXPECT_EQ(UTF16ToUTF8(surrounding_text_
),
821 mock_ime_engine_handler_
->last_set_surrounding_text());
823 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
825 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
828 TEST_F(InputMethodChromeOSTest
, SurroundingText_SelectionTest
) {
830 // Click a text input form.
831 input_type_
= TEXT_INPUT_TYPE_TEXT
;
832 ime_
->OnTextInputTypeChanged(this);
834 // Set the TextInputClient behaviors.
835 surrounding_text_
= UTF8ToUTF16("abcdef");
836 text_range_
= gfx::Range(0, 6);
837 selection_range_
= gfx::Range(2, 5);
839 // Set the verifier for SetSurroundingText mock call.
840 SetSurroundingTextVerifier
verifier(UTF16ToUTF8(surrounding_text_
), 2, 5);
842 ime_
->OnCaretBoundsChanged(this);
844 // Check the call count.
846 mock_ime_engine_handler_
->set_surrounding_text_call_count());
847 EXPECT_EQ(UTF16ToUTF8(surrounding_text_
),
848 mock_ime_engine_handler_
->last_set_surrounding_text());
850 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
852 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
855 TEST_F(InputMethodChromeOSTest
, SurroundingText_PartialText
) {
857 // Click a text input form.
858 input_type_
= TEXT_INPUT_TYPE_TEXT
;
859 ime_
->OnTextInputTypeChanged(this);
861 // Set the TextInputClient behaviors.
862 surrounding_text_
= UTF8ToUTF16("abcdefghij");
863 text_range_
= gfx::Range(5, 10);
864 selection_range_
= gfx::Range(7, 9);
866 ime_
->OnCaretBoundsChanged(this);
868 // Check the call count.
870 mock_ime_engine_handler_
->set_surrounding_text_call_count());
871 // Set the verifier for SetSurroundingText mock call.
872 // Here (2, 4) is selection range in expected surrounding text coordinates.
874 mock_ime_engine_handler_
->last_set_surrounding_text());
876 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
878 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
881 TEST_F(InputMethodChromeOSTest
, SurroundingText_BecomeEmptyText
) {
883 // Click a text input form.
884 input_type_
= TEXT_INPUT_TYPE_TEXT
;
885 ime_
->OnTextInputTypeChanged(this);
887 // Set the TextInputClient behaviors.
888 // If the surrounding text becomes empty, text_range become (0, 0) and
889 // selection range become invalid.
890 surrounding_text_
= UTF8ToUTF16("");
891 text_range_
= gfx::Range(0, 0);
892 selection_range_
= gfx::Range::InvalidRange();
894 ime_
->OnCaretBoundsChanged(this);
896 // Check the call count.
898 mock_ime_engine_handler_
->set_surrounding_text_call_count());
900 // Should not be called twice with same condition.
901 ime_
->OnCaretBoundsChanged(this);
903 mock_ime_engine_handler_
->set_surrounding_text_call_count());
906 class InputMethodChromeOSKeyEventTest
: public InputMethodChromeOSTest
{
908 InputMethodChromeOSKeyEventTest() {}
909 ~InputMethodChromeOSKeyEventTest() override
{}
911 void SetUp() override
{
912 InputMethodChromeOSTest::SetUp();
916 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest
);
919 TEST_F(InputMethodChromeOSKeyEventTest
, KeyEventDelayResponseTest
) {
920 const int kFlags
= ui::EF_SHIFT_DOWN
;
921 ScopedXI2Event xevent
;
922 xevent
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_A
, kFlags
);
923 const ui::KeyEvent
event(xevent
);
926 input_type_
= TEXT_INPUT_TYPE_TEXT
;
927 ime_
->OnTextInputTypeChanged(this);
928 ime_
->DispatchKeyEvent(event
);
930 // Check before state.
931 const ui::KeyEvent
* key_event
=
932 mock_ime_engine_handler_
->last_processed_key_event();
933 EXPECT_EQ(1, mock_ime_engine_handler_
->process_key_event_call_count());
934 EXPECT_EQ(ui::VKEY_A
, key_event
->key_code());
935 EXPECT_EQ(ui::DomCode::KEY_A
, key_event
->code());
936 EXPECT_EQ(kFlags
, key_event
->flags());
937 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
940 mock_ime_engine_handler_
->last_passed_callback().Run(true);
943 EXPECT_EQ(1, ime_
->process_key_event_post_ime_call_count());
944 const ui::KeyEvent
* stored_event
=
945 ime_
->process_key_event_post_ime_args().event
;
946 EXPECT_TRUE(stored_event
->HasNativeEvent());
947 EXPECT_TRUE(IsEqualXKeyEvent(*xevent
, *(stored_event
->native_event())));
948 EXPECT_TRUE(ime_
->process_key_event_post_ime_args().handled
);
951 TEST_F(InputMethodChromeOSKeyEventTest
, MultiKeyEventDelayResponseTest
) {
953 input_type_
= TEXT_INPUT_TYPE_TEXT
;
954 ime_
->OnTextInputTypeChanged(this);
956 const int kFlags
= ui::EF_SHIFT_DOWN
;
957 ScopedXI2Event xevent
;
958 xevent
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_B
, kFlags
);
959 const ui::KeyEvent
event(xevent
);
962 ime_
->DispatchKeyEvent(event
);
963 const ui::KeyEvent
* key_event
=
964 mock_ime_engine_handler_
->last_processed_key_event();
965 EXPECT_EQ(ui::VKEY_B
, key_event
->key_code());
966 EXPECT_EQ(ui::DomCode::KEY_B
, key_event
->code());
967 EXPECT_EQ(kFlags
, key_event
->flags());
969 KeyEventCallback first_callback
=
970 mock_ime_engine_handler_
->last_passed_callback();
972 // Do key event again.
973 ScopedXI2Event xevent2
;
974 xevent2
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_C
, kFlags
);
975 const ui::KeyEvent
event2(xevent2
);
977 ime_
->DispatchKeyEvent(event2
);
978 const ui::KeyEvent
* key_event2
=
979 mock_ime_engine_handler_
->last_processed_key_event();
980 EXPECT_EQ(ui::VKEY_C
, key_event2
->key_code());
981 EXPECT_EQ(ui::DomCode::KEY_C
, key_event2
->code());
982 EXPECT_EQ(kFlags
, key_event2
->flags());
984 // Check before state.
986 mock_ime_engine_handler_
->process_key_event_call_count());
987 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
989 // Do callback for first key event.
990 first_callback
.Run(true);
992 // Check the results for first key event.
993 EXPECT_EQ(1, ime_
->process_key_event_post_ime_call_count());
994 const ui::KeyEvent
* stored_event
=
995 ime_
->process_key_event_post_ime_args().event
;
996 EXPECT_TRUE(stored_event
->HasNativeEvent());
997 EXPECT_TRUE(IsEqualXKeyEvent(*xevent
, *(stored_event
->native_event())));
998 EXPECT_TRUE(ime_
->process_key_event_post_ime_args().handled
);
1000 // Do callback for second key event.
1001 mock_ime_engine_handler_
->last_passed_callback().Run(false);
1003 // Check the results for second key event.
1004 EXPECT_EQ(2, ime_
->process_key_event_post_ime_call_count());
1005 stored_event
= ime_
->process_key_event_post_ime_args().event
;
1006 EXPECT_TRUE(stored_event
->HasNativeEvent());
1007 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2
, *(stored_event
->native_event())));
1008 EXPECT_FALSE(ime_
->process_key_event_post_ime_args().handled
);
1011 TEST_F(InputMethodChromeOSKeyEventTest
, KeyEventDelayResponseResetTest
) {
1012 ScopedXI2Event xevent
;
1013 xevent
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_A
, ui::EF_SHIFT_DOWN
);
1014 const ui::KeyEvent
event(xevent
);
1017 input_type_
= TEXT_INPUT_TYPE_TEXT
;
1018 ime_
->OnTextInputTypeChanged(this);
1019 ime_
->DispatchKeyEvent(event
);
1021 // Check before state.
1022 EXPECT_EQ(1, mock_ime_engine_handler_
->process_key_event_call_count());
1023 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
1025 ime_
->ResetContext();
1028 mock_ime_engine_handler_
->last_passed_callback().Run(true);
1030 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
1032 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).