Commit 578251db authored by Sebastien Marchand's avatar Sebastien Marchand Committed by Chromium LUCI CQ

[PM] Add the page freezer mechanism

This will be used by the freezing policy to freeze pages.

Bug: 1144025
Change-Id: Ica001f0f3f5288200f1c082cc48e9a3019be9368
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2584605
Commit-Queue: Sébastien Marchand <sebmarchand@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835982}
parent d92823c7
...@@ -1097,6 +1097,8 @@ static_library("browser") { ...@@ -1097,6 +1097,8 @@ static_library("browser") {
"performance_manager/decorators/process_metrics_decorator.h", "performance_manager/decorators/process_metrics_decorator.h",
"performance_manager/decorators/process_priority_aggregator.cc", "performance_manager/decorators/process_priority_aggregator.cc",
"performance_manager/decorators/process_priority_aggregator.h", "performance_manager/decorators/process_priority_aggregator.h",
"performance_manager/mechanisms/page_freezer.cc",
"performance_manager/mechanisms/page_freezer.h",
"performance_manager/mechanisms/working_set_trimmer.cc", "performance_manager/mechanisms/working_set_trimmer.cc",
"performance_manager/mechanisms/working_set_trimmer.h", "performance_manager/mechanisms/working_set_trimmer.h",
"performance_manager/metrics/memory_pressure_metrics.cc", "performance_manager/metrics/memory_pressure_metrics.cc",
......
// Copyright 2020 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 "chrome/browser/performance_manager/mechanisms/page_freezer.h"
#include "base/bind.h"
#include "base/task/task_traits.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/performance_manager/public/graph/page_node.h"
#include "components/performance_manager/public/web_contents_proxy.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_result.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
namespace performance_manager {
namespace mechanism {
namespace {
// Try to freeze a page on the UI thread.
void MaybeFreezePageOnUIThread(const WebContentsProxy& contents_proxy) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::WebContents* const contents = contents_proxy.Get();
if (!contents)
return;
// Page with the notification permission shouldn't be frozen as this is a
// strong signal that the user wants to receive updates from this page while
// it's in background. This information isn't available in the PM graph, this
// has to be checked on the UI thread.
auto notif_permission =
PermissionManagerFactory::GetForProfile(
Profile::FromBrowserContext(contents->GetBrowserContext()))
->GetPermissionStatus(ContentSettingsType::NOTIFICATIONS,
contents->GetLastCommittedURL(),
contents->GetLastCommittedURL());
if (notif_permission.content_setting == CONTENT_SETTING_ALLOW)
return;
contents->SetPageFrozen(true);
}
void UnfreezePageOnUIThread(const WebContentsProxy& contents_proxy) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::WebContents* const content = contents_proxy.Get();
if (!content)
return;
content->SetPageFrozen(false);
}
} // namespace
void PageFreezer::MaybeFreezePageNode(const PageNode* page_node) {
DCHECK(page_node);
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&MaybeFreezePageOnUIThread,
page_node->GetContentsProxy()));
}
void PageFreezer::UnfreezePageNode(const PageNode* page_node) {
DCHECK(page_node);
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&UnfreezePageOnUIThread, page_node->GetContentsProxy()));
}
} // namespace mechanism
} // namespace performance_manager
// Copyright 2020 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.
#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_PAGE_FREEZER_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_PAGE_FREEZER_H_
namespace performance_manager {
class PageNode;
namespace mechanism {
// Mechanism to freeze a PageNode.
class PageFreezer {
public:
PageFreezer() = default;
~PageFreezer() = default;
PageFreezer(const PageFreezer& other) = delete;
PageFreezer& operator=(const PageFreezer&) = delete;
// Attempt to freeze |page_node|.
void MaybeFreezePageNode(const PageNode* page_node);
// Unfreeze |page_node|.
void UnfreezePageNode(const PageNode* page_node);
};
} // namespace mechanism
} // namespace performance_manager
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_MECHANISMS_PAGE_FREEZER_H_
// Copyright 2020 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 "chrome/browser/performance_manager/mechanisms/page_freezer.h"
#include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/performance_manager/public/performance_manager.h"
#include "components/performance_manager/test_support/performance_manager_test_harness.h"
#include "components/performance_manager/test_support/test_harness_helper.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_result.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace performance_manager {
namespace mechanism {
namespace {
static constexpr char kUrl[] = "https://www.foo.com/";
void MaybeFreezePageNode(content::WebContents* content) {
base::RunLoop run_loop;
auto quit_closure = run_loop.QuitClosure();
PerformanceManager::CallOnGraph(
FROM_HERE, base::BindOnce(
[](base::WeakPtr<PageNode> page_node,
base::OnceClosure quit_closure) {
EXPECT_TRUE(page_node);
mechanism::PageFreezer freezer;
freezer.MaybeFreezePageNode(page_node.get());
std::move(quit_closure).Run();
},
PerformanceManager::GetPageNodeForWebContents(content),
std::move(quit_closure)));
run_loop.Run();
}
void UnfreezePageNode(content::WebContents* content) {
base::RunLoop run_loop;
auto quit_closure = run_loop.QuitClosure();
PerformanceManager::CallOnGraph(
FROM_HERE, base::BindOnce(
[](base::WeakPtr<PageNode> page_node,
base::OnceClosure quit_closure) {
EXPECT_TRUE(page_node);
mechanism::PageFreezer freezer;
freezer.UnfreezePageNode(page_node.get());
std::move(quit_closure).Run();
},
PerformanceManager::GetPageNodeForWebContents(content),
std::move(quit_closure)));
run_loop.Run();
}
} // namespace
class PageFreezerTest : public ChromeRenderViewHostTestHarness {
public:
PageFreezerTest() = default;
~PageFreezerTest() override = default;
PageFreezerTest(const PageFreezerTest& other) = delete;
PageFreezerTest& operator=(const PageFreezerTest&) = delete;
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
pm_harness_.SetUp();
}
void TearDown() override {
pm_harness_.TearDown();
ChromeRenderViewHostTestHarness::TearDown();
}
private:
performance_manager::PerformanceManagerTestHarnessHelper pm_harness_;
};
TEST_F(PageFreezerTest, FreezeAndUnfreezePage) {
SetContents(CreateTestWebContents());
content::WebContentsTester* web_contents_tester =
content::WebContentsTester::For(web_contents());
EXPECT_TRUE(web_contents_tester);
web_contents_tester->NavigateAndCommit(GURL(kUrl));
MaybeFreezePageNode(web_contents());
EXPECT_TRUE(web_contents_tester->IsPageFrozen());
UnfreezePageNode(web_contents());
EXPECT_FALSE(web_contents_tester->IsPageFrozen());
}
TEST_F(PageFreezerTest, CantFreezePageWithNotificationPermission) {
SetContents(CreateTestWebContents());
// Give the notification permission to |web_contents()|.
permissions::PermissionRequestManager::CreateForWebContents(web_contents());
content_settings::PageSpecificContentSettings::CreateForWebContents(
web_contents(),
std::make_unique<chrome::PageSpecificContentSettingsDelegate>(
web_contents()));
auto* manager =
permissions::PermissionRequestManager::FromWebContents(web_contents());
permissions::MockPermissionPromptFactory mock_prompt_factory(manager);
NavigateAndCommit(GURL(kUrl));
manager->DocumentOnLoadCompletedInMainFrame();
base::RunLoop run_loop;
PermissionManagerFactory::GetForProfile(profile())->RequestPermission(
ContentSettingsType::NOTIFICATIONS, main_rfh(), GURL(kUrl), true,
base::BindOnce([](ContentSetting content_setting) {
EXPECT_EQ(content_setting, CONTENT_SETTING_ALLOW);
}).Then(run_loop.QuitClosure()));
task_environment()->RunUntilIdle();
ASSERT_TRUE(manager->IsRequestInProgress());
manager->Accept();
run_loop.Run();
// Try to freeze the page node, this should fail.
MaybeFreezePageNode(web_contents());
EXPECT_FALSE(content::WebContentsTester::For(web_contents())->IsPageFrozen());
}
} // namespace mechanism
} // namespace performance_manager
...@@ -3592,6 +3592,7 @@ test("unit_tests") { ...@@ -3592,6 +3592,7 @@ test("unit_tests") {
"../browser/performance_manager/decorators/page_aggregator_unittest.cc", "../browser/performance_manager/decorators/page_aggregator_unittest.cc",
"../browser/performance_manager/decorators/process_metrics_decorator_unittest.cc", "../browser/performance_manager/decorators/process_metrics_decorator_unittest.cc",
"../browser/performance_manager/decorators/process_priority_aggregator_unittest.cc", "../browser/performance_manager/decorators/process_priority_aggregator_unittest.cc",
"../browser/performance_manager/mechanisms/page_freezer_unittest.cc",
"../browser/performance_manager/metrics/memory_pressure_metrics_unittest.cc", "../browser/performance_manager/metrics/memory_pressure_metrics_unittest.cc",
"../browser/performance_manager/observers/isolation_context_metrics_unittest.cc", "../browser/performance_manager/observers/isolation_context_metrics_unittest.cc",
"../browser/performance_manager/observers/metrics_collector_unittest.cc", "../browser/performance_manager/observers/metrics_collector_unittest.cc",
......
...@@ -168,6 +168,10 @@ class WebContentsTester { ...@@ -168,6 +168,10 @@ class WebContentsTester {
std::unique_ptr<WebContents> portal_web_contents) = 0; std::unique_ptr<WebContents> portal_web_contents) = 0;
virtual WebContents* GetPortalContents( virtual WebContents* GetPortalContents(
const blink::PortalToken& portal_token) = 0; const blink::PortalToken& portal_token) = 0;
// Indicates if this WebContents has been frozen via a call to
// SetPageFrozen().
virtual bool IsPageFrozen() = 0;
}; };
} // namespace content } // namespace content
......
...@@ -51,7 +51,8 @@ TestWebContents::TestWebContents(BrowserContext* browser_context) ...@@ -51,7 +51,8 @@ TestWebContents::TestWebContents(BrowserContext* browser_context)
expect_set_history_offset_and_length_(false), expect_set_history_offset_and_length_(false),
expect_set_history_offset_and_length_history_length_(0), expect_set_history_offset_and_length_history_length_(0),
pause_subresource_loading_called_(false), pause_subresource_loading_called_(false),
audio_group_id_(base::UnguessableToken::Create()) { audio_group_id_(base::UnguessableToken::Create()),
is_page_frozen_(false) {
if (!RenderProcessHostImpl::get_render_process_host_factory_for_testing()) { if (!RenderProcessHostImpl::get_render_process_host_factory_for_testing()) {
// Most unit tests should prefer to create a generic MockRenderProcessHost // Most unit tests should prefer to create a generic MockRenderProcessHost
// (instead of a real RenderProcessHostImpl). Tests that need to use a // (instead of a real RenderProcessHostImpl). Tests that need to use a
...@@ -205,6 +206,10 @@ void TestWebContents::OnWebPreferencesChanged() { ...@@ -205,6 +206,10 @@ void TestWebContents::OnWebPreferencesChanged() {
++*web_preferences_changed_counter_; ++*web_preferences_changed_counter_;
} }
bool TestWebContents::IsPageFrozen() {
return is_page_frozen_;
}
bool TestWebContents::TestDidDownloadImage( bool TestWebContents::TestDidDownloadImage(
const GURL& url, const GURL& url,
int http_status_code, int http_status_code,
...@@ -453,4 +458,8 @@ WebContents* TestWebContents::GetPortalContents( ...@@ -453,4 +458,8 @@ WebContents* TestWebContents::GetPortalContents(
return portal->GetPortalContents(); return portal->GetPortalContents();
} }
void TestWebContents::SetPageFrozen(bool frozen) {
is_page_frozen_ = frozen;
}
} // namespace content } // namespace content
...@@ -161,6 +161,8 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { ...@@ -161,6 +161,8 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester {
web_preferences_changed_counter_ = counter; web_preferences_changed_counter_ = counter;
} }
bool IsPageFrozen() override;
protected: protected:
// The deprecated WebContentsTester still needs to subclass this. // The deprecated WebContentsTester still needs to subclass this.
explicit TestWebContents(BrowserContext* browser_context); explicit TestWebContents(BrowserContext* browser_context);
...@@ -195,6 +197,7 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { ...@@ -195,6 +197,7 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester {
const std::string& headers, const std::string& headers,
const base::string16& suggested_filename) override; const base::string16& suggested_filename) override;
void ReattachToOuterWebContentsFrame() override {} void ReattachToOuterWebContentsFrame() override {}
void SetPageFrozen(bool frozen) override;
RenderViewHostDelegateView* delegate_view_override_; RenderViewHostDelegateView* delegate_view_override_;
...@@ -213,6 +216,7 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { ...@@ -213,6 +216,7 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester {
base::Optional<base::string16> title_; base::Optional<base::string16> title_;
bool pause_subresource_loading_called_; bool pause_subresource_loading_called_;
base::UnguessableToken audio_group_id_; base::UnguessableToken audio_group_id_;
bool is_page_frozen_;
}; };
} // namespace content } // namespace content
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment