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.
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"
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 {
33 explicit TestFocusController(aura::Window* root)
35 root_->AddPreTargetHandler(this);
38 virtual ~TestFocusController() {
39 root_->RemovePreTargetHandler(this);
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);
53 DISALLOW_COPY_AND_ASSIGN(TestFocusController);
56 class TestKeyboardControllerProxy : public KeyboardControllerProxy {
58 TestKeyboardControllerProxy()
59 : window_(new aura::Window(&delegate_)),
60 input_method_(ui::CreateInputMethod(NULL,
61 gfx::kNullAcceleratedWidget)) {
62 window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
63 window_->set_owned_by_parent(false);
66 virtual ~TestKeyboardControllerProxy() {
67 // Destroy the window before the delegate.
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();
77 virtual void RequestAudioInput(content::WebContents* web_contents,
78 const content::MediaStreamRequest& request,
79 const content::MediaResponseCallback& callback) OVERRIDE { return; }
82 scoped_ptr<aura::Window> window_;
83 aura::test::TestWindowDelegate delegate_;
84 scoped_ptr<ui::InputMethod> input_method_;
86 DISALLOW_COPY_AND_ASSIGN(TestKeyboardControllerProxy);
89 // Keeps a count of all the events a window receives.
90 class EventObserver : public ui::EventHandler {
93 virtual ~EventObserver() {}
95 int GetEventCount(ui::EventType type) {
96 return event_counts_[type];
100 // Overridden from ui::EventHandler:
101 virtual void OnEvent(ui::Event* event) OVERRIDE {
102 ui::EventHandler::OnEvent(event);
103 event_counts_[event->type()]++;
106 std::map<ui::EventType, int> event_counts_;
107 DISALLOW_COPY_AND_ASSIGN(EventObserver);
110 class TestTextInputClient : public ui::TextInputClient {
112 explicit TestTextInputClient(ui::TextInputType type)
114 virtual ~TestTextInputClient() {}
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);
127 virtual ui::TextInputType GetTextInputType() const OVERRIDE {
130 virtual ui::TextInputMode GetTextInputMode() const OVERRIDE {
131 return ui::TEXT_INPUT_MODE_DEFAULT;
133 virtual bool CanComposeInline() const OVERRIDE { return false; }
134 virtual gfx::Rect GetCaretBounds() const OVERRIDE { return gfx::Rect(); }
136 virtual bool GetCompositionCharacterBounds(
138 gfx::Rect* rect) const OVERRIDE {
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 {
146 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
149 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
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 {
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 {}
166 ui::TextInputType type_;
168 DISALLOW_COPY_AND_ASSIGN(TestTextInputClient);
171 class KeyboardContainerObserver : public aura::WindowObserver {
173 explicit KeyboardContainerObserver(aura::Window* window) : window_(window) {
174 window_->AddObserver(this);
176 virtual ~KeyboardContainerObserver() {
177 window_->RemoveObserver(this);
181 virtual void OnWindowVisibilityChanged(aura::Window* window,
182 bool visible) OVERRIDE {
184 base::MessageLoop::current()->Quit();
187 aura::Window* window_;
189 DISALLOW_COPY_AND_ASSIGN(KeyboardContainerObserver);
194 class KeyboardControllerTest : public testing::Test {
196 KeyboardControllerTest() {}
197 virtual ~KeyboardControllerTest() {}
199 virtual void SetUp() OVERRIDE {
200 aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
201 bool allow_test_contexts = true;
202 aura_test_helper_->SetUp(allow_test_contexts);
203 ui::SetUpInputMethodFactoryForTesting();
204 focus_controller_.reset(new TestFocusController(root_window()));
205 proxy_ = new TestKeyboardControllerProxy();
206 controller_.reset(new KeyboardController(proxy_));
209 virtual void TearDown() OVERRIDE {
211 focus_controller_.reset();
212 aura_test_helper_->TearDown();
215 aura::Window* root_window() { return aura_test_helper_->root_window(); }
216 KeyboardControllerProxy* proxy() { return proxy_; }
217 KeyboardController* controller() { return controller_.get(); }
219 void ShowKeyboard() {
220 TestTextInputClient test_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
221 SetFocus(&test_text_input_client);
225 void SetFocus(ui::TextInputClient* client) {
226 ui::InputMethod* input_method = proxy()->GetInputMethod();
227 input_method->SetFocusedTextInputClient(client);
228 if (client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE)
229 input_method->ShowImeIfNeeded();
232 bool WillHideKeyboard() {
233 return controller_->WillHideKeyboard();
236 base::MessageLoopForUI message_loop_;
237 scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_;
238 scoped_ptr<TestFocusController> focus_controller_;
241 KeyboardControllerProxy* proxy_;
242 scoped_ptr<KeyboardController> controller_;
244 DISALLOW_COPY_AND_ASSIGN(KeyboardControllerTest);
247 TEST_F(KeyboardControllerTest, KeyboardSize) {
248 aura::Window* container(controller()->GetContainerWindow());
249 gfx::Rect bounds(0, 0, 100, 100);
250 container->SetBounds(bounds);
252 const gfx::Rect& before_bounds = proxy()->GetKeyboardWindow()->bounds();
253 gfx::Rect new_bounds(
254 before_bounds.x(), before_bounds.y(),
255 before_bounds.width() / 2, before_bounds.height() / 2);
257 // The KeyboardController's LayoutManager shouldn't let this happen
258 proxy()->GetKeyboardWindow()->SetBounds(new_bounds);
259 ASSERT_EQ(before_bounds, proxy()->GetKeyboardWindow()->bounds());
262 // Tests that tapping/clicking inside the keyboard does not give it focus.
263 TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) {
264 const gfx::Rect& root_bounds = root_window()->bounds();
265 aura::test::EventCountDelegate delegate;
266 scoped_ptr<aura::Window> window(new aura::Window(&delegate));
267 window->Init(aura::WINDOW_LAYER_NOT_DRAWN);
268 window->SetBounds(root_bounds);
269 root_window()->AddChild(window.get());
273 aura::Window* keyboard_container(controller()->GetContainerWindow());
274 keyboard_container->SetBounds(root_bounds);
276 root_window()->AddChild(keyboard_container);
277 keyboard_container->Show();
281 EXPECT_TRUE(window->IsVisible());
282 EXPECT_TRUE(keyboard_container->IsVisible());
283 EXPECT_TRUE(window->HasFocus());
284 EXPECT_FALSE(keyboard_container->HasFocus());
286 // Click on the keyboard. Make sure the keyboard receives the event, but does
288 EventObserver observer;
289 keyboard_container->AddPreTargetHandler(&observer);
291 aura::test::EventGenerator generator(root_window());
292 generator.MoveMouseTo(proxy()->GetKeyboardWindow()->bounds().CenterPoint());
293 generator.ClickLeftButton();
294 EXPECT_TRUE(window->HasFocus());
295 EXPECT_FALSE(keyboard_container->HasFocus());
296 EXPECT_EQ("0 0", delegate.GetMouseButtonCountsAndReset());
297 EXPECT_EQ(1, observer.GetEventCount(ui::ET_MOUSE_PRESSED));
298 EXPECT_EQ(1, observer.GetEventCount(ui::ET_MOUSE_RELEASED));
300 // Click outside of the keyboard. It should reach the window behind.
301 generator.MoveMouseTo(gfx::Point());
302 generator.ClickLeftButton();
303 EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset());
304 keyboard_container->RemovePreTargetHandler(&observer);
307 TEST_F(KeyboardControllerTest, EventHitTestingInContainer) {
308 const gfx::Rect& root_bounds = root_window()->bounds();
309 aura::test::EventCountDelegate delegate;
310 scoped_ptr<aura::Window> window(new aura::Window(&delegate));
311 window->Init(aura::WINDOW_LAYER_NOT_DRAWN);
312 window->SetBounds(root_bounds);
313 root_window()->AddChild(window.get());
317 aura::Window* keyboard_container(controller()->GetContainerWindow());
318 keyboard_container->SetBounds(root_bounds);
320 root_window()->AddChild(keyboard_container);
321 keyboard_container->Show();
325 EXPECT_TRUE(window->IsVisible());
326 EXPECT_TRUE(keyboard_container->IsVisible());
327 EXPECT_TRUE(window->HasFocus());
328 EXPECT_FALSE(keyboard_container->HasFocus());
330 // Make sure hit testing works correctly while the keyboard is visible.
331 aura::Window* keyboard_window = proxy()->GetKeyboardWindow();
332 ui::EventTarget* root = root_window();
333 ui::EventTargeter* targeter = root->GetEventTargeter();
334 gfx::Point location = keyboard_window->bounds().CenterPoint();
335 ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
337 EXPECT_EQ(keyboard_window, targeter->FindTargetForEvent(root, &mouse1));
340 location.set_y(keyboard_window->bounds().y() - 5);
341 ui::MouseEvent mouse2(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
343 EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root, &mouse2));
346 TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) {
347 const gfx::Rect& root_bounds = root_window()->bounds();
349 TestTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT);
350 TestTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT);
351 TestTextInputClient input_client_2(ui::TEXT_INPUT_TYPE_TEXT);
352 TestTextInputClient no_input_client_0(ui::TEXT_INPUT_TYPE_NONE);
353 TestTextInputClient no_input_client_1(ui::TEXT_INPUT_TYPE_NONE);
355 aura::Window* keyboard_container(controller()->GetContainerWindow());
356 scoped_ptr<KeyboardContainerObserver> keyboard_container_observer(
357 new KeyboardContainerObserver(keyboard_container));
358 keyboard_container->SetBounds(root_bounds);
359 root_window()->AddChild(keyboard_container);
361 SetFocus(&input_client_0);
363 EXPECT_TRUE(keyboard_container->IsVisible());
365 SetFocus(&no_input_client_0);
366 // Keyboard should not immediately hide itself. It is delayed to avoid layout
367 // flicker when the focus of input field quickly change.
368 EXPECT_TRUE(keyboard_container->IsVisible());
369 EXPECT_TRUE(WillHideKeyboard());
370 // Wait for hide keyboard to finish.
371 base::MessageLoop::current()->Run();
372 EXPECT_FALSE(keyboard_container->IsVisible());
374 SetFocus(&input_client_1);
375 EXPECT_TRUE(keyboard_container->IsVisible());
377 // Schedule to hide keyboard.
378 SetFocus(&no_input_client_1);
379 EXPECT_TRUE(WillHideKeyboard());
380 // Cancel keyboard hide.
381 SetFocus(&input_client_2);
383 EXPECT_FALSE(WillHideKeyboard());
384 EXPECT_TRUE(keyboard_container->IsVisible());
387 TEST_F(KeyboardControllerTest, AlwaysVisibleWhenLocked) {
388 const gfx::Rect& root_bounds = root_window()->bounds();
390 TestTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT);
391 TestTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT);
392 TestTextInputClient no_input_client_0(ui::TEXT_INPUT_TYPE_NONE);
393 TestTextInputClient no_input_client_1(ui::TEXT_INPUT_TYPE_NONE);
395 aura::Window* keyboard_container(controller()->GetContainerWindow());
396 scoped_ptr<KeyboardContainerObserver> keyboard_container_observer(
397 new KeyboardContainerObserver(keyboard_container));
398 keyboard_container->SetBounds(root_bounds);
399 root_window()->AddChild(keyboard_container);
401 SetFocus(&input_client_0);
403 EXPECT_TRUE(keyboard_container->IsVisible());
406 controller()->set_lock_keyboard(true);
408 SetFocus(&no_input_client_0);
409 // Keyboard should not try to hide itself as it is locked.
410 EXPECT_TRUE(keyboard_container->IsVisible());
411 EXPECT_FALSE(WillHideKeyboard());
413 SetFocus(&input_client_1);
414 EXPECT_TRUE(keyboard_container->IsVisible());
417 controller()->set_lock_keyboard(false);
419 // Keyboard should hide when focus on no input client.
420 SetFocus(&no_input_client_1);
421 EXPECT_TRUE(WillHideKeyboard());
423 // Wait for hide keyboard to finish.
424 base::MessageLoop::current()->Run();
425 EXPECT_FALSE(keyboard_container->IsVisible());
428 TEST_F(KeyboardControllerTest, KeyboardResizingFromContents) {
429 aura::Window* keyboard_container = controller()->GetContainerWindow();
430 aura::Window* keyboard_window = proxy()->GetKeyboardWindow();
431 keyboard_container->SetBounds(gfx::Rect(800, 600));
432 keyboard_container->AddChild(keyboard_window);
434 // Default keyboard size.
435 EXPECT_EQ(180, keyboard_window->bounds().height());
437 // Resizes from contents when flag is unset.
438 keyboard_window->SetBounds(gfx::Rect(100, 80));
439 EXPECT_EQ(180, keyboard_window->bounds().height());
441 // Resizes from contents when flag is set.
442 proxy()->set_resizing_from_contents(true);
443 keyboard_window->SetBounds(gfx::Rect(100, 80));
444 EXPECT_EQ(80, keyboard_window->bounds().height());
446 // Resizes from container when flag is set.
447 keyboard_container->SetBounds(gfx::Rect(400, 300));
448 EXPECT_EQ(80, keyboard_window->bounds().height());
450 // Resizes from container when flag is unset.
451 proxy()->set_resizing_from_contents(false);
452 keyboard_container->SetBounds(gfx::Rect(800, 600));
453 EXPECT_EQ(180, keyboard_window->bounds().height());
456 class KeyboardControllerUsabilityTest : public KeyboardControllerTest {
458 KeyboardControllerUsabilityTest() {}
459 virtual ~KeyboardControllerUsabilityTest() {}
461 virtual void SetUp() OVERRIDE {
462 CommandLine::ForCurrentProcess()->AppendSwitch(
463 switches::kKeyboardUsabilityExperiment);
464 KeyboardControllerTest::SetUp();
468 DISALLOW_COPY_AND_ASSIGN(KeyboardControllerUsabilityTest);
471 TEST_F(KeyboardControllerUsabilityTest, KeyboardAlwaysVisibleInUsabilityTest) {
472 const gfx::Rect& root_bounds = root_window()->bounds();
474 TestTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT);
475 TestTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE);
477 aura::Window* keyboard_container(controller()->GetContainerWindow());
478 keyboard_container->SetBounds(root_bounds);
479 root_window()->AddChild(keyboard_container);
481 SetFocus(&input_client);
482 EXPECT_TRUE(keyboard_container->IsVisible());
484 SetFocus(&no_input_client);
485 // Keyboard should not hide itself after lost focus.
486 EXPECT_TRUE(keyboard_container->IsVisible());
487 EXPECT_FALSE(WillHideKeyboard());
490 } // namespace keyboard