blob: 58c95a0dda77711c3d04e6cbe15ac437179c7aa3 [file] [log] [blame]
// 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/public/cpp/shell_window_ids.h"
#include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/window_state.h"
#include "base/command_line.h"
#include "services/ws/public/mojom/window_tree_constants.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/keyboard_switches.h"
#include "ui/keyboard/keyboard_ui.h"
#include "ui/keyboard/keyboard_util.h"
#include "ui/keyboard/test/keyboard_test_util.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
namespace {
const int kVirtualKeyboardHeight = 100;
// A login implementation of WidgetDelegate.
class LoginTestWidgetDelegate : public views::WidgetDelegate {
public:
explicit LoginTestWidgetDelegate(views::Widget* widget) : widget_(widget) {}
~LoginTestWidgetDelegate() override = default;
// Overridden from WidgetDelegate:
void DeleteDelegate() override { delete this; }
views::Widget* GetWidget() override { return widget_; }
const views::Widget* GetWidget() const override { return widget_; }
bool CanActivate() const override { return true; }
bool ShouldAdvanceFocusToTopLevelWidget() const override { return true; }
private:
views::Widget* widget_;
DISALLOW_COPY_AND_ASSIGN(LoginTestWidgetDelegate);
};
} // namespace
class LockLayoutManagerTest : public AshTestBase {
public:
void SetUp() override {
// Allow a virtual keyboard (and initialize it per default).
base::CommandLine::ForCurrentProcess()->AppendSwitch(
keyboard::switches::kEnableVirtualKeyboard);
AshTestBase::SetUp();
Shell::GetPrimaryRootWindowController()->ActivateKeyboard(
keyboard::KeyboardController::Get());
}
void TearDown() override {
Shell::GetPrimaryRootWindowController()->DeactivateKeyboard(
keyboard::KeyboardController::Get());
AshTestBase::TearDown();
}
aura::Window* CreateTestLoginWindow(views::Widget::InitParams params,
bool use_delegate) {
aura::Window* parent =
Shell::GetPrimaryRootWindowController()->GetContainer(
ash::kShellWindowId_LockScreenContainer);
params.parent = parent;
views::Widget* widget = new views::Widget;
if (use_delegate)
params.delegate = new LoginTestWidgetDelegate(widget);
params.context = CurrentContext();
widget->Init(params);
widget->Show();
aura::Window* window = widget->GetNativeView();
return window;
}
// Show or hide the keyboard.
void ShowKeyboard(bool show) {
auto* keyboard = keyboard::KeyboardController::Get();
ASSERT_TRUE(keyboard->IsEnabled());
if (show == keyboard->IsKeyboardVisible())
return;
if (show) {
keyboard->ShowKeyboard(false);
if (keyboard->GetKeyboardWindow()->bounds().height() == 0) {
keyboard->GetKeyboardWindow()->SetBounds(
keyboard::KeyboardBoundsFromRootBounds(
Shell::GetPrimaryRootWindow()->bounds(),
kVirtualKeyboardHeight));
keyboard->NotifyKeyboardWindowLoaded();
}
} else {
keyboard->HideKeyboardByUser();
}
DCHECK_EQ(show, keyboard->IsKeyboardVisible());
}
void SetKeyboardOverscrollBehavior(
keyboard::mojom::KeyboardOverscrollBehavior overscroll_behavior) {
auto config = keyboard::KeyboardController::Get()->keyboard_config();
config.overscroll_behavior = overscroll_behavior;
keyboard::KeyboardController::Get()->UpdateKeyboardConfig(config);
}
};
TEST_F(LockLayoutManagerTest, NorwmalWindowBoundsArePreserved) {
gfx::Rect screen_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW);
const gfx::Rect bounds = gfx::Rect(10, 10, 300, 300);
widget_params.bounds = bounds;
std::unique_ptr<aura::Window> window(
CreateTestLoginWindow(widget_params, false /* use_delegate */));
EXPECT_EQ(bounds.ToString(), window->GetBoundsInScreen().ToString());
gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(window.get());
window->SetBounds(work_area);
EXPECT_EQ(work_area.ToString(), window->GetBoundsInScreen().ToString());
EXPECT_NE(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
const gfx::Rect bounds2 = gfx::Rect(100, 100, 200, 200);
window->SetBounds(bounds2);
EXPECT_EQ(bounds2.ToString(), window->GetBoundsInScreen().ToString());
}
TEST_F(LockLayoutManagerTest, MaximizedFullscreenWindowBoundsAreEqualToScreen) {
gfx::Rect screen_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget_params.show_state = ui::SHOW_STATE_MAXIMIZED;
const gfx::Rect bounds = gfx::Rect(10, 10, 300, 300);
widget_params.bounds = bounds;
// Maximized TYPE_WINDOW_FRAMELESS windows needs a delegate defined otherwise
// it won't get initial SetBounds event.
std::unique_ptr<aura::Window> maximized_window(
CreateTestLoginWindow(widget_params, true /* use_delegate */));
widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
widget_params.delegate = NULL;
std::unique_ptr<aura::Window> fullscreen_window(
CreateTestLoginWindow(widget_params, false /* use_delegate */));
EXPECT_EQ(screen_bounds.ToString(),
maximized_window->GetBoundsInScreen().ToString());
EXPECT_EQ(screen_bounds.ToString(),
fullscreen_window->GetBoundsInScreen().ToString());
gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(maximized_window.get());
maximized_window->SetBounds(work_area);
EXPECT_NE(work_area.ToString(),
maximized_window->GetBoundsInScreen().ToString());
EXPECT_EQ(screen_bounds.ToString(),
maximized_window->GetBoundsInScreen().ToString());
work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(fullscreen_window.get());
fullscreen_window->SetBounds(work_area);
EXPECT_NE(work_area.ToString(),
fullscreen_window->GetBoundsInScreen().ToString());
EXPECT_EQ(screen_bounds.ToString(),
fullscreen_window->GetBoundsInScreen().ToString());
const gfx::Rect bounds2 = gfx::Rect(100, 100, 200, 200);
maximized_window->SetBounds(bounds2);
fullscreen_window->SetBounds(bounds2);
EXPECT_EQ(screen_bounds.ToString(),
maximized_window->GetBoundsInScreen().ToString());
EXPECT_EQ(screen_bounds.ToString(),
fullscreen_window->GetBoundsInScreen().ToString());
}
TEST_F(LockLayoutManagerTest, AccessibilityPanel) {
ash::ShelfLayoutManager* shelf_layout_manager =
GetPrimaryShelf()->shelf_layout_manager();
ASSERT_TRUE(shelf_layout_manager);
// Set the accessibility panel height.
int accessibility_panel_height = 45;
shelf_layout_manager->SetAccessibilityPanelHeight(accessibility_panel_height);
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
std::unique_ptr<aura::Window> window(
CreateTestLoginWindow(widget_params, false /* use_delegate */));
display::Display primary_display =
display::Screen::GetScreen()->GetPrimaryDisplay();
gfx::Rect target_bounds = primary_display.bounds();
target_bounds.Inset(0 /* left */, accessibility_panel_height /* top */,
0 /* right */, 0 /* bottom */);
EXPECT_EQ(target_bounds, window->GetBoundsInScreen());
// Update the accessibility panel height, and verify the window bounds are
// updated accordingly.
accessibility_panel_height = 25;
shelf_layout_manager->SetAccessibilityPanelHeight(accessibility_panel_height);
target_bounds = primary_display.bounds();
target_bounds.Inset(0 /* left */, accessibility_panel_height /* top */,
0 /* right */, 0 /* bottom */);
EXPECT_EQ(target_bounds, window->GetBoundsInScreen());
}
TEST_F(LockLayoutManagerTest, KeyboardBounds) {
display::Display primary_display =
display::Screen::GetScreen()->GetPrimaryDisplay();
gfx::Rect screen_bounds = primary_display.bounds();
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
std::unique_ptr<aura::Window> window(
CreateTestLoginWindow(widget_params, false /* use_delegate */));
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
// When virtual keyboard overscroll is enabled keyboard bounds should not
// affect window bounds.
keyboard::KeyboardController* keyboard = keyboard::KeyboardController::Get();
SetKeyboardOverscrollBehavior(
keyboard::mojom::KeyboardOverscrollBehavior::kEnabled);
ShowKeyboard(true);
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
gfx::Rect keyboard_bounds = keyboard->visual_bounds_in_screen();
EXPECT_NE(keyboard_bounds, gfx::Rect());
ShowKeyboard(false);
// When keyboard is hidden make sure that rotating the screen gives 100% of
// screen size to window.
// Repro steps for http://crbug.com/401667:
// 1. Set up login screen defaults: VK overscroll disabled
// 2. Show/hide keyboard, make sure that no stale keyboard bounds are cached.
SetKeyboardOverscrollBehavior(
keyboard::mojom::KeyboardOverscrollBehavior::kDisabled);
ShowKeyboard(true);
ShowKeyboard(false);
display_manager()->SetDisplayRotation(
primary_display.id(), display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
primary_display = display::Screen::GetScreen()->GetPrimaryDisplay();
screen_bounds = primary_display.bounds();
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
display_manager()->SetDisplayRotation(
primary_display.id(), display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
// When virtual keyboard overscroll is disabled keyboard bounds do
// affect window bounds.
SetKeyboardOverscrollBehavior(
keyboard::mojom::KeyboardOverscrollBehavior::kDisabled);
ShowKeyboard(true);
primary_display = display::Screen::GetScreen()->GetPrimaryDisplay();
screen_bounds = primary_display.bounds();
gfx::Rect target_bounds(screen_bounds);
target_bounds.set_height(target_bounds.height() -
keyboard->GetKeyboardWindow()->bounds().height());
EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString());
ShowKeyboard(false);
SetKeyboardOverscrollBehavior(
keyboard::mojom::KeyboardOverscrollBehavior::kDefault);
keyboard->SetContainerType(keyboard::ContainerType::FLOATING,
base::nullopt /* target_bounds */,
base::BindOnce([](bool success) {}));
ShowKeyboard(true);
primary_display = display::Screen::GetScreen()->GetPrimaryDisplay();
screen_bounds = primary_display.bounds();
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
ShowKeyboard(false);
}
TEST_F(LockLayoutManagerTest, MultipleMonitors) {
UpdateDisplay("300x400,400x500");
gfx::Rect screen_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
std::unique_ptr<aura::Window> window(
CreateTestLoginWindow(widget_params, false /* use_delegate */));
window->SetProperty(aura::client::kResizeBehaviorKey,
ws::mojom::kResizeBehaviorCanMaximize);
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
EXPECT_EQ(root_windows[0], window->GetRootWindow());
wm::WindowState* window_state = wm::GetWindowState(window.get());
window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
// Maximize the window with as the restore bounds is inside 2nd display but
// lock container windows are always on primary display.
window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
EXPECT_EQ(root_windows[0], window->GetRootWindow());
EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
window_state->Restore();
EXPECT_EQ(root_windows[0], window->GetRootWindow());
EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40));
window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
EXPECT_EQ(root_windows[0], window->GetRootWindow());
EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
window_state->Restore();
EXPECT_EQ(root_windows[0], window->GetRootWindow());
EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(window.get());
window->SetBounds(work_area);
// Usually work_area takes Shelf into account but that doesn't affect
// LockScreen container windows.
EXPECT_NE(work_area.ToString(), window->GetBoundsInScreen().ToString());
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
}
TEST_F(LockLayoutManagerTest, AccessibilityPanelWithMultipleMonitors) {
UpdateDisplay("300x400,400x500");
ash::ShelfLayoutManager* shelf_layout_manager =
GetPrimaryShelf()->shelf_layout_manager();
ASSERT_TRUE(shelf_layout_manager);
int accessibility_panel_height = 45;
shelf_layout_manager->SetAccessibilityPanelHeight(accessibility_panel_height);
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
std::unique_ptr<aura::Window> window(
CreateTestLoginWindow(widget_params, false /* use_delegate */));
window->SetProperty(aura::client::kResizeBehaviorKey,
ws::mojom::kResizeBehaviorCanMaximize);
gfx::Rect target_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
target_bounds.Inset(0, accessibility_panel_height, 0, 0);
EXPECT_EQ(target_bounds, window->GetBoundsInScreen());
// Restore window with bounds in the second display, the window should be
// shown in the primary display.
wm::WindowState* window_state = wm::GetWindowState(window.get());
window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
window_state->Restore();
EXPECT_EQ(root_windows[0], window->GetRootWindow());
EXPECT_EQ(target_bounds, window->GetBoundsInScreen());
// Force the window to secondary display - accessibility panel is only set
// for the primary shelf, so it should not influence the screen bounds.
window->SetBoundsInScreen(gfx::Rect(0, 0, 30, 40), GetSecondaryDisplay());
target_bounds = gfx::Rect(300, 0, 400, 500);
EXPECT_EQ(root_windows[1], window->GetRootWindow());
EXPECT_EQ(target_bounds, window->GetBoundsInScreen());
}
} // namespace ash