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