blob: f189b71d9079b279323d8b875551891acbe4a688 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "content/browser/permissions/permission_controller_impl.h"
#include "content/browser/screen_details/screen_details_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
#include "ui/display/screen_base.h"
namespace content {
using ScreenDetailsTest = ContentBrowserTest;
// Test ScreenDetails API promise rejection without permission.
IN_PROC_BROWSER_TEST_F(ScreenDetailsTest, GetScreensNoPermission) {
ASSERT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(shell(), "'getScreenDetails' in self"));
// getScreenDetails() rejects its promise without permission.
EXPECT_FALSE(EvalJs(shell(), "await getScreenDetails()").error.empty());
}
// TODO(crbug.com/1119974): Test ScreenDetails API values with permission.
IN_PROC_BROWSER_TEST_F(ScreenDetailsTest, DISABLED_GetScreensBasic) {
ASSERT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(shell(), "'getScreenDetails' in self"));
auto result = EvalJs(shell(), content::test::kGetScreenDetailsScript);
EXPECT_EQ(content::test::GetExpectedScreenDetails(), result.value);
}
// Test that screen.isExtended matches the availability of multiple displays.
IN_PROC_BROWSER_TEST_F(ScreenDetailsTest, IsExtendedBasic) {
ASSERT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(shell(), "'isExtended' in screen"));
EXPECT_EQ("boolean", EvalJs(shell(), "typeof screen.isExtended"));
EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays() > 1,
EvalJs(shell(), "screen.isExtended"));
}
// Test ScreenDetails functionality with a fake display::Screen object.
class FakeScreenDetailsTest : public ScreenDetailsTest {
public:
FakeScreenDetailsTest() = default;
~FakeScreenDetailsTest() override = default;
FakeScreenDetailsTest(const FakeScreenDetailsTest&) = delete;
void operator=(const FakeScreenDetailsTest&) = delete;
protected:
// ScreenDetailsTest:
void SetUp() override {
display::Screen::SetScreenInstance(&screen_);
screen()->display_list().AddDisplay({0, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::PRIMARY);
ScreenDetailsTest::SetUp();
}
void TearDown() override {
ScreenDetailsTest::TearDown();
display::Screen::SetScreenInstance(nullptr);
}
void SetUpOnMainThread() override {
ScreenDetailsTest::SetUpOnMainThread();
// Create a shell that observes the fake screen.
test_shell_ = CreateBrowser();
}
display::ScreenBase* screen() { return &screen_; }
Shell* test_shell() { return test_shell_; }
private:
display::ScreenBase screen_;
raw_ptr<Shell, DanglingUntriaged> test_shell_ = nullptr;
};
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_GetScreensFaked DISABLED_GetScreensFaked
#else
#define MAYBE_GetScreensFaked GetScreensFaked
#endif
IN_PROC_BROWSER_TEST_F(FakeScreenDetailsTest, MAYBE_GetScreensFaked) {
ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(test_shell(), "'getScreenDetails' in self"));
content::WebContents* contents = test_shell()->web_contents();
PermissionControllerImpl* permission_controller =
PermissionControllerImpl::FromBrowserContext(
contents->GetBrowserContext());
permission_controller->GrantPermissionOverrides(
contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
{blink::PermissionType::WINDOW_MANAGEMENT});
screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::NOT_PRIMARY);
display::Display display(2, gfx::Rect(901, 100, 801, 802));
// Test that UTF-8 characters are correctly reflected in the JS output.
display.set_label("Inbyggd skärm");
screen()->display_list().AddDisplay(display,
display::DisplayList::Type::NOT_PRIMARY);
EvalJsResult result =
EvalJs(test_shell(), content::test::kGetScreenDetailsScript);
EXPECT_EQ(content::test::GetExpectedScreenDetails(), result.value);
}
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_IsExtendedFaked DISABLED_IsExtendedFaked
#else
#define MAYBE_IsExtendedFaked IsExtendedFaked
#endif
IN_PROC_BROWSER_TEST_F(FakeScreenDetailsTest, MAYBE_IsExtendedFaked) {
ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::NOT_PRIMARY);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
screen()->display_list().RemoveDisplay(1);
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
}
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_ScreenOnchangeNoPermission DISABLED_ScreenOnchangeNoPermission
#else
#define MAYBE_ScreenOnchangeNoPermission ScreenOnchangeNoPermission
#endif
// Sites with no permission only get an event if screen.isExtended changes.
// TODO(crbug.com/1119974): Need content_browsertests permission controls.
IN_PROC_BROWSER_TEST_F(FakeScreenDetailsTest,
MAYBE_ScreenOnchangeNoPermission) {
ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(test_shell(), "'onchange' in screen"));
constexpr char kSetScreenOnchange[] = R"(
window.screen.onchange = function() { ++document.title; };
document.title = 0;
)";
EXPECT_EQ(0, EvalJs(test_shell(), kSetScreenOnchange));
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("0", EvalJs(test_shell(), "document.title"));
// screen.isExtended changes from false to true here, so an event is sent.
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::NOT_PRIMARY);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// screen.isExtended remains unchanged, so no event is sent.
screen()->display_list().AddDisplay({2, gfx::Rect(901, 100, 801, 802)},
display::DisplayList::Type::NOT_PRIMARY);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// screen.isExtended remains unchanged, so no event is sent.
EXPECT_NE(0u, screen()->display_list().UpdateDisplay(
{2, gfx::Rect(902, 100, 801, 802)}));
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// screen.isExtended remains unchanged, so no event is sent.
screen()->display_list().RemoveDisplay(2);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// screen.isExtended changes from true to false here, so an event is sent.
screen()->display_list().RemoveDisplay(1);
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("2", EvalJs(test_shell(), "document.title"));
}
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_ScreenOnChangeForIsExtended DISABLED_ScreenOnChangeForIsExtended
#else
#define MAYBE_ScreenOnChangeForIsExtended ScreenOnChangeForIsExtended
#endif
// Sites should get Screen.change events anytime Screen.isExtended changes.
IN_PROC_BROWSER_TEST_F(FakeScreenDetailsTest,
MAYBE_ScreenOnChangeForIsExtended) {
ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(test_shell(), "'onchange' in screen"));
constexpr char kSetScreenOnChange[] = R"(
screen.onchange = function() { ++document.title; };
document.title = 0;
)";
EXPECT_EQ(0, EvalJs(test_shell(), kSetScreenOnChange));
EXPECT_EQ("0", EvalJs(test_shell(), "document.title"));
// Screen.isExtended changes from false to true here, so an event is sent.
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
display::DisplayList::Type::NOT_PRIMARY);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// The current Screen remains unchanged, so no event is sent.
screen()->display_list().AddDisplay({2, gfx::Rect(901, 100, 801, 802)},
display::DisplayList::Type::NOT_PRIMARY);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// The current Screen remains unchanged, so no event is sent.
EXPECT_NE(0u, screen()->display_list().UpdateDisplay(
{2, gfx::Rect(902, 100, 801, 802)}));
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// The current Screen remains unchanged, so no event is sent.
screen()->display_list().RemoveDisplay(2);
EXPECT_TRUE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// Screen.isExtended changes from true to false here, so an event is sent.
screen()->display_list().RemoveDisplay(1);
EXPECT_FALSE(EvalJs(test_shell(), "screen.isExtended").ExtractBool());
EXPECT_EQ("2", EvalJs(test_shell(), "document.title"));
}
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
#define MAYBE_ScreenOnChangeForAttributes DISABLED_ScreenOnChangeForAttributes
#else
#define MAYBE_ScreenOnChangeForAttributes ScreenOnChangeForAttributes
#endif
// Sites should get Screen.change events anytime other Screen attributes change.
IN_PROC_BROWSER_TEST_F(FakeScreenDetailsTest,
MAYBE_ScreenOnChangeForAttributes) {
ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
ASSERT_EQ(true, EvalJs(test_shell(), "'onchange' in screen"));
constexpr char kSetScreenOnChange[] = R"(
screen.onchange = function() { ++document.title; };
document.title = 0;
)";
EXPECT_EQ(0, EvalJs(test_shell(), kSetScreenOnChange));
EXPECT_EQ("0", EvalJs(test_shell(), "document.title"));
// An event is sent when Screen work area changes.
// work_area translates into Screen.available_rect.
display::Display display = screen()->display_list().displays()[0];
display.set_work_area(gfx::Rect(101, 102, 903, 904));
EXPECT_NE(0u, screen()->display_list().UpdateDisplay(display));
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
// An event is sent when Screen scaling changes.
display.set_device_scale_factor(display.device_scale_factor() * 2);
EXPECT_NE(0u, screen()->display_list().UpdateDisplay(display));
EXPECT_EQ("2", EvalJs(test_shell(), "document.title"));
}
} // namespace content