OLD | NEW |
| (Empty) |
1 // Copyright 2013 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. | |
4 | |
5 #include "ui/base/ime/remote_input_method_win.h" | |
6 | |
7 #include <InputScope.h> | |
8 #include <stddef.h> | |
9 #include <stdint.h> | |
10 | |
11 #include <vector> | |
12 | |
13 #include "base/macros.h" | |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/scoped_observer.h" | |
16 #include "base/strings/string16.h" | |
17 #include "testing/gtest/include/gtest/gtest.h" | |
18 #include "ui/base/ime/composition_text.h" | |
19 #include "ui/base/ime/dummy_text_input_client.h" | |
20 #include "ui/base/ime/input_method.h" | |
21 #include "ui/base/ime/input_method_delegate.h" | |
22 #include "ui/base/ime/input_method_observer.h" | |
23 #include "ui/base/ime/remote_input_method_delegate_win.h" | |
24 #include "ui/events/event.h" | |
25 | |
26 namespace ui { | |
27 namespace { | |
28 | |
29 class MockTextInputClient : public DummyTextInputClient { | |
30 public: | |
31 MockTextInputClient() | |
32 : text_input_type_(TEXT_INPUT_TYPE_NONE), | |
33 text_input_mode_(TEXT_INPUT_MODE_DEFAULT), | |
34 call_count_set_composition_text_(0), | |
35 call_count_insert_char_(0), | |
36 call_count_insert_text_(0), | |
37 emulate_pepper_flash_(false) { | |
38 } | |
39 | |
40 size_t call_count_set_composition_text() const { | |
41 return call_count_set_composition_text_; | |
42 } | |
43 const base::string16& inserted_text() const { | |
44 return inserted_text_; | |
45 } | |
46 size_t call_count_insert_char() const { | |
47 return call_count_insert_char_; | |
48 } | |
49 size_t call_count_insert_text() const { | |
50 return call_count_insert_text_; | |
51 } | |
52 void Reset() { | |
53 text_input_type_ = TEXT_INPUT_TYPE_NONE; | |
54 text_input_mode_ = TEXT_INPUT_MODE_DEFAULT; | |
55 call_count_set_composition_text_ = 0; | |
56 inserted_text_.clear(); | |
57 call_count_insert_char_ = 0; | |
58 call_count_insert_text_ = 0; | |
59 caret_bounds_ = gfx::Rect(); | |
60 composition_character_bounds_.clear(); | |
61 emulate_pepper_flash_ = false; | |
62 } | |
63 void set_text_input_type(ui::TextInputType type) { | |
64 text_input_type_ = type; | |
65 } | |
66 void set_text_input_mode(ui::TextInputMode mode) { | |
67 text_input_mode_ = mode; | |
68 } | |
69 void set_caret_bounds(const gfx::Rect& caret_bounds) { | |
70 caret_bounds_ = caret_bounds; | |
71 } | |
72 void set_composition_character_bounds( | |
73 const std::vector<gfx::Rect>& composition_character_bounds) { | |
74 composition_character_bounds_ = composition_character_bounds; | |
75 } | |
76 void set_emulate_pepper_flash(bool enabled) { | |
77 emulate_pepper_flash_ = enabled; | |
78 } | |
79 | |
80 private: | |
81 // Overriden from DummyTextInputClient. | |
82 void SetCompositionText(const ui::CompositionText& composition) override { | |
83 ++call_count_set_composition_text_; | |
84 } | |
85 void InsertChar(const ui::KeyEvent& event) override { | |
86 inserted_text_.append(1, event.GetCharacter()); | |
87 ++call_count_insert_char_; | |
88 } | |
89 void InsertText(const base::string16& text) override { | |
90 inserted_text_.append(text); | |
91 ++call_count_insert_text_; | |
92 } | |
93 ui::TextInputType GetTextInputType() const override { | |
94 return text_input_type_; | |
95 } | |
96 ui::TextInputMode GetTextInputMode() const override { | |
97 return text_input_mode_; | |
98 } | |
99 gfx::Rect GetCaretBounds() const override { return caret_bounds_; } | |
100 bool GetCompositionCharacterBounds(uint32_t index, | |
101 gfx::Rect* rect) const override { | |
102 // Emulate the situation of crbug.com/328237. | |
103 if (emulate_pepper_flash_) | |
104 return false; | |
105 if (!rect || composition_character_bounds_.size() <= index) | |
106 return false; | |
107 *rect = composition_character_bounds_[index]; | |
108 return true; | |
109 } | |
110 bool HasCompositionText() const override { | |
111 return !composition_character_bounds_.empty(); | |
112 } | |
113 bool GetCompositionTextRange(gfx::Range* range) const override { | |
114 if (composition_character_bounds_.empty()) | |
115 return false; | |
116 *range = gfx::Range(0, composition_character_bounds_.size()); | |
117 return true; | |
118 } | |
119 | |
120 ui::TextInputType text_input_type_; | |
121 ui::TextInputMode text_input_mode_; | |
122 gfx::Rect caret_bounds_; | |
123 std::vector<gfx::Rect> composition_character_bounds_; | |
124 base::string16 inserted_text_; | |
125 size_t call_count_set_composition_text_; | |
126 size_t call_count_insert_char_; | |
127 size_t call_count_insert_text_; | |
128 bool emulate_pepper_flash_; | |
129 DISALLOW_COPY_AND_ASSIGN(MockTextInputClient); | |
130 }; | |
131 | |
132 class MockInputMethodDelegate : public internal::InputMethodDelegate { | |
133 public: | |
134 MockInputMethodDelegate() {} | |
135 | |
136 const std::vector<ui::KeyboardCode>& fabricated_key_events() const { | |
137 return fabricated_key_events_; | |
138 } | |
139 void Reset() { | |
140 fabricated_key_events_.clear(); | |
141 } | |
142 | |
143 private: | |
144 ui::EventDispatchDetails DispatchKeyEventPostIME( | |
145 ui::KeyEvent* event) override { | |
146 EXPECT_FALSE(event->HasNativeEvent()); | |
147 fabricated_key_events_.push_back(event->key_code()); | |
148 event->SetHandled(); | |
149 return ui::EventDispatchDetails(); | |
150 } | |
151 | |
152 std::vector<ui::KeyboardCode> fabricated_key_events_; | |
153 DISALLOW_COPY_AND_ASSIGN(MockInputMethodDelegate); | |
154 }; | |
155 | |
156 class MockRemoteInputMethodDelegateWin | |
157 : public internal::RemoteInputMethodDelegateWin { | |
158 public: | |
159 MockRemoteInputMethodDelegateWin() | |
160 : cancel_composition_called_(false), | |
161 text_input_client_updated_called_(false) { | |
162 } | |
163 | |
164 bool cancel_composition_called() const { | |
165 return cancel_composition_called_; | |
166 } | |
167 bool text_input_client_updated_called() const { | |
168 return text_input_client_updated_called_; | |
169 } | |
170 const std::vector<int32_t>& input_scopes() const { return input_scopes_; } | |
171 const std::vector<gfx::Rect>& composition_character_bounds() const { | |
172 return composition_character_bounds_; | |
173 } | |
174 void Reset() { | |
175 cancel_composition_called_ = false; | |
176 text_input_client_updated_called_ = false; | |
177 input_scopes_.clear(); | |
178 composition_character_bounds_.clear(); | |
179 } | |
180 | |
181 private: | |
182 void CancelComposition() override { cancel_composition_called_ = true; } | |
183 | |
184 void OnTextInputClientUpdated( | |
185 const std::vector<int32_t>& input_scopes, | |
186 const std::vector<gfx::Rect>& composition_character_bounds) override { | |
187 text_input_client_updated_called_ = true; | |
188 input_scopes_ = input_scopes; | |
189 composition_character_bounds_ = composition_character_bounds; | |
190 } | |
191 | |
192 bool cancel_composition_called_; | |
193 bool text_input_client_updated_called_; | |
194 std::vector<int32_t> input_scopes_; | |
195 std::vector<gfx::Rect> composition_character_bounds_; | |
196 DISALLOW_COPY_AND_ASSIGN(MockRemoteInputMethodDelegateWin); | |
197 }; | |
198 | |
199 class MockInputMethodObserver : public InputMethodObserver { | |
200 public: | |
201 MockInputMethodObserver() | |
202 : on_text_input_state_changed_(0), | |
203 on_input_method_destroyed_changed_(0) { | |
204 } | |
205 ~MockInputMethodObserver() override {} | |
206 void Reset() { | |
207 on_text_input_state_changed_ = 0; | |
208 on_input_method_destroyed_changed_ = 0; | |
209 } | |
210 size_t on_text_input_state_changed() const { | |
211 return on_text_input_state_changed_; | |
212 } | |
213 size_t on_input_method_destroyed_changed() const { | |
214 return on_input_method_destroyed_changed_; | |
215 } | |
216 | |
217 private: | |
218 // Overriden from InputMethodObserver. | |
219 void OnTextInputTypeChanged(const TextInputClient* client) override {} | |
220 void OnFocus() override {} | |
221 void OnBlur() override {} | |
222 void OnCaretBoundsChanged(const TextInputClient* client) override {} | |
223 void OnTextInputStateChanged(const TextInputClient* client) override { | |
224 ++on_text_input_state_changed_; | |
225 } | |
226 void OnInputMethodDestroyed(const InputMethod* client) override { | |
227 ++on_input_method_destroyed_changed_; | |
228 } | |
229 void OnShowImeIfNeeded() override {} | |
230 | |
231 size_t on_text_input_state_changed_; | |
232 size_t on_input_method_destroyed_changed_; | |
233 DISALLOW_COPY_AND_ASSIGN(MockInputMethodObserver); | |
234 }; | |
235 | |
236 typedef ScopedObserver<InputMethod, InputMethodObserver> | |
237 InputMethodScopedObserver; | |
238 | |
239 TEST(RemoteInputMethodWinTest, RemoteInputMethodPrivateWin) { | |
240 InputMethod* other_ptr = static_cast<InputMethod*>(NULL) + 1; | |
241 | |
242 // Use typed NULL to make EXPECT_NE happy until nullptr becomes available. | |
243 RemoteInputMethodPrivateWin* kNull = | |
244 static_cast<RemoteInputMethodPrivateWin*>(NULL); | |
245 EXPECT_EQ(kNull, RemoteInputMethodPrivateWin::Get(other_ptr)); | |
246 | |
247 MockInputMethodDelegate delegate_; | |
248 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
249 EXPECT_NE(kNull, RemoteInputMethodPrivateWin::Get(input_method.get())); | |
250 | |
251 InputMethod* dangling_ptr = input_method.get(); | |
252 input_method.reset(NULL); | |
253 EXPECT_EQ(kNull, RemoteInputMethodPrivateWin::Get(dangling_ptr)); | |
254 } | |
255 | |
256 TEST(RemoteInputMethodWinTest, OnInputSourceChanged) { | |
257 MockInputMethodDelegate delegate_; | |
258 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
259 RemoteInputMethodPrivateWin* private_ptr = | |
260 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
261 ASSERT_TRUE(private_ptr != NULL); | |
262 | |
263 private_ptr->OnInputSourceChanged( | |
264 MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), true); | |
265 EXPECT_EQ("ja-JP", input_method->GetInputLocale()); | |
266 | |
267 private_ptr->OnInputSourceChanged( | |
268 MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_QATAR), true); | |
269 EXPECT_EQ("ar-QA", input_method->GetInputLocale()); | |
270 } | |
271 | |
272 TEST(RemoteInputMethodWinTest, OnCandidatePopupChanged) { | |
273 MockInputMethodDelegate delegate_; | |
274 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
275 RemoteInputMethodPrivateWin* private_ptr = | |
276 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
277 ASSERT_TRUE(private_ptr != NULL); | |
278 | |
279 // Initial value | |
280 EXPECT_FALSE(input_method->IsCandidatePopupOpen()); | |
281 | |
282 // RemoteInputMethodWin::OnCandidatePopupChanged can be called even when the | |
283 // focused text input client is NULL. | |
284 ASSERT_TRUE(input_method->GetTextInputClient() == NULL); | |
285 private_ptr->OnCandidatePopupChanged(false); | |
286 private_ptr->OnCandidatePopupChanged(true); | |
287 | |
288 MockTextInputClient mock_text_input_client; | |
289 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
290 | |
291 mock_text_input_client.Reset(); | |
292 | |
293 private_ptr->OnCandidatePopupChanged(true); | |
294 EXPECT_TRUE(input_method->IsCandidatePopupOpen()); | |
295 | |
296 private_ptr->OnCandidatePopupChanged(false); | |
297 EXPECT_FALSE(input_method->IsCandidatePopupOpen()); | |
298 } | |
299 | |
300 TEST(RemoteInputMethodWinTest, CancelComposition) { | |
301 MockInputMethodDelegate delegate_; | |
302 MockTextInputClient mock_text_input_client; | |
303 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
304 | |
305 // This must not cause a crash. | |
306 input_method->CancelComposition(&mock_text_input_client); | |
307 | |
308 RemoteInputMethodPrivateWin* private_ptr = | |
309 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
310 ASSERT_TRUE(private_ptr != NULL); | |
311 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
312 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
313 | |
314 input_method->CancelComposition(&mock_text_input_client); | |
315 EXPECT_FALSE(mock_remote_delegate.cancel_composition_called()); | |
316 | |
317 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
318 input_method->CancelComposition(&mock_text_input_client); | |
319 EXPECT_TRUE(mock_remote_delegate.cancel_composition_called()); | |
320 } | |
321 | |
322 TEST(RemoteInputMethodWinTest, SetFocusedTextInputClient) { | |
323 MockInputMethodDelegate delegate_; | |
324 MockTextInputClient mock_text_input_client; | |
325 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
326 | |
327 mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20)); | |
328 mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL); | |
329 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
330 | |
331 RemoteInputMethodPrivateWin* private_ptr = | |
332 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
333 ASSERT_TRUE(private_ptr != NULL); | |
334 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
335 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
336 | |
337 // Initial state must be synced. | |
338 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
339 ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); | |
340 EXPECT_EQ(gfx::Rect(10, 0, 10, 20), | |
341 mock_remote_delegate.composition_character_bounds()[0]); | |
342 ASSERT_EQ(1u, mock_remote_delegate.input_scopes().size()); | |
343 EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]); | |
344 | |
345 // State must be cleared by SetFocusedTextInputClient(NULL). | |
346 mock_remote_delegate.Reset(); | |
347 input_method->SetFocusedTextInputClient(NULL); | |
348 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
349 EXPECT_TRUE(mock_remote_delegate.composition_character_bounds().empty()); | |
350 EXPECT_TRUE(mock_remote_delegate.input_scopes().empty()); | |
351 } | |
352 | |
353 TEST(RemoteInputMethodWinTest, DetachTextInputClient) { | |
354 MockInputMethodDelegate delegate_; | |
355 MockTextInputClient mock_text_input_client; | |
356 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
357 | |
358 mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20)); | |
359 mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL); | |
360 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
361 | |
362 RemoteInputMethodPrivateWin* private_ptr = | |
363 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
364 ASSERT_TRUE(private_ptr != NULL); | |
365 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
366 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
367 | |
368 // Initial state must be synced. | |
369 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
370 ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); | |
371 EXPECT_EQ(gfx::Rect(10, 0, 10, 20), | |
372 mock_remote_delegate.composition_character_bounds()[0]); | |
373 ASSERT_EQ(1u, mock_remote_delegate.input_scopes().size()); | |
374 EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]); | |
375 | |
376 // State must be cleared by DetachTextInputClient | |
377 mock_remote_delegate.Reset(); | |
378 input_method->DetachTextInputClient(&mock_text_input_client); | |
379 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
380 EXPECT_TRUE(mock_remote_delegate.composition_character_bounds().empty()); | |
381 EXPECT_TRUE(mock_remote_delegate.input_scopes().empty()); | |
382 } | |
383 | |
384 TEST(RemoteInputMethodWinTest, OnCaretBoundsChanged) { | |
385 MockInputMethodDelegate delegate_; | |
386 MockTextInputClient mock_text_input_client; | |
387 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
388 | |
389 // This must not cause a crash. | |
390 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
391 | |
392 mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20)); | |
393 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
394 | |
395 RemoteInputMethodPrivateWin* private_ptr = | |
396 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
397 ASSERT_TRUE(private_ptr != NULL); | |
398 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
399 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
400 | |
401 // Initial state must be synced. | |
402 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
403 ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); | |
404 EXPECT_EQ(gfx::Rect(10, 0, 10, 20), | |
405 mock_remote_delegate.composition_character_bounds()[0]); | |
406 | |
407 // Redundant OnCaretBoundsChanged must be ignored. | |
408 mock_remote_delegate.Reset(); | |
409 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
410 EXPECT_FALSE(mock_remote_delegate.text_input_client_updated_called()); | |
411 | |
412 // Check OnCaretBoundsChanged is handled. (w/o composition) | |
413 mock_remote_delegate.Reset(); | |
414 mock_text_input_client.Reset(); | |
415 mock_text_input_client.set_caret_bounds(gfx::Rect(10, 20, 30, 40)); | |
416 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
417 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
418 ASSERT_EQ(1u, mock_remote_delegate.composition_character_bounds().size()); | |
419 EXPECT_EQ(gfx::Rect(10, 20, 30, 40), | |
420 mock_remote_delegate.composition_character_bounds()[0]); | |
421 | |
422 // Check OnCaretBoundsChanged is handled. (w/ composition) | |
423 { | |
424 mock_remote_delegate.Reset(); | |
425 mock_text_input_client.Reset(); | |
426 | |
427 std::vector<gfx::Rect> bounds; | |
428 bounds.push_back(gfx::Rect(10, 20, 30, 40)); | |
429 bounds.push_back(gfx::Rect(40, 30, 20, 10)); | |
430 mock_text_input_client.set_composition_character_bounds(bounds); | |
431 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
432 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
433 EXPECT_EQ(bounds, mock_remote_delegate.composition_character_bounds()); | |
434 } | |
435 } | |
436 | |
437 // Test case against crbug.com/328237. | |
438 TEST(RemoteInputMethodWinTest, OnCaretBoundsChangedForPepperFlash) { | |
439 MockInputMethodDelegate delegate_; | |
440 MockTextInputClient mock_text_input_client; | |
441 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
442 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
443 | |
444 RemoteInputMethodPrivateWin* private_ptr = | |
445 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
446 ASSERT_TRUE(private_ptr != NULL); | |
447 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
448 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
449 | |
450 mock_remote_delegate.Reset(); | |
451 mock_text_input_client.Reset(); | |
452 mock_text_input_client.set_emulate_pepper_flash(true); | |
453 | |
454 std::vector<gfx::Rect> caret_bounds; | |
455 caret_bounds.push_back(gfx::Rect(5, 15, 25, 35)); | |
456 mock_text_input_client.set_caret_bounds(caret_bounds[0]); | |
457 | |
458 std::vector<gfx::Rect> composition_bounds; | |
459 composition_bounds.push_back(gfx::Rect(10, 20, 30, 40)); | |
460 composition_bounds.push_back(gfx::Rect(40, 30, 20, 10)); | |
461 mock_text_input_client.set_composition_character_bounds(composition_bounds); | |
462 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
463 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
464 // The caret bounds must be used when | |
465 // TextInputClient::GetCompositionCharacterBounds failed. | |
466 EXPECT_EQ(caret_bounds, mock_remote_delegate.composition_character_bounds()); | |
467 } | |
468 | |
469 TEST(RemoteInputMethodWinTest, OnTextInputTypeChanged) { | |
470 MockInputMethodDelegate delegate_; | |
471 MockTextInputClient mock_text_input_client; | |
472 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
473 | |
474 // This must not cause a crash. | |
475 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
476 | |
477 mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL); | |
478 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
479 | |
480 RemoteInputMethodPrivateWin* private_ptr = | |
481 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
482 ASSERT_TRUE(private_ptr != NULL); | |
483 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
484 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
485 | |
486 // Initial state must be synced. | |
487 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
488 ASSERT_EQ(1u, mock_remote_delegate.input_scopes().size()); | |
489 EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]); | |
490 | |
491 // Check TEXT_INPUT_TYPE_NONE is handled. | |
492 mock_remote_delegate.Reset(); | |
493 mock_text_input_client.Reset(); | |
494 mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_NONE); | |
495 mock_text_input_client.set_text_input_mode(ui::TEXT_INPUT_MODE_KATAKANA); | |
496 input_method->OnTextInputTypeChanged(&mock_text_input_client); | |
497 EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called()); | |
498 EXPECT_TRUE(mock_remote_delegate.input_scopes().empty()); | |
499 | |
500 // Redundant OnTextInputTypeChanged must be ignored. | |
501 mock_remote_delegate.Reset(); | |
502 input_method->OnTextInputTypeChanged(&mock_text_input_client); | |
503 EXPECT_FALSE(mock_remote_delegate.text_input_client_updated_called()); | |
504 | |
505 mock_remote_delegate.Reset(); | |
506 mock_text_input_client.Reset(); | |
507 mock_text_input_client.set_caret_bounds(gfx::Rect(10, 20, 30, 40)); | |
508 input_method->OnCaretBoundsChanged(&mock_text_input_client); | |
509 } | |
510 | |
511 TEST(RemoteInputMethodWinTest, DispatchKeyEvent_NativeKeyEvent) { | |
512 // Basically RemoteInputMethodWin does not handle native keydown event. | |
513 | |
514 MockInputMethodDelegate delegate_; | |
515 MockTextInputClient mock_text_input_client; | |
516 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
517 | |
518 const MSG wm_keydown = { NULL, WM_KEYDOWN, ui::VKEY_A }; | |
519 ui::KeyEvent new_keydown(wm_keydown); | |
520 ui::KeyEvent native_keydown(new_keydown); | |
521 | |
522 // This must not cause a crash. | |
523 input_method->DispatchKeyEvent(&native_keydown); | |
524 EXPECT_FALSE(native_keydown.handled()); | |
525 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
526 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
527 delegate_.Reset(); | |
528 mock_text_input_client.Reset(); | |
529 | |
530 RemoteInputMethodPrivateWin* private_ptr = | |
531 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
532 ASSERT_TRUE(private_ptr != NULL); | |
533 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
534 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
535 | |
536 // TextInputClient is not focused yet here. | |
537 native_keydown = new_keydown; | |
538 input_method->DispatchKeyEvent(&native_keydown); | |
539 EXPECT_FALSE(native_keydown.handled()); | |
540 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
541 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
542 delegate_.Reset(); | |
543 mock_text_input_client.Reset(); | |
544 | |
545 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
546 | |
547 // TextInputClient is now focused here. | |
548 native_keydown = new_keydown; | |
549 input_method->DispatchKeyEvent(&native_keydown); | |
550 EXPECT_FALSE(native_keydown.handled()); | |
551 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
552 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
553 delegate_.Reset(); | |
554 mock_text_input_client.Reset(); | |
555 } | |
556 | |
557 TEST(RemoteInputMethodWinTest, DispatchKeyEvent_NativeCharEvent) { | |
558 // RemoteInputMethodWin handles native char event if possible. | |
559 | |
560 MockInputMethodDelegate delegate_; | |
561 MockTextInputClient mock_text_input_client; | |
562 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
563 | |
564 const MSG wm_char = { NULL, WM_CHAR, 'A', 0 }; | |
565 ui::KeyEvent new_char(wm_char); | |
566 ui::KeyEvent native_char(new_char); | |
567 | |
568 // This must not cause a crash. | |
569 input_method->DispatchKeyEvent(&native_char); | |
570 EXPECT_FALSE(native_char.handled()); | |
571 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
572 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
573 delegate_.Reset(); | |
574 mock_text_input_client.Reset(); | |
575 | |
576 RemoteInputMethodPrivateWin* private_ptr = | |
577 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
578 ASSERT_TRUE(private_ptr != NULL); | |
579 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
580 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
581 | |
582 // TextInputClient is not focused yet here. | |
583 native_char = new_char; | |
584 input_method->DispatchKeyEvent(&native_char); | |
585 EXPECT_FALSE(native_char.handled()); | |
586 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
587 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
588 delegate_.Reset(); | |
589 mock_text_input_client.Reset(); | |
590 | |
591 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
592 | |
593 // TextInputClient is now focused here. | |
594 native_char = new_char; | |
595 input_method->DispatchKeyEvent(&native_char); | |
596 EXPECT_TRUE(native_char.handled()); | |
597 EXPECT_EQ(L"A", mock_text_input_client.inserted_text()); | |
598 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
599 delegate_.Reset(); | |
600 mock_text_input_client.Reset(); | |
601 } | |
602 | |
603 TEST(RemoteInputMethodWinTest, DispatchKeyEvent_FabricatedKeyDown) { | |
604 // Fabricated non-char event will be delegated to | |
605 // InputMethodDelegate::DispatchFabricatedKeyEventPostIME as long as the | |
606 // delegate is installed. | |
607 | |
608 MockInputMethodDelegate delegate_; | |
609 MockTextInputClient mock_text_input_client; | |
610 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
611 | |
612 ui::KeyEvent new_keydown(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE); | |
613 new_keydown.set_character(L'A'); | |
614 ui::KeyEvent fabricated_keydown(new_keydown); | |
615 | |
616 // This must not cause a crash. | |
617 input_method->DispatchKeyEvent(&fabricated_keydown); | |
618 EXPECT_TRUE(fabricated_keydown.handled()); | |
619 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
620 ASSERT_EQ(1u, delegate_.fabricated_key_events().size()); | |
621 EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]); | |
622 delegate_.Reset(); | |
623 mock_text_input_client.Reset(); | |
624 | |
625 RemoteInputMethodPrivateWin* private_ptr = | |
626 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
627 ASSERT_TRUE(private_ptr != NULL); | |
628 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
629 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
630 | |
631 // TextInputClient is not focused yet here. | |
632 fabricated_keydown = new_keydown; | |
633 input_method->DispatchKeyEvent(&fabricated_keydown); | |
634 EXPECT_TRUE(fabricated_keydown.handled()); | |
635 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
636 ASSERT_EQ(1u, delegate_.fabricated_key_events().size()); | |
637 EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]); | |
638 delegate_.Reset(); | |
639 mock_text_input_client.Reset(); | |
640 | |
641 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
642 // TextInputClient is now focused here. | |
643 fabricated_keydown = new_keydown; | |
644 input_method->DispatchKeyEvent(&fabricated_keydown); | |
645 EXPECT_TRUE(fabricated_keydown.handled()); | |
646 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
647 ASSERT_EQ(1u, delegate_.fabricated_key_events().size()); | |
648 EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]); | |
649 delegate_.Reset(); | |
650 mock_text_input_client.Reset(); | |
651 | |
652 input_method->SetDelegate(NULL); | |
653 // RemoteInputMethodDelegateWin is no longer set here. | |
654 fabricated_keydown = new_keydown; | |
655 input_method->DispatchKeyEvent(&fabricated_keydown); | |
656 EXPECT_FALSE(fabricated_keydown.handled()); | |
657 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
658 } | |
659 | |
660 TEST(RemoteInputMethodWinTest, DispatchKeyEvent_FabricatedChar) { | |
661 // Note: RemoteInputMethodWin::DispatchKeyEvent should always return true | |
662 // for fabricated character events. | |
663 | |
664 MockInputMethodDelegate delegate_; | |
665 MockTextInputClient mock_text_input_client; | |
666 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
667 | |
668 ui::KeyEvent new_char(L'A', ui::VKEY_A, ui::EF_NONE); | |
669 ui::KeyEvent fabricated_char(new_char); | |
670 | |
671 // This must not cause a crash. | |
672 input_method->DispatchKeyEvent(&fabricated_char); | |
673 EXPECT_TRUE(fabricated_char.handled()); | |
674 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
675 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
676 delegate_.Reset(); | |
677 mock_text_input_client.Reset(); | |
678 | |
679 RemoteInputMethodPrivateWin* private_ptr = | |
680 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
681 ASSERT_TRUE(private_ptr != NULL); | |
682 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
683 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
684 | |
685 // TextInputClient is not focused yet here. | |
686 fabricated_char = new_char; | |
687 input_method->DispatchKeyEvent(&fabricated_char); | |
688 EXPECT_TRUE(fabricated_char.handled()); | |
689 EXPECT_TRUE(mock_text_input_client.inserted_text().empty()); | |
690 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
691 delegate_.Reset(); | |
692 mock_text_input_client.Reset(); | |
693 | |
694 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
695 | |
696 // TextInputClient is now focused here. | |
697 fabricated_char = new_char; | |
698 input_method->DispatchKeyEvent(&fabricated_char); | |
699 EXPECT_TRUE(fabricated_char.handled()); | |
700 EXPECT_EQ(L"A", mock_text_input_client.inserted_text()); | |
701 EXPECT_TRUE(delegate_.fabricated_key_events().empty()); | |
702 delegate_.Reset(); | |
703 mock_text_input_client.Reset(); | |
704 } | |
705 | |
706 TEST(RemoteInputMethodWinTest, OnCompositionChanged) { | |
707 MockInputMethodDelegate delegate_; | |
708 MockTextInputClient mock_text_input_client; | |
709 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
710 | |
711 RemoteInputMethodPrivateWin* private_ptr = | |
712 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
713 ASSERT_TRUE(private_ptr != NULL); | |
714 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
715 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
716 | |
717 CompositionText composition_text; | |
718 | |
719 // TextInputClient is not focused yet here. | |
720 | |
721 private_ptr->OnCompositionChanged(composition_text); | |
722 EXPECT_EQ(0u, mock_text_input_client.call_count_set_composition_text()); | |
723 delegate_.Reset(); | |
724 mock_text_input_client.Reset(); | |
725 | |
726 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
727 | |
728 // TextInputClient is now focused here. | |
729 | |
730 private_ptr->OnCompositionChanged(composition_text); | |
731 EXPECT_EQ(1u, mock_text_input_client.call_count_set_composition_text()); | |
732 delegate_.Reset(); | |
733 mock_text_input_client.Reset(); | |
734 } | |
735 | |
736 TEST(RemoteInputMethodWinTest, OnTextCommitted) { | |
737 MockInputMethodDelegate delegate_; | |
738 MockTextInputClient mock_text_input_client; | |
739 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
740 | |
741 RemoteInputMethodPrivateWin* private_ptr = | |
742 RemoteInputMethodPrivateWin::Get(input_method.get()); | |
743 ASSERT_TRUE(private_ptr != NULL); | |
744 MockRemoteInputMethodDelegateWin mock_remote_delegate; | |
745 private_ptr->SetRemoteDelegate(&mock_remote_delegate); | |
746 | |
747 base::string16 committed_text = L"Hello"; | |
748 | |
749 // TextInputClient is not focused yet here. | |
750 | |
751 mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_TEXT); | |
752 private_ptr->OnTextCommitted(committed_text); | |
753 EXPECT_EQ(0u, mock_text_input_client.call_count_insert_char()); | |
754 EXPECT_EQ(0u, mock_text_input_client.call_count_insert_text()); | |
755 EXPECT_EQ(L"", mock_text_input_client.inserted_text()); | |
756 delegate_.Reset(); | |
757 mock_text_input_client.Reset(); | |
758 | |
759 input_method->SetFocusedTextInputClient(&mock_text_input_client); | |
760 | |
761 // TextInputClient is now focused here. | |
762 | |
763 mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_TEXT); | |
764 private_ptr->OnTextCommitted(committed_text); | |
765 EXPECT_EQ(0u, mock_text_input_client.call_count_insert_char()); | |
766 EXPECT_EQ(1u, mock_text_input_client.call_count_insert_text()); | |
767 EXPECT_EQ(committed_text, mock_text_input_client.inserted_text()); | |
768 delegate_.Reset(); | |
769 mock_text_input_client.Reset(); | |
770 | |
771 // When TextInputType is TEXT_INPUT_TYPE_NONE, TextInputClient::InsertText | |
772 // should not be used. | |
773 mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_NONE); | |
774 private_ptr->OnTextCommitted(committed_text); | |
775 EXPECT_EQ(committed_text.size(), | |
776 mock_text_input_client.call_count_insert_char()); | |
777 EXPECT_EQ(0u, mock_text_input_client.call_count_insert_text()); | |
778 EXPECT_EQ(committed_text, mock_text_input_client.inserted_text()); | |
779 delegate_.Reset(); | |
780 mock_text_input_client.Reset(); | |
781 } | |
782 | |
783 TEST(RemoteInputMethodWinTest, OnTextInputStateChanged_Observer) { | |
784 DummyTextInputClient text_input_client; | |
785 DummyTextInputClient text_input_client_the_other; | |
786 | |
787 MockInputMethodObserver input_method_observer; | |
788 MockInputMethodDelegate delegate_; | |
789 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
790 InputMethodScopedObserver scoped_observer(&input_method_observer); | |
791 scoped_observer.Add(input_method.get()); | |
792 | |
793 input_method->SetFocusedTextInputClient(&text_input_client); | |
794 ASSERT_EQ(&text_input_client, input_method->GetTextInputClient()); | |
795 EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed()); | |
796 input_method_observer.Reset(); | |
797 | |
798 input_method->SetFocusedTextInputClient(&text_input_client); | |
799 ASSERT_EQ(&text_input_client, input_method->GetTextInputClient()); | |
800 EXPECT_EQ(0u, input_method_observer.on_text_input_state_changed()); | |
801 input_method_observer.Reset(); | |
802 | |
803 input_method->SetFocusedTextInputClient(&text_input_client_the_other); | |
804 ASSERT_EQ(&text_input_client_the_other, input_method->GetTextInputClient()); | |
805 EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed()); | |
806 input_method_observer.Reset(); | |
807 | |
808 input_method->DetachTextInputClient(&text_input_client_the_other); | |
809 ASSERT_TRUE(input_method->GetTextInputClient() == NULL); | |
810 EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed()); | |
811 input_method_observer.Reset(); | |
812 } | |
813 | |
814 TEST(RemoteInputMethodWinTest, OnInputMethodDestroyed_Observer) { | |
815 DummyTextInputClient text_input_client; | |
816 DummyTextInputClient text_input_client_the_other; | |
817 | |
818 MockInputMethodObserver input_method_observer; | |
819 InputMethodScopedObserver scoped_observer(&input_method_observer); | |
820 | |
821 MockInputMethodDelegate delegate_; | |
822 scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_)); | |
823 input_method->AddObserver(&input_method_observer); | |
824 | |
825 EXPECT_EQ(0u, input_method_observer.on_input_method_destroyed_changed()); | |
826 input_method.reset(); | |
827 EXPECT_EQ(1u, input_method_observer.on_input_method_destroyed_changed()); | |
828 } | |
829 | |
830 } // namespace | |
831 } // namespace ui | |
OLD | NEW |