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 int GetTextInputFlags() const override {
285 virtual bool CanComposeInline() const override {
286 return can_compose_inline_;
288 virtual gfx::Rect GetCaretBounds() const override {
289 return caret_bounds_;
291 virtual bool GetCompositionCharacterBounds(uint32 index,
292 gfx::Rect* rect) const override {
295 virtual bool HasCompositionText() const override {
296 CompositionText empty;
297 return composition_text_ != empty;
299 virtual bool GetTextRange(gfx::Range* range) const override {
300 *range = text_range_;
303 virtual bool GetCompositionTextRange(gfx::Range* range) const override {
306 virtual bool GetSelectionRange(gfx::Range* range) const override {
307 *range = selection_range_;
311 virtual bool SetSelectionRange(const gfx::Range& range) override {
314 virtual bool DeleteRange(const gfx::Range& range) override { return false; }
315 virtual bool GetTextFromRange(const gfx::Range& range,
316 base::string16* text) const override {
317 *text = surrounding_text_.substr(range.GetMin(), range.length());
320 virtual void OnInputMethodChanged() override {
321 ++on_input_method_changed_call_count_;
323 virtual bool ChangeTextDirectionAndLayoutAlignment(
324 base::i18n::TextDirection direction) override { return false; }
325 virtual void ExtendSelectionAndDelete(size_t before,
326 size_t after) override {}
327 virtual void EnsureCaretInRect(const gfx::Rect& rect) override {}
328 virtual void OnCandidateWindowShown() override {}
329 virtual void OnCandidateWindowUpdated() override {}
330 virtual void OnCandidateWindowHidden() override {}
331 virtual bool IsEditingCommandEnabled(int command_id) override {
334 virtual void ExecuteEditingCommand(int command_id) override {}
336 bool HasNativeEvent() const {
337 return dispatched_key_event_.HasNativeEvent();
341 dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN,
344 composition_text_.Clear();
345 confirmed_text_.Clear();
346 inserted_text_.clear();
348 inserted_char_flags_ = 0;
349 on_input_method_changed_call_count_ = 0;
351 input_type_ = TEXT_INPUT_TYPE_NONE;
352 input_mode_ = TEXT_INPUT_MODE_DEFAULT;
353 can_compose_inline_ = true;
354 caret_bounds_ = gfx::Rect();
357 scoped_ptr<TestableInputMethodChromeOS> ime_;
359 // Copy of the dispatched key event.
360 ui::KeyEvent dispatched_key_event_;
362 // Variables for remembering the parameters that are passed to
363 // ui::TextInputClient functions.
364 CompositionText composition_text_;
365 CompositionText confirmed_text_;
366 base::string16 inserted_text_;
367 base::char16 inserted_char_;
368 unsigned int on_input_method_changed_call_count_;
369 int inserted_char_flags_;
371 // Variables that will be returned from the ui::TextInputClient functions.
372 TextInputType input_type_;
373 TextInputMode input_mode_;
374 bool can_compose_inline_;
375 gfx::Rect caret_bounds_;
376 gfx::Range text_range_;
377 gfx::Range selection_range_;
378 base::string16 surrounding_text_;
380 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
381 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
382 mock_ime_candidate_window_handler_;
384 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
387 // Tests public APIs in ui::InputMethod first.
389 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
390 // ui::InputMethodChromeOS does not support the API.
392 EXPECT_EQ("", ime_->GetInputLocale());
395 TEST_F(InputMethodChromeOSTest, IsActive) {
397 // ui::InputMethodChromeOS always returns true.
398 EXPECT_TRUE(ime_->IsActive());
401 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
403 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
404 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
405 ime_->OnTextInputTypeChanged(this);
406 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
407 input_type_ = TEXT_INPUT_TYPE_TEXT;
408 ime_->OnTextInputTypeChanged(this);
409 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
412 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
414 EXPECT_TRUE(ime_->CanComposeInline());
415 can_compose_inline_ = false;
416 ime_->OnTextInputTypeChanged(this);
417 EXPECT_FALSE(ime_->CanComposeInline());
420 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
422 EXPECT_EQ(this, ime_->GetTextInputClient());
423 if (switches::IsTextInputFocusManagerEnabled())
424 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
426 ime_->SetFocusedTextInputClient(NULL);
427 EXPECT_EQ(NULL, ime_->GetTextInputClient());
430 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
432 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
433 if (switches::IsTextInputFocusManagerEnabled())
434 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
436 ime_->SetFocusedTextInputClient(NULL);
437 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
438 ime_->OnTextInputTypeChanged(this);
439 // The OnTextInputTypeChanged() call above should be ignored since |this| is
440 // not the current focused client.
441 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
443 if (switches::IsTextInputFocusManagerEnabled())
444 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
446 ime_->SetFocusedTextInputClient(this);
447 ime_->OnTextInputTypeChanged(this);
448 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
451 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
453 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
454 if (switches::IsTextInputFocusManagerEnabled())
455 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
458 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
459 ime_->OnTextInputTypeChanged(this);
460 // The OnTextInputTypeChanged() call above should be ignored since the top-
461 // level window which the ime_ is attached to is not currently focused.
462 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
464 if (switches::IsTextInputFocusManagerEnabled())
465 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
468 ime_->OnTextInputTypeChanged(this);
469 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
472 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
473 // We no longer support the case that |ime_->Init(false)| because no one
475 if (switches::IsTextInputFocusManagerEnabled())
478 ime_->Init(false); // the top-level is initially unfocused.
479 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
480 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
481 ime_->OnTextInputTypeChanged(this);
482 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
485 ime_->OnTextInputTypeChanged(this);
486 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
489 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
491 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
493 // A context shouldn't be created since the daemon is not running.
494 EXPECT_EQ(0U, on_input_method_changed_call_count_);
495 // Click a text input form.
496 input_type_ = TEXT_INPUT_TYPE_TEXT;
497 ime_->OnTextInputTypeChanged(this);
498 // Since a form has focus, IBusClient::FocusIn() should be called.
499 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
502 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
503 // ui::TextInputClient::OnInputMethodChanged() should be called when
504 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
505 // current text input type is not NONE.
506 EXPECT_EQ(1U, on_input_method_changed_call_count_);
509 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
510 // input_type_ is PASSWORD.
511 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
513 EXPECT_EQ(0U, on_input_method_changed_call_count_);
514 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
515 ime_->OnTextInputTypeChanged(this);
516 // InputMethodEngine::FocusIn() should be called even for password field.
517 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
518 EXPECT_EQ(1U, on_input_method_changed_call_count_);
521 // Confirm that IBusClient::FocusOut is called as expected.
522 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
523 input_type_ = TEXT_INPUT_TYPE_TEXT;
525 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
526 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
527 input_type_ = TEXT_INPUT_TYPE_NONE;
528 ime_->OnTextInputTypeChanged(this);
529 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
530 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
533 // Confirm that IBusClient::FocusOut is called as expected.
534 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
535 input_type_ = TEXT_INPUT_TYPE_TEXT;
537 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
538 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
539 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
540 ime_->OnTextInputTypeChanged(this);
541 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
542 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
545 // FocusIn/FocusOut scenario test
546 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
548 // Confirm that both FocusIn and FocusOut are NOT called.
549 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
550 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
551 EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
552 mock_ime_engine_handler_->last_text_input_context().type);
553 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
554 mock_ime_engine_handler_->last_text_input_context().mode);
556 input_type_ = TEXT_INPUT_TYPE_TEXT;
557 input_mode_ = TEXT_INPUT_MODE_LATIN;
558 ime_->OnTextInputTypeChanged(this);
559 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
560 // TextInputMode is LATIN..
561 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
562 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
563 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
564 mock_ime_engine_handler_->last_text_input_context().type);
565 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
566 mock_ime_engine_handler_->last_text_input_context().mode);
568 input_mode_ = TEXT_INPUT_MODE_KANA;
569 ime_->OnTextInputTypeChanged(this);
570 // Confirm that both FocusIn and FocusOut are called for mode change.
571 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
572 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
573 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
574 mock_ime_engine_handler_->last_text_input_context().type);
575 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
576 mock_ime_engine_handler_->last_text_input_context().mode);
578 input_type_ = TEXT_INPUT_TYPE_URL;
579 ime_->OnTextInputTypeChanged(this);
580 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
582 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
583 EXPECT_EQ(2, mock_ime_engine_handler_->focus_out_call_count());
584 EXPECT_EQ(TEXT_INPUT_TYPE_URL,
585 mock_ime_engine_handler_->last_text_input_context().type);
586 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
587 mock_ime_engine_handler_->last_text_input_context().mode);
589 // When IsTextInputFocusManagerEnabled, InputMethod::SetFocusedTextInputClient
590 // is not supported and it's no-op.
591 if (switches::IsTextInputFocusManagerEnabled())
593 // Confirm that FocusOut is called when set focus to NULL client.
594 ime_->SetFocusedTextInputClient(NULL);
595 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
596 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
597 // Confirm that FocusIn is called when set focus to this client.
598 ime_->SetFocusedTextInputClient(this);
599 EXPECT_EQ(4, mock_ime_engine_handler_->focus_in_call_count());
600 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
603 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
604 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
605 input_type_ = TEXT_INPUT_TYPE_TEXT;
609 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
610 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
611 ime_->OnCaretBoundsChanged(this);
614 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
615 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
616 ime_->OnCaretBoundsChanged(this);
619 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
620 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
621 ime_->OnCaretBoundsChanged(this);
622 // Current InputMethodChromeOS implementation performs the IPC
623 // regardless of the bounds are changed or not.
626 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
629 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
630 const base::string16 kSampleAsciiText = UTF8ToUTF16("Sample Text");
631 const uint32 kCursorPos = 2UL;
633 chromeos::CompositionText chromeos_composition_text;
634 chromeos_composition_text.set_text(kSampleAsciiText);
636 CompositionText composition_text;
637 ime_->ExtractCompositionText(
638 chromeos_composition_text, kCursorPos, &composition_text);
639 EXPECT_EQ(kSampleAsciiText, composition_text.text);
640 // If there is no selection, |selection| represents cursor position.
641 EXPECT_EQ(kCursorPos, composition_text.selection.start());
642 EXPECT_EQ(kCursorPos, composition_text.selection.end());
643 // If there is no underline, |underlines| contains one underline and it is
644 // whole text underline.
645 ASSERT_EQ(1UL, composition_text.underlines.size());
646 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
647 EXPECT_EQ(kSampleAsciiText.size(), composition_text.underlines[0].end_offset);
648 EXPECT_FALSE(composition_text.underlines[0].thick);
651 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
652 const uint32 kCursorPos = 2UL;
654 // Set up chromeos composition text with one underline attribute.
655 chromeos::CompositionText chromeos_composition_text;
656 chromeos_composition_text.set_text(kSampleText);
657 chromeos::CompositionText::UnderlineAttribute underline;
658 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE;
659 underline.start_index = 1UL;
660 underline.end_index = 4UL;
661 chromeos_composition_text.mutable_underline_attributes()->push_back(
664 CompositionText composition_text;
665 ime_->ExtractCompositionText(
666 chromeos_composition_text, kCursorPos, &composition_text);
667 EXPECT_EQ(kSampleText, composition_text.text);
668 // If there is no selection, |selection| represents cursor position.
669 EXPECT_EQ(kCursorPos, composition_text.selection.start());
670 EXPECT_EQ(kCursorPos, composition_text.selection.end());
671 ASSERT_EQ(1UL, composition_text.underlines.size());
672 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
673 composition_text.underlines[0].start_offset);
674 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
675 composition_text.underlines[0].end_offset);
676 // Single underline represents as black thin line.
677 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
678 EXPECT_FALSE(composition_text.underlines[0].thick);
679 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
680 composition_text.underlines[0].background_color);
683 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
684 const uint32 kCursorPos = 2UL;
686 // Set up chromeos composition text with one underline attribute.
687 chromeos::CompositionText chromeos_composition_text;
688 chromeos_composition_text.set_text(kSampleText);
689 chromeos::CompositionText::UnderlineAttribute underline;
690 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE;
691 underline.start_index = 1UL;
692 underline.end_index = 4UL;
693 chromeos_composition_text.mutable_underline_attributes()->push_back(
696 CompositionText composition_text;
697 ime_->ExtractCompositionText(
698 chromeos_composition_text, kCursorPos, &composition_text);
699 EXPECT_EQ(kSampleText, composition_text.text);
700 // If there is no selection, |selection| represents cursor position.
701 EXPECT_EQ(kCursorPos, composition_text.selection.start());
702 EXPECT_EQ(kCursorPos, composition_text.selection.end());
703 ASSERT_EQ(1UL, composition_text.underlines.size());
704 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
705 composition_text.underlines[0].start_offset);
706 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
707 composition_text.underlines[0].end_offset);
708 // Double underline represents as black thick line.
709 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
710 EXPECT_TRUE(composition_text.underlines[0].thick);
711 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
712 composition_text.underlines[0].background_color);
715 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
716 const uint32 kCursorPos = 2UL;
718 // Set up chromeos composition text with one underline attribute.
719 chromeos::CompositionText chromeos_composition_text;
720 chromeos_composition_text.set_text(kSampleText);
721 chromeos::CompositionText::UnderlineAttribute underline;
722 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR;
723 underline.start_index = 1UL;
724 underline.end_index = 4UL;
725 chromeos_composition_text.mutable_underline_attributes()->push_back(
728 CompositionText composition_text;
729 ime_->ExtractCompositionText(
730 chromeos_composition_text, kCursorPos, &composition_text);
731 EXPECT_EQ(kSampleText, composition_text.text);
732 EXPECT_EQ(kCursorPos, composition_text.selection.start());
733 EXPECT_EQ(kCursorPos, composition_text.selection.end());
734 ASSERT_EQ(1UL, composition_text.underlines.size());
735 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
736 composition_text.underlines[0].start_offset);
737 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
738 composition_text.underlines[0].end_offset);
739 // Error underline represents as red thin line.
740 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
741 EXPECT_FALSE(composition_text.underlines[0].thick);
744 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
745 const uint32 kCursorPos = 2UL;
747 // Set up chromeos composition text with one underline attribute.
748 chromeos::CompositionText chromeos_composition_text;
749 chromeos_composition_text.set_text(kSampleText);
750 chromeos_composition_text.set_selection_start(1UL);
751 chromeos_composition_text.set_selection_end(4UL);
753 CompositionText composition_text;
754 ime_->ExtractCompositionText(
755 chromeos_composition_text, kCursorPos, &composition_text);
756 EXPECT_EQ(kSampleText, composition_text.text);
757 EXPECT_EQ(kCursorPos, composition_text.selection.start());
758 EXPECT_EQ(kCursorPos, composition_text.selection.end());
759 ASSERT_EQ(1UL, composition_text.underlines.size());
760 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
761 chromeos_composition_text.selection_start()),
762 composition_text.underlines[0].start_offset);
763 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
764 chromeos_composition_text.selection_end()),
765 composition_text.underlines[0].end_offset);
766 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
767 EXPECT_TRUE(composition_text.underlines[0].thick);
768 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
769 composition_text.underlines[0].background_color);
772 TEST_F(InputMethodChromeOSTest,
773 ExtractCompositionTextTest_SelectionStartWithCursor) {
774 const uint32 kCursorPos = 1UL;
776 // Set up chromeos composition text with one underline attribute.
777 chromeos::CompositionText chromeos_composition_text;
778 chromeos_composition_text.set_text(kSampleText);
779 chromeos_composition_text.set_selection_start(kCursorPos);
780 chromeos_composition_text.set_selection_end(4UL);
782 CompositionText composition_text;
783 ime_->ExtractCompositionText(
784 chromeos_composition_text, kCursorPos, &composition_text);
785 EXPECT_EQ(kSampleText, composition_text.text);
786 // If the cursor position is same as selection bounds, selection start
787 // position become opposit side of selection from cursor.
788 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
789 chromeos_composition_text.selection_end()),
790 composition_text.selection.start());
791 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
792 composition_text.selection.end());
793 ASSERT_EQ(1UL, composition_text.underlines.size());
794 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
795 chromeos_composition_text.selection_start()),
796 composition_text.underlines[0].start_offset);
797 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
798 chromeos_composition_text.selection_end()),
799 composition_text.underlines[0].end_offset);
800 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
801 EXPECT_TRUE(composition_text.underlines[0].thick);
802 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
803 composition_text.underlines[0].background_color);
806 TEST_F(InputMethodChromeOSTest,
807 ExtractCompositionTextTest_SelectionEndWithCursor) {
808 const uint32 kCursorPos = 4UL;
810 // Set up chromeos composition text with one underline attribute.
811 chromeos::CompositionText chromeos_composition_text;
812 chromeos_composition_text.set_text(kSampleText);
813 chromeos_composition_text.set_selection_start(1UL);
814 chromeos_composition_text.set_selection_end(kCursorPos);
816 CompositionText composition_text;
817 ime_->ExtractCompositionText(
818 chromeos_composition_text, kCursorPos, &composition_text);
819 EXPECT_EQ(kSampleText, composition_text.text);
820 // If the cursor position is same as selection bounds, selection start
821 // position become opposit side of selection from cursor.
822 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
823 chromeos_composition_text.selection_start()),
824 composition_text.selection.start());
825 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
826 composition_text.selection.end());
827 ASSERT_EQ(1UL, composition_text.underlines.size());
828 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
829 chromeos_composition_text.selection_start()),
830 composition_text.underlines[0].start_offset);
831 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
832 chromeos_composition_text.selection_end()),
833 composition_text.underlines[0].end_offset);
834 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
835 EXPECT_TRUE(composition_text.underlines[0].thick);
836 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
837 composition_text.underlines[0].background_color);
840 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
842 // Click a text input form.
843 input_type_ = TEXT_INPUT_TYPE_TEXT;
844 ime_->OnTextInputTypeChanged(this);
846 // Set the TextInputClient behaviors.
847 surrounding_text_ = UTF8ToUTF16("abcdef");
848 text_range_ = gfx::Range(0, 6);
849 selection_range_ = gfx::Range(3, 3);
851 // Set the verifier for SetSurroundingText mock call.
852 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
855 ime_->OnCaretBoundsChanged(this);
857 // Check the call count.
859 mock_ime_engine_handler_->set_surrounding_text_call_count());
860 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
861 mock_ime_engine_handler_->last_set_surrounding_text());
863 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
865 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
868 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
870 // Click a text input form.
871 input_type_ = TEXT_INPUT_TYPE_TEXT;
872 ime_->OnTextInputTypeChanged(this);
874 // Set the TextInputClient behaviors.
875 surrounding_text_ = UTF8ToUTF16("abcdef");
876 text_range_ = gfx::Range(0, 6);
877 selection_range_ = gfx::Range(2, 5);
879 // Set the verifier for SetSurroundingText mock call.
880 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
882 ime_->OnCaretBoundsChanged(this);
884 // Check the call count.
886 mock_ime_engine_handler_->set_surrounding_text_call_count());
887 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
888 mock_ime_engine_handler_->last_set_surrounding_text());
890 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
892 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
895 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
897 // Click a text input form.
898 input_type_ = TEXT_INPUT_TYPE_TEXT;
899 ime_->OnTextInputTypeChanged(this);
901 // Set the TextInputClient behaviors.
902 surrounding_text_ = UTF8ToUTF16("abcdefghij");
903 text_range_ = gfx::Range(5, 10);
904 selection_range_ = gfx::Range(7, 9);
906 ime_->OnCaretBoundsChanged(this);
908 // Check the call count.
910 mock_ime_engine_handler_->set_surrounding_text_call_count());
911 // Set the verifier for SetSurroundingText mock call.
912 // Here (2, 4) is selection range in expected surrounding text coordinates.
914 mock_ime_engine_handler_->last_set_surrounding_text());
916 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
918 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
921 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
923 // Click a text input form.
924 input_type_ = TEXT_INPUT_TYPE_TEXT;
925 ime_->OnTextInputTypeChanged(this);
927 // Set the TextInputClient behaviors.
928 // If the surrounding text becomes empty, text_range become (0, 0) and
929 // selection range become invalid.
930 surrounding_text_ = UTF8ToUTF16("");
931 text_range_ = gfx::Range(0, 0);
932 selection_range_ = gfx::Range::InvalidRange();
934 ime_->OnCaretBoundsChanged(this);
936 // Check the call count.
938 mock_ime_engine_handler_->set_surrounding_text_call_count());
940 // Should not be called twice with same condition.
941 ime_->OnCaretBoundsChanged(this);
943 mock_ime_engine_handler_->set_surrounding_text_call_count());
946 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
948 InputMethodChromeOSKeyEventTest() {}
949 virtual ~InputMethodChromeOSKeyEventTest() {}
951 virtual void SetUp() override {
952 InputMethodChromeOSTest::SetUp();
956 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
959 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
960 const int kFlags = ui::EF_SHIFT_DOWN;
961 ScopedXI2Event xevent;
962 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
963 const ui::KeyEvent event(xevent);
966 input_type_ = TEXT_INPUT_TYPE_TEXT;
967 ime_->OnTextInputTypeChanged(this);
968 ime_->DispatchKeyEvent(event);
970 // Check before state.
971 const ui::KeyEvent* key_event =
972 mock_ime_engine_handler_->last_processed_key_event();
973 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
974 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
975 EXPECT_EQ("KeyA", key_event->code());
976 EXPECT_EQ(kFlags, key_event->flags());
977 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
980 mock_ime_engine_handler_->last_passed_callback().Run(true);
983 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
984 const ui::KeyEvent* stored_event =
985 ime_->process_key_event_post_ime_args().event;
986 EXPECT_TRUE(stored_event->HasNativeEvent());
987 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
988 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
991 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
993 input_type_ = TEXT_INPUT_TYPE_TEXT;
994 ime_->OnTextInputTypeChanged(this);
996 const int kFlags = ui::EF_SHIFT_DOWN;
997 ScopedXI2Event xevent;
998 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
999 const ui::KeyEvent event(xevent);
1002 ime_->DispatchKeyEvent(event);
1003 const ui::KeyEvent* key_event =
1004 mock_ime_engine_handler_->last_processed_key_event();
1005 EXPECT_EQ(ui::VKEY_B, key_event->key_code());
1006 EXPECT_EQ("KeyB", key_event->code());
1007 EXPECT_EQ(kFlags, key_event->flags());
1009 KeyEventCallback first_callback =
1010 mock_ime_engine_handler_->last_passed_callback();
1012 // Do key event again.
1013 ScopedXI2Event xevent2;
1014 xevent2.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
1015 const ui::KeyEvent event2(xevent2);
1017 ime_->DispatchKeyEvent(event2);
1018 const ui::KeyEvent* key_event2 =
1019 mock_ime_engine_handler_->last_processed_key_event();
1020 EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
1021 EXPECT_EQ("KeyC", key_event2->code());
1022 EXPECT_EQ(kFlags, key_event2->flags());
1024 // Check before state.
1026 mock_ime_engine_handler_->process_key_event_call_count());
1027 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1029 // Do callback for first key event.
1030 first_callback.Run(true);
1032 // Check the results for first key event.
1033 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
1034 const ui::KeyEvent* stored_event =
1035 ime_->process_key_event_post_ime_args().event;
1036 EXPECT_TRUE(stored_event->HasNativeEvent());
1037 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
1038 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
1040 // Do callback for second key event.
1041 mock_ime_engine_handler_->last_passed_callback().Run(false);
1043 // Check the results for second key event.
1044 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
1045 stored_event = ime_->process_key_event_post_ime_args().event;
1046 EXPECT_TRUE(stored_event->HasNativeEvent());
1047 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2, *(stored_event->native_event())));
1048 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
1051 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) {
1052 ScopedXI2Event xevent;
1053 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN);
1054 const ui::KeyEvent event(xevent);
1057 input_type_ = TEXT_INPUT_TYPE_TEXT;
1058 ime_->OnTextInputTypeChanged(this);
1059 ime_->DispatchKeyEvent(event);
1061 // Check before state.
1062 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
1063 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1065 ime_->ResetContext();
1068 mock_ime_engine_handler_->last_passed_callback().Run(true);
1070 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1072 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).