| 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..118ecef74aa714ac606cb4871f77ca48fe7cfff5 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
|
|
|