keyboard_controller_unittest.cc revision 4311e82a78ceafbe0585f51d4c8a86df9f21aa0d
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/memory/scoped_ptr.h"
6#include "base/message_loop/message_loop.h"
7#include "testing/gtest/include/gtest/gtest.h"
8#include "ui/aura/client/focus_client.h"
9#include "ui/aura/root_window.h"
10#include "ui/aura/test/aura_test_helper.h"
11#include "ui/aura/test/event_generator.h"
12#include "ui/aura/test/test_window_delegate.h"
13#include "ui/aura/window.h"
14#include "ui/base/ime/input_method.h"
15#include "ui/base/ime/input_method_factory.h"
16#include "ui/base/ime/text_input_client.h"
17#include "ui/compositor/layer_type.h"
18#include "ui/gfx/rect.h"
19#include "ui/keyboard/keyboard_controller.h"
20#include "ui/keyboard/keyboard_controller_proxy.h"
21
22namespace keyboard {
23namespace {
24
25// An event handler that focuses a window when it is clicked/touched on. This is
26// used to match the focus manger behaviour in ash and views.
27class TestFocusController : public ui::EventHandler {
28 public:
29  explicit TestFocusController(aura::RootWindow* root)
30      : root_(root) {
31    root_->AddPreTargetHandler(this);
32  }
33
34  virtual ~TestFocusController() {
35    root_->RemovePreTargetHandler(this);
36  }
37
38 private:
39  // Overridden from ui::EventHandler:
40  virtual void OnEvent(ui::Event* event) OVERRIDE {
41    aura::Window* target = static_cast<aura::Window*>(event->target());
42    if (event->type() == ui::ET_MOUSE_PRESSED ||
43        event->type() == ui::ET_TOUCH_PRESSED) {
44      aura::client::GetFocusClient(target)->FocusWindow(target);
45    }
46  }
47
48  aura::Window* root_;
49  DISALLOW_COPY_AND_ASSIGN(TestFocusController);
50};
51
52class TestKeyboardControllerProxy : public KeyboardControllerProxy {
53 public:
54  TestKeyboardControllerProxy()
55      : window_(new aura::Window(&delegate_)),
56        input_method_(ui::CreateInputMethod(NULL,
57                                            gfx::kNullAcceleratedWidget)) {
58    window_->Init(ui::LAYER_NOT_DRAWN);
59    window_->set_owned_by_parent(false);
60  }
61
62  virtual ~TestKeyboardControllerProxy() {
63    // Destroy the window before the delegate.
64    window_.reset();
65  }
66
67  // Overridden from KeyboardControllerProxy:
68  virtual aura::Window* GetKeyboardWindow() OVERRIDE { return window_.get(); }
69  virtual content::BrowserContext* GetBrowserContext() OVERRIDE { return NULL; }
70  virtual ui::InputMethod* GetInputMethod() OVERRIDE {
71    return input_method_.get();
72  }
73  virtual void RequestAudioInput(content::WebContents* web_contents,
74      const content::MediaStreamRequest& request,
75      const content::MediaResponseCallback& callback) OVERRIDE { return; }
76
77 private:
78  scoped_ptr<aura::Window> window_;
79  aura::test::TestWindowDelegate delegate_;
80  scoped_ptr<ui::InputMethod> input_method_;
81
82  DISALLOW_COPY_AND_ASSIGN(TestKeyboardControllerProxy);
83};
84
85// Keeps a count of all the events a window receives.
86class EventObserver : public ui::EventHandler {
87 public:
88  EventObserver() {}
89  virtual ~EventObserver() {}
90
91  int GetEventCount(ui::EventType type) {
92    return event_counts_[type];
93  }
94
95 private:
96  // Overridden from ui::EventHandler:
97  virtual void OnEvent(ui::Event* event) OVERRIDE {
98    ui::EventHandler::OnEvent(event);
99    event_counts_[event->type()]++;
100  }
101
102  std::map<ui::EventType, int> event_counts_;
103  DISALLOW_COPY_AND_ASSIGN(EventObserver);
104};
105
106class TestTextInputClient : public ui::TextInputClient {
107 public:
108  explicit TestTextInputClient(ui::TextInputType type)
109      : type_(type) {}
110  virtual ~TestTextInputClient() {}
111
112 private:
113  // Overridden from ui::TextInputClient:
114  virtual void SetCompositionText(
115      const ui::CompositionText& composition) OVERRIDE {}
116  virtual void ConfirmCompositionText() OVERRIDE {}
117  virtual void ClearCompositionText() OVERRIDE {}
118  virtual void InsertText(const base::string16& text) OVERRIDE {}
119  virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {}
120  virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
121    return static_cast<gfx::NativeWindow>(NULL);
122  }
123  virtual ui::TextInputType GetTextInputType() const OVERRIDE {
124    return type_;
125  }
126  virtual ui::TextInputMode GetTextInputMode() const OVERRIDE {
127    return ui::TEXT_INPUT_MODE_DEFAULT;
128  }
129  virtual bool CanComposeInline() const OVERRIDE { return false; }
130  virtual gfx::Rect GetCaretBounds() OVERRIDE { return gfx::Rect(); }
131
132  virtual bool GetCompositionCharacterBounds(uint32 index,
133                                             gfx::Rect* rect) OVERRIDE {
134    return false;
135  }
136  virtual bool HasCompositionText() OVERRIDE { return false; }
137  virtual bool GetTextRange(ui::Range* range) OVERRIDE { return false; }
138  virtual bool GetCompositionTextRange(ui::Range* range) OVERRIDE {
139    return false;
140  }
141  virtual bool GetSelectionRange(ui::Range* range) OVERRIDE { return false; }
142  virtual bool SetSelectionRange(const ui::Range& range) OVERRIDE {
143    return false;
144  }
145  virtual bool DeleteRange(const ui::Range& range) OVERRIDE { return false; }
146  virtual bool GetTextFromRange(const ui::Range& range,
147                                base::string16* text) OVERRIDE {
148    return false;
149  }
150  virtual void OnInputMethodChanged() OVERRIDE {}
151  virtual bool ChangeTextDirectionAndLayoutAlignment(
152      base::i18n::TextDirection direction) OVERRIDE { return false; }
153  virtual void ExtendSelectionAndDelete(size_t before, size_t after) OVERRIDE {}
154  virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {}
155
156  ui::TextInputType type_;
157
158  DISALLOW_COPY_AND_ASSIGN(TestTextInputClient);
159};
160
161}  // namespace
162
163class KeyboardControllerTest : public testing::Test {
164 public:
165  KeyboardControllerTest() {}
166  virtual ~KeyboardControllerTest() {}
167
168  virtual void SetUp() OVERRIDE {
169    aura_test_helper_.reset(new aura::test::AuraTestHelper(&message_loop_));
170    aura_test_helper_->SetUp();
171    ui::SetUpInputMethodFactoryForTesting();
172    focus_controller_.reset(new TestFocusController(root_window()));
173  }
174
175  virtual void TearDown() OVERRIDE {
176    focus_controller_.reset();
177    aura_test_helper_->TearDown();
178  }
179
180  aura::RootWindow* root_window() { return aura_test_helper_->root_window(); }
181
182  void ShowKeyboard(KeyboardController* controller) {
183    TestTextInputClient test_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
184    controller->OnTextInputStateChanged(&test_text_input_client);
185  }
186
187 protected:
188  base::MessageLoopForUI message_loop_;
189  scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_;
190  scoped_ptr<TestFocusController> focus_controller_;
191
192 private:
193  DISALLOW_COPY_AND_ASSIGN(KeyboardControllerTest);
194};
195
196TEST_F(KeyboardControllerTest, KeyboardSize) {
197  KeyboardControllerProxy* proxy = new TestKeyboardControllerProxy();
198  KeyboardController controller(proxy);
199
200  scoped_ptr<aura::Window> container(controller.GetContainerWindow());
201  gfx::Rect bounds(0, 0, 100, 100);
202  container->SetBounds(bounds);
203
204  const gfx::Rect& before_bounds = proxy->GetKeyboardWindow()->bounds();
205  gfx::Rect new_bounds(
206      before_bounds.x(), before_bounds.y(),
207      before_bounds.width() / 2, before_bounds.height() / 2);
208
209  // The KeyboardController's LayoutManager shouldn't let this happen
210  proxy->GetKeyboardWindow()->SetBounds(new_bounds);
211  ASSERT_EQ(before_bounds, proxy->GetKeyboardWindow()->bounds());
212}
213
214// Tests that tapping/clicking inside the keyboard does not give it focus.
215TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) {
216  const gfx::Rect& root_bounds = root_window()->bounds();
217  aura::test::EventCountDelegate delegate;
218  scoped_ptr<aura::Window> window(new aura::Window(&delegate));
219  window->Init(ui::LAYER_NOT_DRAWN);
220  window->SetBounds(root_bounds);
221  root_window()->AddChild(window.get());
222  window->Show();
223  window->Focus();
224
225  KeyboardControllerProxy* proxy = new TestKeyboardControllerProxy();
226  KeyboardController controller(proxy);
227
228  scoped_ptr<aura::Window> keyboard_container(controller.GetContainerWindow());
229  keyboard_container->SetBounds(root_bounds);
230
231  root_window()->AddChild(keyboard_container.get());
232  keyboard_container->Show();
233
234  ShowKeyboard(&controller);
235
236  EXPECT_TRUE(window->IsVisible());
237  EXPECT_TRUE(keyboard_container->IsVisible());
238  EXPECT_TRUE(window->HasFocus());
239  EXPECT_FALSE(keyboard_container->HasFocus());
240
241  // Click on the keyboard. Make sure the keyboard receives the event, but does
242  // not get focus.
243  EventObserver observer;
244  keyboard_container->AddPreTargetHandler(&observer);
245
246  aura::test::EventGenerator generator(root_window());
247  generator.MoveMouseTo(proxy->GetKeyboardWindow()->bounds().CenterPoint());
248  generator.ClickLeftButton();
249  EXPECT_TRUE(window->HasFocus());
250  EXPECT_FALSE(keyboard_container->HasFocus());
251  EXPECT_EQ("0 0", delegate.GetMouseButtonCountsAndReset());
252  EXPECT_EQ(1, observer.GetEventCount(ui::ET_MOUSE_PRESSED));
253  EXPECT_EQ(1, observer.GetEventCount(ui::ET_MOUSE_RELEASED));
254
255  // Click outside of the keyboard. It should reach the window behind.
256  generator.MoveMouseTo(gfx::Point());
257  generator.ClickLeftButton();
258  EXPECT_EQ("1 1", delegate.GetMouseButtonCountsAndReset());
259}
260
261TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) {
262  const gfx::Rect& root_bounds = root_window()->bounds();
263  aura::test::EventCountDelegate delegate;
264  scoped_ptr<aura::Window> window(new aura::Window(&delegate));
265  window->Init(ui::LAYER_NOT_DRAWN);
266  window->SetBounds(root_bounds);
267  root_window()->AddChild(window.get());
268  window->Show();
269  window->Focus();
270
271  KeyboardControllerProxy* proxy = new TestKeyboardControllerProxy();
272  ui::InputMethod* input_method = proxy->GetInputMethod();
273  TestTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT);
274  TestTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE);
275  input_method->SetFocusedTextInputClient(&input_client);
276
277  KeyboardController controller(proxy);
278
279  scoped_ptr<aura::Window> keyboard_container(controller.GetContainerWindow());
280  keyboard_container->SetBounds(root_bounds);
281  root_window()->AddChild(keyboard_container.get());
282
283  EXPECT_TRUE(keyboard_container->IsVisible());
284
285  input_method->SetFocusedTextInputClient(&no_input_client);
286  EXPECT_FALSE(keyboard_container->IsVisible());
287
288  input_method->SetFocusedTextInputClient(&input_client);
289  EXPECT_TRUE(keyboard_container->IsVisible());
290}
291
292}  // namespace keyboard
293