blob: 1859b07335b22b065ed9a0959fe7fc02b857a51d [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "chrome/browser/ui/test/popup_test_base.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "third_party/blink/public/common/features_generated.h"
namespace {
// Log filter on messages ignoring a fullscreen [popup] request.
bool FullscreenRequestIgnoredMessageFilter(
const content::WebContentsConsoleObserver::Message& message) {
return message.message.starts_with(u"Fullscreen request ignored:");
}
// Log filter on messages indicating fullscreen permission policy is denied.
bool FullscreenPermissionPolicyViolationMessageFilter(
const content::WebContentsConsoleObserver::Message& message) {
return message.message.starts_with(
u"Permissions policy violation: fullscreen is not allowed");
}
// Base class for fullscreen popup tests.
class PopupFullscreenTestBase : public PopupTestBase {
public:
PopupFullscreenTestBase() {
scoped_feature_list_.InitWithFeatures(
{blink::features::kFullscreenPopupWindows}, {});
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// Required for permission policy violations to be logged.
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"FeaturePolicyReporting");
PopupTestBase::SetUpCommandLine(command_line);
}
void SetUpOnMainThread() override {
content::SetupCrossSiteRedirector(embedded_test_server());
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(NavigateToURL(web_contents,
embedded_test_server()->GetURL("/simple.html")));
EXPECT_TRUE(WaitForRenderFrameReady(web_contents->GetPrimaryMainFrame()));
console_observer_ = std::make_unique<content::WebContentsConsoleObserver>(
browser()->tab_strip_model()->GetActiveWebContents());
console_observer_->SetFilter(
base::BindRepeating(&FullscreenRequestIgnoredMessageFilter));
}
protected:
std::unique_ptr<content::WebContentsConsoleObserver> console_observer_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests opening popups as fullscreen windows.
// See https://chromestatus.com/feature/6002307972464640 for more information.
// Tests are run with and without the requisite Window Management permission.
class PopupFullscreenTest
: public PopupFullscreenTestBase,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
public:
void SetUpOnMainThread() override {
PopupFullscreenTestBase::SetUpOnMainThread();
if (ShouldTestWindowManagement()) {
SetUpWindowManagement(browser());
// The permission prompt creates a user gesture. If we are trying to test
// without a user gesture, wait for the existing one to expire.
if (!ShouldTestWithUserGesture()) {
WaitForUserActivationExpiry(browser());
}
}
}
protected:
bool IsFullscreenExpected() {
return ShouldTestWithUserGesture() && ShouldTestWindowManagement();
}
bool ShouldTestWithUserGesture() { return std::get<0>(GetParam()); }
bool ShouldTestWindowManagement() { return std::get<1>(GetParam()); }
};
INSTANTIATE_TEST_SUITE_P(,
PopupFullscreenTest,
::testing::Combine(::testing::Bool(),
::testing::Bool()));
IN_PROC_BROWSER_TEST_P(PopupFullscreenTest, BasicFullscreen) {
Browser* popup =
OpenPopup(browser(), "open('/simple.html', '_blank', 'popup,fullscreen')",
ShouldTestWithUserGesture());
content::WebContents* popup_contents =
popup->tab_strip_model()->GetActiveWebContents();
if (IsFullscreenExpected()) {
WaitForHTMLFullscreen(popup_contents);
} else {
ASSERT_TRUE(console_observer_->Wait());
}
EXPECT_EQ(EvalJs(popup_contents,
"!!document.fullscreenElement && "
"document.fullscreenElement == document.documentElement")
.ExtractBool(),
IsFullscreenExpected());
FullscreenController* fullscreen_controller =
popup->exclusive_access_manager()->fullscreen_controller();
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_EQ(fullscreen_controller->IsTabFullscreen(), IsFullscreenExpected());
EXPECT_EQ(EvalJs(popup_contents, "document.exitFullscreen()").error.empty(),
IsFullscreenExpected());
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
// Test that a navigation doesn't re-trigger fullscreen.
EXPECT_TRUE(EvalJs(popup_contents,
"window.location.href = '" +
embedded_test_server()->GetURL("/title1.html").spec() +
"'")
.error.empty());
EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
}
IN_PROC_BROWSER_TEST_P(PopupFullscreenTest, AboutBlankFullscreen) {
Browser* popup =
OpenPopup(browser(), "open('about:blank', '_blank', 'popup,fullscreen')",
ShouldTestWithUserGesture());
content::WebContents* popup_contents =
popup->tab_strip_model()->GetActiveWebContents();
if (IsFullscreenExpected()) {
WaitForHTMLFullscreen(popup_contents);
} else {
ASSERT_TRUE(console_observer_->Wait());
}
EXPECT_EQ(EvalJs(popup_contents,
"!!document.fullscreenElement && "
"document.fullscreenElement == document.documentElement")
.ExtractBool(),
IsFullscreenExpected());
FullscreenController* fullscreen_controller =
popup->exclusive_access_manager()->fullscreen_controller();
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_EQ(fullscreen_controller->IsTabFullscreen(), IsFullscreenExpected());
EXPECT_EQ(EvalJs(popup_contents, "document.exitFullscreen()").error.empty(),
IsFullscreenExpected());
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
// Test that a navigation doesn't re-trigger fullscreen.
EXPECT_TRUE(EvalJs(popup_contents,
"window.location.href = '" +
embedded_test_server()->GetURL("/title1.html").spec() +
"'")
.error.empty());
EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
}
IN_PROC_BROWSER_TEST_P(PopupFullscreenTest, FullscreenWithBounds) {
Browser* popup =
OpenPopup(browser(),
"open('/simple.html', '_blank', "
"'height=200,width=200,top=100,left=100,fullscreen')",
ShouldTestWithUserGesture());
content::WebContents* popup_contents =
popup->tab_strip_model()->GetActiveWebContents();
if (IsFullscreenExpected()) {
WaitForHTMLFullscreen(popup_contents);
} else {
ASSERT_TRUE(console_observer_->Wait());
}
EXPECT_EQ(EvalJs(popup_contents,
"!!document.fullscreenElement && "
"document.fullscreenElement == document.documentElement")
.ExtractBool(),
IsFullscreenExpected());
FullscreenController* fullscreen_controller =
popup->exclusive_access_manager()->fullscreen_controller();
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_EQ(fullscreen_controller->IsTabFullscreen(), IsFullscreenExpected());
}
// Tests that a fullscreen popup consumes a user gesture even with "popups &
// redirects" enabled and therefore a second fullscreen popup without a gesture
// will not transition to fullscreen.
IN_PROC_BROWSER_TEST_P(PopupFullscreenTest, ConsumesGesture) {
if (!IsFullscreenExpected()) {
// This test is only applicable when testing the normal use case.
GTEST_SKIP();
}
// Enable Popups & Redirects.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
HostContentSettingsMapFactory::GetForProfile(browser()->profile())
->SetContentSettingDefaultScope(web_contents->GetURL(), GURL(),
ContentSettingsType::POPUPS,
CONTENT_SETTING_ALLOW);
// Open a fullscreen popup with a gesture and validate.
Browser* popup = OpenPopup(
browser(), "open('/simple.html', '_blank', 'popup,fullscreen')");
content::WebContents* popup_contents =
popup->tab_strip_model()->GetActiveWebContents();
WaitForHTMLFullscreen(popup_contents);
EXPECT_TRUE(EvalJs(popup_contents,
"!!document.fullscreenElement && "
"document.fullscreenElement == document.documentElement")
.ExtractBool());
// Open another fullscreen popup without a gesture. The popup should open but
// the fullscreen should be ignored since the user activation was consumed.
popup =
OpenPopup(browser(), "open('/simple.html', '_blank', 'popup,fullscreen')",
/*user_gesture=*/false);
popup_contents = popup->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(console_observer_->Wait());
EXPECT_FALSE(EvalJs(popup_contents,
"!!document.fullscreenElement && "
"document.fullscreenElement == document.documentElement")
.ExtractBool());
}
// Fullscreen should not work if the new window is not specified as a popup.
IN_PROC_BROWSER_TEST_P(PopupFullscreenTest, FullscreenRequiresPopupFeature) {
// OpenPopup() cannot be used here since it waits for a new browser which
// would not open in this case.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(
EvalJs(web_contents, "open('/simple.html', '_blank', 'fullscreen')")
.error.empty());
EXPECT_EQ(console_observer_->messages().size(), 1u);
EXPECT_FALSE(
EvalJs(web_contents, "!!document.fullscreenElement").ExtractBool());
FullscreenController* fullscreen_controller =
browser()->exclusive_access_manager()->fullscreen_controller();
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
}
// Tests that the fullscreen flag is ignored if the window.open() does not
// result in a new window.
IN_PROC_BROWSER_TEST_P(PopupFullscreenTest, FullscreenRequiresNewWindow) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(NavigateToURL(web_contents, embedded_test_server()->GetURL(
"/iframe_about_blank.html")));
EXPECT_TRUE(WaitForRenderFrameReady(web_contents->GetPrimaryMainFrame()));
// OpenPopup() cannot be used here since it waits for a new browser which
// would not open in this case. open() targeting a frame named "test" in
// "iframe.html" will not create a new window.
EXPECT_TRUE(
EvalJs(web_contents,
"open('/simple.html', 'about_blank_iframe', 'popup,fullscreen')")
.error.empty());
EXPECT_EQ(console_observer_->messages().size(), 1u);
EXPECT_EQ(browser()->tab_strip_model()->count(), 1);
EXPECT_FALSE(
EvalJs(web_contents, "!!document.fullscreenElement").ExtractBool());
FullscreenController* fullscreen_controller =
browser()->exclusive_access_manager()->fullscreen_controller();
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_FALSE(fullscreen_controller->IsTabFullscreen());
}
struct PopupFullscreenPermissionPolicyTestParams {
enum ExpectedFullscreenState { Allowed, BlockedByOpener, BlockedByOpened };
std::string test_name;
std::string opener_permission_policy_header;
std::string opened_permission_policy_header;
ExpectedFullscreenState fullscreen_expected;
bool is_fullscreen_expected_allowed() const {
return fullscreen_expected == ExpectedFullscreenState::Allowed;
}
};
constexpr char kOpenerPath[] = "/simple.html";
constexpr char kOpenedPath[] = "/title1.html";
std::unique_ptr<net::test_server::HttpResponse> SetPermissionsPolicyHeader(
std::string opener_header,
std::string opened_header,
const net::test_server::HttpRequest& request) {
const GURL& url = request.GetURL();
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
// The hostname is always 127.0.0.1 here regardless of hostname used in the
// browser request. The path is used to differentiate between the opener and
// opened frame.
if (url.path() == kOpenerPath && !opener_header.empty()) {
response->AddCustomHeader("Permissions-Policy", opener_header);
}
if (url.path() == kOpenedPath && !opened_header.empty()) {
response->AddCustomHeader("Permissions-Policy", opened_header);
}
return response;
}
// Tests fullscreen popup functionality with `fullscreen` permission policy
// being allowed or blocked in the opener (initiator) and/or opened frame.
class PopupFullscreenPermissionPolicyTest
: public PopupFullscreenTestBase,
public ::testing::WithParamInterface<
PopupFullscreenPermissionPolicyTestParams> {
public:
void SetUpOnMainThread() override {
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&SetPermissionsPolicyHeader, GetParam().opener_permission_policy_header,
GetParam().opened_permission_policy_header));
PopupFullscreenTestBase::SetUpOnMainThread();
SetUpWindowManagement(browser());
}
};
using ExpectedState =
PopupFullscreenPermissionPolicyTestParams::ExpectedFullscreenState;
INSTANTIATE_TEST_SUITE_P(
,
PopupFullscreenPermissionPolicyTest,
testing::ValuesIn(std::vector<PopupFullscreenPermissionPolicyTestParams>{
{.test_name = "DefaultOpener_DefaultOpened",
.opener_permission_policy_header = "",
.opened_permission_policy_header = "",
.fullscreen_expected = ExpectedState::Allowed},
{.test_name = "DefaultOpener_SelfOpened",
.opener_permission_policy_header = "",
.opened_permission_policy_header = "fullscreen=(self)",
.fullscreen_expected = ExpectedState::Allowed},
{.test_name = "SelfOpener_DefaultOpened",
.opener_permission_policy_header = "fullscreen=(self)",
.opened_permission_policy_header = "",
.fullscreen_expected = ExpectedState::Allowed},
{.test_name = "SelfOpener_SelfOpened",
.opener_permission_policy_header = "fullscreen=(self)",
.opened_permission_policy_header = "fullscreen=(self)",
.fullscreen_expected = ExpectedState::Allowed},
{.test_name = "BlockedOpener_SelfOpened",
.opener_permission_policy_header = "fullscreen=()",
.opened_permission_policy_header = "fullscreen=(self)",
.fullscreen_expected = ExpectedState::BlockedByOpener},
{.test_name = "SelfOpener_BlockedOpened",
.opener_permission_policy_header = "fullscreen=(self)",
.opened_permission_policy_header = "fullscreen=()",
.fullscreen_expected = ExpectedState::BlockedByOpened},
{.test_name = "BlockedOpener_BlockedOpened",
.opener_permission_policy_header = "fullscreen=()",
.opened_permission_policy_header = "fullscreen=()",
.fullscreen_expected = ExpectedState::BlockedByOpener}}),
[](const testing::TestParamInfo<PopupFullscreenPermissionPolicyTestParams>&
info) { return info.param.test_name; });
// Opens a fullscreen popup and checks if fullscreen is granted based on the
// expected result for the given permission policy configurations in the test
// parameters.
IN_PROC_BROWSER_TEST_P(PopupFullscreenPermissionPolicyTest,
PermissionPolicyTest) {
std::string url =
embedded_test_server()->GetURL("cross-origin.com", kOpenedPath).spec();
Browser* popup =
OpenPopup(browser(), "open('" + url + "', '_blank', 'popup,fullscreen')");
content::WebContents* popup_contents =
popup->tab_strip_model()->GetActiveWebContents();
if (GetParam().fullscreen_expected == ExpectedState::BlockedByOpened) {
// In cases where fullscreen is blocked by the *opened* frame:
// Switch the console observer to the opened window.
console_observer_ =
std::make_unique<content::WebContentsConsoleObserver>(popup_contents);
console_observer_->SetFilter(
base::BindRepeating(&FullscreenPermissionPolicyViolationMessageFilter));
}
if (GetParam().is_fullscreen_expected_allowed()) {
WaitForHTMLFullscreen(popup_contents);
} else {
ASSERT_TRUE(console_observer_->Wait());
}
EXPECT_EQ(EvalJs(popup_contents,
"!!document.fullscreenElement && "
"document.fullscreenElement == document.documentElement")
.ExtractBool(),
GetParam().is_fullscreen_expected_allowed());
FullscreenController* fullscreen_controller =
popup->exclusive_access_manager()->fullscreen_controller();
EXPECT_FALSE(fullscreen_controller->IsFullscreenForBrowser());
EXPECT_EQ(fullscreen_controller->IsTabFullscreen(),
GetParam().is_fullscreen_expected_allowed());
}
} // namespace