blob: c60c4565a47061211b001a1bf4b8fef490970f2b [file] [log] [blame]
// Copyright 2015 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/bind.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/common/content_switches.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 "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 {
namespace {
const char kBlinkWakeLockFeature[] = "WakeLock";
void OnHasWakeLock(bool* out, bool has_wakelock) {
*out = has_wakelock;
base::RunLoop::QuitCurrentDeprecated();
}
} // namespace
class WakeLockTest : public ContentBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
kBlinkWakeLockFeature);
command_line->AppendSwitch(switches::kSitePerProcess);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
EXPECT_TRUE(embedded_test_server()->Start());
// To prevent occlusion events from changing page visibility.
GetWebContents()->IncrementCapturerCount(gfx::Size());
}
void TearDownOnMainThread() override {
GetWebContents()->DecrementCapturerCount();
}
protected:
WebContents* GetWebContents() { return shell()->web_contents(); }
WebContentsImpl* GetWebContentsImpl() {
return static_cast<WebContentsImpl*>(GetWebContents());
}
RenderFrameHost* GetMainFrame() { return GetWebContents()->GetMainFrame(); }
FrameTreeNode* GetNestedFrameNode() {
FrameTreeNode* root = GetWebContentsImpl()->GetFrameTree()->root();
CHECK_EQ(1U, root->child_count());
return root->child_at(0);
}
RenderFrameHost* GetNestedFrame() {
return GetNestedFrameNode()->current_frame_host();
}
device::mojom::WakeLock* GetRendererWakeLock() {
return GetWebContentsImpl()->GetRendererWakeLock();
}
bool HasWakeLock() {
bool has_wakelock = false;
base::RunLoop run_loop;
GetRendererWakeLock()->HasWakeLockForTests(
base::BindOnce(&OnHasWakeLock, &has_wakelock));
run_loop.Run();
return has_wakelock;
}
void WaitForPossibleUpdate() {
// As Mojo channels have no common FIFO order in respect to each other and
// to the Chromium IPC, we cannot assume that when screen.keepAwake state
// is changed from within a script, mojom::WakeLock will receive an
// update request before ExecuteScript() returns. Therefore, some time slack
// is needed to make sure that mojom::WakeLock has received any
// possible update requests before checking the resulting wake lock state.
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
RunAllPendingInMessageLoop();
}
void ScreenWakeLockInMainFrame() {
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
EXPECT_TRUE(HasWakeLock());
}
bool EvaluateAsBool(const ToRenderFrameHost& adapter,
const std::string& expr) {
bool result;
CHECK(ExecuteScriptAndExtractBool(
adapter, "window.domAutomationController.send(" + expr + ");",
&result));
return result;
}
};
IN_PROC_BROWSER_TEST_F(WakeLockTest, WakeLockApiIsPresent) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
EXPECT_TRUE(
EvaluateAsBool(GetMainFrame(), "typeof screen.keepAwake !== undefined"));
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, LockAndUnlockScreenInMainFrame) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
// Should not have screen wake lock initially.
EXPECT_FALSE(HasWakeLock());
// Check attribute 'screen.keepAwake' in main frame.
EXPECT_FALSE(EvaluateAsBool(GetMainFrame(), "screen.keepAwake"));
// Set keep awake flag in main frame.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Keep awake flag should be set in main frame.
EXPECT_TRUE(EvaluateAsBool(GetMainFrame(), "screen.keepAwake"));
// Should create screen wake lock.
EXPECT_TRUE(HasWakeLock());
// Reset keep awake flag in main frame.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = false;"));
WaitForPossibleUpdate();
// Keep awake flag should not be set in main frame.
EXPECT_FALSE(EvaluateAsBool(GetMainFrame(), "screen.keepAwake"));
// Should release screen wake lock.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, MultipleLockThenUnlock) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
// Set keep awake flag.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Set keep awake flag again.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Screen should still be locked.
EXPECT_TRUE(HasWakeLock());
// Reset keep awake flag.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = false;"));
WaitForPossibleUpdate();
// Should release screen wake lock.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, LockInMainFrameAndNestedFrame) {
NavigateToURL(shell(),
embedded_test_server()->GetURL("/frame_tree/2-4.html"));
EXPECT_FALSE(HasWakeLock());
// Lock screen in nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Should create screen wake lock.
EXPECT_TRUE(HasWakeLock());
// screen.keepAwake should be false in the main frame.
EXPECT_FALSE(EvaluateAsBool(GetMainFrame(), "screen.keepAwake"));
// Lock screen in main frame.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// screen.keepAwake should be true in the main frame.
EXPECT_TRUE(EvaluateAsBool(GetMainFrame(), "screen.keepAwake"));
// Screen wake lock should not change.
EXPECT_TRUE(HasWakeLock());
// Unlock screen in nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = false;"));
WaitForPossibleUpdate();
// Screen wake lock should be present, as the main frame is still requesting
// it.
EXPECT_TRUE(HasWakeLock());
// Unlock screen in main frame.
EXPECT_TRUE(ExecuteScript(GetMainFrame(), "screen.keepAwake = false;"));
WaitForPossibleUpdate();
// Screen wake lock should be released, as no frames are requesting it.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, FrameRemoved) {
NavigateToURL(shell(),
embedded_test_server()->GetURL("/frame_tree/2-4.html"));
EXPECT_FALSE(HasWakeLock());
// Lock screen in nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
EXPECT_TRUE(HasWakeLock());
// Remove nested frame.
EXPECT_TRUE(ExecuteScript(GetMainFrame(),
"var iframe = document.getElementById('3-1-id');"
"iframe.parentNode.removeChild(iframe);"));
// Screen wake lock should be released.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, UnlockAfterTabCrashed) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
ScreenWakeLockInMainFrame();
// Crash the tab.
CrashTab(GetWebContents());
// Screen wake lock should be released.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, UnlockAfterNavigation) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
ScreenWakeLockInMainFrame();
// Navigate to a different document.
NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
// Screen wake lock should be released after navigation.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, UnlockAfterNavigationToSelf) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
ScreenWakeLockInMainFrame();
// Navigate to the same document.
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
// Screen wake lock should be released after navigation to the same URL.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, KeepLockAfterInPageNavigation) {
GURL test_url(
embedded_test_server()->GetURL("/session_history/fragment.html"));
GURL test_in_page_url(test_url.spec() + "#a");
NavigateToURL(shell(), test_url);
ScreenWakeLockInMainFrame();
NavigateToURL(shell(), test_in_page_url);
EXPECT_TRUE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, UnlockAfterReload) {
NavigateToURL(shell(), embedded_test_server()->GetURL("/simple_page.html"));
ScreenWakeLockInMainFrame();
shell()->Reload();
WaitForLoadStop(GetWebContents());
// Screen wake lock should be released after reload.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, BrowserInitiatedFrameNavigation) {
NavigateToURL(shell(),
embedded_test_server()->GetURL("/frame_tree/2-4.html"));
EXPECT_FALSE(HasWakeLock());
// Lock screen in nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Screen wake lock should be present.
EXPECT_TRUE(HasWakeLock());
// Navigate the nested frame (browser-initiated).
NavigateFrameToURL(GetNestedFrameNode(),
embedded_test_server()->GetURL("/simple_page.html"));
WaitForPossibleUpdate();
// Screen wake lock should be released.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, RendererInitiatedFrameNavigation) {
NavigateToURL(shell(),
embedded_test_server()->GetURL("/frame_tree/2-4.html"));
EXPECT_FALSE(HasWakeLock());
// Lock screen in nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Screen wake lock should be present.
EXPECT_TRUE(HasWakeLock());
// Navigate the nested frame (renderer-initiated).
NavigateIframeToURL(GetWebContents(), "3-1-id",
embedded_test_server()->GetURL("/simple_page.html"));
WaitForPossibleUpdate();
// Screen wake lock should be released.
EXPECT_FALSE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, OutOfProcessFrame) {
NavigateToURL(shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(a)"));
EXPECT_FALSE(HasWakeLock());
// Ensure that the nested frame is same-process.
EXPECT_FALSE(GetNestedFrame()->IsCrossProcessSubframe());
// Lock screen in same-site nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
EXPECT_TRUE(HasWakeLock());
// Grab a watcher for the RenderFrameHost of the first site.
RenderFrameDeletedObserver frame_observer(
GetNestedFrameNode()->current_frame_host());
// Navigate nested frame to a cross-site document.
NavigateFrameToURL(GetNestedFrameNode(), embedded_test_server()->GetURL(
"b.com", "/simple_page.html"));
WaitForPossibleUpdate();
// Ensure that a new process has been created for the nested frame.
EXPECT_TRUE(GetNestedFrame()->IsCrossProcessSubframe());
// While the navigation to the second URL has completed, the teardown of the
// host-side objects for the first URL may not have yet. The WakeLock will
// only be released once the first renderer is cleaned up since it is held
// by that renderer.
// TODO(crbug.com/899384): This races with the new renderer then, would it
// cause us to release a WakeLock that it requested before the old renderer
// was torn down?
frame_observer.WaitUntilDeleted();
// Screen wake lock should be released.
EXPECT_FALSE(HasWakeLock());
// Lock screen in the cross-site nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
// Screen wake lock should be created.
EXPECT_TRUE(HasWakeLock());
}
IN_PROC_BROWSER_TEST_F(WakeLockTest, UnlockAfterCrashOutOfProcessFrame) {
// Load a page with cross-site iframe.
NavigateToURL(shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_FALSE(HasWakeLock());
// Ensure that a new process has been created for the nested frame.
EXPECT_TRUE(GetNestedFrame()->IsCrossProcessSubframe());
// Lock screen in cross-site nested frame.
EXPECT_TRUE(ExecuteScript(GetNestedFrame(), "screen.keepAwake = true;"));
WaitForPossibleUpdate();
EXPECT_TRUE(HasWakeLock());
// Crash process that owns the out-of-process frame.
RenderProcessHostWatcher watcher(
GetNestedFrame()->GetProcess(),
RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
GetNestedFrame()->GetProcess()->Shutdown(0);
watcher.Wait();
// Screen wake lock should be released.
EXPECT_FALSE(HasWakeLock());
}
} // namespace content