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_chromeos.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/events/event.h"
27 #include "ui/events/event_utils.h"
28 #include "ui/events/keycodes/dom/dom_code.h"
29 #include "ui/events/keycodes/dom/keycode_converter.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 enum KeyEventHandlerBehavior
{
62 class TestableInputMethodChromeOS
: public InputMethodChromeOS
{
64 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate
* delegate
)
65 : InputMethodChromeOS(delegate
),
66 process_key_event_post_ime_call_count_(0) {
69 struct ProcessKeyEventPostIMEArgs
{
70 ProcessKeyEventPostIMEArgs() : event(NULL
), handled(false) {}
71 const ui::KeyEvent
* event
;
75 // Overridden from InputMethodChromeOS:
76 void ProcessKeyEventPostIME(ui::KeyEvent
* key_event
,
77 bool handled
) override
{
78 InputMethodChromeOS::ProcessKeyEventPostIME(key_event
, handled
);
79 process_key_event_post_ime_args_
.event
= key_event
;
80 process_key_event_post_ime_args_
.handled
= handled
;
81 ++process_key_event_post_ime_call_count_
;
84 void ResetCallCount() {
85 process_key_event_post_ime_call_count_
= 0;
88 const ProcessKeyEventPostIMEArgs
& process_key_event_post_ime_args() const {
89 return process_key_event_post_ime_args_
;
92 int process_key_event_post_ime_call_count() const {
93 return process_key_event_post_ime_call_count_
;
96 // Change access rights for testing.
97 using InputMethodChromeOS::ExtractCompositionText
;
98 using InputMethodChromeOS::ResetContext
;
101 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_
;
102 int process_key_event_post_ime_call_count_
;
105 class SynchronousKeyEventHandler
{
107 SynchronousKeyEventHandler(uint32 expected_keyval
,
108 uint32 expected_keycode
,
109 uint32 expected_state
,
110 KeyEventHandlerBehavior behavior
)
111 : expected_keyval_(expected_keyval
),
112 expected_keycode_(expected_keycode
),
113 expected_state_(expected_state
),
114 behavior_(behavior
) {}
116 virtual ~SynchronousKeyEventHandler() {}
118 void Run(uint32 keyval
,
121 const KeyEventCallback
& callback
) {
122 EXPECT_EQ(expected_keyval_
, keyval
);
123 EXPECT_EQ(expected_keycode_
, keycode
);
124 EXPECT_EQ(expected_state_
, state
);
125 callback
.Run(behavior_
== KEYEVENT_CONSUME
);
129 const uint32 expected_keyval_
;
130 const uint32 expected_keycode_
;
131 const uint32 expected_state_
;
132 const KeyEventHandlerBehavior behavior_
;
134 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler
);
137 class AsynchronousKeyEventHandler
{
139 AsynchronousKeyEventHandler(uint32 expected_keyval
,
140 uint32 expected_keycode
,
141 uint32 expected_state
)
142 : expected_keyval_(expected_keyval
),
143 expected_keycode_(expected_keycode
),
144 expected_state_(expected_state
) {}
146 virtual ~AsynchronousKeyEventHandler() {}
148 void Run(uint32 keyval
,
151 const KeyEventCallback
& callback
) {
152 EXPECT_EQ(expected_keyval_
, keyval
);
153 EXPECT_EQ(expected_keycode_
, keycode
);
154 EXPECT_EQ(expected_state_
, state
);
155 callback_
= callback
;
158 void RunCallback(KeyEventHandlerBehavior behavior
) {
159 callback_
.Run(behavior
== KEYEVENT_CONSUME
);
163 const uint32 expected_keyval_
;
164 const uint32 expected_keycode_
;
165 const uint32 expected_state_
;
166 KeyEventCallback callback_
;
168 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler
);
171 class SetSurroundingTextVerifier
{
173 SetSurroundingTextVerifier(const std::string
& expected_surrounding_text
,
174 uint32 expected_cursor_position
,
175 uint32 expected_anchor_position
)
176 : expected_surrounding_text_(expected_surrounding_text
),
177 expected_cursor_position_(expected_cursor_position
),
178 expected_anchor_position_(expected_anchor_position
) {}
180 void Verify(const std::string
& text
,
183 EXPECT_EQ(expected_surrounding_text_
, text
);
184 EXPECT_EQ(expected_cursor_position_
, cursor_pos
);
185 EXPECT_EQ(expected_anchor_position_
, anchor_pos
);
189 const std::string expected_surrounding_text_
;
190 const uint32 expected_cursor_position_
;
191 const uint32 expected_anchor_position_
;
193 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier
);
196 class InputMethodChromeOSTest
: public internal::InputMethodDelegate
,
197 public testing::Test
,
198 public DummyTextInputClient
{
200 InputMethodChromeOSTest()
201 : dispatched_key_event_(ui::ET_UNKNOWN
, ui::VKEY_UNKNOWN
, ui::EF_NONE
),
202 stop_propagation_post_ime_(false) {
206 ~InputMethodChromeOSTest() override
{}
208 void SetUp() override
{
209 chromeos::IMEBridge::Initialize();
211 mock_ime_engine_handler_
.reset(
212 new chromeos::MockIMEEngineHandler());
213 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
214 mock_ime_engine_handler_
.get());
216 mock_ime_candidate_window_handler_
.reset(
217 new chromeos::MockIMECandidateWindowHandler());
218 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
219 mock_ime_candidate_window_handler_
.get());
221 ime_
.reset(new TestableInputMethodChromeOS(this));
222 ime_
->SetFocusedTextInputClient(this);
225 void TearDown() override
{
227 ime_
->SetFocusedTextInputClient(NULL
);
229 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
230 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL
);
231 mock_ime_engine_handler_
.reset();
232 mock_ime_candidate_window_handler_
.reset();
233 chromeos::IMEBridge::Shutdown();
238 // Overridden from ui::internal::InputMethodDelegate:
239 ui::EventDispatchDetails
DispatchKeyEventPostIME(
240 ui::KeyEvent
* event
) override
{
241 dispatched_key_event_
= *event
;
242 if (stop_propagation_post_ime_
)
243 event
->StopPropagation();
244 return ui::EventDispatchDetails();
247 // Overridden from ui::TextInputClient:
248 void SetCompositionText(const CompositionText
& composition
) override
{
249 composition_text_
= composition
;
251 void ConfirmCompositionText() override
{
252 confirmed_text_
= composition_text_
;
253 composition_text_
.Clear();
255 void ClearCompositionText() override
{ composition_text_
.Clear(); }
256 void InsertText(const base::string16
& text
) override
{
257 inserted_text_
= text
;
259 void InsertChar(base::char16 ch
, int flags
) override
{
261 inserted_char_flags_
= flags
;
263 TextInputType
GetTextInputType() const override
{ return input_type_
; }
264 TextInputMode
GetTextInputMode() const override
{ return input_mode_
; }
265 bool CanComposeInline() const override
{ return can_compose_inline_
; }
266 gfx::Rect
GetCaretBounds() const override
{ return caret_bounds_
; }
267 bool HasCompositionText() const override
{
268 CompositionText empty
;
269 return composition_text_
!= empty
;
271 bool GetTextRange(gfx::Range
* range
) const override
{
272 *range
= text_range_
;
275 bool GetSelectionRange(gfx::Range
* range
) const override
{
276 *range
= selection_range_
;
279 bool GetTextFromRange(const gfx::Range
& range
,
280 base::string16
* text
) const override
{
281 *text
= surrounding_text_
.substr(range
.GetMin(), range
.length());
284 void OnInputMethodChanged() override
{
285 ++on_input_method_changed_call_count_
;
288 bool HasNativeEvent() const {
289 return dispatched_key_event_
.HasNativeEvent();
293 dispatched_key_event_
= ui::KeyEvent(ui::ET_UNKNOWN
, ui::VKEY_UNKNOWN
,
296 composition_text_
.Clear();
297 confirmed_text_
.Clear();
298 inserted_text_
.clear();
300 inserted_char_flags_
= 0;
301 on_input_method_changed_call_count_
= 0;
303 input_type_
= TEXT_INPUT_TYPE_NONE
;
304 input_mode_
= TEXT_INPUT_MODE_DEFAULT
;
305 can_compose_inline_
= true;
306 caret_bounds_
= gfx::Rect();
309 scoped_ptr
<TestableInputMethodChromeOS
> ime_
;
311 // Copy of the dispatched key event.
312 ui::KeyEvent dispatched_key_event_
;
314 // Variables for remembering the parameters that are passed to
315 // ui::TextInputClient functions.
316 CompositionText composition_text_
;
317 CompositionText confirmed_text_
;
318 base::string16 inserted_text_
;
319 base::char16 inserted_char_
;
320 unsigned int on_input_method_changed_call_count_
;
321 int inserted_char_flags_
;
323 // Variables that will be returned from the ui::TextInputClient functions.
324 TextInputType input_type_
;
325 TextInputMode input_mode_
;
326 bool can_compose_inline_
;
327 gfx::Rect caret_bounds_
;
328 gfx::Range text_range_
;
329 gfx::Range selection_range_
;
330 base::string16 surrounding_text_
;
332 scoped_ptr
<chromeos::MockIMEEngineHandler
> mock_ime_engine_handler_
;
333 scoped_ptr
<chromeos::MockIMECandidateWindowHandler
>
334 mock_ime_candidate_window_handler_
;
336 bool stop_propagation_post_ime_
;
338 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest
);
341 // Tests public APIs in ui::InputMethod first.
343 TEST_F(InputMethodChromeOSTest
, GetInputLocale
) {
344 // ui::InputMethodChromeOS does not support the API.
346 EXPECT_EQ("", ime_
->GetInputLocale());
349 TEST_F(InputMethodChromeOSTest
, GetInputTextType
) {
351 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
352 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
353 ime_
->OnTextInputTypeChanged(this);
354 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
355 input_type_
= TEXT_INPUT_TYPE_TEXT
;
356 ime_
->OnTextInputTypeChanged(this);
357 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
, ime_
->GetTextInputType());
360 TEST_F(InputMethodChromeOSTest
, CanComposeInline
) {
362 EXPECT_TRUE(ime_
->CanComposeInline());
363 can_compose_inline_
= false;
364 ime_
->OnTextInputTypeChanged(this);
365 EXPECT_FALSE(ime_
->CanComposeInline());
368 TEST_F(InputMethodChromeOSTest
, GetTextInputClient
) {
370 EXPECT_EQ(this, ime_
->GetTextInputClient());
371 ime_
->SetFocusedTextInputClient(NULL
);
372 EXPECT_EQ(NULL
, ime_
->GetTextInputClient());
375 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedClient
) {
377 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
378 ime_
->SetFocusedTextInputClient(NULL
);
379 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
380 ime_
->OnTextInputTypeChanged(this);
381 // The OnTextInputTypeChanged() call above should be ignored since |this| is
382 // not the current focused client.
383 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
385 ime_
->SetFocusedTextInputClient(this);
386 ime_
->OnTextInputTypeChanged(this);
387 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
390 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedWindow
) {
392 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
394 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
395 ime_
->OnTextInputTypeChanged(this);
396 // The OnTextInputTypeChanged() call above should be ignored since the top-
397 // level window which the ime_ is attached to is not currently focused.
398 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
401 ime_
->OnTextInputTypeChanged(this);
402 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
405 TEST_F(InputMethodChromeOSTest
, GetInputTextType_WithoutFocusedWindow2
) {
406 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
407 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
408 ime_
->OnTextInputTypeChanged(this);
409 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
, ime_
->GetTextInputType());
412 ime_
->OnTextInputTypeChanged(this);
413 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD
, ime_
->GetTextInputType());
416 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
418 TEST_F(InputMethodChromeOSTest
, FocusIn_Text
) {
420 // A context shouldn't be created since the daemon is not running.
421 EXPECT_EQ(0U, on_input_method_changed_call_count_
);
422 // Click a text input form.
423 input_type_
= TEXT_INPUT_TYPE_TEXT
;
424 ime_
->OnTextInputTypeChanged(this);
425 // Since a form has focus, IBusClient::FocusIn() should be called.
426 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
429 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
430 // ui::TextInputClient::OnInputMethodChanged() should be called when
431 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
432 // current text input type is not NONE.
433 EXPECT_EQ(1U, on_input_method_changed_call_count_
);
436 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
437 // input_type_ is PASSWORD.
438 TEST_F(InputMethodChromeOSTest
, FocusIn_Password
) {
440 EXPECT_EQ(0U, on_input_method_changed_call_count_
);
441 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
442 ime_
->OnTextInputTypeChanged(this);
443 // InputMethodEngine::FocusIn() should be called even for password field.
444 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
445 EXPECT_EQ(1U, on_input_method_changed_call_count_
);
448 // Confirm that IBusClient::FocusOut is called as expected.
449 TEST_F(InputMethodChromeOSTest
, FocusOut_None
) {
450 input_type_
= TEXT_INPUT_TYPE_TEXT
;
452 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
453 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
454 input_type_
= TEXT_INPUT_TYPE_NONE
;
455 ime_
->OnTextInputTypeChanged(this);
456 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
457 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
460 // Confirm that IBusClient::FocusOut is called as expected.
461 TEST_F(InputMethodChromeOSTest
, FocusOut_Password
) {
462 input_type_
= TEXT_INPUT_TYPE_TEXT
;
464 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
465 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
466 input_type_
= TEXT_INPUT_TYPE_PASSWORD
;
467 ime_
->OnTextInputTypeChanged(this);
468 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_in_call_count());
469 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
472 // FocusIn/FocusOut scenario test
473 TEST_F(InputMethodChromeOSTest
, Focus_Scenario
) {
475 // Confirm that both FocusIn and FocusOut are NOT called.
476 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_in_call_count());
477 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
478 EXPECT_EQ(TEXT_INPUT_TYPE_NONE
,
479 mock_ime_engine_handler_
->last_text_input_context().type
);
480 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT
,
481 mock_ime_engine_handler_
->last_text_input_context().mode
);
483 input_type_
= TEXT_INPUT_TYPE_TEXT
;
484 input_mode_
= TEXT_INPUT_MODE_LATIN
;
485 ime_
->OnTextInputTypeChanged(this);
486 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
487 // TextInputMode is LATIN..
488 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_in_call_count());
489 EXPECT_EQ(0, mock_ime_engine_handler_
->focus_out_call_count());
490 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
,
491 mock_ime_engine_handler_
->last_text_input_context().type
);
492 EXPECT_EQ(TEXT_INPUT_MODE_LATIN
,
493 mock_ime_engine_handler_
->last_text_input_context().mode
);
495 input_mode_
= TEXT_INPUT_MODE_KANA
;
496 ime_
->OnTextInputTypeChanged(this);
497 // Confirm that both FocusIn and FocusOut are called for mode change.
498 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_in_call_count());
499 EXPECT_EQ(1, mock_ime_engine_handler_
->focus_out_call_count());
500 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT
,
501 mock_ime_engine_handler_
->last_text_input_context().type
);
502 EXPECT_EQ(TEXT_INPUT_MODE_KANA
,
503 mock_ime_engine_handler_
->last_text_input_context().mode
);
505 input_type_
= TEXT_INPUT_TYPE_URL
;
506 ime_
->OnTextInputTypeChanged(this);
507 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
509 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_in_call_count());
510 EXPECT_EQ(2, mock_ime_engine_handler_
->focus_out_call_count());
511 EXPECT_EQ(TEXT_INPUT_TYPE_URL
,
512 mock_ime_engine_handler_
->last_text_input_context().type
);
513 EXPECT_EQ(TEXT_INPUT_MODE_KANA
,
514 mock_ime_engine_handler_
->last_text_input_context().mode
);
516 // Confirm that FocusOut is called when set focus to NULL client.
517 ime_
->SetFocusedTextInputClient(NULL
);
518 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_in_call_count());
519 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_out_call_count());
520 // Confirm that FocusIn is called when set focus to this client.
521 ime_
->SetFocusedTextInputClient(this);
522 EXPECT_EQ(4, mock_ime_engine_handler_
->focus_in_call_count());
523 EXPECT_EQ(3, mock_ime_engine_handler_
->focus_out_call_count());
526 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
527 TEST_F(InputMethodChromeOSTest
, OnCaretBoundsChanged
) {
528 input_type_
= TEXT_INPUT_TYPE_TEXT
;
532 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
533 caret_bounds_
= gfx::Rect(1, 2, 3, 4);
534 ime_
->OnCaretBoundsChanged(this);
537 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
538 caret_bounds_
= gfx::Rect(0, 2, 3, 4);
539 ime_
->OnCaretBoundsChanged(this);
542 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
543 caret_bounds_
= gfx::Rect(0, 2, 3, 4); // unchanged
544 ime_
->OnCaretBoundsChanged(this);
545 // Current InputMethodChromeOS implementation performs the IPC
546 // regardless of the bounds are changed or not.
549 mock_ime_candidate_window_handler_
->set_cursor_bounds_call_count());
552 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_NoAttribute
) {
553 const base::string16 kSampleAsciiText
= UTF8ToUTF16("Sample Text");
554 const uint32 kCursorPos
= 2UL;
556 chromeos::CompositionText chromeos_composition_text
;
557 chromeos_composition_text
.set_text(kSampleAsciiText
);
559 CompositionText composition_text
;
560 ime_
->ExtractCompositionText(
561 chromeos_composition_text
, kCursorPos
, &composition_text
);
562 EXPECT_EQ(kSampleAsciiText
, composition_text
.text
);
563 // If there is no selection, |selection| represents cursor position.
564 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
565 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
566 // If there is no underline, |underlines| contains one underline and it is
567 // whole text underline.
568 ASSERT_EQ(1UL, composition_text
.underlines
.size());
569 EXPECT_EQ(0UL, composition_text
.underlines
[0].start_offset
);
570 EXPECT_EQ(kSampleAsciiText
.size(), composition_text
.underlines
[0].end_offset
);
571 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
574 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_SingleUnderline
) {
575 const uint32 kCursorPos
= 2UL;
577 // Set up chromeos composition text with one underline attribute.
578 chromeos::CompositionText chromeos_composition_text
;
579 chromeos_composition_text
.set_text(kSampleText
);
580 chromeos::CompositionText::UnderlineAttribute underline
;
581 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE
;
582 underline
.start_index
= 1UL;
583 underline
.end_index
= 4UL;
584 chromeos_composition_text
.mutable_underline_attributes()->push_back(
587 CompositionText composition_text
;
588 ime_
->ExtractCompositionText(
589 chromeos_composition_text
, kCursorPos
, &composition_text
);
590 EXPECT_EQ(kSampleText
, composition_text
.text
);
591 // If there is no selection, |selection| represents cursor position.
592 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
593 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
594 ASSERT_EQ(1UL, composition_text
.underlines
.size());
595 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
596 composition_text
.underlines
[0].start_offset
);
597 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
598 composition_text
.underlines
[0].end_offset
);
599 // Single underline represents as black thin line.
600 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
601 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
602 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
603 composition_text
.underlines
[0].background_color
);
606 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_DoubleUnderline
) {
607 const uint32 kCursorPos
= 2UL;
609 // Set up chromeos composition text with one underline attribute.
610 chromeos::CompositionText chromeos_composition_text
;
611 chromeos_composition_text
.set_text(kSampleText
);
612 chromeos::CompositionText::UnderlineAttribute underline
;
613 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE
;
614 underline
.start_index
= 1UL;
615 underline
.end_index
= 4UL;
616 chromeos_composition_text
.mutable_underline_attributes()->push_back(
619 CompositionText composition_text
;
620 ime_
->ExtractCompositionText(
621 chromeos_composition_text
, kCursorPos
, &composition_text
);
622 EXPECT_EQ(kSampleText
, composition_text
.text
);
623 // If there is no selection, |selection| represents cursor position.
624 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
625 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
626 ASSERT_EQ(1UL, composition_text
.underlines
.size());
627 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
628 composition_text
.underlines
[0].start_offset
);
629 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
630 composition_text
.underlines
[0].end_offset
);
631 // Double underline represents as black thick line.
632 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
633 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
634 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
635 composition_text
.underlines
[0].background_color
);
638 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_ErrorUnderline
) {
639 const uint32 kCursorPos
= 2UL;
641 // Set up chromeos composition text with one underline attribute.
642 chromeos::CompositionText chromeos_composition_text
;
643 chromeos_composition_text
.set_text(kSampleText
);
644 chromeos::CompositionText::UnderlineAttribute underline
;
645 underline
.type
= chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR
;
646 underline
.start_index
= 1UL;
647 underline
.end_index
= 4UL;
648 chromeos_composition_text
.mutable_underline_attributes()->push_back(
651 CompositionText composition_text
;
652 ime_
->ExtractCompositionText(
653 chromeos_composition_text
, kCursorPos
, &composition_text
);
654 EXPECT_EQ(kSampleText
, composition_text
.text
);
655 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
656 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
657 ASSERT_EQ(1UL, composition_text
.underlines
.size());
658 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.start_index
),
659 composition_text
.underlines
[0].start_offset
);
660 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, underline
.end_index
),
661 composition_text
.underlines
[0].end_offset
);
662 // Error underline represents as red thin line.
663 EXPECT_EQ(SK_ColorRED
, composition_text
.underlines
[0].color
);
664 EXPECT_FALSE(composition_text
.underlines
[0].thick
);
667 TEST_F(InputMethodChromeOSTest
, ExtractCompositionTextTest_Selection
) {
668 const uint32 kCursorPos
= 2UL;
670 // Set up chromeos composition text with one underline attribute.
671 chromeos::CompositionText chromeos_composition_text
;
672 chromeos_composition_text
.set_text(kSampleText
);
673 chromeos_composition_text
.set_selection_start(1UL);
674 chromeos_composition_text
.set_selection_end(4UL);
676 CompositionText composition_text
;
677 ime_
->ExtractCompositionText(
678 chromeos_composition_text
, kCursorPos
, &composition_text
);
679 EXPECT_EQ(kSampleText
, composition_text
.text
);
680 EXPECT_EQ(kCursorPos
, composition_text
.selection
.start());
681 EXPECT_EQ(kCursorPos
, composition_text
.selection
.end());
682 ASSERT_EQ(1UL, composition_text
.underlines
.size());
683 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
684 chromeos_composition_text
.selection_start()),
685 composition_text
.underlines
[0].start_offset
);
686 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
687 chromeos_composition_text
.selection_end()),
688 composition_text
.underlines
[0].end_offset
);
689 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
690 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
691 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
692 composition_text
.underlines
[0].background_color
);
695 TEST_F(InputMethodChromeOSTest
,
696 ExtractCompositionTextTest_SelectionStartWithCursor
) {
697 const uint32 kCursorPos
= 1UL;
699 // Set up chromeos composition text with one underline attribute.
700 chromeos::CompositionText chromeos_composition_text
;
701 chromeos_composition_text
.set_text(kSampleText
);
702 chromeos_composition_text
.set_selection_start(kCursorPos
);
703 chromeos_composition_text
.set_selection_end(4UL);
705 CompositionText composition_text
;
706 ime_
->ExtractCompositionText(
707 chromeos_composition_text
, kCursorPos
, &composition_text
);
708 EXPECT_EQ(kSampleText
, composition_text
.text
);
709 // If the cursor position is same as selection bounds, selection start
710 // position become opposit side of selection from cursor.
711 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
712 chromeos_composition_text
.selection_end()),
713 composition_text
.selection
.start());
714 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, kCursorPos
),
715 composition_text
.selection
.end());
716 ASSERT_EQ(1UL, composition_text
.underlines
.size());
717 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
718 chromeos_composition_text
.selection_start()),
719 composition_text
.underlines
[0].start_offset
);
720 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
721 chromeos_composition_text
.selection_end()),
722 composition_text
.underlines
[0].end_offset
);
723 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
724 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
725 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
726 composition_text
.underlines
[0].background_color
);
729 TEST_F(InputMethodChromeOSTest
,
730 ExtractCompositionTextTest_SelectionEndWithCursor
) {
731 const uint32 kCursorPos
= 4UL;
733 // Set up chromeos composition text with one underline attribute.
734 chromeos::CompositionText chromeos_composition_text
;
735 chromeos_composition_text
.set_text(kSampleText
);
736 chromeos_composition_text
.set_selection_start(1UL);
737 chromeos_composition_text
.set_selection_end(kCursorPos
);
739 CompositionText composition_text
;
740 ime_
->ExtractCompositionText(
741 chromeos_composition_text
, kCursorPos
, &composition_text
);
742 EXPECT_EQ(kSampleText
, composition_text
.text
);
743 // If the cursor position is same as selection bounds, selection start
744 // position become opposit side of selection from cursor.
745 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
746 chromeos_composition_text
.selection_start()),
747 composition_text
.selection
.start());
748 EXPECT_EQ(GetOffsetInUTF16(kSampleText
, kCursorPos
),
749 composition_text
.selection
.end());
750 ASSERT_EQ(1UL, composition_text
.underlines
.size());
751 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
752 chromeos_composition_text
.selection_start()),
753 composition_text
.underlines
[0].start_offset
);
754 EXPECT_EQ(GetOffsetInUTF16(kSampleText
,
755 chromeos_composition_text
.selection_end()),
756 composition_text
.underlines
[0].end_offset
);
757 EXPECT_EQ(SK_ColorBLACK
, composition_text
.underlines
[0].color
);
758 EXPECT_TRUE(composition_text
.underlines
[0].thick
);
759 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
),
760 composition_text
.underlines
[0].background_color
);
763 TEST_F(InputMethodChromeOSTest
, SurroundingText_NoSelectionTest
) {
765 // Click a text input form.
766 input_type_
= TEXT_INPUT_TYPE_TEXT
;
767 ime_
->OnTextInputTypeChanged(this);
769 // Set the TextInputClient behaviors.
770 surrounding_text_
= UTF8ToUTF16("abcdef");
771 text_range_
= gfx::Range(0, 6);
772 selection_range_
= gfx::Range(3, 3);
774 // Set the verifier for SetSurroundingText mock call.
775 SetSurroundingTextVerifier
verifier(UTF16ToUTF8(surrounding_text_
), 3, 3);
778 ime_
->OnCaretBoundsChanged(this);
780 // Check the call count.
782 mock_ime_engine_handler_
->set_surrounding_text_call_count());
783 EXPECT_EQ(UTF16ToUTF8(surrounding_text_
),
784 mock_ime_engine_handler_
->last_set_surrounding_text());
786 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
788 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
791 TEST_F(InputMethodChromeOSTest
, SurroundingText_SelectionTest
) {
793 // Click a text input form.
794 input_type_
= TEXT_INPUT_TYPE_TEXT
;
795 ime_
->OnTextInputTypeChanged(this);
797 // Set the TextInputClient behaviors.
798 surrounding_text_
= UTF8ToUTF16("abcdef");
799 text_range_
= gfx::Range(0, 6);
800 selection_range_
= gfx::Range(2, 5);
802 // Set the verifier for SetSurroundingText mock call.
803 SetSurroundingTextVerifier
verifier(UTF16ToUTF8(surrounding_text_
), 2, 5);
805 ime_
->OnCaretBoundsChanged(this);
807 // Check the call count.
809 mock_ime_engine_handler_
->set_surrounding_text_call_count());
810 EXPECT_EQ(UTF16ToUTF8(surrounding_text_
),
811 mock_ime_engine_handler_
->last_set_surrounding_text());
813 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
815 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
818 TEST_F(InputMethodChromeOSTest
, SurroundingText_PartialText
) {
820 // Click a text input form.
821 input_type_
= TEXT_INPUT_TYPE_TEXT
;
822 ime_
->OnTextInputTypeChanged(this);
824 // Set the TextInputClient behaviors.
825 surrounding_text_
= UTF8ToUTF16("abcdefghij");
826 text_range_
= gfx::Range(5, 10);
827 selection_range_
= gfx::Range(7, 9);
829 ime_
->OnCaretBoundsChanged(this);
831 // Check the call count.
833 mock_ime_engine_handler_
->set_surrounding_text_call_count());
834 // Set the verifier for SetSurroundingText mock call.
835 // Here (2, 4) is selection range in expected surrounding text coordinates.
837 mock_ime_engine_handler_
->last_set_surrounding_text());
839 mock_ime_engine_handler_
->last_set_surrounding_cursor_pos());
841 mock_ime_engine_handler_
->last_set_surrounding_anchor_pos());
844 TEST_F(InputMethodChromeOSTest
, SurroundingText_BecomeEmptyText
) {
846 // Click a text input form.
847 input_type_
= TEXT_INPUT_TYPE_TEXT
;
848 ime_
->OnTextInputTypeChanged(this);
850 // Set the TextInputClient behaviors.
851 // If the surrounding text becomes empty, text_range become (0, 0) and
852 // selection range become invalid.
853 surrounding_text_
= UTF8ToUTF16("");
854 text_range_
= gfx::Range(0, 0);
855 selection_range_
= gfx::Range::InvalidRange();
857 ime_
->OnCaretBoundsChanged(this);
859 // Check the call count.
861 mock_ime_engine_handler_
->set_surrounding_text_call_count());
863 // Should not be called twice with same condition.
864 ime_
->OnCaretBoundsChanged(this);
866 mock_ime_engine_handler_
->set_surrounding_text_call_count());
869 class InputMethodChromeOSKeyEventTest
: public InputMethodChromeOSTest
{
871 InputMethodChromeOSKeyEventTest() {}
872 ~InputMethodChromeOSKeyEventTest() override
{}
874 void SetUp() override
{
875 InputMethodChromeOSTest::SetUp();
879 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest
);
882 TEST_F(InputMethodChromeOSKeyEventTest
, KeyEventDelayResponseTest
) {
883 const int kFlags
= ui::EF_SHIFT_DOWN
;
884 ui::KeyEvent
event(ui::ET_KEY_PRESSED
, ui::VKEY_A
, kFlags
);
887 input_type_
= TEXT_INPUT_TYPE_TEXT
;
888 ime_
->OnTextInputTypeChanged(this);
889 ime_
->DispatchKeyEvent(&event
);
891 // Check before state.
892 const ui::KeyEvent
* key_event
=
893 mock_ime_engine_handler_
->last_processed_key_event();
894 EXPECT_EQ(1, mock_ime_engine_handler_
->process_key_event_call_count());
895 EXPECT_EQ(ui::VKEY_A
, key_event
->key_code());
896 EXPECT_EQ(kFlags
, key_event
->flags());
897 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
899 (static_cast<chromeos::IMEInputContextHandlerInterface
*>(ime_
.get()))
902 EXPECT_EQ(0, inserted_char_
);
905 mock_ime_engine_handler_
->last_passed_callback().Run(true);
908 EXPECT_EQ(1, ime_
->process_key_event_post_ime_call_count());
909 const ui::KeyEvent
* stored_event
=
910 ime_
->process_key_event_post_ime_args().event
;
911 EXPECT_EQ(ui::VKEY_A
, stored_event
->key_code());
912 EXPECT_EQ(kFlags
, stored_event
->flags());
913 EXPECT_TRUE(ime_
->process_key_event_post_ime_args().handled
);
915 EXPECT_EQ(L
'A', inserted_char_
);
918 TEST_F(InputMethodChromeOSKeyEventTest
, MultiKeyEventDelayResponseTest
) {
920 input_type_
= TEXT_INPUT_TYPE_TEXT
;
921 ime_
->OnTextInputTypeChanged(this);
923 const int kFlags
= ui::EF_SHIFT_DOWN
;
924 ui::KeyEvent
event(ui::ET_KEY_PRESSED
, ui::VKEY_B
, kFlags
);
927 ime_
->DispatchKeyEvent(&event
);
928 const ui::KeyEvent
* key_event
=
929 mock_ime_engine_handler_
->last_processed_key_event();
930 EXPECT_EQ(ui::VKEY_B
, key_event
->key_code());
931 EXPECT_EQ(kFlags
, key_event
->flags());
933 KeyEventCallback first_callback
=
934 mock_ime_engine_handler_
->last_passed_callback();
936 // Do key event again.
937 ui::KeyEvent
event2(ui::ET_KEY_PRESSED
, ui::VKEY_C
, kFlags
);
939 ime_
->DispatchKeyEvent(&event2
);
940 const ui::KeyEvent
* key_event2
=
941 mock_ime_engine_handler_
->last_processed_key_event();
942 EXPECT_EQ(ui::VKEY_C
, key_event2
->key_code());
943 EXPECT_EQ(kFlags
, key_event2
->flags());
945 // Check before state.
947 mock_ime_engine_handler_
->process_key_event_call_count());
948 EXPECT_EQ(0, ime_
->process_key_event_post_ime_call_count());
950 chromeos::CompositionText comp
;
951 comp
.set_text(base::ASCIIToUTF16("B"));
952 (static_cast<chromeos::IMEInputContextHandlerInterface
*>(ime_
.get()))
953 ->UpdateCompositionText(comp
, comp
.text().length(), true);
955 EXPECT_EQ(0, composition_text_
.text
[0]);
957 // Do callback for first key event.
958 first_callback
.Run(true);
960 EXPECT_EQ(comp
.text(), composition_text_
.text
);
962 // Check the results for first key event.
963 EXPECT_EQ(1, ime_
->process_key_event_post_ime_call_count());
964 const ui::KeyEvent
* stored_event
=
965 ime_
->process_key_event_post_ime_args().event
;
966 EXPECT_EQ(ui::VKEY_B
, stored_event
->key_code());
967 EXPECT_EQ(kFlags
, stored_event
->flags());
968 EXPECT_TRUE(ime_
->process_key_event_post_ime_args().handled
);
969 EXPECT_EQ(0, inserted_char_
);
971 // Do callback for second key event.
972 mock_ime_engine_handler_
->last_passed_callback().Run(false);
974 // Check the results for second key event.
975 EXPECT_EQ(2, ime_
->process_key_event_post_ime_call_count());
976 stored_event
= ime_
->process_key_event_post_ime_args().event
;
977 EXPECT_EQ(ui::VKEY_C
, stored_event
->key_code());
978 EXPECT_EQ(kFlags
, stored_event
->flags());
979 EXPECT_FALSE(ime_
->process_key_event_post_ime_args().handled
);
981 EXPECT_EQ(L
'C', inserted_char_
);
984 TEST_F(InputMethodChromeOSKeyEventTest
, StopPropagationTest
) {
986 input_type_
= TEXT_INPUT_TYPE_TEXT
;
987 ime_
->OnTextInputTypeChanged(this);
989 // Do key event with event being stopped propagation.
990 stop_propagation_post_ime_
= true;
991 ui::KeyEvent
eventA(ui::ET_KEY_PRESSED
, ui::VKEY_A
, EF_NONE
);
992 eventA
.set_character(L
'A');
993 ime_
->DispatchKeyEvent(&eventA
);
994 mock_ime_engine_handler_
->last_passed_callback().Run(false);
996 const ui::KeyEvent
* key_event
=
997 mock_ime_engine_handler_
->last_processed_key_event();
998 EXPECT_EQ(ui::VKEY_A
, key_event
->key_code());
999 EXPECT_EQ(0, inserted_char_
);
1001 // Do key event with event not being stopped propagation.
1002 stop_propagation_post_ime_
= false;
1003 ime_
->DispatchKeyEvent(&eventA
);
1004 mock_ime_engine_handler_
->last_passed_callback().Run(false);
1006 key_event
= mock_ime_engine_handler_
->last_processed_key_event();
1007 EXPECT_EQ(ui::VKEY_A
, key_event
->key_code());
1008 EXPECT_EQ(L
'A', inserted_char_
);