Commit 40e62c42 authored by Hailey Wang's avatar Hailey Wang Committed by Commit Bot

[PM] Add scoring in BackgroundTabLoadingPolicy

Added scoring of tabs before loading.

Used for loading tabs based on their priority.

Bug: 1059341
Change-Id: Ia059be9f35b16b01f58eb20db0057ff7e4278849
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2140298
Commit-Queue: Hailey Wang <haileywang@google.com>
Reviewed-by: default avatarSébastien Marchand <sebmarchand@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759298}
parent 2ea24bc3
...@@ -5,11 +5,16 @@ ...@@ -5,11 +5,16 @@
#include "chrome/browser/performance_manager/graph/policies/background_tab_loading_policy.h" #include "chrome/browser/performance_manager/graph/policies/background_tab_loading_policy.h"
#include "base/system/sys_info.h" #include "base/system/sys_info.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/performance_manager/graph/policies/background_tab_loading_policy_helpers.h" #include "chrome/browser/performance_manager/graph/policies/background_tab_loading_policy_helpers.h"
#include "chrome/browser/performance_manager/mechanisms/page_loader.h" #include "chrome/browser/performance_manager/mechanisms/page_loader.h"
#include "components/performance_manager/graph/page_node_impl.h"
#include "components/performance_manager/public/decorators/tab_properties_decorator.h" #include "components/performance_manager/public/decorators/tab_properties_decorator.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/policies/background_tab_loading_policy.h" #include "components/performance_manager/public/graph/policies/background_tab_loading_policy.h"
#include "components/performance_manager/public/performance_manager.h" #include "components/performance_manager/public/performance_manager.h"
#include "content/public/common/url_constants.h"
namespace performance_manager { namespace performance_manager {
...@@ -95,7 +100,7 @@ void BackgroundTabLoadingPolicy::OnIsLoadingChanged(const PageNode* page_node) { ...@@ -95,7 +100,7 @@ void BackgroundTabLoadingPolicy::OnIsLoadingChanged(const PageNode* page_node) {
// from the set of PageNodes for which a load needs to be initiated and from // from the set of PageNodes for which a load needs to be initiated and from
// the set of PageNodes for which a load has been initiated but hasn't // the set of PageNodes for which a load has been initiated but hasn't
// started. // started.
base::Erase(page_nodes_to_load_, page_node); ErasePageNodeToLoadData(page_node);
base::Erase(page_nodes_load_initiated_, page_node); base::Erase(page_nodes_load_initiated_, page_node);
// Keep track of all PageNodes that are loading, even when the load isn't // Keep track of all PageNodes that are loading, even when the load isn't
...@@ -117,12 +122,17 @@ void BackgroundTabLoadingPolicy::ScheduleLoadForRestoredTabs( ...@@ -117,12 +122,17 @@ void BackgroundTabLoadingPolicy::ScheduleLoadForRestoredTabs(
std::vector<PageNode*> page_nodes) { std::vector<PageNode*> page_nodes) {
for (auto* page_node : page_nodes) { for (auto* page_node : page_nodes) {
// Put the |page_node| in the queue for loading. // Put the |page_node| in the queue for loading.
DCHECK(!base::Contains(page_nodes_to_load_, page_node)); DCHECK(!FindPageNodeToLoadData(page_node));
page_nodes_to_load_.push_back(page_node);
DCHECK( DCHECK(
TabPropertiesDecorator::Data::FromPageNode(page_node)->IsInTabStrip()); TabPropertiesDecorator::Data::FromPageNode(page_node)->IsInTabStrip());
page_nodes_to_load_.push_back(
std::make_unique<PageNodeToLoadData>(page_node));
}
for (auto& page_node_to_load_data : page_nodes_to_load_) {
SetUsedInBackgroundAsync(page_node_to_load_data.get());
} }
MaybeLoadSomeTabs();
} }
void BackgroundTabLoadingPolicy::SetMockLoaderForTesting( void BackgroundTabLoadingPolicy::SetMockLoaderForTesting(
...@@ -148,6 +158,19 @@ BackgroundTabLoadingPolicy* BackgroundTabLoadingPolicy::GetInstance() { ...@@ -148,6 +158,19 @@ BackgroundTabLoadingPolicy* BackgroundTabLoadingPolicy::GetInstance() {
return g_background_tab_loading_policy; return g_background_tab_loading_policy;
} }
BackgroundTabLoadingPolicy::PageNodeToLoadData::PageNodeToLoadData(
PageNode* page_node)
: page_node(page_node) {}
BackgroundTabLoadingPolicy::PageNodeToLoadData::~PageNodeToLoadData() = default;
struct BackgroundTabLoadingPolicy::ScoredTabComparator {
bool operator()(const std::unique_ptr<PageNodeToLoadData>& tab0,
const std::unique_ptr<PageNodeToLoadData>& tab1) {
// Greater scores sort first.
return tab0->score > tab1->score;
}
};
bool BackgroundTabLoadingPolicy::ShouldLoad(const PageNode* page_node) { bool BackgroundTabLoadingPolicy::ShouldLoad(const PageNode* page_node) {
if (tab_loads_started_ < kMinTabsToLoad) if (tab_loads_started_ < kMinTabsToLoad)
return true; return true;
...@@ -166,16 +189,85 @@ bool BackgroundTabLoadingPolicy::ShouldLoad(const PageNode* page_node) { ...@@ -166,16 +189,85 @@ bool BackgroundTabLoadingPolicy::ShouldLoad(const PageNode* page_node) {
return false; return false;
} }
// TODO: Enforce the site engagement score for tabs that don't make use of // TODO(crbug.com/1071100): Enforce the site engagement score for tabs that
// background communication mechanisms. // don't make use of background communication mechanisms.
return true; return true;
} }
void BackgroundTabLoadingPolicy::OnUsedInBackgroundAvailable(
base::WeakPtr<PageNode> page_node) {
if (!page_node) {
// Ignore the value if the PageNode was deleted.
return;
}
PageNodeToLoadData* page_node_to_load_data =
FindPageNodeToLoadData(page_node.get());
if (!page_node_to_load_data) {
// Ignore the value if the PageNode is no longer in the list of PageNodes to
// load (it may already have started loading).
return;
}
// TODO(crbug.com/1071100): Use real |used_in_bg| data from the database.
DCHECK(!page_node_to_load_data->used_in_bg.has_value());
page_node_to_load_data->used_in_bg = false;
++tabs_scored_;
ScoreTab(page_node_to_load_data);
DispatchNotifyAllTabsScoredIfNeeded();
}
void BackgroundTabLoadingPolicy::ScoreTab(
PageNodeToLoadData* page_node_to_load_data) {
DCHECK_EQ(page_node_to_load_data->score, 0.0f);
float score = 0.0f;
// Give higher priorities to tabs used in the background, and lowest
// priority to internal tabs. Apps and pinned tabs are simply treated as
// normal tabs.
if (page_node_to_load_data->used_in_bg == true) {
score = 2;
} else if (!page_node_to_load_data->page_node->GetMainFrameUrl().SchemeIs(
content::kChromeUIScheme)) {
score = 1;
}
// Refine the score using the age of the tab. More recently used tabs have
// higher scores.
score += CalculateAgeScore(
page_node_to_load_data->page_node->GetTimeSinceLastVisibilityChange()
.InSecondsF());
page_node_to_load_data->score = score;
}
void BackgroundTabLoadingPolicy::SetUsedInBackgroundAsync(
PageNodeToLoadData* page_node_to_load_data) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&BackgroundTabLoadingPolicy::OnUsedInBackgroundAvailable,
weak_factory_.GetWeakPtr(),
std::move(PageNodeImpl::FromNode(page_node_to_load_data->page_node))
->GetWeakPtr()));
}
void BackgroundTabLoadingPolicy::DispatchNotifyAllTabsScoredIfNeeded() {
if (tabs_scored_ == page_nodes_to_load_.size()) {
NotifyAllTabsScored();
}
}
void BackgroundTabLoadingPolicy::NotifyAllTabsScored() {
std::stable_sort(page_nodes_to_load_.begin(), page_nodes_to_load_.end(),
ScoredTabComparator());
MaybeLoadSomeTabs();
}
void BackgroundTabLoadingPolicy::InitiateLoad(const PageNode* page_node) { void BackgroundTabLoadingPolicy::InitiateLoad(const PageNode* page_node) {
// Mark |page_node| as load initiated. Ensure that InitiateLoad is only called // Mark |page_node| as load initiated. Ensure that InitiateLoad is only called
// for a PageNode that is tracked by the policy. // for a PageNode that is tracked by the policy.
size_t num_removed = base::Erase(page_nodes_to_load_, page_node); ErasePageNodeToLoadData(page_node);
DCHECK_EQ(num_removed, 1U); DCHECK(!FindPageNodeToLoadData(page_node));
page_nodes_load_initiated_.push_back(page_node); page_nodes_load_initiated_.push_back(page_node);
tab_loads_started_++; tab_loads_started_++;
...@@ -184,7 +276,7 @@ void BackgroundTabLoadingPolicy::InitiateLoad(const PageNode* page_node) { ...@@ -184,7 +276,7 @@ void BackgroundTabLoadingPolicy::InitiateLoad(const PageNode* page_node) {
} }
void BackgroundTabLoadingPolicy::RemovePageNode(const PageNode* page_node) { void BackgroundTabLoadingPolicy::RemovePageNode(const PageNode* page_node) {
base::Erase(page_nodes_to_load_, page_node); ErasePageNodeToLoadData(page_node);
base::Erase(page_nodes_load_initiated_, page_node); base::Erase(page_nodes_load_initiated_, page_node);
base::Erase(page_nodes_loading_, page_node); base::Erase(page_nodes_loading_, page_node);
} }
...@@ -223,7 +315,7 @@ void BackgroundTabLoadingPolicy::LoadNextTab() { ...@@ -223,7 +315,7 @@ void BackgroundTabLoadingPolicy::LoadNextTab() {
// Find the next PageNode to load. // Find the next PageNode to load.
while (!page_nodes_to_load_.empty()) { while (!page_nodes_to_load_.empty()) {
const PageNode* page_node = page_nodes_to_load_.front(); const PageNode* page_node = page_nodes_to_load_.front()->page_node;
if (ShouldLoad(page_node)) { if (ShouldLoad(page_node)) {
InitiateLoad(page_node); InitiateLoad(page_node);
return; return;
...@@ -231,7 +323,7 @@ void BackgroundTabLoadingPolicy::LoadNextTab() { ...@@ -231,7 +323,7 @@ void BackgroundTabLoadingPolicy::LoadNextTab() {
// |page_node| should not be loaded at this time. Remove |page_node| from // |page_node| should not be loaded at this time. Remove |page_node| from
// the policy. // the policy.
base::Erase(page_nodes_to_load_, page_node); ErasePageNodeToLoadData(page_node);
} }
} }
...@@ -245,6 +337,37 @@ size_t BackgroundTabLoadingPolicy::GetFreePhysicalMemoryMib() const { ...@@ -245,6 +337,37 @@ size_t BackgroundTabLoadingPolicy::GetFreePhysicalMemoryMib() const {
return free_mem; return free_mem;
} }
void BackgroundTabLoadingPolicy::ErasePageNodeToLoadData(
const PageNode* page_node) {
for (auto& page_node_to_load_data : page_nodes_to_load_) {
if (page_node_to_load_data->page_node == page_node) {
if (page_node_to_load_data->used_in_bg.has_value()) {
// If the PageNode has already been scored, remove it from the
// |tabs_scored_| count.
--tabs_scored_;
base::Erase(page_nodes_to_load_, page_node_to_load_data);
} else {
base::Erase(page_nodes_to_load_, page_node_to_load_data);
// If the PageNode has not been scored yet, then removing it may trigger
// all tabs scored notification.
DispatchNotifyAllTabsScoredIfNeeded();
}
return;
}
}
}
BackgroundTabLoadingPolicy::PageNodeToLoadData*
BackgroundTabLoadingPolicy::FindPageNodeToLoadData(const PageNode* page_node) {
for (auto& page_node_to_load_data : page_nodes_to_load_) {
if (page_node_to_load_data->page_node == page_node) {
return page_node_to_load_data.get();
}
}
return nullptr;
}
} // namespace policies } // namespace policies
} // namespace performance_manager } // namespace performance_manager
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "components/performance_manager/public/graph/graph.h" #include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/page_node.h" #include "components/performance_manager/public/graph/page_node.h"
#include "url/gurl.h"
namespace performance_manager { namespace performance_manager {
...@@ -50,12 +51,52 @@ class BackgroundTabLoadingPolicy : public GraphOwned, ...@@ -50,12 +51,52 @@ class BackgroundTabLoadingPolicy : public GraphOwned,
static BackgroundTabLoadingPolicy* GetInstance(); static BackgroundTabLoadingPolicy* GetInstance();
private: private:
// Holds a handful of data about a tab which is used to prioritize it during
// session restore.
struct PageNodeToLoadData {
explicit PageNodeToLoadData(PageNode* page_node);
PageNodeToLoadData(const PageNodeToLoadData&) = delete;
~PageNodeToLoadData();
PageNodeToLoadData& operator=(const PageNodeToLoadData&) = delete;
// Keeps a pointer to the corresponding PageNode.
const PageNode* page_node;
// A higher value here means the tab has higher priority for restoring.
float score = 0.0f;
// Indicates whether or not the tab communicates with the user even when it
// is in the background (tab title changes, favicons, etc).
// It is initialized to nullopt and set asynchronously to the proper value.
base::Optional<bool> used_in_bg;
};
// Comparator used to sort PageNodeToLoadData.
struct ScoredTabComparator;
// Determines whether or not the given PageNode should be loaded. If this // Determines whether or not the given PageNode should be loaded. If this
// returns false, then the policy no longer attempts to load |page_node| and // returns false, then the policy no longer attempts to load |page_node| and
// removes it from the policy's internal state. This is called immediately // removes it from the policy's internal state. This is called immediately
// prior to trying to load the PageNode. // prior to trying to load the PageNode.
bool ShouldLoad(const PageNode* page_node); bool ShouldLoad(const PageNode* page_node);
// This will initialize |page_node_to_load_data->used_in_bg| to the proper
// value, score the tab and call DispatchNotifyAllTabsScoredIfNeeded().
void OnUsedInBackgroundAvailable(base::WeakPtr<PageNode> page_node);
// Calculates a |score| for the given tab.
void ScoreTab(PageNodeToLoadData* page_node_to_load_data);
// Schedule the task that will initialize |PageNodeToLoadData::used_in_bg|
// from the local site characteristics database.
void SetUsedInBackgroundAsync(PageNodeToLoadData* page_node_to_load_data);
// Invoke "NotifyAllTabsScored" if all tabs are scored.
void DispatchNotifyAllTabsScoredIfNeeded();
// Notifying that all tabs have final scores and starts loading.
void NotifyAllTabsScored();
// Move the PageNode from |page_nodes_to_load_| to // Move the PageNode from |page_nodes_to_load_| to
// |page_nodes_load_initiated_| and make the call to load the PageNode. // |page_nodes_load_initiated_| and make the call to load the PageNode.
void InitiateLoad(const PageNode* page_node); void InitiateLoad(const PageNode* page_node);
...@@ -80,12 +121,16 @@ class BackgroundTabLoadingPolicy : public GraphOwned, ...@@ -80,12 +121,16 @@ class BackgroundTabLoadingPolicy : public GraphOwned,
// Compute the amount of free memory on the system. // Compute the amount of free memory on the system.
size_t GetFreePhysicalMemoryMib() const; size_t GetFreePhysicalMemoryMib() const;
// Helper function for a vector of PageNodeToLoadData.
void ErasePageNodeToLoadData(const PageNode* page_node);
PageNodeToLoadData* FindPageNodeToLoadData(const PageNode* page_node);
// The mechanism used to load the pages. // The mechanism used to load the pages.
std::unique_ptr<performance_manager::mechanism::PageLoader> page_loader_; std::unique_ptr<performance_manager::mechanism::PageLoader> page_loader_;
// The set of PageNodes that have been restored for which we need to schedule // The set of PageNodes that have been restored for which we need to schedule
// loads. // loads.
std::vector<const PageNode*> page_nodes_to_load_; std::vector<std::unique_ptr<PageNodeToLoadData>> page_nodes_to_load_;
// The set of PageNodes that BackgroundTabLoadingPolicy has initiated loading, // The set of PageNodes that BackgroundTabLoadingPolicy has initiated loading,
// and for which we are waiting for the loading to actually start. This signal // and for which we are waiting for the loading to actually start. This signal
...@@ -103,6 +148,11 @@ class BackgroundTabLoadingPolicy : public GraphOwned, ...@@ -103,6 +148,11 @@ class BackgroundTabLoadingPolicy : public GraphOwned,
// increments this value. // increments this value.
size_t tab_loads_started_ = 0; size_t tab_loads_started_ = 0;
// The number of tabs for which an accurate initial score has been assigned.
// This is incremented only after all tab data is available, which
// may happen asynchronously.
size_t tabs_scored_ = 0;
// Used to overwrite the amount of free memory available on the system. // Used to overwrite the amount of free memory available on the system.
size_t free_memory_mb_for_testing_ = 0; size_t free_memory_mb_for_testing_ = 0;
...@@ -129,6 +179,11 @@ class BackgroundTabLoadingPolicy : public GraphOwned, ...@@ -129,6 +179,11 @@ class BackgroundTabLoadingPolicy : public GraphOwned,
// load. // load.
static constexpr uint32_t kCoresPerSimultaneousTabLoad = 2; static constexpr uint32_t kCoresPerSimultaneousTabLoad = 2;
// It's possible for this policy object to be destroyed while it has posted
// tasks. The tasks are bound to a weak pointer so that they are not executed
// after the policy object is destroyed.
base::WeakPtrFactory<BackgroundTabLoadingPolicy> weak_factory_{this};
FRIEND_TEST_ALL_PREFIXES(BackgroundTabLoadingPolicyTest, FRIEND_TEST_ALL_PREFIXES(BackgroundTabLoadingPolicyTest,
ShouldLoad_MaxTabsToRestore); ShouldLoad_MaxTabsToRestore);
FRIEND_TEST_ALL_PREFIXES(BackgroundTabLoadingPolicyTest, FRIEND_TEST_ALL_PREFIXES(BackgroundTabLoadingPolicyTest,
......
...@@ -85,6 +85,7 @@ TEST_F(BackgroundTabLoadingPolicyTest, ScheduleLoadForRestoredTabs) { ...@@ -85,6 +85,7 @@ TEST_F(BackgroundTabLoadingPolicyTest, ScheduleLoadForRestoredTabs) {
} }
policy()->ScheduleLoadForRestoredTabs(raw_page_nodes); policy()->ScheduleLoadForRestoredTabs(raw_page_nodes);
task_env().RunUntilIdle();
} }
TEST_F(BackgroundTabLoadingPolicyTest, AllLoadingSlotsUsed) { TEST_F(BackgroundTabLoadingPolicyTest, AllLoadingSlotsUsed) {
...@@ -113,6 +114,7 @@ TEST_F(BackgroundTabLoadingPolicyTest, AllLoadingSlotsUsed) { ...@@ -113,6 +114,7 @@ TEST_F(BackgroundTabLoadingPolicyTest, AllLoadingSlotsUsed) {
policy()->SetMaxSimultaneousLoadsForTesting(2); policy()->SetMaxSimultaneousLoadsForTesting(2);
policy()->ScheduleLoadForRestoredTabs(raw_page_nodes); policy()->ScheduleLoadForRestoredTabs(raw_page_nodes);
task_env().RunUntilIdle();
testing::Mock::VerifyAndClear(loader()); testing::Mock::VerifyAndClear(loader());
// Simulate load start of a PageNode that initiated load. // Simulate load start of a PageNode that initiated load.
...@@ -211,6 +213,74 @@ TEST_F(BackgroundTabLoadingPolicyTest, ShouldLoad_OldTab) { ...@@ -211,6 +213,74 @@ TEST_F(BackgroundTabLoadingPolicyTest, ShouldLoad_OldTab) {
EXPECT_FALSE(policy()->ShouldLoad(raw_page_node)); EXPECT_FALSE(policy()->ShouldLoad(raw_page_node));
} }
TEST_F(BackgroundTabLoadingPolicyTest, ScoreAndScheduleTabLoad) {
// Use 1 loading slot so only one PageNode loads at a time.
policy()->SetMaxSimultaneousLoadsForTesting(1);
// Create PageNodes with decreasing last visibility time (oldest to newest).
std::vector<
performance_manager::TestNodeWrapper<performance_manager::PageNodeImpl>>
page_nodes;
std::vector<PageNode*> raw_page_nodes;
// Add a old tab to restore.
page_nodes.push_back(CreateNode<performance_manager::PageNodeImpl>(
WebContentsProxy(), std::string(), GURL(), false, false,
base::TimeTicks::Now() - base::TimeDelta::FromDays(30)));
raw_page_nodes.push_back(page_nodes.back().get());
// Add a recent tab to restore.
page_nodes.push_back(CreateNode<performance_manager::PageNodeImpl>(
WebContentsProxy(), std::string(), GURL(), false, false,
base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1)));
raw_page_nodes.push_back(page_nodes.back().get());
// Add an internal page to restore.
page_nodes.push_back(CreateNode<performance_manager::PageNodeImpl>(
WebContentsProxy(), std::string(), GURL("chrome://newtab"), false, false,
base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1)));
raw_page_nodes.push_back(page_nodes.back().get());
// Set |is_tab| property as this is a requirement to pass the PageNode to
// ScheduleLoadForRestoredTabs().
for (auto* page_node : raw_page_nodes) {
TabPropertiesDecorator::SetIsTabForTesting(page_node, true);
}
// Test that the score produces the expected loading order
EXPECT_CALL(*loader(), LoadPageNode(raw_page_nodes[1]));
policy()->ScheduleLoadForRestoredTabs(raw_page_nodes);
task_env().RunUntilIdle();
testing::Mock::VerifyAndClear(loader());
PageNodeImpl* page_node_impl = page_nodes[1].get();
// Simulate load start of a PageNode that initiated load.
page_node_impl->SetIsLoading(true);
// The policy should allow one more PageNode to load after a PageNode finishes
// loading.
EXPECT_CALL(*loader(), LoadPageNode(raw_page_nodes[0]));
// Simulate load finish of a PageNode.
page_node_impl->SetIsLoading(false);
testing::Mock::VerifyAndClear(loader());
page_node_impl = page_nodes[0].get();
// Simulate load start of a PageNode that initiated load.
page_node_impl->SetIsLoading(true);
// The policy should allow one more PageNode to load after a PageNode finishes
// loading.
EXPECT_CALL(*loader(), LoadPageNode(raw_page_nodes[2]));
// Simulate load finish of a PageNode.
page_node_impl->SetIsLoading(false);
}
} // namespace policies } // namespace policies
} // namespace performance_manager } // namespace performance_manager
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