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 "chromeos/ime/composition_text.h"
19 #include "testing/gtest/include/gtest/gtest.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/input_method_delegate.h"
24 #include "ui/base/ime/text_input_client.h"
25 #include "ui/base/ime/text_input_focus_manager.h"
26 #include "ui/base/ui_base_switches_util.h"
27 #include "ui/events/event.h"
28 #include "ui/events/test/events_test_utils_x11.h"
29 #include "ui/gfx/geometry/rect.h"
31 using base::UTF8ToUTF16
;
32 using base::UTF16ToUTF8
;
37 const base::string16 kSampleText
= base::UTF8ToUTF16(
38 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
40 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
43 uint32
GetOffsetInUTF16(
44 const base::string16
& utf16_string
, uint32 utf8_offset
) {
45 DCHECK_LT(utf8_offset
, utf16_string
.size());
46 base::i18n::UTF16CharIterator
char_iterator(&utf16_string
);
47 for (size_t i
= 0; i
< utf8_offset
; ++i
)
48 char_iterator
.Advance();
49 return char_iterator
.array_pos();
52 bool IsEqualXKeyEvent(const XEvent
& e1
, const XEvent
& e2
) {
53 if ((e1
.type
== KeyPress
&& e2
.type
== KeyPress
) ||
54 (e1
.type
== KeyRelease
&& e2
.type
== KeyRelease
)) {
55 return !std::memcmp(&e1
.xkey
, &e2
.xkey
, sizeof(XKeyEvent
));
60 enum KeyEventHandlerBehavior
{
68 class TestableInputMethodChromeOS
: public InputMethodChromeOS
{
70 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate
* delegate
)
71 : InputMethodChromeOS(delegate
),
72 process_key_event_post_ime_call_count_(0) {
75 struct ProcessKeyEventPostIMEArgs
{
76 ProcessKeyEventPostIMEArgs() : event(NULL
), handled(false) {}
77 const ui::KeyEvent
* event
;
81 // Overridden from InputMethodChromeOS:
82 virtual void ProcessKeyEventPostIME(const ui::KeyEvent
& key_event
,
83 bool handled
) OVERRIDE
{
84 process_key_event_post_ime_args_
.event
= &key_event
;
85 process_key_event_post_ime_args_
.handled
= handled
;
86 ++process_key_event_post_ime_call_count_
;
89 void ResetCallCount() {
90 process_key_event_post_ime_call_count_
= 0;
93 const ProcessKeyEventPostIMEArgs
& process_key_event_post_ime_args() const {
94 return process_key_event_post_ime_args_
;
97 int process_key_event_post_ime_call_count() const {
98 return process_key_event_post_ime_call_count_
;
101 // Change access rights for testing.
102 using InputMethodChromeOS::ExtractCompositionText
;
103 using InputMethodChromeOS::ResetContext
;
106 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_
;
107 int process_key_event_post_ime_call_count_
;
110 class SynchronousKeyEventHandler
{
112 SynchronousKeyEventHandler(uint32 expected_keyval
,
113 uint32 expected_keycode
,
114 uint32 expected_state
,
115 KeyEventHandlerBehavior behavior
)
116 : expected_keyval_(expected_keyval
),
117 expected_keycode_(expected_keycode
),
118 expected_state_(expected_state
),
119 behavior_(behavior
) {}
121 virtual ~SynchronousKeyEventHandler() {}
123 void Run(uint32 keyval
,
126 const KeyEventCallback
& callback
) {
127 EXPECT_EQ(expected_keyval_
, keyval
);
128 EXPECT_EQ(expected_keycode_
, keycode
);
129 EXPECT_EQ(expected_state_
, state
);
130 callback
.Run(behavior_
== KEYEVENT_CONSUME
);
134 const uint32 expected_keyval_
;
135 const uint32 expected_keycode_
;
136 const uint32 expected_state_
;
137 const KeyEventHandlerBehavior behavior_
;
139 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler
);
142 class AsynchronousKeyEventHandler
{
144 AsynchronousKeyEventHandler(uint32 expected_keyval
,
145 uint32 expected_keycode
,
146 uint32 expected_state
)
147 : expected_keyval_(expected_keyval
),
148 expected_keycode_(expected_keycode
),
149 expected_state_(expected_state
) {}
151 virtual ~AsynchronousKeyEventHandler() {}
153 void Run(uint32 keyval
,
156 const KeyEventCallback
& callback
) {
157 EXPECT_EQ(expected_keyval_
, keyval
);
158 EXPECT_EQ(expected_keycode_
, keycode
);
159 EXPECT_EQ(expected_state_
, state
);
160 callback_
= callback
;
163 void RunCallback(KeyEventHandlerBehavior behavior
) {
164 callback_
.Run(behavior
== KEYEVENT_CONSUME
);
168 const uint32 expected_keyval_
;
169 const uint32 expected_keycode_
;
170 const uint32 expected_state_
;
171 KeyEventCallback callback_
;
173 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler
);
176 class SetSurroundingTextVerifier
{
178 SetSurroundingTextVerifier(const std::string
& expected_surrounding_text
,
179 uint32 expected_cursor_position
,
180 uint32 expected_anchor_position
)
181 : expected_surrounding_text_(expected_surrounding_text
),
182 expected_cursor_position_(expected_cursor_position
),
183 expected_anchor_position_(expected_anchor_position
) {}
185 void Verify(const std::string
& text
,
188 EXPECT_EQ(expected_surrounding_text_
, text
);
189 EXPECT_EQ(expected_cursor_position_
, cursor_pos
);
190 EXPECT_EQ(expected_anchor_position_
, anchor_pos
);
194 const std::string expected_surrounding_text_
;
195 const uint32 expected_cursor_position_
;
196 const uint32 expected_anchor_position_
;
198 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier
);
201 class InputMethodChromeOSTest
: public internal::InputMethodDelegate
,
202 public testing::Test
,
203 public TextInputClient
{
205 InputMethodChromeOSTest()
206 : dispatched_key_event_(ui::ET_UNKNOWN
, ui::VKEY_UNKNOWN
, ui::EF_NONE
) {
210 virtual ~InputMethodChromeOSTest() {
213 virtual void SetUp() OVERRIDE
{
214 chromeos::IMEBridge::Initialize();
216 mock_ime_engine_handler_
.reset(
217 new chromeos::MockIMEEngineHandler());
218 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
219 mock_ime_engine_handler_
.get());
221 mock_ime_candidate_window_handler_
.reset(
222 new chromeos::MockIMECandidateWindowHandler());
223 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
224 mock_ime_candidate_window_handler_
.get());
226 ime_
.reset(new TestableInputMethodChromeOS(this));
227 if (switches::IsTextInputFocusManagerEnabled())
228 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
230 ime_
->SetFocusedTextInputClient(this);
233 virtual void TearDown() OVERRIDE
{
235 if (switches::IsTextInputFocusManagerEnabled())
236 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
238 ime_
->SetFocusedTextInputClient(NULL
);
241 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
242 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
243 mock_ime_engine_handler_
.reset();
244 mock_ime_candidate_window_handler_
.reset();
245 chromeos::IMEBridge::Shutdown();
248 // Overridden from ui::internal::InputMethodDelegate:
249 virtual bool DispatchKeyEventPostIME(const ui::KeyEvent
& event
) OVERRIDE
{
250 dispatched_key_event_
= event
;
254 // Overridden from ui::TextInputClient:
255 virtual void SetCompositionText(
256 const CompositionText
& composition
) OVERRIDE
{
257 composition_text_
= composition
;
259 virtual void ConfirmCompositionText() OVERRIDE
{
260 confirmed_text_
= composition_text_
;
261 composition_text_
.Clear();
263 virtual void ClearCompositionText() OVERRIDE
{
264 composition_text_
.Clear();
266 virtual void InsertText(const base::string16
& text
) OVERRIDE
{
267 inserted_text_
= text
;
269 virtual void InsertChar(base::char16 ch
, int flags
) OVERRIDE
{
271 inserted_char_flags_
= flags
;
273 virtual gfx::NativeWindow
GetAttachedWindow() const OVERRIDE
{
274 return static_cast<gfx::NativeWindow
>(NULL
);
276 virtual TextInputType
GetTextInputType() const OVERRIDE
{
279 virtual TextInputMode
GetTextInputMode() const OVERRIDE
{
282 virtual bool CanComposeInline() const OVERRIDE
{
283 return can_compose_inline_
;
285 virtual gfx::Rect
GetCaretBounds() const OVERRIDE
{
286 return caret_bounds_
;
288 virtual bool GetCompositionCharacterBounds(uint32 index
,
289 gfx::Rect
* rect
) const OVERRIDE
{
292 virtual bool HasCompositionText() const OVERRIDE
{
293 CompositionText empty
;
294 return composition_text_
!= empty
;
296 virtual bool GetTextRange(gfx::Range
* range
) const OVERRIDE
{
297 *range
= text_range_
;
300 virtual bool GetCompositionTextRange(gfx::Range
* range
) const OVERRIDE
{
303 virtual bool GetSelectionRange(gfx::Range
* range
) const OVERRIDE
{
304 *range
= selection_range_
;
308 virtual bool SetSelectionRange(const gfx::Range
& range
) OVERRIDE
{
311 virtual bool DeleteRange(const gfx::Range
& range
) OVERRIDE
{ return false; }
312 virtual bool GetTextFromRange(const gfx::Range
& range
,
313 base::string16
* text
) const OVERRIDE
{
314 *text
= surrounding_text_
.substr(range
.GetMin(), range
.length());
317 virtual void OnInputMethodChanged() OVERRIDE
{
318 ++on_input_method_changed_call_count_
;
320 virtual bool ChangeTextDirectionAndLayoutAlignment(
321 base::i18n::TextDirection direction
) OVERRIDE
{ return false; }
322 virtual void ExtendSelectionAndDelete(size_t before
,
323 size_t after
) OVERRIDE
{}
324 virtual void EnsureCaretInRect(const gfx::Rect
& rect
) OVERRIDE
{}
325 virtual void OnCandidateWindowShown() OVERRIDE
{}
326 virtual void OnCandidateWindowUpdated() OVERRIDE
{}
327 virtual void OnCandidateWindowHidden() OVERRIDE
{}
328 virtual bool IsEditingCommandEnabled(int command_id
) OVERRIDE
{
331 virtual void ExecuteEditingCommand(int command_id
) OVERRIDE
{}
333 bool HasNativeEvent() const {
334 return dispatched_key_event_
.HasNativeEvent();
338 dispatched_key_event_
= ui::KeyEvent(ui::ET_UNKNOWN
, ui::VKEY_UNKNOWN
,
341 composition_text_
.Clear();
342 confirmed_text_
.Clear();
343 inserted_text_
.clear();
345 inserted_char_flags_
= 0;
346 on_input_method_changed_call_count_
= 0;
348 input_type_
= TEXT_INPUT_TYPE_NONE
;
349 input_mode_
= TEXT_INPUT_MODE_DEFAULT
;
350 can_compose_inline_
= true;
351 caret_bounds_
= gfx::Rect();
354 scoped_ptr
<TestableInputMethodChromeOS
> ime_
;
356 // Copy of the dispatched key event.
357 ui::KeyEvent dispatched_key_event_
;
359 // Variables for remembering the parameters that are passed to
360 // ui::TextInputClient functions.
361 CompositionText composition_text_
;
362 CompositionText confirmed_text_
;
363 base::string16 inserted_text_
;
364 base::char16 inserted_char_
;
365 unsigned int on_input_method_changed_call_count_
;
366 int inserted_char_flags_
;
368 // Variables that will be returned from the ui::TextInputClient functions.
369 TextInputType input_type_
;
370 TextInputMode input_mode_
;
371 bool can_compose_inline_
;
372 gfx::Rect caret_bounds_
;
373 gfx::Range text_range_
;
374 gfx::Range selection_range_
;
375 base::string16 surrounding_text_
;
377 scoped_ptr
<chromeos::MockIMEEngineHandler
> mock_ime_engine_handler_
;
378 scoped_ptr
<chromeos::MockIMECandidateWindowHandler
>
379 mock_ime_candidate_window_handler_
;
381 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest
);
384 // Tests public APIs in ui::InputMethod first.
386 TEST_F(InputMethodChromeOSTest
, GetInputLocale
) {
387 // ui::InputMethodChromeOS does not support the API.
389 EXPECT_EQ("", ime_
->GetInputLocale());
392 TEST_F(InputMethodChromeOSTest
, IsActive
) {
394 // ui::InputMethodChromeOS always returns true.
395 EXPECT_TRUE(ime_
->IsActive());
398 TEST_F(InputMethodChromeOSTest
, GetInputTextType
) {
400 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
401 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
402 ime_
->OnTextInputTypeChanged(this);
403 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
404 input_type_
= TEXT_INPUT_TYPE_TEXT
;
405 ime_
->OnTextInputTypeChanged(this);
406 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
, ime_
->GetTextInputType());
409 TEST_F(InputMethodChromeOSTest
, CanComposeInline
) {
411 EXPECT_TRUE(ime_
->CanComposeInline());
412 can_compose_inline_
= false;
413 ime_
->OnTextInputTypeChanged(this);
414 EXPECT_FALSE(ime_
->CanComposeInline());
417 TEST_F(InputMethodChromeOSTest
, GetTextInputClient
) {
419 EXPECT_EQ(this, ime_
->GetTextInputClient());
420 if (switches::IsTextInputFocusManagerEnabled())
421 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
423 ime_
->SetFocusedTextInputClient(NULL
);
424 EXPECT_EQ(NULL
, ime_
->GetTextInputClient());
427 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedClient
) {
429 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
430 if (switches::IsTextInputFocusManagerEnabled())
431 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
433 ime_
->SetFocusedTextInputClient(NULL
);
434 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
435 ime_
->OnTextInputTypeChanged(this);
436 // The OnTextInputTypeChanged() call above should be ignored since |this| is
437 // not the current focused client.
438 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
440 if (switches::IsTextInputFocusManagerEnabled())
441 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
443 ime_
->SetFocusedTextInputClient(this);
444 ime_
->OnTextInputTypeChanged(this);
445 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
448 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedWindow
) {
450 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
451 if (switches::IsTextInputFocusManagerEnabled())
452 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
455 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
456 ime_
->OnTextInputTypeChanged(this);
457 // The OnTextInputTypeChanged() call above should be ignored since the top-
458 // level window which the ime_ is attached to is not currently focused.
459 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
461 if (switches::IsTextInputFocusManagerEnabled())
462 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
465 ime_
->OnTextInputTypeChanged(this);
466 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
469 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedWindow2
) {
470 // We no longer support the case that |ime_->Init(false)| because no one
472 if (switches::IsTextInputFocusManagerEnabled())
475 ime_
->Init(false); // the top-level is initially unfocused.
476 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
477 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
478 ime_
->OnTextInputTypeChanged(this);
479 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
482 ime_
->OnTextInputTypeChanged(this);
483 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
486 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
488 TEST_F(InputMethodChromeOSTest
, FocusIn_Text
) {
490 // A context shouldn't be created since the daemon is not running.
491 EXPECT_EQ(0U, on_input_method_changed_call_count_
);
492 // Click a text input form.
493 input_type_
= TEXT_INPUT_TYPE_TEXT
;
494 ime_
->OnTextInputTypeChanged(this);
495 // Since a form has focus, IBusClient::FocusIn() should be called.
496 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
499 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
500 // ui::TextInputClient::OnInputMethodChanged() should be called when
501 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
502 // current text input type is not NONE.
503 EXPECT_EQ(1U, on_input_method_changed_call_count_
);
506 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
507 // input_type_ is PASSWORD.
508 TEST_F(InputMethodChromeOSTest
, FocusIn_Password
) {
510 EXPECT_EQ(0U, on_input_method_changed_call_count_
);
511 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
512 ime_
->OnTextInputTypeChanged(this);
513 // InputMethodEngine::FocusIn() should be called even for password field.
514 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
515 EXPECT_EQ(1U, on_input_method_changed_call_count_
);
518 // Confirm that IBusClient::FocusOut is called as expected.
519 TEST_F(InputMethodChromeOSTest
, FocusOut_None
) {
520 input_type_
= TEXT_INPUT_TYPE_TEXT
;
522 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
523 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
524 input_type_
= TEXT_INPUT_TYPE_NONE
;
525 ime_
->OnTextInputTypeChanged(this);
526 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
527 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
530 // Confirm that IBusClient::FocusOut is called as expected.
531 TEST_F(InputMethodChromeOSTest
, FocusOut_Password
) {
532 input_type_
= TEXT_INPUT_TYPE_TEXT
;
534 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
535 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
536 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
537 ime_
->OnTextInputTypeChanged(this);
538 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_in_call_count());
539 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
542 // FocusIn/FocusOut scenario test
543 TEST_F(InputMethodChromeOSTest
, Focus_Scenario
) {
545 // Confirm that both FocusIn and FocusOut are NOT called.
546 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_in_call_count());
547 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
548 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
,
549 mock_ime_engine_handler_
->last_text_input_context().type
);
550 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT
,
551 mock_ime_engine_handler_
->last_text_input_context().mode
);
553 input_type_
= TEXT_INPUT_TYPE_TEXT
;
554 input_mode_
= TEXT_INPUT_MODE_LATIN
;
555 ime_
->OnTextInputTypeChanged(this);
556 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
557 // TextInputMode is LATIN..
558 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
559 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
560 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
,
561 mock_ime_engine_handler_
->last_text_input_context().type
);
562 EXPECT_EQ(TEXT_INPUT_MODE_LATIN
,
563 mock_ime_engine_handler_
->last_text_input_context().mode
);
565 input_mode_
= TEXT_INPUT_MODE_KANA
;
566 ime_
->OnTextInputTypeChanged(this);
567 // Confirm that both FocusIn and FocusOut are called for mode change.
568 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_in_call_count());
569 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
570 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
,
571 mock_ime_engine_handler_
->last_text_input_context().type
);
572 EXPECT_EQ(TEXT_INPUT_MODE_KANA
,
573 mock_ime_engine_handler_
->last_text_input_context().mode
);
575 input_type_
= TEXT_INPUT_TYPE_URL
;
576 ime_
->OnTextInputTypeChanged(this);
577 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
579 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_in_call_count());
580 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_out_call_count());
581 EXPECT_EQ(TEXT_INPUT_TYPE_URL
,
582 mock_ime_engine_handler_
->last_text_input_context().type
);
583 EXPECT_EQ(TEXT_INPUT_MODE_KANA
,
584 mock_ime_engine_handler_
->last_text_input_context().mode
);
586 // When IsTextInputFocusManagerEnabled, InputMethod::SetFocusedTextInputClient
587 // is not supported and it's no-op.
588 if (switches::IsTextInputFocusManagerEnabled())
590 // Confirm that FocusOut is called when set focus to NULL client.
591 ime_
->SetFocusedTextInputClient(NULL
);
592 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_in_call_count());
593 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_out_call_count());
594 // Confirm that FocusIn is called when set focus to this client.
595 ime_
->SetFocusedTextInputClient(this);
596 EXPECT_EQ(4, mock_ime_engine_handler_
->focus_in_call_count());
597 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_out_call_count());
600 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
601 TEST_F(InputMethodChromeOSTest
, OnCaretBoundsChanged
) {
602 input_type_
= TEXT_INPUT_TYPE_TEXT
;
606 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
607 caret_bounds_
= gfx::Rect(1, 2, 3, 4);
608 ime_
->OnCaretBoundsChanged(this);
611 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
612 caret_bounds_
= gfx::Rect(0, 2, 3, 4);
613 ime_
->OnCaretBoundsChanged(this);
616 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
617 caret_bounds_
= gfx::Rect(0, 2, 3, 4); // unchanged
618 ime_
->OnCaretBoundsChanged(this);
619 // Current InputMethodChromeOS implementation performs the IPC
620 // regardless of the bounds are changed or not.
623 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
626 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_NoAttribute
) {
627 const base::string16 kSampleAsciiText
= UTF8ToUTF16("Sample Text");
628 const uint32 kCursorPos
= 2UL;
630 chromeos::CompositionText chromeos_composition_text
;
631 chromeos_composition_text
.set_text(kSampleAsciiText
);
633 CompositionText composition_text
;
634 ime_
->ExtractCompositionText(
635 chromeos_composition_text
, kCursorPos
, &composition_text
);
636 EXPECT_EQ(kSampleAsciiText
, composition_text
.text
);
637 // If there is no selection, |selection| represents cursor position.
638 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
639 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
640 // If there is no underline, |underlines| contains one underline and it is
641 // whole text underline.
642 ASSERT_EQ(1UL, composition_text
.underlines
.size());
643 EXPECT_EQ(0UL, composition_text
.underlines
[0].start_offset
);
644 EXPECT_EQ(kSampleAsciiText
.size(), composition_text
.underlines
[0].end_offset
);
645 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
648 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_SingleUnderline
) {
649 const uint32 kCursorPos
= 2UL;
651 // Set up chromeos composition text with one underline attribute.
652 chromeos::CompositionText chromeos_composition_text
;
653 chromeos_composition_text
.set_text(kSampleText
);
654 chromeos::CompositionText::UnderlineAttribute underline
;
655 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE
;
656 underline
.start_index
= 1UL;
657 underline
.end_index
= 4UL;
658 chromeos_composition_text
.mutable_underline_attributes()->push_back(
661 CompositionText composition_text
;
662 ime_
->ExtractCompositionText(
663 chromeos_composition_text
, kCursorPos
, &composition_text
);
664 EXPECT_EQ(kSampleText
, composition_text
.text
);
665 // If there is no selection, |selection| represents cursor position.
666 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
667 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
668 ASSERT_EQ(1UL, composition_text
.underlines
.size());
669 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
670 composition_text
.underlines
[0].start_offset
);
671 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
672 composition_text
.underlines
[0].end_offset
);
673 // Single underline represents as black thin line.
674 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
675 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
676 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
677 composition_text
.underlines
[0].background_color
);
680 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_DoubleUnderline
) {
681 const uint32 kCursorPos
= 2UL;
683 // Set up chromeos composition text with one underline attribute.
684 chromeos::CompositionText chromeos_composition_text
;
685 chromeos_composition_text
.set_text(kSampleText
);
686 chromeos::CompositionText::UnderlineAttribute underline
;
687 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE
;
688 underline
.start_index
= 1UL;
689 underline
.end_index
= 4UL;
690 chromeos_composition_text
.mutable_underline_attributes()->push_back(
693 CompositionText composition_text
;
694 ime_
->ExtractCompositionText(
695 chromeos_composition_text
, kCursorPos
, &composition_text
);
696 EXPECT_EQ(kSampleText
, composition_text
.text
);
697 // If there is no selection, |selection| represents cursor position.
698 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
699 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
700 ASSERT_EQ(1UL, composition_text
.underlines
.size());
701 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
702 composition_text
.underlines
[0].start_offset
);
703 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
704 composition_text
.underlines
[0].end_offset
);
705 // Double underline represents as black thick line.
706 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
707 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
708 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
709 composition_text
.underlines
[0].background_color
);
712 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_ErrorUnderline
) {
713 const uint32 kCursorPos
= 2UL;
715 // Set up chromeos composition text with one underline attribute.
716 chromeos::CompositionText chromeos_composition_text
;
717 chromeos_composition_text
.set_text(kSampleText
);
718 chromeos::CompositionText::UnderlineAttribute underline
;
719 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR
;
720 underline
.start_index
= 1UL;
721 underline
.end_index
= 4UL;
722 chromeos_composition_text
.mutable_underline_attributes()->push_back(
725 CompositionText composition_text
;
726 ime_
->ExtractCompositionText(
727 chromeos_composition_text
, kCursorPos
, &composition_text
);
728 EXPECT_EQ(kSampleText
, composition_text
.text
);
729 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
730 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
731 ASSERT_EQ(1UL, composition_text
.underlines
.size());
732 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
733 composition_text
.underlines
[0].start_offset
);
734 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
735 composition_text
.underlines
[0].end_offset
);
736 // Error underline represents as red thin line.
737 EXPECT_EQ(SK_ColorRED
, composition_text
.underlines
[0].color
);
738 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
741 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_Selection
) {
742 const uint32 kCursorPos
= 2UL;
744 // Set up chromeos composition text with one underline attribute.
745 chromeos::CompositionText chromeos_composition_text
;
746 chromeos_composition_text
.set_text(kSampleText
);
747 chromeos_composition_text
.set_selection_start(1UL);
748 chromeos_composition_text
.set_selection_end(4UL);
750 CompositionText composition_text
;
751 ime_
->ExtractCompositionText(
752 chromeos_composition_text
, kCursorPos
, &composition_text
);
753 EXPECT_EQ(kSampleText
, composition_text
.text
);
754 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
755 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
756 ASSERT_EQ(1UL, composition_text
.underlines
.size());
757 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
758 chromeos_composition_text
.selection_start()),
759 composition_text
.underlines
[0].start_offset
);
760 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
761 chromeos_composition_text
.selection_end()),
762 composition_text
.underlines
[0].end_offset
);
763 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
764 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
765 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
766 composition_text
.underlines
[0].background_color
);
769 TEST_F(InputMethodChromeOSTest
,
770 ExtractCompositionTextTest_SelectionStartWithCursor
) {
771 const uint32 kCursorPos
= 1UL;
773 // Set up chromeos composition text with one underline attribute.
774 chromeos::CompositionText chromeos_composition_text
;
775 chromeos_composition_text
.set_text(kSampleText
);
776 chromeos_composition_text
.set_selection_start(kCursorPos
);
777 chromeos_composition_text
.set_selection_end(4UL);
779 CompositionText composition_text
;
780 ime_
->ExtractCompositionText(
781 chromeos_composition_text
, kCursorPos
, &composition_text
);
782 EXPECT_EQ(kSampleText
, composition_text
.text
);
783 // If the cursor position is same as selection bounds, selection start
784 // position become opposit side of selection from cursor.
785 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
786 chromeos_composition_text
.selection_end()),
787 composition_text
.selection
.start());
788 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, kCursorPos
),
789 composition_text
.selection
.end());
790 ASSERT_EQ(1UL, composition_text
.underlines
.size());
791 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
792 chromeos_composition_text
.selection_start()),
793 composition_text
.underlines
[0].start_offset
);
794 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
795 chromeos_composition_text
.selection_end()),
796 composition_text
.underlines
[0].end_offset
);
797 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
798 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
799 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
800 composition_text
.underlines
[0].background_color
);
803 TEST_F(InputMethodChromeOSTest
,
804 ExtractCompositionTextTest_SelectionEndWithCursor
) {
805 const uint32 kCursorPos
= 4UL;
807 // Set up chromeos composition text with one underline attribute.
808 chromeos::CompositionText chromeos_composition_text
;
809 chromeos_composition_text
.set_text(kSampleText
);
810 chromeos_composition_text
.set_selection_start(1UL);
811 chromeos_composition_text
.set_selection_end(kCursorPos
);
813 CompositionText composition_text
;
814 ime_
->ExtractCompositionText(
815 chromeos_composition_text
, kCursorPos
, &composition_text
);
816 EXPECT_EQ(kSampleText
, composition_text
.text
);
817 // If the cursor position is same as selection bounds, selection start
818 // position become opposit side of selection from cursor.
819 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
820 chromeos_composition_text
.selection_start()),
821 composition_text
.selection
.start());
822 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, kCursorPos
),
823 composition_text
.selection
.end());
824 ASSERT_EQ(1UL, composition_text
.underlines
.size());
825 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
826 chromeos_composition_text
.selection_start()),
827 composition_text
.underlines
[0].start_offset
);
828 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
829 chromeos_composition_text
.selection_end()),
830 composition_text
.underlines
[0].end_offset
);
831 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
832 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
833 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
834 composition_text
.underlines
[0].background_color
);
837 TEST_F(InputMethodChromeOSTest
, SurroundingText_NoSelectionTest
) {
839 // Click a text input form.
840 input_type_
= TEXT_INPUT_TYPE_TEXT
;
841 ime_
->OnTextInputTypeChanged(this);
843 // Set the TextInputClient behaviors.
844 surrounding_text_
= UTF8ToUTF16("abcdef");
845 text_range_
= gfx::Range(0, 6);
846 selection_range_
= gfx::Range(3, 3);
848 // Set the verifier for SetSurroundingText mock call.
849 SetSurroundingTextVerifier
verifier(UTF16ToUTF8(surrounding_text_
), 3, 3);
852 ime_
->OnCaretBoundsChanged(this);
854 // Check the call count.
856 mock_ime_engine_handler_
->set_surrounding_text_call_count());
857 EXPECT_EQ(UTF16ToUTF8(surrounding_text_
),
858 mock_ime_engine_handler_
->last_set_surrounding_text());
860 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
862 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
865 TEST_F(InputMethodChromeOSTest
, SurroundingText_SelectionTest
) {
867 // Click a text input form.
868 input_type_
= TEXT_INPUT_TYPE_TEXT
;
869 ime_
->OnTextInputTypeChanged(this);
871 // Set the TextInputClient behaviors.
872 surrounding_text_
= UTF8ToUTF16("abcdef");
873 text_range_
= gfx::Range(0, 6);
874 selection_range_
= gfx::Range(2, 5);
876 // Set the verifier for SetSurroundingText mock call.
877 SetSurroundingTextVerifier
verifier(UTF16ToUTF8(surrounding_text_
), 2, 5);
879 ime_
->OnCaretBoundsChanged(this);
881 // Check the call count.
883 mock_ime_engine_handler_
->set_surrounding_text_call_count());
884 EXPECT_EQ(UTF16ToUTF8(surrounding_text_
),
885 mock_ime_engine_handler_
->last_set_surrounding_text());
887 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
889 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
892 TEST_F(InputMethodChromeOSTest
, SurroundingText_PartialText
) {
894 // Click a text input form.
895 input_type_
= TEXT_INPUT_TYPE_TEXT
;
896 ime_
->OnTextInputTypeChanged(this);
898 // Set the TextInputClient behaviors.
899 surrounding_text_
= UTF8ToUTF16("abcdefghij");
900 text_range_
= gfx::Range(5, 10);
901 selection_range_
= gfx::Range(7, 9);
903 ime_
->OnCaretBoundsChanged(this);
905 // Check the call count.
907 mock_ime_engine_handler_
->set_surrounding_text_call_count());
908 // Set the verifier for SetSurroundingText mock call.
909 // Here (2, 4) is selection range in expected surrounding text coordinates.
911 mock_ime_engine_handler_
->last_set_surrounding_text());
913 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
915 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
918 TEST_F(InputMethodChromeOSTest
, SurroundingText_BecomeEmptyText
) {
920 // Click a text input form.
921 input_type_
= TEXT_INPUT_TYPE_TEXT
;
922 ime_
->OnTextInputTypeChanged(this);
924 // Set the TextInputClient behaviors.
925 // If the surrounding text becomes empty, text_range become (0, 0) and
926 // selection range become invalid.
927 surrounding_text_
= UTF8ToUTF16("");
928 text_range_
= gfx::Range(0, 0);
929 selection_range_
= gfx::Range::InvalidRange();
931 ime_
->OnCaretBoundsChanged(this);
933 // Check the call count.
935 mock_ime_engine_handler_
->set_surrounding_text_call_count());
937 // Should not be called twice with same condition.
938 ime_
->OnCaretBoundsChanged(this);
940 mock_ime_engine_handler_
->set_surrounding_text_call_count());
943 class InputMethodChromeOSKeyEventTest
: public InputMethodChromeOSTest
{
945 InputMethodChromeOSKeyEventTest() {}
946 virtual ~InputMethodChromeOSKeyEventTest() {}
948 virtual void SetUp() OVERRIDE
{
949 InputMethodChromeOSTest::SetUp();
953 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest
);
956 TEST_F(InputMethodChromeOSKeyEventTest
, KeyEventDelayResponseTest
) {
957 const int kFlags
= ui::EF_SHIFT_DOWN
;
958 ScopedXI2Event xevent
;
959 xevent
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_A
, kFlags
);
960 const ui::KeyEvent
event(xevent
);
963 input_type_
= TEXT_INPUT_TYPE_TEXT
;
964 ime_
->OnTextInputTypeChanged(this);
965 ime_
->DispatchKeyEvent(event
);
967 // Check before state.
968 const ui::KeyEvent
* key_event
=
969 mock_ime_engine_handler_
->last_processed_key_event();
970 EXPECT_EQ(1, mock_ime_engine_handler_
->process_key_event_call_count());
971 EXPECT_EQ(ui::VKEY_A
, key_event
->key_code());
972 EXPECT_EQ("KeyA", key_event
->code());
973 EXPECT_EQ(kFlags
, key_event
->flags());
974 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
977 mock_ime_engine_handler_
->last_passed_callback().Run(true);
980 EXPECT_EQ(1, ime_
->process_key_event_post_ime_call_count());
981 const ui::KeyEvent
* stored_event
=
982 ime_
->process_key_event_post_ime_args().event
;
983 EXPECT_TRUE(stored_event
->HasNativeEvent());
984 EXPECT_TRUE(IsEqualXKeyEvent(*xevent
, *(stored_event
->native_event())));
985 EXPECT_TRUE(ime_
->process_key_event_post_ime_args().handled
);
988 TEST_F(InputMethodChromeOSKeyEventTest
, MultiKeyEventDelayResponseTest
) {
990 input_type_
= TEXT_INPUT_TYPE_TEXT
;
991 ime_
->OnTextInputTypeChanged(this);
993 const int kFlags
= ui::EF_SHIFT_DOWN
;
994 ScopedXI2Event xevent
;
995 xevent
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_B
, kFlags
);
996 const ui::KeyEvent
event(xevent
);
999 ime_
->DispatchKeyEvent(event
);
1000 const ui::KeyEvent
* key_event
=
1001 mock_ime_engine_handler_
->last_processed_key_event();
1002 EXPECT_EQ(ui::VKEY_B
, key_event
->key_code());
1003 EXPECT_EQ("KeyB", key_event
->code());
1004 EXPECT_EQ(kFlags
, key_event
->flags());
1006 KeyEventCallback first_callback
=
1007 mock_ime_engine_handler_
->last_passed_callback();
1009 // Do key event again.
1010 ScopedXI2Event xevent2
;
1011 xevent2
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_C
, kFlags
);
1012 const ui::KeyEvent
event2(xevent2
);
1014 ime_
->DispatchKeyEvent(event2
);
1015 const ui::KeyEvent
* key_event2
=
1016 mock_ime_engine_handler_
->last_processed_key_event();
1017 EXPECT_EQ(ui::VKEY_C
, key_event2
->key_code());
1018 EXPECT_EQ("KeyC", key_event2
->code());
1019 EXPECT_EQ(kFlags
, key_event2
->flags());
1021 // Check before state.
1023 mock_ime_engine_handler_
->process_key_event_call_count());
1024 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
1026 // Do callback for first key event.
1027 first_callback
.Run(true);
1029 // Check the results for first key event.
1030 EXPECT_EQ(1, ime_
->process_key_event_post_ime_call_count());
1031 const ui::KeyEvent
* stored_event
=
1032 ime_
->process_key_event_post_ime_args().event
;
1033 EXPECT_TRUE(stored_event
->HasNativeEvent());
1034 EXPECT_TRUE(IsEqualXKeyEvent(*xevent
, *(stored_event
->native_event())));
1035 EXPECT_TRUE(ime_
->process_key_event_post_ime_args().handled
);
1037 // Do callback for second key event.
1038 mock_ime_engine_handler_
->last_passed_callback().Run(false);
1040 // Check the results for second key event.
1041 EXPECT_EQ(2, ime_
->process_key_event_post_ime_call_count());
1042 stored_event
= ime_
->process_key_event_post_ime_args().event
;
1043 EXPECT_TRUE(stored_event
->HasNativeEvent());
1044 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2
, *(stored_event
->native_event())));
1045 EXPECT_FALSE(ime_
->process_key_event_post_ime_args().handled
);
1048 TEST_F(InputMethodChromeOSKeyEventTest
, KeyEventDelayResponseResetTest
) {
1049 ScopedXI2Event xevent
;
1050 xevent
.InitKeyEvent(ui::ET_KEY_PRESSED
, ui::VKEY_A
, ui::EF_SHIFT_DOWN
);
1051 const ui::KeyEvent
event(xevent
);
1054 input_type_
= TEXT_INPUT_TYPE_TEXT
;
1055 ime_
->OnTextInputTypeChanged(this);
1056 ime_
->DispatchKeyEvent(event
);
1058 // Check before state.
1059 EXPECT_EQ(1, mock_ime_engine_handler_
->process_key_event_call_count());
1060 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
1062 ime_
->ResetContext();
1065 mock_ime_engine_handler_
->last_passed_callback().Run(true);
1067 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
1069 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).