blob: 7c31642c00ed50ace41196b6bc46dfed9e35e3d5 [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/command_line.h"
#include "base/location.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/common/content_switches.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/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
class PageLifecycleStateManagerBrowserTest : public ContentBrowserTest {
public:
~PageLifecycleStateManagerBrowserTest() override = default;
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"VisibilityStateEntry");
}
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
void StartRecordingEvents(RenderFrameHostImpl* rfh) {
EXPECT_TRUE(ExecJs(rfh, R"(
window.testObservedEvents = [];
let event_list = [
'freeze',
'resume',
'visibilitychange',
];
for (event_name of event_list) {
let result = event_name;
document.addEventListener(event_name, event => {
window.testObservedEvents.push('document.' + result);
});
}
)"));
}
void StartPerformanceObserver(RenderFrameHostImpl* rfh, int numEntries) {
EXPECT_TRUE(ExecJs(rfh,
R"(
window.performanceObserverEntries = [];
window.performanceObserverPromise = new Promise(resolve => {
new PerformanceObserver(entries => {
entries.getEntries().forEach(e => {
console.log(e.name + " " + e.startTime);
window.performanceObserverEntries.push(e.name);
});
if (window.performanceObserverEntries.length === )" +
base::NumberToString(numEntries) + R"()
resolve(true);
}).observe({type: 'visibility-state', buffered: true});
});
)",
EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
}
void MatchEventList(RenderFrameHostImpl* rfh,
base::Value list,
base::Location location = base::Location::Current()) {
EXPECT_EQ(list, EvalJs(rfh, "window.testObservedEvents"))
<< location.ToString();
}
RenderViewHostImpl* render_view_host() {
return static_cast<RenderViewHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost());
}
RenderFrameHostImpl* current_frame_host() {
return static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root()
->current_frame_host();
}
};
IN_PROC_BROWSER_TEST_F(PageLifecycleStateManagerBrowserTest, SetFrozen) {
EXPECT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/empty.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderFrameHostImpl* rfh = current_frame_host();
StartRecordingEvents(rfh);
// Hide and freeze the page.
shell()->web_contents()->WasHidden();
EXPECT_EQ(PageVisibilityState::kHidden, rfh->GetVisibilityState());
shell()->web_contents()->SetPageFrozen(true);
// Resume the page.
shell()->web_contents()->SetPageFrozen(false);
MatchEventList(rfh, ListValueOf("document.visibilitychange",
"document.freeze", "document.resume"));
}
IN_PROC_BROWSER_TEST_F(PageLifecycleStateManagerBrowserTest, SetVisibility) {
EXPECT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/empty.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderFrameHostImpl* rfh = current_frame_host();
EXPECT_EQ(PageVisibilityState::kVisible, rfh->GetVisibilityState());
StartRecordingEvents(rfh);
StartPerformanceObserver(rfh, 2);
// Hide the page.
shell()->web_contents()->WasHidden();
EXPECT_EQ(PageVisibilityState::kHidden, rfh->GetVisibilityState());
MatchEventList(rfh, ListValueOf("document.visibilitychange"));
EXPECT_EQ(true, EvalJs(rfh,
"(async () => { return await "
"window.performanceObserverPromise;})()"));
EXPECT_EQ(ListValueOf("visible", "hidden"),
EvalJs(rfh, "window.performanceObserverEntries"));
}
// TODO(crbug.com/40786254): Test is flaky on Win
#if BUILDFLAG(IS_WIN)
#define MAYBE_CrossProcessIframeHiddenAnFrozen \
DISABLED_CrossProcessIframeHiddenAnFrozen
#else
#define MAYBE_CrossProcessIframeHiddenAnFrozen CrossProcessIframeHiddenAnFrozen
#endif
IN_PROC_BROWSER_TEST_F(PageLifecycleStateManagerBrowserTest,
MAYBE_CrossProcessIframeHiddenAnFrozen) {
EXPECT_TRUE(embedded_test_server()->Start());
// Load a page with a cross-process iframe.
GURL url_a_b(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_TRUE(NavigateToURL(shell(), url_a_b));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
StartRecordingEvents(rfh_a);
StartRecordingEvents(rfh_b);
// Hide and freeze the page.
shell()->web_contents()->WasHidden();
EXPECT_EQ(PageVisibilityState::kHidden, rfh_a->GetVisibilityState());
EXPECT_EQ(PageVisibilityState::kHidden, rfh_b->GetVisibilityState());
shell()->web_contents()->SetPageFrozen(true);
// Resume the page.
shell()->web_contents()->SetPageFrozen(false);
// Make sure that the cross-process iframe also got events.
MatchEventList(rfh_a, ListValueOf("document.visibilitychange",
"document.freeze", "document.resume"));
MatchEventList(rfh_b, ListValueOf("document.visibilitychange",
"document.freeze", "document.resume"));
}
IN_PROC_BROWSER_TEST_F(PageLifecycleStateManagerBrowserTest,
CreateIframeInHiddenPage) {
EXPECT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/empty.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderFrameHostImpl* rfh_parent = current_frame_host();
// Hide the page.
shell()->web_contents()->WasHidden();
EXPECT_EQ(PageVisibilityState::kHidden, rfh_parent->GetVisibilityState());
// Create an iframe in a hidden page.
EXPECT_TRUE(ExecJs(rfh_parent, R"(
let iframe = document.createElement('iframe');
document.body.append(iframe);
)"));
ASSERT_EQ(1u, rfh_parent->child_count());
// Make sure that the created iframe's initial visibility is correctly set.
RenderFrameHostImpl* rfh_child =
rfh_parent->child_at(0)->current_frame_host();
EXPECT_EQ(PageVisibilityState::kHidden, rfh_child->GetVisibilityState());
// Show the page.
shell()->web_contents()->WasShown();
EXPECT_EQ(PageVisibilityState::kVisible, rfh_parent->GetVisibilityState());
EXPECT_EQ(PageVisibilityState::kVisible, rfh_child->GetVisibilityState());
}
IN_PROC_BROWSER_TEST_F(PageLifecycleStateManagerBrowserTest,
CreateNewWindowVisibilityChange) {
if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites())
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
// 1) Navigate to A and open a popup.
EXPECT_TRUE(NavigateToURL(shell(), url_a));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_EQ(1u, rfh_a->GetSiteInstance()->GetRelatedActiveContentsCount());
Shell* popup = OpenPopup(rfh_a, url_a, "");
EXPECT_EQ(2u, rfh_a->GetSiteInstance()->GetRelatedActiveContentsCount());
RenderFrameHostImpl* popup_frame = static_cast<RenderFrameHostImpl*>(
popup->web_contents()->GetPrimaryMainFrame());
StartRecordingEvents(popup_frame);
popup->web_contents()->WasHidden();
EXPECT_EQ(PageVisibilityState::kHidden, popup_frame->GetVisibilityState());
popup->web_contents()->WasShown();
EXPECT_EQ(PageVisibilityState::kVisible, popup_frame->GetVisibilityState());
MatchEventList(popup_frame, ListValueOf("document.visibilitychange",
"document.visibilitychange"));
}
} // namespace content