blob: 94aed62cb0414d6143cff5addd871d1cc5c20bc5 [file] [log] [blame]
// Copyright 2021 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 "base/command_line.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/permissions/permission_request_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "third_party/blink/public/common/switches.h"
#include "ui/display/screen_base.h"
#include "ui/display/test/scoped_screen_override.h"
#include "ui/display/test/test_screen.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/shell.h"
#include "ui/display/test/display_manager_test_api.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
class WindowPlacementTest : public InProcessBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kEnableBlinkFeatures, "WindowPlacement");
InProcessBrowserTest::SetUpCommandLine(command_line);
}
};
// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
// SetScreenInstance and observers not being notified.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_OnScreensChangeEvent DISABLED_OnScreensChangeEvent
#else
#define MAYBE_OnScreensChangeEvent OnScreensChangeEvent
#endif
IN_PROC_BROWSER_TEST_F(WindowPlacementTest, MAYBE_OnScreensChangeEvent) {
// Updates the display configuration to add a secondary display.
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+1-801x802");
#else
display::ScreenBase screen;
screen.display_list().AddDisplay({1, gfx::Rect(100, 1, 801, 802)},
display::DisplayList::Type::PRIMARY);
display::test::ScopedScreenOverride screen_override(&screen);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/simple.html"));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
// TODO(crbug.com/1119974): this test could be in content_browsertests
// and not browser_tests if permission controls were supported.
// Auto-accept the Window Placement permission request.
permissions::PermissionRequestManager* permission_request_manager =
permissions::PermissionRequestManager::FromWebContents(tab);
permission_request_manager->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
auto initial_result = std::vector<base::Value>();
initial_result.emplace_back(801);
ASSERT_EQ(initial_result, EvalJs(tab, R"(
var screensInterface;
var promiseForEvent = (target, evt) => {
return new Promise((resolve) => {
const handler = (e) => {
target.removeEventListener(evt, handler);
resolve(e);
};
target.addEventListener(evt, handler);
});
}
var makeScreensChangePromise = () => {
return promiseForEvent(screensInterface, 'screenschange');
};
var getScreenWidths = () => {
return screensInterface.screens.map((d) => d.width).sort();
};
(async () => {
screensInterface = await self.getScreens();
return getScreenWidths();
})();
)"));
// Add a second display.
EXPECT_TRUE(
ExecJs(tab, R"(var screensChange = makeScreensChangePromise();)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-801x802,901+100-802x802");
#else
screen.display_list().AddDisplay({2, gfx::Rect(901, 100, 802, 802)},
display::DisplayList::Type::PRIMARY);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
{
auto result = std::vector<base::Value>();
result.emplace_back(801);
result.emplace_back(802);
EXPECT_EQ(result, EvalJs(tab, R"(
(async () => {
await screensChange;
return getScreenWidths();
})();
)"));
}
// Remove the first display.
EXPECT_TRUE(
ExecJs(tab, R"(var screensChange = makeScreensChangePromise();)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("901+100-802x802");
#else
// Make the second display primary so we can remove the first.
EXPECT_EQ(screen.display_list().displays().size(), 2u);
screen.display_list().RemoveDisplay(1);
EXPECT_EQ(screen.display_list().displays().size(), 1u);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_EQ(1, display::Screen::GetScreen()->GetNumDisplays());
{
auto result = std::vector<base::Value>();
result.emplace_back(802);
EXPECT_EQ(result, EvalJs(tab, R"(
(async () => {
await screensChange;
return getScreenWidths();
})();
)"));
}
// Remove one display, add two displays.
// TODO(enne): we add two displays here because DisplayManagerTestApi
// would consider remove+add to just be an update (with the same id).
// An alternative is to modify DisplayManagerTestApi to let us set ids.
EXPECT_TRUE(
ExecJs(tab, R"(var screensChange = makeScreensChangePromise();)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("0+0-803x600,1000+0-804x600");
#else
screen.display_list().RemoveDisplay(2);
screen.display_list().AddDisplay({3, gfx::Rect(0, 4, 803, 600)},
display::DisplayList::Type::PRIMARY);
screen.display_list().AddDisplay({4, gfx::Rect(0, 4, 804, 600)},
display::DisplayList::Type::NOT_PRIMARY);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
{
auto result = std::vector<base::Value>();
result.emplace_back(803);
result.emplace_back(804);
EXPECT_EQ(result, EvalJs(tab, R"(
(async () => {
await screensChange;
return getScreenWidths();
})();
)"));
}
}
// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
// SetScreenInstance and observers not being notified.
// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_OnCurrentScreenChangeEvent DISABLED_OnCurrentScreenChangeEvent
#else
#define MAYBE_OnCurrentScreenChangeEvent OnCurrentScreenChangeEvent
#endif
// Test that the oncurrentscreenchange handler fires correctly for screen
// changes and property updates.
IN_PROC_BROWSER_TEST_F(WindowPlacementTest, MAYBE_OnCurrentScreenChangeEvent) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-801x802,901+100-802x802");
#else
display::ScreenBase screen;
screen.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::PRIMARY);
screen.display_list().AddDisplay({2, gfx::Rect(901, 100, 802, 802)},
display::DisplayList::Type::NOT_PRIMARY);
display::test::ScopedScreenOverride screen_override(&screen);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/simple.html"));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
// TODO(crbug.com/1119974): this test could be in content_browsertests
// and not browser_tests if permission controls were supported.
// Auto-accept the Window Placement permission request.
permissions::PermissionRequestManager* permission_request_manager =
permissions::PermissionRequestManager::FromWebContents(tab);
permission_request_manager->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
EXPECT_EQ(801, EvalJs(tab, R"(
var screensInterface;
var promiseForEvent = (target, evt) => {
return new Promise((resolve) => {
const handler = (e) => {
target.removeEventListener(evt, handler);
resolve(e);
};
target.addEventListener(evt, handler);
});
}
var makeCurrentScreenChangePromise = () => {
return promiseForEvent(screensInterface, 'currentscreenchange');
};
(async () => {
screensInterface = await self.getScreens();
return screensInterface.currentScreen.width;
})();
)"));
// Switch to a second display. This should fire an event.
EXPECT_TRUE(ExecJs(tab, R"(var change = makeCurrentScreenChangePromise();)"));
const gfx::Rect new_bounds(1000, 150, 600, 500);
browser()->window()->SetBounds(new_bounds);
EXPECT_EQ(802, EvalJs(tab, R"(
(async () => {
await change;
return screensInterface.currentScreen.width;
})();
)"));
// Update the second display to have a height of 300. Validate that a change
// event is fired when attributes of the current screen change.
EXPECT_TRUE(ExecJs(tab, R"(var change = makeCurrentScreenChangePromise();)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-801x802,901+100-802x300");
#else
screen.display_list().UpdateDisplay({2, gfx::Rect(901, 100, 802, 300)},
display::DisplayList::Type::NOT_PRIMARY);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_EQ(300, EvalJs(tab, R"(
(async () => {
await change;
return screensInterface.currentScreen.height;
})();
)"));
}
// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
// SetScreenInstance and observers not being notified.
// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_ScreenAdvancedOnChange DISABLED_ScreenAdvancedOnChange
#else
#define MAYBE_ScreenAdvancedOnChange ScreenAdvancedOnChange
#endif
// Test that onchange events for individual screens in the screen list are
// supported.
IN_PROC_BROWSER_TEST_F(WindowPlacementTest, MAYBE_ScreenAdvancedOnChange) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-801x802,901+100-802x802");
#else
display::ScreenBase screen;
screen.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::PRIMARY);
screen.display_list().AddDisplay({2, gfx::Rect(901, 100, 802, 802)},
display::DisplayList::Type::NOT_PRIMARY);
display::test::ScopedScreenOverride screen_override(&screen);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
ASSERT_EQ(2, display::Screen::GetScreen()->GetNumDisplays());
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url(embedded_test_server()->GetURL("/simple.html"));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
auto* tab = browser()->tab_strip_model()->GetActiveWebContents();
// TODO(crbug.com/1119974): this test could be in content_browsertests
// and not browser_tests if permission controls were supported.
// Auto-accept the Window Placement permission request.
permissions::PermissionRequestManager* permission_request_manager =
permissions::PermissionRequestManager::FromWebContents(tab);
permission_request_manager->set_auto_response_for_test(
permissions::PermissionRequestManager::ACCEPT_ALL);
EXPECT_EQ(true, EvalJs(tab, R"(
var screensInterface;
var promiseForEvent = (target, evt) => {
return new Promise((resolve) => {
const handler = (e) => {
target.removeEventListener(evt, handler);
resolve(e);
};
target.addEventListener(evt, handler);
});
}
var screenChanges0 = 0;
var screenChanges1 = 0;
(async () => {
screensInterface = await self.getScreens();
if (screensInterface.screens.length !== 2)
return false;
// Add some event listeners for individual screens.
screensInterface.screens[0].addEventListener('change', () => {
screenChanges0++;
});
screensInterface.screens[1].addEventListener('change', () => {
screenChanges1++;
});
return true;
})();
)"));
// Update only the first display to have a different height.
EXPECT_TRUE(ExecJs(tab,
R"(
var change0 = promiseForEvent(screensInterface.screens[0], 'change');
)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-801x301,901+100-802x802");
#else
screen.display_list().UpdateDisplay({1, gfx::Rect(100, 100, 801, 301)},
display::DisplayList::Type::PRIMARY);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_EQ(301, EvalJs(tab, R"(
(async () => {
await change0;
// Only screen[0] should have changed.
if (screenChanges0 !== 1)
return -1;
if (screenChanges1 !== 0)
return -2;
return screensInterface.screens[0].height;
})();
)"));
// Update only the second display to have a different height.
EXPECT_TRUE(ExecJs(tab,
R"(
var change1 = promiseForEvent(screensInterface.screens[1], 'change');
)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-801x301,901+100-802x302");
#else
screen.display_list().UpdateDisplay({2, gfx::Rect(901, 100, 802, 302)},
display::DisplayList::Type::NOT_PRIMARY);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_EQ(302, EvalJs(tab, R"(
(async () => {
await change1;
// Both screens have one change.
if (screenChanges0 !== 1)
return -1;
if (screenChanges1 !== 1)
return -2;
return screensInterface.screens[1].height;
})();
)"));
// Change the width of both displays at the same time.
EXPECT_TRUE(ExecJs(tab,
R"(
var change0 = promiseForEvent(screensInterface.screens[0], 'change');
var change1 = promiseForEvent(screensInterface.screens[1], 'change');
)"));
#if BUILDFLAG(IS_CHROMEOS_ASH)
display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
.UpdateDisplay("100+100-401x301,901+100-402x302");
#else
screen.display_list().UpdateDisplay({1, gfx::Rect(100, 100, 401, 301)},
display::DisplayList::Type::PRIMARY);
screen.display_list().UpdateDisplay({2, gfx::Rect(901, 100, 402, 302)},
display::DisplayList::Type::NOT_PRIMARY);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_EQ(true, EvalJs(tab, R"(
(async () => {
await change0;
await change1;
// Both screens have two changes
if (screenChanges0 !== 2)
return false;
if (screenChanges1 !== 2)
return false;
if (screensInterface.screens[0].width !== 401)
return false;
if (screensInterface.screens[1].width !== 402)
return false;
return true;
})();
)"));
}