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: 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. 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|. 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. 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 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 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 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: 127 InputMethodBaseTest() { 128 } 129 virtual ~InputMethodBaseTest() { 130 } 131 132 virtual void SetUp() { 133 message_loop_.reset(new base::MessageLoopForUI); 134 } 135 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|. 148 MockInputMethodBase(ClientChangeVerifier* verifier) : verifier_(verifier) { 149 } 150 virtual ~MockInputMethodBase() { 151 } 152 153 private: 154 // Overriden from InputMethod. 155 virtual bool OnUntranslatedIMEMessage( 156 const base::NativeEvent& event, 157 InputMethod::NativeEventResult* result) OVERRIDE { 158 return false; 159 } 160 virtual bool DispatchKeyEvent(const ui::KeyEvent&) OVERRIDE { 161 return false; 162 } 163 virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE { 164 } 165 virtual void CancelComposition(const TextInputClient* client) OVERRIDE { 166 } 167 virtual void OnInputLocaleChanged() OVERRIDE { 168 } 169 virtual std::string GetInputLocale() OVERRIDE{ 170 return ""; 171 } 172 virtual bool IsActive() OVERRIDE { 173 return false; 174 } 175 virtual bool IsCandidatePopupOpen() const OVERRIDE { 176 return false; 177 } 178 // Overriden from InputMethodBase. 179 virtual void OnWillChangeFocusedClient(TextInputClient* focused_before, 180 TextInputClient* focused) OVERRIDE { 181 verifier_->OnWillChangeFocusedClient(focused_before, focused); 182 } 183 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|. 198 explicit MockInputMethodObserver(ClientChangeVerifier* verifier) 199 : verifier_(verifier) { 200 } 201 virtual ~MockInputMethodObserver() { 202 } 203 204 private: 205 virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE { 206 } 207 virtual void OnFocus() OVERRIDE { 208 } 209 virtual void OnBlur() OVERRIDE { 210 } 211 virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE { 212 } 213 virtual void OnTextInputStateChanged(const TextInputClient* client) OVERRIDE { 214 verifier_->OnTextInputStateChanged(client); 215 } 216 virtual void OnShowImeIfNeeded() OVERRIDE { 217 } 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: 227 MockTextInputClient() 228 : shown_event_count_(0), updated_event_count_(0), hidden_event_count_(0) { 229 } 230 virtual ~MockTextInputClient() { 231 } 232 233 virtual void OnCandidateWindowShown() OVERRIDE { 234 ++shown_event_count_; 235 } 236 virtual void OnCandidateWindowUpdated() OVERRIDE { 237 ++updated_event_count_; 238 } 239 virtual void OnCandidateWindowHidden() OVERRIDE { 240 ++hidden_event_count_; 241 } 242 243 int shown_event_count() const { return shown_event_count_; } 244 int updated_event_count() const { return updated_event_count_; } 245 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 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 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 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 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