// Copyright 2017 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/wm/lock_action_handler_layout_manager.h" #include #include #include "ash/keyboard/keyboard_controller_impl.h" #include "ash/keyboard/ui/keyboard_ui.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" #include "ash/keyboard/ui/keyboard_util.h" #include "ash/keyboard/ui/test/keyboard_test_util.h" #include "ash/lock_screen_action/lock_screen_action_background_controller.h" #include "ash/lock_screen_action/lock_screen_action_background_controller_stub.h" #include "ash/lock_screen_action/test_lock_screen_action_background_controller.h" #include "ash/public/cpp/keyboard/keyboard_switches.h" #include "ash/public/cpp/shelf_config.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/mojom/tray_action.mojom.h" #include "ash/root_window_controller.h" #include "ash/screen_util.h" #include "ash/session/test_session_controller_client.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ash/tray_action/test_tray_action_client.h" #include "ash/tray_action/tray_action.h" #include "ash/wm/window_state.h" #include "base/bind.h" #include "base/callback.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" namespace ash { namespace { aura::Window* GetContainer(ShellWindowId container_id) { return Shell::GetPrimaryRootWindowController()->GetContainer(container_id); } class TestWindowDelegate : public views::WidgetDelegate { public: TestWindowDelegate() { SetCanMaximize(true); SetCanResize(true); SetOwnedByWidget(true); SetFocusTraversesOut(true); } TestWindowDelegate(const TestWindowDelegate&) = delete; TestWindowDelegate& operator=(const TestWindowDelegate&) = delete; ~TestWindowDelegate() override = default; // views::WidgetDelegate: views::Widget* GetWidget() override { return widget_; } const views::Widget* GetWidget() const override { return widget_; } bool CanActivate() const override { return true; } void set_widget(views::Widget* widget) { widget_ = widget; } private: views::Widget* widget_ = nullptr; }; } // namespace class LockActionHandlerLayoutManagerTest : public AshTestBase { public: LockActionHandlerLayoutManagerTest() = default; LockActionHandlerLayoutManagerTest( const LockActionHandlerLayoutManagerTest&) = delete; LockActionHandlerLayoutManagerTest& operator=( const LockActionHandlerLayoutManagerTest&) = delete; ~LockActionHandlerLayoutManagerTest() override = default; void SetUp() override { // Allow a virtual keyboard (and initialize it per default). base::CommandLine::ForCurrentProcess()->AppendSwitch( keyboard::switches::kEnableVirtualKeyboard); action_background_controller_factory_ = base::BindRepeating( &LockActionHandlerLayoutManagerTest::CreateActionBackgroundController, base::Unretained(this)); LockScreenActionBackgroundController::SetFactoryCallbackForTesting( &action_background_controller_factory_); AshTestBase::SetUp(); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_FULLSCREEN; lock_window_ = CreateTestingWindow(std::move(widget_params), kShellWindowId_LockScreenContainer, std::make_unique()); } void TearDown() override { lock_window_.reset(); AshTestBase::TearDown(); LockScreenActionBackgroundController::SetFactoryCallbackForTesting(nullptr); } std::unique_ptr CreateTestingWindow( views::Widget::InitParams params, ShellWindowId parent_id, std::unique_ptr window_delegate) { params.parent = GetContainer(parent_id); views::Widget* widget = new views::Widget; if (window_delegate) { window_delegate->set_widget(widget); params.delegate = window_delegate.release(); } widget->Init(std::move(params)); widget->Show(); return base::WrapUnique(widget->GetNativeView()); } // Show or hide the keyboard. void ShowKeyboard(bool show) { auto* keyboard = keyboard::KeyboardUIController::Get(); ASSERT_TRUE(keyboard->IsEnabled()); if (show == keyboard->IsKeyboardVisible()) return; if (show) { keyboard->ShowKeyboard(true); ASSERT_TRUE(keyboard::WaitUntilShown()); } else { keyboard->HideKeyboardByUser(); } DCHECK_EQ(show, keyboard->IsKeyboardVisible()); } void SetUpTrayActionClientAndLockSession(mojom::TrayActionState state) { Shell::Get()->tray_action()->SetClient( tray_action_client_.CreateRemoteAndBind(), state); GetSessionControllerClient()->SetSessionState( session_manager::SessionState::LOCKED); } // Virtual so test specializations can override the background controller // implementation used in tests. virtual std::unique_ptr CreateActionBackgroundController() { return std::make_unique(); } private: std::unique_ptr lock_window_; LockScreenActionBackgroundController::FactoryCallback action_background_controller_factory_; TestTrayActionClient tray_action_client_; }; class LockActionHandlerLayoutManagerTestWithTestBackgroundController : public LockActionHandlerLayoutManagerTest { public: LockActionHandlerLayoutManagerTestWithTestBackgroundController() = default; LockActionHandlerLayoutManagerTestWithTestBackgroundController( const LockActionHandlerLayoutManagerTestWithTestBackgroundController&) = delete; LockActionHandlerLayoutManagerTestWithTestBackgroundController& operator=( const LockActionHandlerLayoutManagerTestWithTestBackgroundController&) = delete; ~LockActionHandlerLayoutManagerTestWithTestBackgroundController() override = default; void TearDown() override { LockActionHandlerLayoutManagerTest::TearDown(); background_controller_ = nullptr; } std::unique_ptr CreateActionBackgroundController() override { auto result = std::make_unique(); EXPECT_FALSE(background_controller_); background_controller_ = result.get(); return result; } TestLockScreenActionBackgroundController* background_controller() { return background_controller_; } private: // The lock screen action background controller created by // |CreateActionBackgroundController|. TestLockScreenActionBackgroundController* background_controller_ = nullptr; }; TEST_F(LockActionHandlerLayoutManagerTest, PreserveNormalWindowBounds) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); const gfx::Rect bounds = gfx::Rect(10, 10, 300, 300); widget_params.bounds = bounds; // Note: default window delegate (used when no widget delegate is set) does // not allow the window to be maximized. std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, nullptr /* window_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()); const gfx::Rect bounds2 = gfx::Rect(100, 100, 200, 200); window->SetBounds(bounds2); EXPECT_EQ(bounds2.ToString(), window->GetBoundsInScreen().ToString()); } TEST_F(LockActionHandlerLayoutManagerTest, MaximizedWindowBounds) { // Cange the shelf alignment before locking the session. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kRight); // This should change the shelf alignment to bottom (temporarily for locked // state). SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, std::make_unique()); // Verify that the window bounds are equal to work area for the bottom shelf // alignment, which matches how the shelf is aligned on the lock screen, gfx::Rect target_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); target_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); } TEST_F(LockActionHandlerLayoutManagerTest, FullscreenWindowBounds) { // Cange the shelf alignment before locking the session. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kRight); // This should change the shelf alignment to bottom (temporarily for locked // state). SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_FULLSCREEN; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, std::make_unique()); // Verify that the window bounds are equal to work area for the bottom shelf // alignment, which matches how the shelf is aligned on the lock screen, gfx::Rect target_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); target_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); } TEST_F(LockActionHandlerLayoutManagerTest, MaximizeResizableWindow) { GetSessionControllerClient()->SetSessionState( session_manager::SessionState::LOCKED); SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, std::make_unique()); gfx::Rect target_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); target_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); } TEST_F(LockActionHandlerLayoutManagerTest, KeyboardBounds) { gfx::Rect initial_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); initial_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, std::make_unique()); ASSERT_EQ(initial_bounds.ToString(), window->GetBoundsInScreen().ToString()); ShowKeyboard(true); gfx::Rect keyboard_bounds = keyboard::KeyboardUIController::Get()->GetVisualBoundsInScreen(); // Sanity check that the keyboard intersects with original window bounds - if // this is not true, the window bounds would remain unchanged. ASSERT_TRUE(keyboard_bounds.Intersects(initial_bounds)); gfx::Rect target_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); target_bounds.Inset(gfx::Insets().set_bottom(keyboard_bounds.height())); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); // Verify that window bounds get updated when Chromevox bounds are shown (so // the Chromevox panel does not overlay with the action handler window). ShelfLayoutManager* shelf_layout_manager = GetPrimaryShelf()->shelf_layout_manager(); ASSERT_TRUE(shelf_layout_manager); const int kAccessibilityPanelHeight = 45; std::unique_ptr widget = CreateTestWidget(nullptr, kShellWindowId_AccessibilityPanelContainer); SetAccessibilityPanelHeight(kAccessibilityPanelHeight); target_bounds.Inset(gfx::Insets().set_top(kAccessibilityPanelHeight)); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); ShowKeyboard(false); } TEST_F(LockActionHandlerLayoutManagerTest, AddingWindowInActiveState) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, nullptr /* window_delegate */); EXPECT_TRUE(window->IsVisible()); EXPECT_TRUE(window->HasFocus()); } TEST_F(LockActionHandlerLayoutManagerTest, AddingWindowInLaunchingState) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, nullptr /* window_delegate */); EXPECT_TRUE(window->IsVisible()); EXPECT_TRUE(window->HasFocus()); } TEST_F(LockActionHandlerLayoutManagerTest, AddingWindowInNonActiveState) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kAvailable); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, nullptr /* window_delegate */); // The window should not be visible if the note action is not in active state. EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(window->HasFocus()); // When the action state changes back to active, the window should be // shown. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kActive); EXPECT_EQ(GetContainer(kShellWindowId_LockActionHandlerContainer), window->parent()); EXPECT_TRUE(window->IsVisible()); EXPECT_TRUE(window->HasFocus()); // The window should be hidden again upon leaving the active state. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kAvailable); EXPECT_EQ(GetContainer(kShellWindowId_LockActionHandlerContainer), window->parent()); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(window->HasFocus()); } TEST_F(LockActionHandlerLayoutManagerTest, FocusWindowWhileInNonActiveState) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kAvailable); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, nullptr /* window_delegate */); EXPECT_EQ(GetContainer(kShellWindowId_LockActionHandlerContainer), window->parent()); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(window->HasFocus()); window->Focus(); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(window->HasFocus()); } TEST_F(LockActionHandlerLayoutManagerTest, RequestShowWindowOutsideActiveState) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kAvailable); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_MAXIMIZED; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, nullptr /* window_delegate */); EXPECT_EQ(GetContainer(kShellWindowId_LockActionHandlerContainer), window->parent()); EXPECT_FALSE(window->IsVisible()); window->Show(); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(window->HasFocus()); } TEST_F(LockActionHandlerLayoutManagerTest, MultipleMonitors) { UpdateDisplay("300x400,400x500"); aura::Window::Windows root_windows = Shell::GetAllRootWindows(); SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW); widget_params.show_state = ui::SHOW_STATE_FULLSCREEN; std::unique_ptr window = CreateTestingWindow( std::move(widget_params), kShellWindowId_LockActionHandlerContainer, std::make_unique()); gfx::Rect target_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); target_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); EXPECT_EQ(root_windows[0], window->GetRootWindow()); WindowState* window_state = WindowState::Get(window.get()); window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40)); window_state->Maximize(); // Maximize the window width as the restore bounds is inside 2nd display but // lock container windows are always on primary display. EXPECT_EQ(root_windows[0], window->GetRootWindow()); target_bounds = gfx::Rect(300, 400); target_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); window_state->Restore(); EXPECT_EQ(root_windows[0], window->GetRootWindow()); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40)); window_state->Maximize(); EXPECT_EQ(root_windows[0], window->GetRootWindow()); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); window_state->Restore(); EXPECT_EQ(root_windows[0], window->GetRootWindow()); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); window->SetBoundsInScreen(gfx::Rect(0, 0, 30, 40), GetSecondaryDisplay()); target_bounds = gfx::Rect(400, 500); target_bounds.Offset(300, 0); EXPECT_EQ(root_windows[1], window->GetRootWindow()); EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, WindowAddedWhileBackgroundShowing) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); EXPECT_EQ(LockScreenActionBackgroundState::kShowing, background_controller()->state()); EXPECT_FALSE(window->IsVisible()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); // Move action to active - this will make the app window showable. Though, // showing the window should be delayed until the background finishes // showing. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kActive); EXPECT_FALSE(window->IsVisible()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); // Finish showing the background - this should make the app window visible. ASSERT_TRUE(background_controller()->FinishShow()); EXPECT_TRUE(window->IsVisible()); EXPECT_TRUE(window->HasFocus()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); // Move action state back to available - this should hide the app window, and // request the background window to be hidden. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kAvailable); EXPECT_FALSE(window->IsVisible()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); ASSERT_TRUE(background_controller()->FinishHide()); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(background_controller()->GetWindow()->IsVisible()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, WindowAddedWhenBackgroundShown) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kActive); // Finish showing the background - this should make the app window visible // once it's created. ASSERT_TRUE(background_controller()->FinishShow()); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); EXPECT_TRUE(window->IsVisible()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); EXPECT_EQ(LockScreenActionBackgroundState::kShown, background_controller()->state()); // Move action state back to not available - this should immediately hide both // the app and background window, Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kNotAvailable); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(background_controller()->GetWindow()->IsVisible()); EXPECT_EQ(LockScreenActionBackgroundState::kHidden, background_controller()->state()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, SecondWindowAddedWhileShowingBackground) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); EXPECT_EQ(LockScreenActionBackgroundState::kShowing, background_controller()->state()); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); EXPECT_FALSE(window->IsVisible()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); std::unique_ptr second_window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(second_window->IsVisible()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); // Finish showing the background. The app windows should be shown at this // point. ASSERT_TRUE(background_controller()->FinishShow()); EXPECT_TRUE(window->IsVisible()); EXPECT_FALSE(window->HasFocus()); EXPECT_TRUE(second_window->IsVisible()); EXPECT_TRUE(second_window->HasFocus()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); // Deactivate the action - the windows should get hidden. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kAvailable); EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(second_window->IsVisible()); EXPECT_EQ(LockScreenActionBackgroundState::kHiding, background_controller()->state()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, RelaunchWhileHidingBackground) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); ASSERT_TRUE(background_controller()->FinishShow()); Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kAvailable); ASSERT_EQ(LockScreenActionBackgroundState::kHiding, background_controller()->state()); // Create new app window to show. window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kActive); ASSERT_EQ(LockScreenActionBackgroundState::kShowing, background_controller()->state()); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); EXPECT_FALSE(window->IsVisible()); background_controller()->FinishShow(); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); EXPECT_TRUE(window->IsVisible()); EXPECT_TRUE(window->HasFocus()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, ActionDeactivatedWhileShowingTheBackground) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); // Lock screen note action was launched, so the background window is expected // to start being shown. EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); EXPECT_FALSE(window->IsVisible()); EXPECT_EQ(LockScreenActionBackgroundState::kShowing, background_controller()->state()); // Move lock screen note action back to available state (i.e. not activated // state), and test that the background window starts hiding. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kAvailable); EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); EXPECT_FALSE(window->IsVisible()); EXPECT_EQ(LockScreenActionBackgroundState::kHiding, background_controller()->state()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, ActionDisabledWhileShowingTheBackground) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kLaunching); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); // Lock screen note action was launched, so the background window is expected // to start being shown. EXPECT_TRUE(background_controller()->GetWindow()->IsVisible()); EXPECT_FALSE(window->IsVisible()); EXPECT_EQ(LockScreenActionBackgroundState::kShowing, background_controller()->state()); // Make lock screen note action unavailable, and test that the background // window is hidden immediately. Shell::Get()->tray_action()->UpdateLockScreenNoteState( mojom::TrayActionState::kNotAvailable); EXPECT_FALSE(background_controller()->GetWindow()->IsVisible()); EXPECT_FALSE(window->IsVisible()); } TEST_F(LockActionHandlerLayoutManagerTestWithTestBackgroundController, BackgroundWindowBounds) { SetUpTrayActionClientAndLockSession(mojom::TrayActionState::kActive); ASSERT_TRUE(background_controller()->FinishShow()); std::unique_ptr window = CreateTestingWindow( views::Widget::InitParams(views::Widget::InitParams::TYPE_WINDOW), kShellWindowId_LockActionHandlerContainer, std::make_unique()); ASSERT_TRUE(window->IsVisible()); ASSERT_TRUE(background_controller()->GetWindow()->IsVisible()); // Verify that the window bounds are equal to work area for the bottom shelf // alignment, which matches how the shelf is aligned on the lock screen, gfx::Rect target_app_window_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); target_app_window_bounds.Inset( gfx::Insets().set_bottom(ShelfConfig::Get()->shelf_size())); EXPECT_EQ(target_app_window_bounds, window->GetBoundsInScreen()); EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().bounds(), background_controller()->GetWindow()->GetBoundsInScreen()); } } // namespace ash