OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/controls/textfield/textfield.h" | 5 #include "ui/views/controls/textfield/textfield.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/pickle.h" | 12 #include "base/pickle.h" |
13 #include "base/strings/string16.h" | 13 #include "base/strings/string16.h" |
14 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
15 #include "ui/accessibility/ax_view_state.h" | 15 #include "ui/accessibility/ax_view_state.h" |
| 16 #include "ui/aura/window.h" |
| 17 #include "ui/aura/window_tree_host.h" |
16 #include "ui/base/clipboard/clipboard.h" | 18 #include "ui/base/clipboard/clipboard.h" |
17 #include "ui/base/clipboard/scoped_clipboard_writer.h" | 19 #include "ui/base/clipboard/scoped_clipboard_writer.h" |
18 #include "ui/base/dragdrop/drag_drop_types.h" | 20 #include "ui/base/dragdrop/drag_drop_types.h" |
| 21 #include "ui/base/ime/input_method_base.h" |
| 22 #include "ui/base/ime/input_method_delegate.h" |
| 23 #include "ui/base/ime/input_method_factory.h" |
19 #include "ui/base/ime/text_input_client.h" | 24 #include "ui/base/ime/text_input_client.h" |
20 #include "ui/base/l10n/l10n_util.h" | 25 #include "ui/base/l10n/l10n_util.h" |
21 #include "ui/base/ui_base_switches.h" | 26 #include "ui/base/ui_base_switches.h" |
22 #include "ui/base/ui_base_switches_util.h" | 27 #include "ui/base/ui_base_switches_util.h" |
23 #include "ui/events/event.h" | 28 #include "ui/events/event.h" |
| 29 #include "ui/events/event_processor.h" |
24 #include "ui/events/event_utils.h" | 30 #include "ui/events/event_utils.h" |
25 #include "ui/events/keycodes/keyboard_codes.h" | 31 #include "ui/events/keycodes/keyboard_codes.h" |
26 #include "ui/events/test/event_generator.h" | 32 #include "ui/events/test/event_generator.h" |
27 #include "ui/gfx/render_text.h" | 33 #include "ui/gfx/render_text.h" |
28 #include "ui/strings/grit/ui_strings.h" | 34 #include "ui/strings/grit/ui_strings.h" |
29 #include "ui/views/controls/textfield/textfield_controller.h" | 35 #include "ui/views/controls/textfield/textfield_controller.h" |
30 #include "ui/views/controls/textfield/textfield_model.h" | 36 #include "ui/views/controls/textfield/textfield_model.h" |
31 #include "ui/views/controls/textfield/textfield_test_api.h" | 37 #include "ui/views/controls/textfield/textfield_test_api.h" |
32 #include "ui/views/focus/focus_manager.h" | 38 #include "ui/views/focus/focus_manager.h" |
33 #include "ui/views/ime/mock_input_method.h" | |
34 #include "ui/views/test/test_views_delegate.h" | 39 #include "ui/views/test/test_views_delegate.h" |
35 #include "ui/views/test/views_test_base.h" | 40 #include "ui/views/test/views_test_base.h" |
36 #include "ui/views/test/widget_test.h" | 41 #include "ui/views/test/widget_test.h" |
37 #include "ui/views/widget/widget.h" | 42 #include "ui/views/widget/widget.h" |
38 #include "url/gurl.h" | 43 #include "url/gurl.h" |
39 | 44 |
40 #if defined(OS_WIN) | 45 #if defined(OS_WIN) |
41 #include "base/win/windows_version.h" | 46 #include "base/win/windows_version.h" |
42 #endif | 47 #endif |
43 | 48 |
44 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | 49 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
45 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" | 50 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h" |
46 #endif | 51 #endif |
47 | 52 |
48 #if defined(USE_X11) | 53 #if defined(USE_X11) |
49 #include "ui/events/event_utils.h" | 54 #include "ui/events/event_utils.h" |
50 #endif | 55 #endif |
51 | 56 |
52 using base::ASCIIToUTF16; | 57 using base::ASCIIToUTF16; |
53 using base::UTF8ToUTF16; | 58 using base::UTF8ToUTF16; |
54 using base::WideToUTF16; | 59 using base::WideToUTF16; |
55 | 60 |
56 #define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(ASCIIToUTF16(ascii), utf16) | 61 #define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(ASCIIToUTF16(ascii), utf16) |
57 | 62 |
58 namespace { | 63 namespace { |
59 | 64 |
60 const base::char16 kHebrewLetterSamekh = 0x05E1; | 65 const base::char16 kHebrewLetterSamekh = 0x05E1; |
61 | 66 |
| 67 class MockInputMethod : public ui::InputMethodBase { |
| 68 public: |
| 69 MockInputMethod(); |
| 70 ~MockInputMethod() override; |
| 71 |
| 72 // Overridden from InputMethod: |
| 73 bool OnUntranslatedIMEMessage(const base::NativeEvent& event, |
| 74 NativeEventResult* result) override; |
| 75 bool DispatchKeyEvent(const ui::KeyEvent& key) override; |
| 76 void OnTextInputTypeChanged(const ui::TextInputClient* client) override; |
| 77 void OnCaretBoundsChanged(const ui::TextInputClient* client) override {} |
| 78 void CancelComposition(const ui::TextInputClient* client) override; |
| 79 void OnInputLocaleChanged() override {} |
| 80 std::string GetInputLocale() override; |
| 81 bool IsActive() override; |
| 82 bool IsCandidatePopupOpen() const override; |
| 83 void ShowImeIfNeeded() override {} |
| 84 |
| 85 bool untranslated_ime_message_called() const { |
| 86 return untranslated_ime_message_called_; |
| 87 } |
| 88 bool text_input_type_changed() const { return text_input_type_changed_; } |
| 89 bool cancel_composition_called() const { return cancel_composition_called_; } |
| 90 |
| 91 // Clears all internal states and result. |
| 92 void Clear(); |
| 93 |
| 94 void SetCompositionTextForNextKey(const ui::CompositionText& composition); |
| 95 void SetResultTextForNextKey(const base::string16& result); |
| 96 |
| 97 private: |
| 98 // Overridden from InputMethodBase. |
| 99 void OnWillChangeFocusedClient(ui::TextInputClient* focused_before, |
| 100 ui::TextInputClient* focused) override; |
| 101 |
| 102 // Clears boolean states defined below. |
| 103 void ClearStates(); |
| 104 |
| 105 // Whether a mock composition or result is scheduled for the next key event. |
| 106 bool HasComposition(); |
| 107 |
| 108 // Clears only composition information and result text. |
| 109 void ClearComposition(); |
| 110 |
| 111 // Composition information for the next key event. It'll be cleared |
| 112 // automatically after dispatching the next key event. |
| 113 ui::CompositionText composition_; |
| 114 |
| 115 // Result text for the next key event. It'll be cleared automatically after |
| 116 // dispatching the next key event. |
| 117 base::string16 result_text_; |
| 118 |
| 119 // Record call state of corresponding methods. They will be set to false |
| 120 // automatically before dispatching a key event. |
| 121 bool untranslated_ime_message_called_; |
| 122 bool text_input_type_changed_; |
| 123 bool cancel_composition_called_; |
| 124 |
| 125 DISALLOW_COPY_AND_ASSIGN(MockInputMethod); |
| 126 }; |
| 127 |
| 128 MockInputMethod::MockInputMethod() |
| 129 : untranslated_ime_message_called_(false), |
| 130 text_input_type_changed_(false), |
| 131 cancel_composition_called_(false) { |
| 132 } |
| 133 |
| 134 MockInputMethod::~MockInputMethod() { |
| 135 } |
| 136 |
| 137 bool MockInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event, |
| 138 NativeEventResult* result) { |
| 139 if (result) |
| 140 *result = NativeEventResult(); |
| 141 return false; |
| 142 } |
| 143 |
| 144 bool MockInputMethod::DispatchKeyEvent(const ui::KeyEvent& key) { |
| 145 // Checks whether the key event is from EventGenerator on Windows which will |
| 146 // generate key event for WM_CHAR. |
| 147 // The MockInputMethod will insert char on WM_KEYDOWN so ignore WM_CHAR here. |
| 148 if (key.is_char() && key.HasNativeEvent()) |
| 149 return true; |
| 150 |
| 151 bool handled = !IsTextInputTypeNone() && HasComposition(); |
| 152 ClearStates(); |
| 153 if (handled) { |
| 154 DCHECK(!key.is_char()); |
| 155 ui::KeyEvent mock_key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, key.flags()); |
| 156 DispatchKeyEventPostIME(mock_key); |
| 157 } else { |
| 158 DispatchKeyEventPostIME(key); |
| 159 } |
| 160 |
| 161 ui::TextInputClient* client = GetTextInputClient(); |
| 162 if (client) { |
| 163 if (handled) { |
| 164 if (result_text_.length()) |
| 165 client->InsertText(result_text_); |
| 166 if (composition_.text.length()) |
| 167 client->SetCompositionText(composition_); |
| 168 else |
| 169 client->ClearCompositionText(); |
| 170 } else if (key.type() == ui::ET_KEY_PRESSED) { |
| 171 base::char16 ch = key.GetCharacter(); |
| 172 if (ch) |
| 173 client->InsertChar(ch, key.flags()); |
| 174 } |
| 175 } |
| 176 |
| 177 ClearComposition(); |
| 178 return true; |
| 179 } |
| 180 |
| 181 void MockInputMethod::OnTextInputTypeChanged( |
| 182 const ui::TextInputClient* client) { |
| 183 if (IsTextInputClientFocused(client)) |
| 184 text_input_type_changed_ = true; |
| 185 InputMethodBase::OnTextInputTypeChanged(client); |
| 186 } |
| 187 |
| 188 void MockInputMethod::CancelComposition(const ui::TextInputClient* client) { |
| 189 if (IsTextInputClientFocused(client)) { |
| 190 cancel_composition_called_ = true; |
| 191 ClearComposition(); |
| 192 } |
| 193 } |
| 194 |
| 195 std::string MockInputMethod::GetInputLocale() { |
| 196 return "en-US"; |
| 197 } |
| 198 |
| 199 bool MockInputMethod::IsActive() { |
| 200 return true; |
| 201 } |
| 202 |
| 203 bool MockInputMethod::IsCandidatePopupOpen() const { |
| 204 return false; |
| 205 } |
| 206 |
| 207 void MockInputMethod::OnWillChangeFocusedClient( |
| 208 ui::TextInputClient* focused_before, |
| 209 ui::TextInputClient* focused) { |
| 210 ui::TextInputClient* client = GetTextInputClient(); |
| 211 if (client && client->HasCompositionText()) |
| 212 client->ConfirmCompositionText(); |
| 213 ClearComposition(); |
| 214 } |
| 215 |
| 216 void MockInputMethod::Clear() { |
| 217 ClearStates(); |
| 218 ClearComposition(); |
| 219 } |
| 220 |
| 221 void MockInputMethod::SetCompositionTextForNextKey( |
| 222 const ui::CompositionText& composition) { |
| 223 composition_ = composition; |
| 224 } |
| 225 |
| 226 void MockInputMethod::SetResultTextForNextKey(const base::string16& result) { |
| 227 result_text_ = result; |
| 228 } |
| 229 |
| 230 void MockInputMethod::ClearStates() { |
| 231 untranslated_ime_message_called_ = false; |
| 232 text_input_type_changed_ = false; |
| 233 cancel_composition_called_ = false; |
| 234 } |
| 235 |
| 236 bool MockInputMethod::HasComposition() { |
| 237 return composition_.text.length() || result_text_.length(); |
| 238 } |
| 239 |
| 240 void MockInputMethod::ClearComposition() { |
| 241 composition_.Clear(); |
| 242 result_text_.clear(); |
| 243 } |
| 244 |
62 // A Textfield wrapper to intercept OnKey[Pressed|Released]() ressults. | 245 // A Textfield wrapper to intercept OnKey[Pressed|Released]() ressults. |
63 class TestTextfield : public views::Textfield { | 246 class TestTextfield : public views::Textfield { |
64 public: | 247 public: |
65 TestTextfield() | 248 TestTextfield() |
66 : Textfield(), | 249 : Textfield(), |
67 key_handled_(false), | 250 key_handled_(false), |
68 key_received_(false), | 251 key_received_(false), |
69 weak_ptr_factory_(this) {} | 252 weak_ptr_factory_(this) {} |
70 | 253 |
71 bool OnKeyPressed(const ui::KeyEvent& e) override { | 254 bool OnKeyPressed(const ui::KeyEvent& e) override { |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 class TextfieldTest : public ViewsTestBase, public TextfieldController { | 347 class TextfieldTest : public ViewsTestBase, public TextfieldController { |
165 public: | 348 public: |
166 TextfieldTest() | 349 TextfieldTest() |
167 : widget_(NULL), | 350 : widget_(NULL), |
168 textfield_(NULL), | 351 textfield_(NULL), |
169 model_(NULL), | 352 model_(NULL), |
170 input_method_(NULL), | 353 input_method_(NULL), |
171 on_before_user_action_(0), | 354 on_before_user_action_(0), |
172 on_after_user_action_(0), | 355 on_after_user_action_(0), |
173 copied_to_clipboard_(ui::CLIPBOARD_TYPE_LAST) { | 356 copied_to_clipboard_(ui::CLIPBOARD_TYPE_LAST) { |
| 357 input_method_ = new MockInputMethod(); |
| 358 ui::SetUpInputMethodForTesting(input_method_); |
174 } | 359 } |
175 | 360 |
176 // ::testing::Test: | 361 // ::testing::Test: |
177 void TearDown() override { | 362 void TearDown() override { |
178 if (widget_) | 363 if (widget_) |
179 widget_->Close(); | 364 widget_->Close(); |
180 ViewsTestBase::TearDown(); | 365 ViewsTestBase::TearDown(); |
181 } | 366 } |
182 | 367 |
183 ui::ClipboardType GetAndResetCopiedToClipboard() { | 368 ui::ClipboardType GetAndResetCopiedToClipboard() { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 textfield_->set_controller(this); | 402 textfield_->set_controller(this); |
218 widget_ = new Widget(); | 403 widget_ = new Widget(); |
219 | 404 |
220 // The widget type must be an activatable type, and we don't want to worry | 405 // The widget type must be an activatable type, and we don't want to worry |
221 // about the non-client view, which leaves just TYPE_WINDOW_FRAMELESS. | 406 // about the non-client view, which leaves just TYPE_WINDOW_FRAMELESS. |
222 Widget::InitParams params = | 407 Widget::InitParams params = |
223 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); | 408 CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
224 | 409 |
225 params.bounds = gfx::Rect(100, 100, 100, 100); | 410 params.bounds = gfx::Rect(100, 100, 100, 100); |
226 widget_->Init(params); | 411 widget_->Init(params); |
| 412 input_method_->SetDelegate(static_cast<ui::internal::InputMethodDelegate*>( |
| 413 widget_->GetNativeWindow()->GetRootWindow()->GetHost())); |
227 View* container = new View(); | 414 View* container = new View(); |
228 widget_->SetContentsView(container); | 415 widget_->SetContentsView(container); |
229 container->AddChildView(textfield_); | 416 container->AddChildView(textfield_); |
230 textfield_->SetBoundsRect(params.bounds); | 417 textfield_->SetBoundsRect(params.bounds); |
231 textfield_->set_id(1); | 418 textfield_->set_id(1); |
232 test_api_.reset(new TextfieldTestApi(textfield_)); | 419 test_api_.reset(new TextfieldTestApi(textfield_)); |
233 | 420 |
234 for (int i = 1; i < count; i++) { | 421 for (int i = 1; i < count; i++) { |
235 Textfield* textfield = new Textfield(); | 422 Textfield* textfield = new Textfield(); |
236 container->AddChildView(textfield); | 423 container->AddChildView(textfield); |
237 textfield->set_id(i + 1); | 424 textfield->set_id(i + 1); |
238 } | 425 } |
239 | 426 |
240 model_ = test_api_->model(); | 427 model_ = test_api_->model(); |
241 model_->ClearEditHistory(); | 428 model_->ClearEditHistory(); |
242 | 429 |
243 input_method_ = new MockInputMethod(); | |
244 widget_->ReplaceInputMethod(input_method_); | |
245 | |
246 // Since the window type is activatable, showing the widget will also | 430 // Since the window type is activatable, showing the widget will also |
247 // activate it. Calling Activate directly is insufficient, since that does | 431 // activate it. Calling Activate directly is insufficient, since that does |
248 // not also _focus_ an aura::Window (i.e. using the FocusClient). Both the | 432 // not also _focus_ an aura::Window (i.e. using the FocusClient). Both the |
249 // widget and the textfield must have focus to properly handle input. | 433 // widget and the textfield must have focus to properly handle input. |
250 widget_->Show(); | 434 widget_->Show(); |
251 textfield_->RequestFocus(); | 435 textfield_->RequestFocus(); |
252 | 436 |
253 // On Mac, activation is asynchronous since desktop widgets are used. We | 437 // On Mac, activation is asynchronous since desktop widgets are used. We |
254 // don't want parallel tests to steal active status either, so fake it. | 438 // don't want parallel tests to steal active status either, so fake it. |
255 #if defined(OS_MACOSX) && !defined(USE_AURA) | 439 #if defined(OS_MACOSX) && !defined(USE_AURA) |
(...skipping 2102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2358 | 2542 |
2359 textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); | 2543 textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); |
2360 ui::AXViewState state_protected; | 2544 ui::AXViewState state_protected; |
2361 textfield_->GetAccessibleState(&state_protected); | 2545 textfield_->GetAccessibleState(&state_protected); |
2362 EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, state_protected.role); | 2546 EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, state_protected.role); |
2363 EXPECT_EQ(ASCIIToUTF16("********"), state_protected.value); | 2547 EXPECT_EQ(ASCIIToUTF16("********"), state_protected.value); |
2364 EXPECT_TRUE(state_protected.HasStateFlag(ui::AX_STATE_PROTECTED)); | 2548 EXPECT_TRUE(state_protected.HasStateFlag(ui::AX_STATE_PROTECTED)); |
2365 } | 2549 } |
2366 | 2550 |
2367 } // namespace views | 2551 } // namespace views |
OLD | NEW |