#include "ash/wm/multi_display/persistent_window_controller.h"
#include "ash/display/display_move_window_util.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/screen_util.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ui/base/display_util.h"
#include "chromeos/ui/base/window_state_type.h"
#include "ui/display/test/display_manager_test_api.h"
using session_manager::SessionState;
namespace ash {
using PersistentWindowControllerTest = AshTestBase;
display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
const gfx::Rect& bounds) {
display::ManagedDisplayInfo info = display::CreateDisplayInfo(id, bounds);
display::ManagedDisplayMode mode(bounds.size(), 60.f,
true,
true);
info.SetManagedDisplayModes({mode});
return info;
}
TEST_F(PersistentWindowControllerTest, DisconnectDisplay) {
UpdateDisplay("500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
const int64_t third_id = secondary_id + 1;
display::ManagedDisplayInfo third_info =
CreateDisplayInfo(third_id, gfx::Rect(0, 501, 600, 500));
display_info_list.push_back(third_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(primary_info);
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
WindowState* w2_state = WindowState::Get(w2);
w2_state->SetBoundsChangedByUser(true);
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, ThreeDisplays) {
UpdateDisplay("500x600,500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
aura::Window* w3 =
CreateTestWindowInShell({.bounds = {1002, 0, 400, 200}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1002, 0, 400, 200), w3->GetBoundsInScreen());
const int64_t primary_id = display_manager()->GetDisplayAt(0).id();
const int64_t second_id = display_manager()->GetDisplayAt(1).id();
const int64_t third_id = display_manager()->GetDisplayAt(2).id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo second_info =
display_manager()->GetDisplayInfo(second_id);
display::ManagedDisplayInfo third_info =
display_manager()->GetDisplayInfo(third_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_info_list.push_back(second_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(2, 0, 400, 200), w3->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(2, 0, 400, 200), w3->GetBoundsInScreen());
display_info_list.push_back(third_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(502, 0, 400, 200), w3->GetBoundsInScreen());
display_info_list.push_back(second_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1002, 0, 400, 200), w3->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(2, 0, 400, 200), w3->GetBoundsInScreen());
display_info_list.push_back(second_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(2, 0, 400, 200), w3->GetBoundsInScreen());
display_info_list.push_back(third_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1002, 0, 400, 200), w3->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, NormalMirrorMode) {
UpdateDisplay("500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest,
MirrorDisplayWithNonIdenticalScaleFactor) {
UpdateDisplay("500x600,500x600*1.2");
ASSERT_EQ(1.2f, display_manager()->GetDisplayAt(1).device_scale_factor());
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_EQ(1.2f, display_manager()->GetDisplayAt(1).device_scale_factor());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, MixedMirrorMode) {
UpdateDisplay("500x600,500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
aura::Window* w3 =
CreateTestWindowInShell({.bounds = {1002, 0, 400, 200}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1002, 0, 400, 200), w3->GetBoundsInScreen());
const int64_t primary_id = display_manager()->GetDisplayAt(0).id();
const int64_t second_id = display_manager()->GetDisplayAt(1).id();
const int64_t third_id = display_manager()->GetDisplayAt(2).id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo second_info =
display_manager()->GetDisplayInfo(second_id);
display::ManagedDisplayInfo third_info =
display_manager()->GetDisplayInfo(third_id);
display::DisplayIdList dst_ids;
dst_ids.emplace_back(second_id);
display_manager()->SetMirrorMode(
display::MirrorMode::kMixed,
std::make_optional<display::MixedMirrorModeParams>(primary_id, dst_ids));
EXPECT_TRUE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(display_manager()->mixed_mirror_mode_params());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(502, 0, 400, 200), w3->GetBoundsInScreen());
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_FALSE(display_manager()->mixed_mirror_mode_params());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1002, 0, 400, 200), w3->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, WindowMovedByAccel) {
UpdateDisplay("500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
wm::ActivateWindow(w2);
display_move_window_util::HandleMoveActiveWindowBetweenDisplays();
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.clear();
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, ReconnectOnLockScreen) {
UpdateDisplay("500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
GetSessionControllerClient()->SetSessionState(SessionState::LOCKED);
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
GetSessionControllerClient()->SetSessionState(SessionState::ACTIVE);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, RecordNumOfWindowsRestored) {
UpdateDisplay("500x600,500x600");
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(
PersistentWindowController::kNumOfWindowsRestoredOnDisplayAdded, 0);
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
histogram_tester.ExpectTotalCount(
PersistentWindowController::kNumOfWindowsRestoredOnDisplayAdded, 1);
}
TEST_F(PersistentWindowControllerTest, SwapPrimaryDisplay) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
const display::ManagedDisplayInfo native_display_info =
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 500, 600));
const display::ManagedDisplayInfo secondary_display_info =
CreateDisplayInfo(10, gfx::Rect(1, 1, 400, 500));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(native_display_info);
display_info_list.push_back(secondary_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 0, 100, 200}, .window_id = 0});
aura::Window* w2 =
CreateTestWindowInShell({.bounds = {501, 0, 200, 100}, .window_id = 0});
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(501, 0, 200, 100), w2->GetBoundsInScreen());
SwapPrimaryDisplay();
ASSERT_EQ(gfx::Rect(-500, 0, 500, 600),
display_manager()->GetDisplayForId(internal_display_id).bounds());
ASSERT_EQ(gfx::Rect(0, 0, 400, 500),
display_manager()->GetDisplayForId(10).bounds());
EXPECT_EQ(gfx::Rect(200, 0, 100, 200), w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(-499, 0, 200, 100), w2->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, RestoreBounds) {
UpdateDisplay("500x600,500x600");
std::unique_ptr<aura::Window> window = CreateTestWindow(gfx::Rect(200, 200));
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::Screen* screen = display::Screen::Get();
ASSERT_EQ(primary_id, screen->GetDisplayNearestWindow(window.get()).id());
display_move_window_util::HandleMoveActiveWindowBetweenDisplays();
ASSERT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
WindowState* window_state = WindowState::Get(window.get());
window_state->Maximize();
EXPECT_TRUE(window_state->HasRestoreBounds());
const gfx::Rect restore_bounds_in_screen =
window_state->GetRestoreBoundsInScreen();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(primary_id, screen->GetDisplayNearestWindow(window.get()).id());
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
EXPECT_TRUE(window_state->IsMaximized());
window_state->Restore();
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_EQ(restore_bounds_in_screen, window->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, RestoreBoundsOnInternalDisplayRemoval) {
UpdateDisplay("500x600,500x700");
std::unique_ptr<aura::Window> window = CreateTestWindow(gfx::Rect(200, 100));
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::Screen* screen = display::Screen::Get();
ASSERT_EQ(primary_id, screen->GetDisplayNearestWindow(window.get()).id());
display_move_window_util::HandleMoveActiveWindowBetweenDisplays();
WindowState* window_state = WindowState::Get(window.get());
const WindowSnapWMEvent snap_primary(WM_EVENT_SNAP_PRIMARY);
window_state->OnWMEvent(&snap_primary);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_TRUE(window_state->HasRestoreBounds());
const gfx::Rect restore_bounds_in_screen =
window_state->GetRestoreBoundsInScreen();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_EQ(restore_bounds_in_screen, window_state->GetRestoreBoundsInScreen());
window_state->Maximize();
ASSERT_TRUE(window_state->IsMaximized());
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
window_state->Restore();
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
window_state->Restore();
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
}
TEST_F(PersistentWindowControllerTest, MRUOrderMatchesStacking) {
UpdateDisplay("500x600,500x600");
const gfx::Rect bounds(500, 0, 200, 200);
std::unique_ptr<aura::Window> window1 = CreateTestWindow(bounds);
std::unique_ptr<aura::Window> window2 = CreateTestWindow(bounds);
std::unique_ptr<aura::Window> window3 = CreateTestWindow(bounds);
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::Screen* screen = display::Screen::Get();
const std::vector<raw_ptr<aura::Window, VectorExperimental>>
expected_mru_order = {window3.get(), window2.get(), window1.get()};
ASSERT_EQ(
expected_mru_order,
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks));
for (aura::Window* window : expected_mru_order) {
ASSERT_EQ(secondary_id, screen->GetDisplayNearestWindow(window).id());
}
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
aura::Window* parent = window1->parent();
ASSERT_TRUE(parent);
std::vector<raw_ptr<aura::Window, VectorExperimental>>
children_ordered_by_stacking = parent->children();
std::ranges::reverse(children_ordered_by_stacking);
EXPECT_EQ(
expected_mru_order,
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks));
EXPECT_EQ(expected_mru_order, children_ordered_by_stacking);
EXPECT_EQ(primary_id, screen->GetDisplayNearestWindow(parent).id());
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
parent = window1->parent();
children_ordered_by_stacking = parent->children();
std::ranges::reverse(children_ordered_by_stacking);
ASSERT_TRUE(parent);
EXPECT_EQ(
expected_mru_order,
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks));
EXPECT_EQ(expected_mru_order, children_ordered_by_stacking);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(parent).id());
}
TEST_F(PersistentWindowControllerTest, MRUOrderMatchesStackingInterleaved) {
UpdateDisplay("500x600,500x600");
const gfx::Rect primary_bounds(200, 200);
const gfx::Rect secondary_bounds(500, 0, 200, 200);
std::unique_ptr<aura::Window> window1 = CreateTestWindow(primary_bounds);
std::unique_ptr<aura::Window> window2 = CreateTestWindow(secondary_bounds);
std::unique_ptr<aura::Window> window3 = CreateTestWindow(primary_bounds);
std::unique_ptr<aura::Window> window4 = CreateTestWindow(secondary_bounds);
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::Screen* screen = display::Screen::Get();
const std::vector<raw_ptr<aura::Window, VectorExperimental>>
expected_mru_order = {window4.get(), window3.get(), window2.get(),
window1.get()};
ASSERT_EQ(
expected_mru_order,
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks));
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
aura::Window* parent = window1->parent();
ASSERT_TRUE(parent);
ASSERT_EQ(parent, window2->parent());
std::vector<raw_ptr<aura::Window, VectorExperimental>>
children_ordered_by_stacking = parent->children();
std::ranges::reverse(children_ordered_by_stacking);
EXPECT_EQ(
expected_mru_order,
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks));
EXPECT_EQ(expected_mru_order, children_ordered_by_stacking);
EXPECT_EQ(primary_id, screen->GetDisplayNearestWindow(parent).id());
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(
expected_mru_order,
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks));
parent = window1->parent();
EXPECT_EQ(primary_id, screen->GetDisplayNearestWindow(parent).id());
ASSERT_EQ(2u, parent->children().size());
EXPECT_EQ(window1.get(), parent->children()[0]);
EXPECT_EQ(window3.get(), parent->children()[1]);
parent = window2->parent();
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(parent).id());
ASSERT_EQ(2u, parent->children().size());
EXPECT_EQ(window2.get(), parent->children()[0]);
EXPECT_EQ(window4.get(), parent->children()[1]);
}
TEST_F(PersistentWindowControllerTest, DisconnectingPrimaryDisplay) {
UpdateDisplay("500x600,1500x500");
const int64_t small_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t large_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(large_id);
ASSERT_EQ(large_id, WindowTreeHostManager::GetPrimaryDisplayId());
const gfx::Rect bounds(0, 200, 1500, 200);
std::unique_ptr<aura::Window> window = CreateTestWindow(bounds);
display::ManagedDisplayInfo small_info =
display_manager()->GetDisplayInfo(small_id);
display::ManagedDisplayInfo large_info =
display_manager()->GetDisplayInfo(large_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(small_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(small_id, WindowTreeHostManager::GetPrimaryDisplayId());
EXPECT_EQ(gfx::Size(500, 200), window->bounds().size());
display_info_list.push_back(large_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(large_id, WindowTreeHostManager::GetPrimaryDisplayId());
EXPECT_EQ(gfx::Size(1500, 200), window->bounds().size());
}
TEST_F(PersistentWindowControllerTest, RestoreBoundsOnScreenRotation) {
UpdateDisplay("800x600");
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
gfx::Rect bounds_in_landscape = gfx::Rect(420, 200, 200, 100);
aura::Window* w1 =
CreateTestWindowInShell({.bounds = bounds_in_landscape, .window_id = 0});
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
EXPECT_EQ(bounds_in_landscape, w1->GetBoundsInScreen());
base::HistogramTester histogram_tester;
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
gfx::Rect bounds_in_portrait = w1->GetBoundsInScreen();
EXPECT_NE(bounds_in_landscape, bounds_in_portrait);
EXPECT_TRUE(GetPrimaryDisplay().bounds().Contains(bounds_in_portrait));
histogram_tester.ExpectTotalCount(
PersistentWindowController::kNumOfWindowsRestoredOnScreenRotation, 0);
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
EXPECT_EQ(bounds_in_landscape, w1->GetBoundsInScreen());
histogram_tester.ExpectTotalCount(
PersistentWindowController::kNumOfWindowsRestoredOnScreenRotation, 1);
auto* window_state = WindowState::Get(w1);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
EXPECT_EQ(bounds_in_portrait, w1->GetBoundsInScreen());
w1->SetBounds(gfx::Rect(
gfx::Point(bounds_in_portrait.x() - 100, bounds_in_portrait.y() - 100),
bounds_in_portrait.size()));
window_state->SetBoundsChangedByUser(true);
bounds_in_portrait = w1->GetBoundsInScreen();
EXPECT_FALSE(window_state->persistent_window_info_of_screen_rotation());
test_api.SetDisplayRotation(display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapeSecondary);
EXPECT_NE(bounds_in_landscape, w1->GetBoundsInScreen());
bounds_in_landscape = w1->GetBoundsInScreen();
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitSecondary);
EXPECT_EQ(bounds_in_portrait, w1->GetBoundsInScreen());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
EXPECT_EQ(bounds_in_landscape, w1->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, RotationOnLockScreen) {
UpdateDisplay("800x600");
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
const gfx::Rect bounds_in_landscape = gfx::Rect(420, 200, 200, 100);
aura::Window* w1 =
CreateTestWindowInShell({.bounds = bounds_in_landscape, .window_id = 0});
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
EXPECT_EQ(bounds_in_landscape, w1->GetBoundsInScreen());
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
GetSessionControllerClient()->SetSessionState(SessionState::LOCKED);
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
GetSessionControllerClient()->SetSessionState(SessionState::ACTIVE);
EXPECT_EQ(bounds_in_landscape, w1->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, RotationOnDisplayReconnecting) {
UpdateDisplay("500x600,500x600");
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
const gfx::Rect w1_bounds_in_landscape = gfx::Rect(200, 0, 100, 200);
const gfx::Rect w2_bounds_in_second_display = gfx::Rect(501, 0, 200, 100);
aura::Window* w1 = CreateTestWindowInShell(
{.bounds = w1_bounds_in_landscape, .window_id = 0});
aura::Window* w2 = CreateTestWindowInShell(
{.bounds = w2_bounds_in_second_display, .window_id = 0});
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(w1_bounds_in_landscape, w1->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(1, 0, 200, 100), w2->GetBoundsInScreen());
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(w2_bounds_in_second_display, w2->GetBoundsInScreen());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(w1_bounds_in_landscape, w1->GetBoundsInScreen());
}
TEST_F(PersistentWindowControllerTest, NoRestoreOnRotationForSnappedWindows) {
UpdateDisplay("800x600");
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
aura::Window* w1 =
CreateTestWindowInShell({.bounds = {200, 200}, .window_id = 0});
auto* split_view_controller =
SplitViewController::Get(Shell::GetPrimaryRootWindow());
WindowSnapWMEvent primary_snap_event(WM_EVENT_SNAP_PRIMARY);
auto* window_state = WindowState::Get(w1);
window_state->OnWMEvent(&primary_snap_event);
EXPECT_FALSE(split_view_controller->InSplitViewMode());
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
window_state->GetStateType());
const gfx::Rect bounds_in_landscape_primary = w1->GetBoundsInScreen();
EXPECT_EQ(0, bounds_in_landscape_primary.x());
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitSecondary);
EXPECT_FALSE(window_state->persistent_window_info_of_screen_rotation());
test_api.SetDisplayRotation(display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapeSecondary);
EXPECT_FALSE(window_state->persistent_window_info_of_screen_rotation());
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
window_state->GetStateType());
const gfx::Rect bounds_in_landscape_secondary = w1->GetBoundsInScreen();
EXPECT_NE(bounds_in_landscape_primary, bounds_in_landscape_secondary);
EXPECT_NE(0, bounds_in_landscape_secondary.x());
EXPECT_EQ(
bounds_in_landscape_secondary.right(),
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(w1)
.right());
}
TEST_F(PersistentWindowControllerTest, WindowStateChangeInSamePhysicalDisplay) {
UpdateDisplay("500x600,500x700");
std::unique_ptr<aura::Window> window =
CreateTestWindow(gfx::Rect(501, 0, 200, 100));
WindowState* window_state = WindowState::Get(window.get());
window_state->Maximize();
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
display::Screen* screen = display::Screen::Get();
ASSERT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
ASSERT_TRUE(window_state->HasRestoreBounds());
const gfx::Rect maximized_bounds = window->GetBoundsInScreen();
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
window_state->Restore();
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_FALSE(window_state->HasRestoreBounds());
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(window.get()).id());
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_FALSE(window_state->HasRestoreBounds());
EXPECT_NE(window->GetBoundsInScreen(), maximized_bounds);
}
}