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/input_method_base.h"
6
7 #include "base/gtest_prod_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/scoped_observer.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/base/ime/dummy_text_input_client.h"
14 #include "ui/base/ime/input_method_observer.h"
15 #include "ui/base/ime/text_input_focus_manager.h"
16 #include "ui/base/ui_base_switches_util.h"
17 #include "ui/events/event.h"
18
19 namespace ui {
20 namespace {
21
22 class ClientChangeVerifier {
23 public:
ClientChangeVerifier()24 ClientChangeVerifier()
25 : previous_client_(NULL),
26 next_client_(NULL),
27 call_expected_(false),
28 on_will_change_focused_client_called_(false),
29 on_did_change_focused_client_called_(false),
30 on_text_input_state_changed_(false) {
31 }
32
33 // Expects that focused text input client will not be changed.
ExpectClientDoesNotChange()34 void ExpectClientDoesNotChange() {
35 previous_client_ = NULL;
36 next_client_ = NULL;
37 call_expected_ = false;
38 on_will_change_focused_client_called_ = false;
39 on_did_change_focused_client_called_ = false;
40 on_text_input_state_changed_ = false;
41 }
42
43 // Expects that focused text input client will be changed from
44 // |previous_client| to |next_client|.
ExpectClientChange(TextInputClient * previous_client,TextInputClient * next_client)45 void ExpectClientChange(TextInputClient* previous_client,
46 TextInputClient* next_client) {
47 previous_client_ = previous_client;
48 next_client_ = next_client;
49 call_expected_ = true;
50 on_will_change_focused_client_called_ = false;
51 on_did_change_focused_client_called_ = false;
52 on_text_input_state_changed_ = false;
53 }
54
55 // Verifies the result satisfies the expectation or not.
Verify()56 void Verify() {
57 if (switches::IsTextInputFocusManagerEnabled()) {
58 EXPECT_FALSE(on_will_change_focused_client_called_);
59 EXPECT_FALSE(on_did_change_focused_client_called_);
60 EXPECT_FALSE(on_text_input_state_changed_);
61 } else {
62 EXPECT_EQ(call_expected_, on_will_change_focused_client_called_);
63 EXPECT_EQ(call_expected_, on_did_change_focused_client_called_);
64 EXPECT_EQ(call_expected_, on_text_input_state_changed_);
65 }
66 }
67
OnWillChangeFocusedClient(TextInputClient * focused_before,TextInputClient * focused)68 void OnWillChangeFocusedClient(TextInputClient* focused_before,
69 TextInputClient* focused) {
70 EXPECT_TRUE(call_expected_);
71
72 // Check arguments
73 EXPECT_EQ(previous_client_, focused_before);
74 EXPECT_EQ(next_client_, focused);
75
76 // Check call order
77 EXPECT_FALSE(on_will_change_focused_client_called_);
78 EXPECT_FALSE(on_did_change_focused_client_called_);
79 EXPECT_FALSE(on_text_input_state_changed_);
80
81 on_will_change_focused_client_called_ = true;
82 }
83
OnDidChangeFocusedClient(TextInputClient * focused_before,TextInputClient * focused)84 void OnDidChangeFocusedClient(TextInputClient* focused_before,
85 TextInputClient* focused) {
86 EXPECT_TRUE(call_expected_);
87
88 // Check arguments
89 EXPECT_EQ(previous_client_, focused_before);
90 EXPECT_EQ(next_client_, focused);
91
92 // Check call order
93 EXPECT_TRUE(on_will_change_focused_client_called_);
94 EXPECT_FALSE(on_did_change_focused_client_called_);
95 EXPECT_FALSE(on_text_input_state_changed_);
96
97 on_did_change_focused_client_called_ = true;
98 }
99
OnTextInputStateChanged(const TextInputClient * client)100 void OnTextInputStateChanged(const TextInputClient* client) {
101 EXPECT_TRUE(call_expected_);
102
103 // Check arguments
104 EXPECT_EQ(next_client_, client);
105
106 // Check call order
107 EXPECT_TRUE(on_will_change_focused_client_called_);
108 EXPECT_TRUE(on_did_change_focused_client_called_);
109 EXPECT_FALSE(on_text_input_state_changed_);
110
111 on_text_input_state_changed_ = true;
112 }
113
114 private:
115 TextInputClient* previous_client_;
116 TextInputClient* next_client_;
117 bool call_expected_;
118 bool on_will_change_focused_client_called_;
119 bool on_did_change_focused_client_called_;
120 bool on_text_input_state_changed_;
121
122 DISALLOW_COPY_AND_ASSIGN(ClientChangeVerifier);
123 };
124
125 class InputMethodBaseTest : public testing::Test {
126 protected:
InputMethodBaseTest()127 InputMethodBaseTest() {
128 }
~InputMethodBaseTest()129 virtual ~InputMethodBaseTest() {
130 }
131
SetUp()132 virtual void SetUp() {
133 message_loop_.reset(new base::MessageLoopForUI);
134 }
135
TearDown()136 virtual void TearDown() {
137 message_loop_.reset();
138 }
139
140 private:
141 scoped_ptr<base::MessageLoop> message_loop_;
142 DISALLOW_COPY_AND_ASSIGN(InputMethodBaseTest);
143 };
144
145 class MockInputMethodBase : public InputMethodBase {
146 public:
147 // Note: this class does not take the ownership of |verifier|.
MockInputMethodBase(ClientChangeVerifier * verifier)148 MockInputMethodBase(ClientChangeVerifier* verifier) : verifier_(verifier) {
149 }
~MockInputMethodBase()150 virtual ~MockInputMethodBase() {
151 }
152
153 private:
154 // Overriden from InputMethod.
OnUntranslatedIMEMessage(const base::NativeEvent & event,InputMethod::NativeEventResult * result)155 virtual bool OnUntranslatedIMEMessage(
156 const base::NativeEvent& event,
157 InputMethod::NativeEventResult* result) OVERRIDE {
158 return false;
159 }
DispatchKeyEvent(const ui::KeyEvent &)160 virtual bool DispatchKeyEvent(const ui::KeyEvent&) OVERRIDE {
161 return false;
162 }
OnCaretBoundsChanged(const TextInputClient * client)163 virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE {
164 }
CancelComposition(const TextInputClient * client)165 virtual void CancelComposition(const TextInputClient* client) OVERRIDE {
166 }
OnInputLocaleChanged()167 virtual void OnInputLocaleChanged() OVERRIDE {
168 }
GetInputLocale()169 virtual std::string GetInputLocale() OVERRIDE{
170 return "";
171 }
IsActive()172 virtual bool IsActive() OVERRIDE {
173 return false;
174 }
IsCandidatePopupOpen() const175 virtual bool IsCandidatePopupOpen() const OVERRIDE {
176 return false;
177 }
178 // Overriden from InputMethodBase.
OnWillChangeFocusedClient(TextInputClient * focused_before,TextInputClient * focused)179 virtual void OnWillChangeFocusedClient(TextInputClient* focused_before,
180 TextInputClient* focused) OVERRIDE {
181 verifier_->OnWillChangeFocusedClient(focused_before, focused);
182 }
183
OnDidChangeFocusedClient(TextInputClient * focused_before,TextInputClient * focused)184 virtual void OnDidChangeFocusedClient(TextInputClient* focused_before,
185 TextInputClient* focused) OVERRIDE {
186 verifier_->OnDidChangeFocusedClient(focused_before, focused);
187 }
188
189 ClientChangeVerifier* verifier_;
190
191 FRIEND_TEST_ALL_PREFIXES(InputMethodBaseTest, CandidateWindowEvents);
192 DISALLOW_COPY_AND_ASSIGN(MockInputMethodBase);
193 };
194
195 class MockInputMethodObserver : public InputMethodObserver {
196 public:
197 // Note: this class does not take the ownership of |verifier|.
MockInputMethodObserver(ClientChangeVerifier * verifier)198 explicit MockInputMethodObserver(ClientChangeVerifier* verifier)
199 : verifier_(verifier) {
200 }
~MockInputMethodObserver()201 virtual ~MockInputMethodObserver() {
202 }
203
204 private:
OnTextInputTypeChanged(const TextInputClient * client)205 virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE {
206 }
OnFocus()207 virtual void OnFocus() OVERRIDE {
208 }
OnBlur()209 virtual void OnBlur() OVERRIDE {
210 }
OnCaretBoundsChanged(const TextInputClient * client)211 virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE {
212 }
OnTextInputStateChanged(const TextInputClient * client)213 virtual void OnTextInputStateChanged(const TextInputClient* client) OVERRIDE {
214 verifier_->OnTextInputStateChanged(client);
215 }
OnShowImeIfNeeded()216 virtual void OnShowImeIfNeeded() OVERRIDE {
217 }
OnInputMethodDestroyed(const InputMethod * client)218 virtual void OnInputMethodDestroyed(const InputMethod* client) OVERRIDE {
219 }
220
221 ClientChangeVerifier* verifier_;
222 DISALLOW_COPY_AND_ASSIGN(MockInputMethodObserver);
223 };
224
225 class MockTextInputClient : public DummyTextInputClient {
226 public:
MockTextInputClient()227 MockTextInputClient()
228 : shown_event_count_(0), updated_event_count_(0), hidden_event_count_(0) {
229 }
~MockTextInputClient()230 virtual ~MockTextInputClient() {
231 }
232
OnCandidateWindowShown()233 virtual void OnCandidateWindowShown() OVERRIDE {
234 ++shown_event_count_;
235 }
OnCandidateWindowUpdated()236 virtual void OnCandidateWindowUpdated() OVERRIDE {
237 ++updated_event_count_;
238 }
OnCandidateWindowHidden()239 virtual void OnCandidateWindowHidden() OVERRIDE {
240 ++hidden_event_count_;
241 }
242
shown_event_count() const243 int shown_event_count() const { return shown_event_count_; }
updated_event_count() const244 int updated_event_count() const { return updated_event_count_; }
hidden_event_count() const245 int hidden_event_count() const { return hidden_event_count_; }
246
247 private:
248 int shown_event_count_;
249 int updated_event_count_;
250 int hidden_event_count_;
251 };
252
253 typedef ScopedObserver<InputMethod, InputMethodObserver>
254 InputMethodScopedObserver;
255
SetFocusedTextInputClient(InputMethod * input_method,TextInputClient * text_input_client)256 void SetFocusedTextInputClient(InputMethod* input_method,
257 TextInputClient* text_input_client) {
258 if (switches::IsTextInputFocusManagerEnabled()) {
259 TextInputFocusManager::GetInstance()->FocusTextInputClient(
260 text_input_client);
261 } else {
262 input_method->SetFocusedTextInputClient(text_input_client);
263 }
264 }
265
TEST_F(InputMethodBaseTest,SetFocusedTextInputClient)266 TEST_F(InputMethodBaseTest, SetFocusedTextInputClient) {
267 DummyTextInputClient text_input_client_1st;
268 DummyTextInputClient text_input_client_2nd;
269
270 ClientChangeVerifier verifier;
271 MockInputMethodBase input_method(&verifier);
272 MockInputMethodObserver input_method_observer(&verifier);
273 InputMethodScopedObserver scoped_observer(&input_method_observer);
274 scoped_observer.Add(&input_method);
275
276 // Assume that the top-level-widget gains focus.
277 input_method.OnFocus();
278
279 {
280 SCOPED_TRACE("Focus from NULL to 1st TextInputClient");
281
282 ASSERT_EQ(NULL, input_method.GetTextInputClient());
283 verifier.ExpectClientChange(NULL, &text_input_client_1st);
284 SetFocusedTextInputClient(&input_method, &text_input_client_1st);
285 EXPECT_EQ(&text_input_client_1st, input_method.GetTextInputClient());
286 verifier.Verify();
287 }
288
289 {
290 SCOPED_TRACE("Redundant focus events must be ignored");
291 verifier.ExpectClientDoesNotChange();
292 SetFocusedTextInputClient(&input_method, &text_input_client_1st);
293 verifier.Verify();
294 }
295
296 {
297 SCOPED_TRACE("Focus from 1st to 2nd TextInputClient");
298
299 ASSERT_EQ(&text_input_client_1st, input_method.GetTextInputClient());
300 verifier.ExpectClientChange(&text_input_client_1st,
301 &text_input_client_2nd);
302 SetFocusedTextInputClient(&input_method, &text_input_client_2nd);
303 EXPECT_EQ(&text_input_client_2nd, input_method.GetTextInputClient());
304 verifier.Verify();
305 }
306
307 {
308 SCOPED_TRACE("Focus from 2nd TextInputClient to NULL");
309
310 ASSERT_EQ(&text_input_client_2nd, input_method.GetTextInputClient());
311 verifier.ExpectClientChange(&text_input_client_2nd, NULL);
312 SetFocusedTextInputClient(&input_method, NULL);
313 EXPECT_EQ(NULL, input_method.GetTextInputClient());
314 verifier.Verify();
315 }
316
317 {
318 SCOPED_TRACE("Redundant focus events must be ignored");
319 verifier.ExpectClientDoesNotChange();
320 SetFocusedTextInputClient(&input_method, NULL);
321 verifier.Verify();
322 }
323 }
324
TEST_F(InputMethodBaseTest,DetachTextInputClient)325 TEST_F(InputMethodBaseTest, DetachTextInputClient) {
326 // DetachTextInputClient is not supported when IsTextInputFocusManagerEnabled.
327 if (switches::IsTextInputFocusManagerEnabled())
328 return;
329
330 DummyTextInputClient text_input_client;
331 DummyTextInputClient text_input_client_the_other;
332
333 ClientChangeVerifier verifier;
334 MockInputMethodBase input_method(&verifier);
335 MockInputMethodObserver input_method_observer(&verifier);
336 InputMethodScopedObserver scoped_observer(&input_method_observer);
337 scoped_observer.Add(&input_method);
338
339 // Assume that the top-level-widget gains focus.
340 input_method.OnFocus();
341
342 // Initialize for the next test.
343 {
344 verifier.ExpectClientChange(NULL, &text_input_client);
345 input_method.SetFocusedTextInputClient(&text_input_client);
346 verifier.Verify();
347 }
348
349 {
350 SCOPED_TRACE("DetachTextInputClient must be ignored for other clients");
351 ASSERT_EQ(&text_input_client, input_method.GetTextInputClient());
352 verifier.ExpectClientDoesNotChange();
353 input_method.DetachTextInputClient(&text_input_client_the_other);
354 EXPECT_EQ(&text_input_client, input_method.GetTextInputClient());
355 verifier.Verify();
356 }
357
358 {
359 SCOPED_TRACE("DetachTextInputClient must succeed even after the "
360 "top-level loses the focus");
361
362 ASSERT_EQ(&text_input_client, input_method.GetTextInputClient());
363 input_method.OnBlur();
364 input_method.OnFocus();
365 verifier.ExpectClientChange(&text_input_client, NULL);
366 input_method.DetachTextInputClient(&text_input_client);
367 EXPECT_EQ(NULL, input_method.GetTextInputClient());
368 verifier.Verify();
369 }
370 }
371
TEST_F(InputMethodBaseTest,CandidateWindowEvents)372 TEST_F(InputMethodBaseTest, CandidateWindowEvents) {
373 MockTextInputClient text_input_client;
374
375 {
376 ClientChangeVerifier verifier;
377 MockInputMethodBase input_method_base(&verifier);
378 input_method_base.OnFocus();
379
380 verifier.ExpectClientChange(NULL, &text_input_client);
381 SetFocusedTextInputClient(&input_method_base, &text_input_client);
382
383 EXPECT_EQ(0, text_input_client.shown_event_count());
384 EXPECT_EQ(0, text_input_client.updated_event_count());
385 EXPECT_EQ(0, text_input_client.hidden_event_count());
386
387 input_method_base.OnCandidateWindowShown();
388 base::RunLoop().RunUntilIdle();
389
390 EXPECT_EQ(1, text_input_client.shown_event_count());
391 EXPECT_EQ(0, text_input_client.updated_event_count());
392 EXPECT_EQ(0, text_input_client.hidden_event_count());
393
394 input_method_base.OnCandidateWindowUpdated();
395 base::RunLoop().RunUntilIdle();
396
397 EXPECT_EQ(1, text_input_client.shown_event_count());
398 EXPECT_EQ(1, text_input_client.updated_event_count());
399 EXPECT_EQ(0, text_input_client.hidden_event_count());
400
401 input_method_base.OnCandidateWindowHidden();
402 base::RunLoop().RunUntilIdle();
403
404 EXPECT_EQ(1, text_input_client.shown_event_count());
405 EXPECT_EQ(1, text_input_client.updated_event_count());
406 EXPECT_EQ(1, text_input_client.hidden_event_count());
407
408 input_method_base.OnCandidateWindowShown();
409 }
410
411 // If InputMethod is deleted immediately after an event happens, but before
412 // its callback is invoked, the callback will be cancelled.
413 base::RunLoop().RunUntilIdle();
414 EXPECT_EQ(1, text_input_client.shown_event_count());
415 EXPECT_EQ(1, text_input_client.updated_event_count());
416 EXPECT_EQ(1, text_input_client.hidden_event_count());
417 }
418
419 } // namespace
420 } // namespace ui
421