1 // Copyright (c) 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 "base/bind.h" 6 #include "base/command_line.h" 7 #include "base/memory/scoped_ptr.h" 8 #include "base/message_loop/message_loop.h" 9 #include "testing/gtest/include/gtest/gtest.h" 10 #include "ui/aura/client/focus_client.h" 11 #include "ui/aura/layout_manager.h" 12 #include "ui/aura/root_window.h" 13 #include "ui/aura/test/aura_test_helper.h" 14 #include "ui/aura/test/event_generator.h" 15 #include "ui/aura/test/test_window_delegate.h" 16 #include "ui/aura/window.h" 17 #include "ui/base/ime/input_method.h" 18 #include "ui/base/ime/input_method_factory.h" 19 #include "ui/base/ime/text_input_client.h" 20 #include "ui/compositor/layer_type.h" 21 #include "ui/gfx/rect.h" 22 #include "ui/keyboard/keyboard_controller.h" 23 #include "ui/keyboard/keyboard_controller_proxy.h" 24 #include "ui/keyboard/keyboard_switches.h" 25 26 namespace keyboard { 27 namespace { 28 29 // An event handler that focuses a window when it is clicked/touched on. This is 30 // used to match the focus manger behaviour in ash and views. 31 class TestFocusController : public ui::EventHandler { 32 public: 33 explicit TestFocusController(aura::Window* root) 34 : root_(root) { 35 root_->AddPreTargetHandler(this); 36 } 37 38 virtual ~TestFocusController() { 39 root_->RemovePreTargetHandler(this); 40 } 41 42 private: 43 // Overridden from ui::EventHandler: 44 virtual void OnEvent(ui::Event* event) OVERRIDE { 45 aura::Window* target = static_cast<aura::Window*>(event->target()); 46 if (event->type() == ui::ET_MOUSE_PRESSED || 47 event->type() == ui::ET_TOUCH_PRESSED) { 48 aura::client::GetFocusClient(target)->FocusWindow(target); 49 } 50 } 51 52 aura::Window* root_; 53 DISALLOW_COPY_AND_ASSIGN(TestFocusController); 54 }; 55 56 class TestKeyboardControllerProxy : public KeyboardControllerProxy { 57 public: 58 TestKeyboardControllerProxy() 59 : window_(new aura::Window(&delegate_)), 60 input_method_(ui::CreateInputMethod(NULL, 61 gfx::kNullAcceleratedWidget)) { 62 window_->Init(ui::LAYER_NOT_DRAWN); 63 window_->set_owned_by_parent(false); 64 } 65 66 virtual ~TestKeyboardControllerProxy() { 67 // Destroy the window before the delegate. 68 window_.reset(); 69 } 70 71 // Overridden from KeyboardControllerProxy: 72 virtual aura::Window* GetKeyboardWindow() OVERRIDE { return window_.get(); } 73 virtual content::BrowserContext* GetBrowserContext() OVERRIDE { return NULL; } 74 virtual ui::InputMethod* GetInputMethod() OVERRIDE { 75 return input_method_.get(); 76 } 77 virtual void RequestAudioInput(content::WebContents* web_contents, 78 const content::MediaStreamRequest& request, 79 const content::MediaResponseCallback& callback) OVERRIDE { return; } 80 81 private: 82 scoped_ptr<aura::Window> window_; 83 aura::test::TestWindowDelegate delegate_; 84 scoped_ptr<ui::InputMethod> input_method_; 85 86 DISALLOW_COPY_AND_ASSIGN(TestKeyboardControllerProxy); 87 }; 88 89 // Keeps a count of all the events a window receives. 90 class EventObserver : public ui::EventHandler { 91 public: 92 EventObserver() {} 93 virtual ~EventObserver() {} 94 95 int GetEventCount(ui::EventType type) { 96 return event_counts_[type]; 97 } 98 99 private: 100 // Overridden from ui::EventHandler: 101 virtual void OnEvent(ui::Event* event) OVERRIDE { 102 ui::EventHandler::OnEvent(event); 103 event_counts_[event->type()]++; 104 } 105 106 std::map<ui::EventType, int> event_counts_; 107 DISALLOW_COPY_AND_ASSIGN(EventObserver); 108 }; 109 110 class TestTextInputClient : public ui::TextInputClient { 111 public: 112 explicit TestTextInputClient(ui::TextInputType type) 113 : type_(type) {} 114 virtual ~TestTextInputClient() {} 115 116 private: 117 // Overridden from ui::TextInputClient: 118 virtual void SetCompositionText( 119 const ui::CompositionText& composition) OVERRIDE {} 120 virtual void ConfirmCompositionText() OVERRIDE {} 121 virtual void ClearCompositionText() OVERRIDE {} 122 virtual void InsertText(const base::string16& text) OVERRIDE {} 123 virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {} 124 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE { 125 return static_cast<gfx::NativeWindow>(NULL); 126 } 127 virtual ui::TextInputType GetTextInputType() const OVERRIDE { 128 return type_; 129 } 130 virtual ui::TextInputMode GetTextInputMode() const OVERRIDE { 131 return ui::TEXT_INPUT_MODE_DEFAULT; 132 } 133 virtual bool CanComposeInline() const OVERRIDE { return false; } 134 virtual gfx::Rect GetCaretBounds() const OVERRIDE { return gfx::Rect(); } 135 136 virtual bool GetCompositionCharacterBounds( 137 uint32 index, 138 gfx::Rect* rect) const OVERRIDE { 139 return false; 140 } 141 virtual bool HasCompositionText() const OVERRIDE { return false; } 142 virtual bool GetTextRange(gfx::Range* range) const OVERRIDE { return false; } 143 virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE { 144 return false; 145 } 146 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE { 147 return false; 148 } 149 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE { 150 return false; 151 } 152 virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; } 153 virtual bool GetTextFromRange(const gfx::Range& range, 154 base::string16* text) const OVERRIDE { 155 return false; 156 } 157 virtual void OnInputMethodChanged() OVERRIDE {} 158 virtual bool ChangeTextDirectionAndLayoutAlignment( 159 base::i18n::TextDirection direction) OVERRIDE { return false; } 160 virtual void ExtendSelectionAndDelete(size_t before, size_t after) OVERRIDE {} 161 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {} 162 virtual void OnCandidateWindowShown() OVERRIDE {} 163 virtual void OnCandidateWindowUpdated() OVERRIDE {} 164 virtual void OnCandidateWindowHidden() OVERRIDE {} 165 166 ui::TextInputType type_; 167 168 DISALLOW_COPY_AND_ASSIGN(TestTextInputClient); 169 }; 170 171 class KeyboardContainerObserver : public aura::WindowObserver { 172 public: 173 explicit KeyboardContainerObserver(aura::Window* window) : window_(window) { 174 window_->AddObserver(this); 175 } 176 virtual ~KeyboardContainerObserver() { 177 window_->RemoveObserver(this); 178 } 179 180 private: 181 virtual void OnWindowVisibilityChanged(aura::Window* window, 182 bool visible) OVERRIDE { 183 if (!visible) 184 base::MessageLoop::current()->Quit(); 185 } 186 187 aura::Window* window_; 188 189 DISALLOW_COPY_AND_ASSIGN(KeyboardContainerObserver); 190 }; 191 192 } // namespace 193 194 class KeyboardControllerTest : public testing::Test { 195 public: 196 KeyboardControllerTest() {} 197 virtual ~KeyboardControllerTest() {} 198 199 virtual void SetUp() OVERRIDE { 200 aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_)); 201 aura_test_helper_->SetUp(); 202 ui::SetUpInputMethodFactoryForTesting(); 203 focus_controller_.reset(new TestFocusController(root_window())); 204 proxy_ = new TestKeyboardControllerProxy(); 205 controller_.reset(new KeyboardController(proxy_)); 206 } 207 208 virtual void TearDown() OVERRIDE { 209 focus_controller_.reset(); 210 aura_test_helper_->TearDown(); 211 } 212 213 aura::Window* root_window() { return aura_test_helper_->root_window(); } 214 KeyboardControllerProxy* proxy() { return proxy_; } 215 KeyboardController* controller() { return controller_.get(); } 216 217 void ShowKeyboard() { 218 TestTextInputClient test_text_input_client(ui::TEXT_INPUT_TYPE_TEXT); 219 controller_->OnTextInputStateChanged(&test_text_input_client); 220 } 221 222 protected: 223 bool WillHideKeyboard() { 224 return controller_->WillHideKeyboard(); 225 } 226 227 base::MessageLoopForUI message_loop_; 228 scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_; 229 scoped_ptr<TestFocusController> focus_controller_; 230 231 private: 232 KeyboardControllerProxy* proxy_; 233 scoped_ptr<KeyboardController> controller_; 234 235 DISALLOW_COPY_AND_ASSIGN(KeyboardControllerTest); 236 }; 237 238 TEST_F(KeyboardControllerTest, KeyboardSize) { 239 aura::Window* container(controller()->GetContainerWindow()); 240 gfx::Rect bounds(0, 0, 100, 100); 241 container->SetBounds(bounds); 242 243 const gfx::Rect& before_bounds = proxy()->GetKeyboardWindow()->bounds(); 244 gfx::Rect new_bounds( 245 before_bounds.x(), before_bounds.y(), 246 before_bounds.width() / 2, before_bounds.height() / 2); 247 248 // The KeyboardController's LayoutManager shouldn't let this happen 249 proxy()->GetKeyboardWindow()->SetBounds(new_bounds); 250 ASSERT_EQ(before_bounds, proxy()->GetKeyboardWindow()->bounds()); 251 } 252 253 // Tests that tapping/clicking inside the keyboard does not give it focus. 254 TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) { 255 const gfx::Rect& root_bounds = root_window()->bounds(); 256 aura::test::EventCountDelegate delegate; 257 scoped_ptr<aura::Window> window(new aura::Window(&delegate)); 258 window->Init(ui::LAYER_NOT_DRAWN); 259 window->SetBounds(root_bounds); 260 root_window()->AddChild(window.get()); 261 window->Show(); 262 window->Focus(); 263 264 aura::Window* keyboard_container(controller()->GetContainerWindow()); 265 keyboard_container->SetBounds(root_bounds); 266 267 root_window()->AddChild(keyboard_container); 268 keyboard_container->Show(); 269 270 ShowKeyboard(); 271 272 EXPECT_TRUE(window->IsVisible()); 273 EXPECT_TRUE(keyboard_container->IsVisible()); 274 EXPECT_TRUE(window->HasFocus()); 275 EXPECT_FALSE(keyboard_container->HasFocus()); 276 277 // Click on the keyboard. Make sure the keyboard receives the event, but does 278 // not get focus. 279 EventObserver observer; 280 keyboard_container->AddPreTargetHandler(&observer); 281 282 aura::test::EventGenerator generator(root_window()); 283 generator.MoveMouseTo(proxy()->GetKeyboardWindow()->bounds().CenterPoint()); 284 generator.ClickLeftButton(); 285 EXPECT_TRUE(window->HasFocus()); 286 EXPECT_FALSE(keyboard_container->HasFocus()); 287 EXPECT_EQ("0 0", delegate.GetMouseButtonCountsAndReset()); 288 EXPECT_EQ(1, observer.GetEventCount(ui::ET_MOUSE_PRESSED)); 289 EXPECT_EQ(1, observer.GetEventCount(ui::ET_MOUSE_RELEASED)); 290 291 // Click outside of the keyboard. It should reach the window behind. 292 generator.MoveMouseTo(gfx::Point()); 293 generator.ClickLeftButton(); 294 EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset()); 295 } 296 297 TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) { 298 const gfx::Rect& root_bounds = root_window()->bounds(); 299 300 ui::InputMethod* input_method = proxy()->GetInputMethod(); 301 TestTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT); 302 TestTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT); 303 TestTextInputClient input_client_2(ui::TEXT_INPUT_TYPE_TEXT); 304 TestTextInputClient no_input_client_0(ui::TEXT_INPUT_TYPE_NONE); 305 TestTextInputClient no_input_client_1(ui::TEXT_INPUT_TYPE_NONE); 306 input_method->SetFocusedTextInputClient(&input_client_0); 307 308 aura::Window* keyboard_container(controller()->GetContainerWindow()); 309 scoped_ptr<KeyboardContainerObserver> keyboard_container_observer( 310 new KeyboardContainerObserver(keyboard_container)); 311 keyboard_container->SetBounds(root_bounds); 312 root_window()->AddChild(keyboard_container); 313 314 EXPECT_TRUE(keyboard_container->IsVisible()); 315 316 input_method->SetFocusedTextInputClient(&no_input_client_0); 317 // Keyboard should not immediately hide itself. It is delayed to avoid layout 318 // flicker when the focus of input field quickly change. 319 EXPECT_TRUE(keyboard_container->IsVisible()); 320 EXPECT_TRUE(WillHideKeyboard()); 321 // Wait for hide keyboard to finish. 322 base::MessageLoop::current()->Run(); 323 EXPECT_FALSE(keyboard_container->IsVisible()); 324 325 input_method->SetFocusedTextInputClient(&input_client_1); 326 EXPECT_TRUE(keyboard_container->IsVisible()); 327 328 // Schedule to hide keyboard. 329 input_method->SetFocusedTextInputClient(&no_input_client_1); 330 EXPECT_TRUE(WillHideKeyboard()); 331 // Cancel keyboard hide. 332 input_method->SetFocusedTextInputClient(&input_client_2); 333 334 EXPECT_FALSE(WillHideKeyboard()); 335 EXPECT_TRUE(keyboard_container->IsVisible()); 336 } 337 338 TEST_F(KeyboardControllerTest, AlwaysVisibleWhenLocked) { 339 const gfx::Rect& root_bounds = root_window()->bounds(); 340 341 ui::InputMethod* input_method = proxy()->GetInputMethod(); 342 TestTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT); 343 TestTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT); 344 TestTextInputClient no_input_client_0(ui::TEXT_INPUT_TYPE_NONE); 345 TestTextInputClient no_input_client_1(ui::TEXT_INPUT_TYPE_NONE); 346 input_method->SetFocusedTextInputClient(&input_client_0); 347 348 aura::Window* keyboard_container(controller()->GetContainerWindow()); 349 scoped_ptr<KeyboardContainerObserver> keyboard_container_observer( 350 new KeyboardContainerObserver(keyboard_container)); 351 keyboard_container->SetBounds(root_bounds); 352 root_window()->AddChild(keyboard_container); 353 354 EXPECT_TRUE(keyboard_container->IsVisible()); 355 356 // Lock keyboard. 357 controller()->set_lock_keyboard(true); 358 359 input_method->SetFocusedTextInputClient(&no_input_client_0); 360 // Keyboard should not try to hide itself as it is locked. 361 EXPECT_TRUE(keyboard_container->IsVisible()); 362 EXPECT_FALSE(WillHideKeyboard()); 363 364 input_method->SetFocusedTextInputClient(&input_client_1); 365 EXPECT_TRUE(keyboard_container->IsVisible()); 366 367 // Unlock keyboard. 368 controller()->set_lock_keyboard(false); 369 370 // Keyboard should hide when focus on no input client. 371 input_method->SetFocusedTextInputClient(&no_input_client_1); 372 EXPECT_TRUE(WillHideKeyboard()); 373 374 // Wait for hide keyboard to finish. 375 base::MessageLoop::current()->Run(); 376 EXPECT_FALSE(keyboard_container->IsVisible()); 377 } 378 379 TEST_F(KeyboardControllerTest, KeyboardResizingFromContents) { 380 aura::Window* keyboard_container = controller()->GetContainerWindow(); 381 aura::Window* keyboard_window = proxy()->GetKeyboardWindow(); 382 keyboard_container->SetBounds(gfx::Rect(800, 600)); 383 keyboard_container->AddChild(keyboard_window); 384 385 // Default keyboard size. 386 EXPECT_EQ(180, keyboard_window->bounds().height()); 387 388 // Resizes from contents when flag is unset. 389 keyboard_window->SetBounds(gfx::Rect(100, 80)); 390 EXPECT_EQ(180, keyboard_window->bounds().height()); 391 392 // Resizes from contents when flag is set. 393 proxy()->set_resizing_from_contents(true); 394 keyboard_window->SetBounds(gfx::Rect(100, 80)); 395 EXPECT_EQ(80, keyboard_window->bounds().height()); 396 397 // Resizes from container when flag is set. 398 keyboard_container->SetBounds(gfx::Rect(400, 300)); 399 EXPECT_EQ(80, keyboard_window->bounds().height()); 400 401 // Resizes from container when flag is unset. 402 proxy()->set_resizing_from_contents(false); 403 keyboard_container->SetBounds(gfx::Rect(800, 600)); 404 EXPECT_EQ(180, keyboard_window->bounds().height()); 405 } 406 407 class KeyboardControllerUsabilityTest : public KeyboardControllerTest { 408 public: 409 KeyboardControllerUsabilityTest() {} 410 virtual ~KeyboardControllerUsabilityTest() {} 411 412 virtual void SetUp() OVERRIDE { 413 CommandLine::ForCurrentProcess()->AppendSwitch( 414 switches::kKeyboardUsabilityExperiment); 415 KeyboardControllerTest::SetUp(); 416 } 417 418 private: 419 DISALLOW_COPY_AND_ASSIGN(KeyboardControllerUsabilityTest); 420 }; 421 422 TEST_F(KeyboardControllerUsabilityTest, KeyboardAlwaysVisibleInUsabilityTest) { 423 const gfx::Rect& root_bounds = root_window()->bounds(); 424 425 ui::InputMethod* input_method = proxy()->GetInputMethod(); 426 TestTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT); 427 TestTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE); 428 input_method->SetFocusedTextInputClient(&input_client); 429 430 aura::Window* keyboard_container(controller()->GetContainerWindow()); 431 keyboard_container->SetBounds(root_bounds); 432 root_window()->AddChild(keyboard_container); 433 434 EXPECT_TRUE(keyboard_container->IsVisible()); 435 436 input_method->SetFocusedTextInputClient(&no_input_client); 437 // Keyboard should not hide itself after lost focus. 438 EXPECT_TRUE(keyboard_container->IsVisible()); 439 EXPECT_FALSE(WillHideKeyboard()); 440 } 441 442 } // namespace keyboard 443