Index: ui/views/controls/textfield/textfield_unittest.cc |
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc |
index a02456ec6b8a3c2d66a250542ffa610665b104be..11745e2b244a7dac7ae01e1e80b171c88c7f06d8 100644 |
--- a/ui/views/controls/textfield/textfield_unittest.cc |
+++ b/ui/views/controls/textfield/textfield_unittest.cc |
@@ -13,14 +13,20 @@ |
#include "base/strings/string16.h" |
#include "base/strings/utf_string_conversions.h" |
#include "ui/accessibility/ax_view_state.h" |
+#include "ui/aura/window.h" |
+#include "ui/aura/window_tree_host.h" |
#include "ui/base/clipboard/clipboard.h" |
#include "ui/base/clipboard/scoped_clipboard_writer.h" |
#include "ui/base/dragdrop/drag_drop_types.h" |
+#include "ui/base/ime/input_method_base.h" |
+#include "ui/base/ime/input_method_delegate.h" |
+#include "ui/base/ime/input_method_factory.h" |
#include "ui/base/ime/text_input_client.h" |
#include "ui/base/l10n/l10n_util.h" |
#include "ui/base/ui_base_switches.h" |
#include "ui/base/ui_base_switches_util.h" |
#include "ui/events/event.h" |
+#include "ui/events/event_processor.h" |
#include "ui/events/event_utils.h" |
#include "ui/events/keycodes/keyboard_codes.h" |
#include "ui/events/test/event_generator.h" |
@@ -30,7 +36,6 @@ |
#include "ui/views/controls/textfield/textfield_model.h" |
#include "ui/views/controls/textfield/textfield_test_api.h" |
#include "ui/views/focus/focus_manager.h" |
-#include "ui/views/ime/mock_input_method.h" |
#include "ui/views/test/test_views_delegate.h" |
#include "ui/views/test/views_test_base.h" |
#include "ui/views/test/widget_test.h" |
@@ -59,6 +64,184 @@ namespace { |
const base::char16 kHebrewLetterSamekh = 0x05E1; |
+class MockInputMethod : public ui::InputMethodBase { |
+ public: |
+ MockInputMethod(); |
+ ~MockInputMethod() override; |
+ |
+ // Overridden from InputMethod: |
+ bool OnUntranslatedIMEMessage(const base::NativeEvent& event, |
+ NativeEventResult* result) override; |
+ bool DispatchKeyEvent(const ui::KeyEvent& key) override; |
+ void OnTextInputTypeChanged(const ui::TextInputClient* client) override; |
+ void OnCaretBoundsChanged(const ui::TextInputClient* client) override {} |
+ void CancelComposition(const ui::TextInputClient* client) override; |
+ void OnInputLocaleChanged() override {} |
+ std::string GetInputLocale() override; |
+ bool IsActive() override; |
+ bool IsCandidatePopupOpen() const override; |
+ void ShowImeIfNeeded() override {} |
+ |
+ bool untranslated_ime_message_called() const { |
+ return untranslated_ime_message_called_; |
+ } |
+ bool text_input_type_changed() const { return text_input_type_changed_; } |
+ bool cancel_composition_called() const { return cancel_composition_called_; } |
+ |
+ // Clears all internal states and result. |
+ void Clear(); |
+ |
+ void SetCompositionTextForNextKey(const ui::CompositionText& composition); |
+ void SetResultTextForNextKey(const base::string16& result); |
+ |
+ private: |
+ // Overridden from InputMethodBase. |
+ void OnWillChangeFocusedClient(ui::TextInputClient* focused_before, |
+ ui::TextInputClient* focused) override; |
+ |
+ // Clears boolean states defined below. |
+ void ClearStates(); |
+ |
+ // Whether a mock composition or result is scheduled for the next key event. |
+ bool HasComposition(); |
+ |
+ // Clears only composition information and result text. |
+ void ClearComposition(); |
+ |
+ // Composition information for the next key event. It'll be cleared |
+ // automatically after dispatching the next key event. |
+ ui::CompositionText composition_; |
+ |
+ // Result text for the next key event. It'll be cleared automatically after |
+ // dispatching the next key event. |
+ base::string16 result_text_; |
+ |
+ // Record call state of corresponding methods. They will be set to false |
+ // automatically before dispatching a key event. |
+ bool untranslated_ime_message_called_; |
+ bool text_input_type_changed_; |
+ bool cancel_composition_called_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MockInputMethod); |
+}; |
+ |
+MockInputMethod::MockInputMethod() |
+ : untranslated_ime_message_called_(false), |
+ text_input_type_changed_(false), |
+ cancel_composition_called_(false) { |
+} |
+ |
+MockInputMethod::~MockInputMethod() { |
+} |
+ |
+bool MockInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event, |
+ NativeEventResult* result) { |
+ if (result) |
+ *result = NativeEventResult(); |
+ return false; |
+} |
+ |
+bool MockInputMethod::DispatchKeyEvent(const ui::KeyEvent& key) { |
+ // Checks whether the key event is from EventGenerator on Windows which will |
+ // generate key event for WM_CHAR. |
+ // The MockInputMethod will insert char on WM_KEYDOWN so ignore WM_CHAR here. |
+ if (key.is_char() && key.HasNativeEvent()) |
+ return true; |
+ |
+ bool handled = !IsTextInputTypeNone() && HasComposition(); |
+ ClearStates(); |
+ if (handled) { |
+ DCHECK(!key.is_char()); |
+ ui::KeyEvent mock_key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, key.flags()); |
+ DispatchKeyEventPostIME(mock_key); |
+ } else { |
+ DispatchKeyEventPostIME(key); |
+ } |
+ |
+ ui::TextInputClient* client = GetTextInputClient(); |
+ if (client) { |
+ if (handled) { |
+ if (result_text_.length()) |
+ client->InsertText(result_text_); |
+ if (composition_.text.length()) |
+ client->SetCompositionText(composition_); |
+ else |
+ client->ClearCompositionText(); |
+ } else if (key.type() == ui::ET_KEY_PRESSED) { |
+ base::char16 ch = key.GetCharacter(); |
+ if (ch) |
+ client->InsertChar(ch, key.flags()); |
+ } |
+ } |
+ |
+ ClearComposition(); |
+ return true; |
+} |
+ |
+void MockInputMethod::OnTextInputTypeChanged( |
+ const ui::TextInputClient* client) { |
+ if (IsTextInputClientFocused(client)) |
+ text_input_type_changed_ = true; |
+ InputMethodBase::OnTextInputTypeChanged(client); |
+} |
+ |
+void MockInputMethod::CancelComposition(const ui::TextInputClient* client) { |
+ if (IsTextInputClientFocused(client)) { |
+ cancel_composition_called_ = true; |
+ ClearComposition(); |
+ } |
+} |
+ |
+std::string MockInputMethod::GetInputLocale() { |
+ return "en-US"; |
+} |
+ |
+bool MockInputMethod::IsActive() { |
+ return true; |
+} |
+ |
+bool MockInputMethod::IsCandidatePopupOpen() const { |
+ return false; |
+} |
+ |
+void MockInputMethod::OnWillChangeFocusedClient( |
+ ui::TextInputClient* focused_before, |
+ ui::TextInputClient* focused) { |
+ ui::TextInputClient* client = GetTextInputClient(); |
+ if (client && client->HasCompositionText()) |
+ client->ConfirmCompositionText(); |
+ ClearComposition(); |
+} |
+ |
+void MockInputMethod::Clear() { |
+ ClearStates(); |
+ ClearComposition(); |
+} |
+ |
+void MockInputMethod::SetCompositionTextForNextKey( |
+ const ui::CompositionText& composition) { |
+ composition_ = composition; |
+} |
+ |
+void MockInputMethod::SetResultTextForNextKey(const base::string16& result) { |
+ result_text_ = result; |
+} |
+ |
+void MockInputMethod::ClearStates() { |
+ untranslated_ime_message_called_ = false; |
+ text_input_type_changed_ = false; |
+ cancel_composition_called_ = false; |
+} |
+ |
+bool MockInputMethod::HasComposition() { |
+ return composition_.text.length() || result_text_.length(); |
+} |
+ |
+void MockInputMethod::ClearComposition() { |
+ composition_.Clear(); |
+ result_text_.clear(); |
+} |
+ |
// A Textfield wrapper to intercept OnKey[Pressed|Released]() ressults. |
class TestTextfield : public views::Textfield { |
public: |
@@ -171,6 +354,8 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
on_before_user_action_(0), |
on_after_user_action_(0), |
copied_to_clipboard_(ui::CLIPBOARD_TYPE_LAST) { |
+ input_method_ = new MockInputMethod(); |
+ ui::SetUpInputMethodForTesting(input_method_); |
} |
// ::testing::Test: |
@@ -224,6 +409,8 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
params.bounds = gfx::Rect(100, 100, 100, 100); |
widget_->Init(params); |
+ input_method_->SetDelegate(static_cast<ui::internal::InputMethodDelegate*>( |
+ widget_->GetNativeWindow()->GetRootWindow()->GetHost())); |
View* container = new View(); |
widget_->SetContentsView(container); |
container->AddChildView(textfield_); |
@@ -240,9 +427,6 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
model_ = test_api_->model(); |
model_->ClearEditHistory(); |
- input_method_ = new MockInputMethod(); |
- widget_->ReplaceInputMethod(input_method_); |
- |
// Since the window type is activatable, showing the widget will also |
// activate it. Calling Activate directly is insufficient, since that does |
// not also _focus_ an aura::Window (i.e. using the FocusClient). Both the |
@@ -1283,7 +1467,7 @@ TEST_F(TextfieldTest, ReadOnlyTest) { |
TEST_F(TextfieldTest, TextInputClientTest) { |
InitTextfield(); |
- ui::TextInputClient* client = textfield_->GetTextInputClient(); |
+ ui::TextInputClient* client = textfield_; |
EXPECT_TRUE(client); |
EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, client->GetTextInputType()); |
@@ -1380,24 +1564,21 @@ TEST_F(TextfieldTest, TextInputClientTest) { |
// Changing the Textfield to readonly shouldn't change the input client, since |
// it's still required for selections and clipboard copy. |
- ui::TextInputClient* text_input_client = textfield_->GetTextInputClient(); |
+ ui::TextInputClient* text_input_client = textfield_; |
EXPECT_TRUE(text_input_client); |
EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, text_input_client->GetTextInputType()); |
textfield_->SetReadOnly(true); |
EXPECT_TRUE(input_method_->text_input_type_changed()); |
- EXPECT_EQ(text_input_client, textfield_->GetTextInputClient()); |
EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, text_input_client->GetTextInputType()); |
input_method_->Clear(); |
textfield_->SetReadOnly(false); |
EXPECT_TRUE(input_method_->text_input_type_changed()); |
- EXPECT_EQ(text_input_client, textfield_->GetTextInputClient()); |
EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, text_input_client->GetTextInputType()); |
input_method_->Clear(); |
textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); |
EXPECT_TRUE(input_method_->text_input_type_changed()); |
- EXPECT_TRUE(textfield_->GetTextInputClient()); |
} |
TEST_F(TextfieldTest, UndoRedoTest) { |
@@ -1908,7 +2089,7 @@ TEST_F(TextfieldTest, GetCompositionCharacterBoundsTest) { |
ui::CompositionText composition; |
composition.text = UTF8ToUTF16("abc123"); |
const uint32 char_count = static_cast<uint32>(composition.text.length()); |
- ui::TextInputClient* client = textfield_->GetTextInputClient(); |
+ ui::TextInputClient* client = textfield_; |
// Compare the composition character bounds with surrounding cursor bounds. |
for (uint32 i = 0; i < char_count; ++i) { |
@@ -1954,7 +2135,7 @@ TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText) { |
ui::CompositionText composition; |
composition.text.assign(kUtf16Chars, kUtf16Chars + kUtf16CharsCount); |
- ui::TextInputClient* client = textfield_->GetTextInputClient(); |
+ ui::TextInputClient* client = textfield_; |
client->SetCompositionText(composition); |
// Make sure GetCompositionCharacterBounds never fails for index. |