blob: 1ee607bee59f3143510c0d3bb9efe9e45b42af06 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/keyboard/keyboard_controller_impl.h"
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include "ash/constants/ash_pref_names.h"
#include "ash/keyboard/ui/container_behavior.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/keyboard/ui/test/keyboard_test_util.h"
#include "ash/public/cpp/keyboard/keyboard_controller.h"
#include "ash/public/cpp/test/test_keyboard_controller_observer.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/wm/core/window_util.h"
using keyboard::KeyboardConfig;
using keyboard::KeyboardEnableFlag;
namespace ash {
namespace {
ShelfLayoutManager* GetShelfLayoutManager() {
return AshTestBase::GetPrimaryShelf()->shelf_layout_manager();
}
class TestContainerBehavior : public keyboard::ContainerBehavior {
public:
TestContainerBehavior() : keyboard::ContainerBehavior(nullptr) {}
~TestContainerBehavior() override = default;
// keyboard::ContainerBehavior
void DoShowingAnimation(
aura::Window* window,
ui::ScopedLayerAnimationSettings* animation_settings) override {}
void DoHidingAnimation(
aura::Window* window,
wm::ScopedHidingAnimationSettings* animation_settings) override {}
void InitializeShowAnimationStartingState(aura::Window* container) override {}
gfx::Rect AdjustSetBoundsRequest(
const gfx::Rect& display_bounds,
const gfx::Rect& requested_bounds_in_screen_coords) override {
return adjusted_bounds_in_screen_ ? *adjusted_bounds_in_screen_
: requested_bounds_in_screen_coords;
}
void SetCanonicalBounds(aura::Window* container,
const gfx::Rect& display_bounds) override {}
bool IsOverscrollAllowed() const override { return true; }
void SavePosition(const gfx::Rect& keyboard_bounds_in_screen,
const gfx::Size& screen_size) override {}
bool HandlePointerEvent(const ui::LocatedEvent& event,
const display::Display& current_display) override {
return false;
}
bool HandleGestureEvent(const ui::GestureEvent& event,
const gfx::Rect& bounds_in_screen) override {
return false;
}
keyboard::ContainerType GetType() const override { return type_; }
bool TextBlurHidesKeyboard() const override { return false; }
void SetOccludedBounds(const gfx::Rect& bounds) override {
occluded_bounds_ = bounds;
}
gfx::Rect GetOccludedBounds(
const gfx::Rect& visual_bounds_in_window) const override {
return occluded_bounds_;
}
bool OccludedBoundsAffectWorkspaceLayout() const override { return false; }
void SetDraggableArea(const gfx::Rect& rect) override {
draggable_area_ = rect;
}
void SetAreaToRemainOnScreen(const gfx::Rect& rect) override {
area_to_remain_on_screen_ = rect;
}
const gfx::Rect& occluded_bounds() const { return occluded_bounds_; }
const gfx::Rect& draggable_area() const { return draggable_area_; }
const gfx::Rect& area_to_remain_on_screen() const {
return area_to_remain_on_screen_;
}
void set_adjusted_bounds_in_screen(const gfx::Rect& rect) {
adjusted_bounds_in_screen_ = rect;
}
private:
keyboard::ContainerType type_ = keyboard::ContainerType::kFullWidth;
gfx::Rect occluded_bounds_;
gfx::Rect draggable_area_;
gfx::Rect area_to_remain_on_screen_;
std::optional<gfx::Rect> adjusted_bounds_in_screen_;
};
class KeyboardControllerImplTest : public AshTestBase {
public:
KeyboardControllerImplTest() = default;
KeyboardControllerImplTest(const KeyboardControllerImplTest&) = delete;
KeyboardControllerImplTest& operator=(const KeyboardControllerImplTest&) =
delete;
~KeyboardControllerImplTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
// Set the initial observer config to the default config.
test_observer()->set_config(keyboard_controller()->GetKeyboardConfig());
}
void TearDown() override { AshTestBase::TearDown(); }
KeyboardControllerImpl* keyboard_controller() {
return Shell::Get()->keyboard_controller();
}
keyboard::KeyboardUIController* keyboard_ui_controller() {
return keyboard_controller()->keyboard_ui_controller();
}
TestKeyboardControllerObserver* test_observer() {
return ash_test_helper()->test_keyboard_controller_observer();
}
protected:
bool SetContainerType(keyboard::ContainerType container_type,
const gfx::Rect& target_bounds) {
bool result = false;
base::RunLoop run_loop;
keyboard_controller()->SetContainerType(
container_type, target_bounds,
base::BindLambdaForTesting([&](bool success) {
result = success;
run_loop.QuitWhenIdle();
}));
run_loop.Run();
return result;
}
aura::Window* GetPrimaryRootWindow() { return Shell::GetPrimaryRootWindow(); }
aura::Window* GetSecondaryRootWindow() {
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
return root_windows[0] == GetPrimaryRootWindow() ? root_windows[1]
: root_windows[0];
}
void CreateFocusedTestWindowInRootWindow(aura::Window* root_window) {
// Owned by |root_window|.
aura::Window* focusable_window =
CreateTestWindowInShellWithBounds(root_window->GetBoundsInScreen());
focusable_window->Focus();
}
void SetKeyboardConfigToPref(const base::Value& value) {
auto features = base::Value::Dict()
.Set("auto_complete_enabled", value.Clone())
.Set("auto_correct_enabled", value.Clone())
.Set("handwriting_enabled", value.Clone())
.Set("spell_check_enabled", value.Clone())
.Set("voice_input_enabled", value.Clone());
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
prefs->SetDict(prefs::kAccessibilityVirtualKeyboardFeatures,
std::move(features));
}
void VerifyKeyboardConfig(const KeyboardConfig& config, bool expected_value) {
EXPECT_EQ(test_observer()->config().auto_complete, expected_value);
EXPECT_EQ(test_observer()->config().auto_correct, expected_value);
EXPECT_EQ(test_observer()->config().handwriting, expected_value);
EXPECT_EQ(test_observer()->config().spell_check, expected_value);
EXPECT_EQ(test_observer()->config().voice_input, expected_value);
}
};
} // namespace
TEST_F(KeyboardControllerImplTest, SetKeyboardConfig) {
// Enable the keyboard so that config changes trigger observer events.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
keyboard_controller()->GetKeyboardConfig();
KeyboardConfig config = keyboard_controller()->GetKeyboardConfig();
// Set the observer config to the default config.
test_observer()->set_config(config);
// Change the keyboard config.
bool old_auto_complete = config.auto_complete;
config.auto_complete = !config.auto_complete;
keyboard_controller()->SetKeyboardConfig(std::move(config));
// Test that the config changes.
keyboard_controller()->GetKeyboardConfig();
EXPECT_NE(old_auto_complete,
keyboard_controller()->GetKeyboardConfig().auto_complete);
// Test that the test observer received the change.
EXPECT_NE(old_auto_complete, test_observer()->config().auto_complete);
}
TEST_F(KeyboardControllerImplTest, SetKeyboardConfigFromPref) {
// Enable the keyboard so that config changes trigger observer events.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Set the policy for virtual keyboard features.
SetKeyboardConfigToPref(base::Value(false));
// Test that all virtual keyboard features are enabled by default.
VerifyKeyboardConfig(keyboard_controller()->GetKeyboardConfig(), true);
VerifyKeyboardConfig(test_observer()->config(), true);
// Enabled the keyboard config from pref service.
keyboard_controller()->SetKeyboardConfigFromPref(true);
VerifyKeyboardConfig(keyboard_controller()->GetKeyboardConfig(), false);
VerifyKeyboardConfig(test_observer()->config(), false);
// Change the feature values from 'false' to 'true'.
SetKeyboardConfigToPref(base::Value(true));
VerifyKeyboardConfig(keyboard_controller()->GetKeyboardConfig(), true);
VerifyKeyboardConfig(test_observer()->config(), true);
}
TEST_F(KeyboardControllerImplTest,
SetKeyboardConfigFromPref_DefaultPolicyValue) {
// Enable the keyboard so that config changes trigger observer events.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Enabled the keyboard config from pref service. The feature values should be
// false by default.
keyboard_controller()->SetKeyboardConfigFromPref(true);
VerifyKeyboardConfig(keyboard_controller()->GetKeyboardConfig(), false);
VerifyKeyboardConfig(test_observer()->config(), false);
}
TEST_F(KeyboardControllerImplTest,
SetKeyboardConfigFromPref_DefaultFeatureValue) {
// Enable the keyboard so that config changes trigger observer events.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Set the policy for virtual keyboard features.
base::Value features(base::Value::Type::DICT);
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
prefs->Set(prefs::kAccessibilityVirtualKeyboardFeatures, features);
// Enabled the keyboard config from pref service. The feature values should be
// false by default.
keyboard_controller()->SetKeyboardConfigFromPref(true);
VerifyKeyboardConfig(keyboard_controller()->GetKeyboardConfig(), false);
VerifyKeyboardConfig(test_observer()->config(), false);
}
TEST_F(KeyboardControllerImplTest,
SetKeyboardConfigFromPref_InvalidFeatureType) {
// Enable the keyboard so that config changes trigger observer events.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// The Integer type is not acceptable to virtual keyboard features, so they
// fallback to use the default value (false).
SetKeyboardConfigToPref(base::Value(100));
keyboard_controller()->SetKeyboardConfigFromPref(true);
VerifyKeyboardConfig(keyboard_controller()->GetKeyboardConfig(), false);
VerifyKeyboardConfig(test_observer()->config(), false);
}
TEST_F(KeyboardControllerImplTest, EnableFlags) {
EXPECT_FALSE(keyboard_controller()->IsKeyboardEnabled());
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
std::set<keyboard::KeyboardEnableFlag> enable_flags =
keyboard_controller()->GetEnableFlags();
EXPECT_TRUE(
base::Contains(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
EXPECT_EQ(enable_flags, test_observer()->enable_flags());
EXPECT_TRUE(keyboard_controller()->IsKeyboardEnabled());
// Set the enable override to disable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kPolicyDisabled);
enable_flags = keyboard_controller()->GetEnableFlags();
EXPECT_TRUE(
base::Contains(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
EXPECT_TRUE(
base::Contains(enable_flags, KeyboardEnableFlag::kPolicyDisabled));
EXPECT_EQ(enable_flags, test_observer()->enable_flags());
EXPECT_FALSE(keyboard_controller()->IsKeyboardEnabled());
// Clear the enable override; should enable the keyboard.
keyboard_controller()->ClearEnableFlag(KeyboardEnableFlag::kPolicyDisabled);
enable_flags = keyboard_controller()->GetEnableFlags();
EXPECT_TRUE(
base::Contains(enable_flags, KeyboardEnableFlag::kExtensionEnabled));
EXPECT_FALSE(
base::Contains(enable_flags, KeyboardEnableFlag::kPolicyDisabled));
EXPECT_EQ(enable_flags, test_observer()->enable_flags());
EXPECT_TRUE(keyboard_controller()->IsKeyboardEnabled());
}
TEST_F(KeyboardControllerImplTest, RebuildKeyboardIfEnabled) {
EXPECT_EQ(0, test_observer()->destroyed_count());
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
EXPECT_EQ(0, test_observer()->destroyed_count());
// Enable the keyboard again; this should not reload the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
EXPECT_EQ(0, test_observer()->destroyed_count());
// Rebuild the keyboard. This should destroy the previous keyboard window.
keyboard_controller()->RebuildKeyboardIfEnabled();
EXPECT_EQ(1, test_observer()->destroyed_count());
// Disable the keyboard. The keyboard window should be destroyed.
keyboard_controller()->ClearEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
EXPECT_EQ(2, test_observer()->destroyed_count());
}
TEST_F(KeyboardControllerImplTest, ShowAndHideKeyboard) {
// Enable the keyboard. This will create the keyboard window but not show it.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
ASSERT_TRUE(keyboard_ui_controller()->GetKeyboardWindow());
EXPECT_FALSE(keyboard_ui_controller()->GetKeyboardWindow()->IsVisible());
// The keyboard needs to be in a loaded state before being shown.
ASSERT_TRUE(keyboard::test::WaitUntilLoaded());
keyboard_controller()->ShowKeyboard();
EXPECT_TRUE(keyboard_ui_controller()->GetKeyboardWindow()->IsVisible());
keyboard_controller()->HideKeyboard(HideReason::kUser);
EXPECT_FALSE(keyboard_ui_controller()->GetKeyboardWindow()->IsVisible());
// TODO(stevenjb): Also use TestKeyboardControllerObserver and
// IsKeyboardVisible to test visibility changes. https://crbug.com/849995.
}
TEST_F(KeyboardControllerImplTest, SetContainerType) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
const auto default_behavior = keyboard::ContainerType::kFullWidth;
EXPECT_EQ(default_behavior,
keyboard_ui_controller()->GetActiveContainerType());
gfx::Rect target_bounds(0, 0, 10, 10);
// Set the container type to kFloating.
EXPECT_TRUE(
SetContainerType(keyboard::ContainerType::kFloating, target_bounds));
EXPECT_EQ(keyboard::ContainerType::kFloating,
keyboard_ui_controller()->GetActiveContainerType());
// Ensure that the window size is correct (position is determined by Ash).
EXPECT_EQ(
target_bounds.size(),
keyboard_ui_controller()->GetKeyboardWindow()->GetTargetBounds().size());
// Setting the container type to the current type should fail.
EXPECT_FALSE(
SetContainerType(keyboard::ContainerType::kFloating, target_bounds));
EXPECT_EQ(keyboard::ContainerType::kFloating,
keyboard_ui_controller()->GetActiveContainerType());
}
TEST_F(KeyboardControllerImplTest, SetKeyboardLocked) {
ASSERT_FALSE(keyboard_ui_controller()->keyboard_locked());
keyboard_controller()->SetKeyboardLocked(true);
EXPECT_TRUE(keyboard_ui_controller()->keyboard_locked());
keyboard_controller()->SetKeyboardLocked(false);
EXPECT_FALSE(keyboard_ui_controller()->keyboard_locked());
}
TEST_F(KeyboardControllerImplTest, SetOccludedBounds) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Override the container behavior.
auto scoped_behavior = std::make_unique<TestContainerBehavior>();
TestContainerBehavior* behavior = scoped_behavior.get();
keyboard_ui_controller()->set_container_behavior_for_test(
std::move(scoped_behavior));
gfx::Rect bounds(10, 20, 30, 40);
keyboard_controller()->SetOccludedBounds({bounds});
EXPECT_EQ(bounds, behavior->occluded_bounds());
}
TEST_F(KeyboardControllerImplTest, SetHitTestBounds) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
ASSERT_FALSE(keyboard_ui_controller()->GetKeyboardWindow()->targeter());
// Setting the hit test bounds should set a WindowTargeter.
keyboard_ui_controller()->SetHitTestBounds({gfx::Rect(10, 20, 30, 40)});
ASSERT_TRUE(keyboard_ui_controller()->GetKeyboardWindow()->targeter());
}
TEST_F(KeyboardControllerImplTest, SetDraggableArea) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Override the container behavior.
auto scoped_behavior = std::make_unique<TestContainerBehavior>();
TestContainerBehavior* behavior = scoped_behavior.get();
keyboard_ui_controller()->set_container_behavior_for_test(
std::move(scoped_behavior));
gfx::Rect bounds(10, 20, 30, 40);
keyboard_ui_controller()->SetDraggableArea(bounds);
EXPECT_EQ(bounds, behavior->draggable_area());
}
TEST_F(KeyboardControllerImplTest, SetAreaToRemainOnScreen) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Override the container behavior.
auto scoped_behavior = std::make_unique<TestContainerBehavior>();
TestContainerBehavior* behavior = scoped_behavior.get();
keyboard_ui_controller()->set_container_behavior_for_test(
std::move(scoped_behavior));
gfx::Rect bounds(10, 20, 30, 40);
keyboard_ui_controller()->SetAreaToRemainOnScreen(bounds);
EXPECT_EQ(bounds, behavior->area_to_remain_on_screen());
}
TEST_F(KeyboardControllerImplTest, SetWindowBoundsInScreen) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Override the container behavior.
auto scoped_behavior = std::make_unique<TestContainerBehavior>();
keyboard_ui_controller()->set_container_behavior_for_test(
std::move(scoped_behavior));
gfx::Rect bounds(1, 1, 300, 400);
keyboard_controller()->SetWindowBoundsInScreen(bounds);
EXPECT_EQ(bounds,
keyboard_ui_controller()->GetKeyboardWindow()->GetBoundsInScreen());
}
TEST_F(KeyboardControllerImplTest,
SetWindowBoundsInScreenShouldRespectAdjustedBounds) {
gfx::Rect adjusted_bounds_in_screen(10, 10, 30, 40);
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Override the container behavior.
auto scoped_behavior = std::make_unique<TestContainerBehavior>();
scoped_behavior->set_adjusted_bounds_in_screen(adjusted_bounds_in_screen);
keyboard_ui_controller()->set_container_behavior_for_test(
std::move(scoped_behavior));
gfx::Rect requested_bounds(1, 1, 300, 400);
keyboard_controller()->SetWindowBoundsInScreen(requested_bounds);
EXPECT_EQ(adjusted_bounds_in_screen,
keyboard_ui_controller()->GetKeyboardWindow()->GetBoundsInScreen());
}
TEST_F(KeyboardControllerImplTest, ChangingSessionRebuildsKeyboard) {
// Enable the keyboard.
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// LOGGED_IN_NOT_ACTIVE session state needs to rebuild keyboard for supervised
// user profile.
Shell::Get()->keyboard_controller()->OnSessionStateChanged(
session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
EXPECT_EQ(1, test_observer()->destroyed_count());
// ACTIVE session state also needs to rebuild keyboard for guest user profile.
Shell::Get()->keyboard_controller()->OnSessionStateChanged(
session_manager::SessionState::ACTIVE);
EXPECT_EQ(2, test_observer()->destroyed_count());
}
TEST_F(KeyboardControllerImplTest, VisualBoundsInMultipleDisplays) {
UpdateDisplay("800x600,1024x768");
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Show the keyboard in the second display.
keyboard_ui_controller()->ShowKeyboardInDisplay(
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.GetSecondaryDisplay());
ASSERT_TRUE(keyboard::WaitUntilShown());
gfx::Rect root_bounds = keyboard_ui_controller()->visual_bounds_in_root();
EXPECT_EQ(0, root_bounds.x());
gfx::Rect screen_bounds = keyboard_ui_controller()->GetVisualBoundsInScreen();
EXPECT_EQ(800, screen_bounds.x());
}
TEST_F(KeyboardControllerImplTest, OccludedBoundsInMultipleDisplays) {
UpdateDisplay("800x600,1024x768");
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Show the keyboard in the second display.
keyboard_ui_controller()->ShowKeyboardInDisplay(
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.GetSecondaryDisplay());
ASSERT_TRUE(keyboard::WaitUntilShown());
gfx::Rect screen_bounds =
keyboard_ui_controller()->GetWorkspaceOccludedBoundsInScreen();
EXPECT_EQ(800, screen_bounds.x());
}
// Test for http://crbug.com/303429. |GetContainerForDisplay| should move
// keyboard to specified display even when it's not touchable.
TEST_F(KeyboardControllerImplTest, GetContainerForDisplay) {
UpdateDisplay("600x500,600x500");
// Make primary display touchable.
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetTouchSupport(GetPrimaryDisplay().id(),
display::Display::TouchSupport::AVAILABLE);
EXPECT_EQ(display::Display::TouchSupport::AVAILABLE,
GetPrimaryDisplay().touch_support());
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetSecondaryDisplay().touch_support());
// Move to primary display.
EXPECT_EQ(GetPrimaryRootWindow(),
keyboard_controller()
->GetContainerForDisplay(GetPrimaryDisplay())
->GetRootWindow());
// Move to secondary display.
EXPECT_EQ(GetSecondaryRootWindow(),
keyboard_controller()
->GetContainerForDisplay(GetSecondaryDisplay())
->GetRootWindow());
}
// Test for http://crbug.com/297858. |GetContainerForDefaultDisplay| should
// return the primary display if no display has touch capability and
// no window is focused.
TEST_F(KeyboardControllerImplTest,
DefaultContainerInPrimaryDisplayWhenNoDisplayHasTouch) {
UpdateDisplay("600x500,600x500");
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetPrimaryDisplay().touch_support());
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetSecondaryDisplay().touch_support());
EXPECT_EQ(
GetPrimaryRootWindow(),
keyboard_controller()->GetContainerForDefaultDisplay()->GetRootWindow());
}
// Test for http://crbug.com/297858. |GetContainerForDefaultDisplay| should
// move keyboard to focused display if no display has touch capability.
TEST_F(KeyboardControllerImplTest,
DefaultContainerIsInFocusedDisplayWhenNoDisplayHasTouch) {
UpdateDisplay("600x500,600x500");
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetPrimaryDisplay().touch_support());
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetSecondaryDisplay().touch_support());
CreateFocusedTestWindowInRootWindow(GetSecondaryRootWindow());
EXPECT_EQ(
GetSecondaryRootWindow(),
keyboard_controller()->GetContainerForDefaultDisplay()->GetRootWindow());
}
// Test for http://crbug.com/303429. |GetContainerForDefaultDisplay| should
// move keyboard to first touchable display when there is one.
TEST_F(KeyboardControllerImplTest, DefaultContainerIsInFirstTouchableDisplay) {
UpdateDisplay("600x500,600x500");
// Make secondary display touchable.
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetTouchSupport(GetSecondaryDisplay().id(),
display::Display::TouchSupport::AVAILABLE);
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetPrimaryDisplay().touch_support());
EXPECT_EQ(display::Display::TouchSupport::AVAILABLE,
GetSecondaryDisplay().touch_support());
EXPECT_EQ(
GetSecondaryRootWindow(),
keyboard_controller()->GetContainerForDefaultDisplay()->GetRootWindow());
}
// Test for http://crbug.com/303429. |GetContainerForDefaultDisplay| should
// move keyboard to first touchable display when the focused display is not
// touchable.
TEST_F(
KeyboardControllerImplTest,
DefaultContainerIsInFirstTouchableDisplayIfFocusedDisplayIsNotTouchable) {
UpdateDisplay("600x500,600x500");
// Make secondary display touchable.
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetTouchSupport(GetSecondaryDisplay().id(),
display::Display::TouchSupport::AVAILABLE);
EXPECT_NE(display::Display::TouchSupport::AVAILABLE,
GetPrimaryDisplay().touch_support());
EXPECT_EQ(display::Display::TouchSupport::AVAILABLE,
GetSecondaryDisplay().touch_support());
// Focus on primary display.
CreateFocusedTestWindowInRootWindow(GetPrimaryRootWindow());
EXPECT_EQ(
GetSecondaryRootWindow(),
keyboard_controller()->GetContainerForDefaultDisplay()->GetRootWindow());
}
// Test for http://crbug.com/303429. |GetContainerForDefaultDisplay| should
// move keyborad to first touchable display when there is one.
TEST_F(KeyboardControllerImplTest,
DefaultContainerIsInFocusedDisplayIfTouchable) {
UpdateDisplay("600x500,600x500");
// Make both displays touchable.
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetTouchSupport(GetPrimaryDisplay().id(),
display::Display::TouchSupport::AVAILABLE);
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetTouchSupport(GetSecondaryDisplay().id(),
display::Display::TouchSupport::AVAILABLE);
EXPECT_EQ(display::Display::TouchSupport::AVAILABLE,
GetPrimaryDisplay().touch_support());
EXPECT_EQ(display::Display::TouchSupport::AVAILABLE,
GetSecondaryDisplay().touch_support());
// Focus on secondary display.
CreateFocusedTestWindowInRootWindow(GetSecondaryRootWindow());
EXPECT_EQ(
GetSecondaryRootWindow(),
keyboard_controller()->GetContainerForDefaultDisplay()->GetRootWindow());
// Focus on primary display.
CreateFocusedTestWindowInRootWindow(GetPrimaryRootWindow());
EXPECT_EQ(
GetPrimaryRootWindow(),
keyboard_controller()->GetContainerForDefaultDisplay()->GetRootWindow());
}
// Test for https://crbug.com/897007.
TEST_F(KeyboardControllerImplTest, ShowKeyboardInSecondaryDisplay) {
UpdateDisplay("600x500,600x500");
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
// Show in secondary display.
keyboard_ui_controller()->ShowKeyboardInDisplay(GetSecondaryDisplay());
EXPECT_EQ(GetSecondaryRootWindow(),
keyboard_ui_controller()->GetRootWindow());
ASSERT_TRUE(keyboard::WaitUntilShown());
EXPECT_TRUE(
!keyboard_ui_controller()->GetKeyboardWindow()->bounds().IsEmpty());
}
TEST_F(KeyboardControllerImplTest, SwipeUpToShowHotSeat) {
TabletModeControllerTestApi().EnterTabletMode();
std::unique_ptr<aura::Window> window =
CreateTestWindow(gfx::Rect(0, 0, 400, 400));
wm::ActivateWindow(window.get());
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
keyboard_ui_controller()->ShowKeyboard(/* lock */ false);
ASSERT_TRUE(keyboard::WaitUntilShown());
gfx::Rect display_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
const gfx::Point start(display_bounds.bottom_center());
const gfx::Point end(start + gfx::Vector2d(0, -80));
const base::TimeDelta time_delta = base::Milliseconds(100);
const int num_scroll_steps = 4;
GetEventGenerator()->GestureScrollSequence(start, end, time_delta,
num_scroll_steps);
// Keyboard should hide and gesture should forward to the shelf.
ASSERT_TRUE(keyboard::WaitUntilHidden());
EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state());
}
TEST_F(KeyboardControllerImplTest, FlingUpToShowOverviewMode) {
TabletModeControllerTestApi().EnterTabletMode();
std::unique_ptr<aura::Window> window =
CreateTestWindow(gfx::Rect(0, 0, 400, 400));
wm::ActivateWindow(window.get());
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
keyboard_ui_controller()->ShowKeyboard(/* lock */ false);
ASSERT_TRUE(keyboard::WaitUntilShown());
gfx::Rect display_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
const gfx::Point start(display_bounds.bottom_center());
const gfx::Point end(start + gfx::Vector2d(0, -200));
const int fling_speed =
DragWindowFromShelfController::kVelocityToHomeScreenThreshold + 1;
const int scroll_steps = 20;
base::TimeDelta scroll_time =
GetEventGenerator()->CalculateScrollDurationForFlingVelocity(
start, end, fling_speed, scroll_steps);
GetEventGenerator()->GestureScrollSequence(start, end, scroll_time,
scroll_steps);
// Keyboard should hide and gesture should forward to the shelf.
ASSERT_TRUE(keyboard::WaitUntilHidden());
EXPECT_EQ(HotseatState::kShownHomeLauncher,
GetShelfLayoutManager()->hotseat_state());
}
TEST_F(KeyboardControllerImplTest, SwipeUpDoesntHideKeyboardInClamshellMode) {
std::unique_ptr<aura::Window> window =
CreateTestWindow(gfx::Rect(0, 0, 400, 400));
wm::ActivateWindow(window.get());
keyboard_controller()->SetEnableFlag(KeyboardEnableFlag::kExtensionEnabled);
keyboard_ui_controller()->ShowKeyboard(/* lock */ false);
ASSERT_TRUE(keyboard::WaitUntilShown());
gfx::Rect display_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
const gfx::Point start(display_bounds.bottom_center());
const gfx::Point end(start + gfx::Vector2d(0, -80));
const base::TimeDelta time_delta = base::Milliseconds(100);
const int num_scroll_steps = 4;
GetEventGenerator()->GestureScrollSequence(start, end, time_delta,
num_scroll_steps);
EXPECT_FALSE(keyboard::IsKeyboardHiding());
}
TEST_F(KeyboardControllerImplTest, RecordsKeyRepeatSettings) {
// Initially expect no user preferences recorded.
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatDelay", /*count=*/0u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatEnabled", /*count=*/0u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatInterval", /*count=*/0u);
SimulateUserLogin("user1");
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatDelay", /*count=*/1u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatEnabled", /*count=*/1u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatInterval", /*count=*/1u);
SimulateUserLogin("user2");
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatDelay", /*count=*/2u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatEnabled", /*count=*/2u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatInterval", /*count=*/2u);
SimulateUserLogin("user1");
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatDelay", /*count=*/2u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatEnabled", /*count=*/2u);
histogram_tester.ExpectTotalCount(
"ChromeOS.Settings.Device.KeyboardAutoRepeatInterval", /*count=*/2u);
}
} // namespace ash