// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/virtual_keyboard_controller.h" #include #include #include "ash/shell.h" #include "ash/system/chromeos/virtual_keyboard/virtual_keyboard_observer.h" #include "ash/test/ash_test_base.h" #include "ash/wm/maximize_mode/maximize_mode_controller.h" #include "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard.h" #include "ash_switches.h" #include "base/command_line.h" #include "ui/events/devices/device_data_manager.h" #include "ui/events/devices/device_hotplug_event_observer.h" #include "ui/events/devices/input_device.h" #include "ui/events/devices/keyboard_device.h" #include "ui/events/devices/touchscreen_device.h" #include "ui/keyboard/keyboard_export.h" #include "ui/keyboard/keyboard_switches.h" #include "ui/keyboard/keyboard_util.h" namespace ash { namespace test { class VirtualKeyboardControllerTest : public AshTestBase { public: VirtualKeyboardControllerTest() {} ~VirtualKeyboardControllerTest() override {} void UpdateTouchscreenDevices( std::vector touchscreen_devices) { ui::DeviceHotplugEventObserver* manager = ui::DeviceDataManager::GetInstance(); manager->OnTouchscreenDevicesUpdated(touchscreen_devices); } void UpdateKeyboardDevices(std::vector keyboard_devices) { ui::DeviceHotplugEventObserver* manager = ui::DeviceDataManager::GetInstance(); manager->OnKeyboardDevicesUpdated(keyboard_devices); } // Sets the event blocker on the maximized window controller. void SetEventBlocker( scoped_ptr blocker) { Shell::GetInstance()->maximize_mode_controller()->event_blocker_ = std::move(blocker); } void SetUp() override { base::CommandLine::ForCurrentProcess()->AppendSwitch( keyboard::switches::kDisableSmartVirtualKeyboard); AshTestBase::SetUp(); UpdateKeyboardDevices(std::vector()); UpdateTouchscreenDevices(std::vector()); } private: DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardControllerTest); }; TEST_F(VirtualKeyboardControllerTest, EnabledDuringMaximizeMode) { ASSERT_FALSE(keyboard::IsKeyboardEnabled()); // Toggle maximized mode on. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(true); EXPECT_TRUE(keyboard::IsKeyboardEnabled()); // Toggle maximized mode off. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(false); EXPECT_FALSE(keyboard::IsKeyboardEnabled()); } // Mock event blocker that enables the internal keyboard when it's destructor // is called. class MockEventBlocker : public ScopedDisableInternalMouseAndKeyboard { public: MockEventBlocker() {} ~MockEventBlocker() override { std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard")); ui::DeviceHotplugEventObserver* manager = ui::DeviceDataManager::GetInstance(); manager->OnKeyboardDevicesUpdated(keyboards); } private: DISALLOW_COPY_AND_ASSIGN(MockEventBlocker); }; // Tests that reenabling keyboard devices while shutting down does not // cause the Virtual Keyboard Controller to crash. See crbug.com/446204. TEST_F(VirtualKeyboardControllerTest, RestoreKeyboardDevices) { // Toggle maximized mode on. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(true); scoped_ptr blocker( new MockEventBlocker); SetEventBlocker(std::move(blocker)); } class VirtualKeyboardControllerAutoTest : public VirtualKeyboardControllerTest, public VirtualKeyboardObserver { public: VirtualKeyboardControllerAutoTest() : notified_(false), suppressed_(false) {} ~VirtualKeyboardControllerAutoTest() override {} void SetUp() override { AshTestBase::SetUp(); // Set the current list of devices to empty so that they don't interfere // with the test. UpdateKeyboardDevices(std::vector()); UpdateTouchscreenDevices(std::vector()); Shell::GetInstance()->system_tray_notifier()->AddVirtualKeyboardObserver( this); } void TearDown() override { Shell::GetInstance()->system_tray_notifier()->RemoveVirtualKeyboardObserver( this); AshTestBase::TearDown(); } void OnKeyboardSuppressionChanged(bool suppressed) override { notified_ = true; suppressed_ = suppressed; } void ResetObserver() { suppressed_ = false; notified_ = false; } bool IsVirtualKeyboardSuppressed() { return suppressed_; } bool notified() { return notified_; } private: // Whether the observer method was called. bool notified_; // Whether the keeyboard is suppressed. bool suppressed_; DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardControllerAutoTest); }; // Tests that the onscreen keyboard is disabled if an internal keyboard is // present and maximized mode is disabled. TEST_F(VirtualKeyboardControllerAutoTest, DisabledIfInternalKeyboardPresent) { std::vector screens; screens.push_back( ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Touchscreen", gfx::Size(1024, 768), 0)); UpdateTouchscreenDevices(screens); std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard")); UpdateKeyboardDevices(keyboards); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); // Remove the internal keyboard. Virtual keyboard should now show. UpdateKeyboardDevices(std::vector()); EXPECT_TRUE(keyboard::IsKeyboardEnabled()); // Replug in the internal keyboard. Virtual keyboard should hide. UpdateKeyboardDevices(keyboards); EXPECT_FALSE(keyboard::IsKeyboardEnabled()); } TEST_F(VirtualKeyboardControllerAutoTest, DisabledIfNoTouchScreen) { std::vector devices; // Add a touchscreen. Keyboard should deploy. devices.push_back( ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "Touchscreen", gfx::Size(800, 600), 0)); UpdateTouchscreenDevices(devices); EXPECT_TRUE(keyboard::IsKeyboardEnabled()); // Remove touchscreen. Keyboard should hide. UpdateTouchscreenDevices(std::vector()); EXPECT_FALSE(keyboard::IsKeyboardEnabled()); } TEST_F(VirtualKeyboardControllerAutoTest, SuppressedIfExternalKeyboardPresent) { std::vector screens; screens.push_back( ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Touchscreen", gfx::Size(1024, 768), 0)); UpdateTouchscreenDevices(screens); std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "keyboard")); UpdateKeyboardDevices(keyboards); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_TRUE(IsVirtualKeyboardSuppressed()); // Toggle show keyboard. Keyboard should be visible. ResetObserver(); Shell::GetInstance() ->virtual_keyboard_controller() ->ToggleIgnoreExternalKeyboard(); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_TRUE(IsVirtualKeyboardSuppressed()); // Toggle show keyboard. Keyboard should be hidden. ResetObserver(); Shell::GetInstance() ->virtual_keyboard_controller() ->ToggleIgnoreExternalKeyboard(); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_TRUE(IsVirtualKeyboardSuppressed()); // Remove external keyboard. Should be notified that the keyboard is not // suppressed. ResetObserver(); UpdateKeyboardDevices(std::vector()); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_FALSE(IsVirtualKeyboardSuppressed()); } // Tests handling multiple keyboards. Catches crbug.com/430252 TEST_F(VirtualKeyboardControllerAutoTest, HandleMultipleKeyboardsPresent) { std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "keyboard")); keyboards.push_back(ui::KeyboardDevice( 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "keyboard")); keyboards.push_back(ui::KeyboardDevice( 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "keyboard")); UpdateKeyboardDevices(keyboards); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); } // Tests maximized mode interaction without disabling the internal keyboard. TEST_F(VirtualKeyboardControllerAutoTest, EnabledDuringMaximizeMode) { std::vector screens; screens.push_back( ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Touchscreen", gfx::Size(1024, 768), 0)); UpdateTouchscreenDevices(screens); std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard")); UpdateKeyboardDevices(keyboards); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); // Toggle maximized mode on. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(true); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); // Toggle maximized mode off. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(false); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); } // Tests that keyboard gets suppressed in maximized mode. TEST_F(VirtualKeyboardControllerAutoTest, SuppressedInMaximizedMode) { std::vector screens; screens.push_back( ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Touchscreen", gfx::Size(1024, 768), 0)); UpdateTouchscreenDevices(screens); std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Keyboard")); keyboards.push_back(ui::KeyboardDevice( 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "Keyboard")); UpdateKeyboardDevices(keyboards); // Toggle maximized mode on. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(true); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_TRUE(IsVirtualKeyboardSuppressed()); // Toggle show keyboard. Keyboard should be visible. ResetObserver(); Shell::GetInstance() ->virtual_keyboard_controller() ->ToggleIgnoreExternalKeyboard(); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_TRUE(IsVirtualKeyboardSuppressed()); // Toggle show keyboard. Keyboard should be hidden. ResetObserver(); Shell::GetInstance() ->virtual_keyboard_controller() ->ToggleIgnoreExternalKeyboard(); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_TRUE(IsVirtualKeyboardSuppressed()); // Remove external keyboard. Should be notified that the keyboard is not // suppressed. ResetObserver(); keyboards.pop_back(); UpdateKeyboardDevices(keyboards); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); ASSERT_TRUE(notified()); ASSERT_FALSE(IsVirtualKeyboardSuppressed()); // Toggle maximized mode oFF. Shell::GetInstance() ->maximize_mode_controller() ->EnableMaximizeModeWindowManager(false); ASSERT_FALSE(keyboard::IsKeyboardEnabled()); } class VirtualKeyboardControllerAlwaysEnabledTest : public VirtualKeyboardControllerAutoTest { public: VirtualKeyboardControllerAlwaysEnabledTest() : VirtualKeyboardControllerAutoTest() {} ~VirtualKeyboardControllerAlwaysEnabledTest() override {} void SetUp() override { base::CommandLine::ForCurrentProcess()->AppendSwitch( keyboard::switches::kEnableVirtualKeyboard); VirtualKeyboardControllerAutoTest::SetUp(); } private: DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardControllerAlwaysEnabledTest); }; // Tests that the controller cannot suppress the keyboard if the virtual // keyboard always enabled flag is active. TEST_F(VirtualKeyboardControllerAlwaysEnabledTest, DoesNotSuppressKeyboard) { std::vector screens; screens.push_back( ui::TouchscreenDevice(1, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, "Touchscreen", gfx::Size(1024, 768), 0)); UpdateTouchscreenDevices(screens); std::vector keyboards; keyboards.push_back(ui::KeyboardDevice( 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, "keyboard")); UpdateKeyboardDevices(keyboards); ASSERT_TRUE(keyboard::IsKeyboardEnabled()); } } // namespace test } // namespace ash