Use the Intervention Policy DB to assist discarding/freezing decisions
Other bug fix in this CL: Don't use the Local DB heuristics when an
extension try to discard a tab.
Bug: 773383
Change-Id: I1dd378a097d462793f25c382c9f96aa9381775d7
Reviewed-on: https://chromium-review.googlesource.com/1129620
Commit-Queue: Sébastien Marchand <sebmarchand@chromium.org>
Reviewed-by: Nico Weber <thakis@chromium.org>
Reviewed-by: Chris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#574312}
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 7cc7c39..9936798 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -876,7 +876,8 @@
if (!tab_manager_) {
tab_manager_ = std::make_unique<resource_coordinator::TabManager>();
tab_lifecycle_unit_source_ =
- std::make_unique<resource_coordinator::TabLifecycleUnitSource>();
+ std::make_unique<resource_coordinator::TabLifecycleUnitSource>(
+ tab_manager_->intervention_policy_database());
tab_lifecycle_unit_source_->AddObserver(tab_manager_.get());
}
return tab_manager_.get();
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.cc b/chrome/browser/resource_coordinator/intervention_policy_database.cc
index 3e43def5..6210a00 100644
--- a/chrome/browser/resource_coordinator/intervention_policy_database.cc
+++ b/chrome/browser/resource_coordinator/intervention_policy_database.cc
@@ -54,6 +54,13 @@
weak_factory_.GetWeakPtr()));
}
+void InterventionPolicyDatabase::AddOriginPoliciesForTesting(
+ const url::Origin& origin,
+ OriginInterventionPolicies policies) {
+ database_.emplace(SerializeOriginIntoDatabaseKey(origin),
+ std::move(policies));
+}
+
// static
InterventionPolicyDatabase::InterventionsMap
InterventionPolicyDatabase::ReadDatabaseFromProtoFileOnSequence(
diff --git a/chrome/browser/resource_coordinator/intervention_policy_database.h b/chrome/browser/resource_coordinator/intervention_policy_database.h
index 5558fde1..95463f5f 100644
--- a/chrome/browser/resource_coordinator/intervention_policy_database.h
+++ b/chrome/browser/resource_coordinator/intervention_policy_database.h
@@ -50,6 +50,9 @@
const base::Version& version,
std::unique_ptr<base::DictionaryValue> manifest);
+ void AddOriginPoliciesForTesting(const url::Origin& origin,
+ OriginInterventionPolicies policies);
+
protected:
// Map that associates the MD5 hash of an origin to its polices.
using InterventionsMap =
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 5482e3d..e29168f 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -16,11 +16,13 @@
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
#include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
#include "chrome/browser/resource_coordinator/tab_helper.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
+#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
#include "chrome/browser/resource_coordinator/tab_manager_features.h"
#include "chrome/browser/resource_coordinator/time.h"
@@ -219,6 +221,10 @@
DecisionFailureReason::HEURISTIC_TITLE);
}
+InterventionPolicyDatabase* GetInterventionPolicyDatabase() {
+ return TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+}
+
} // namespace
TabLifecycleUnitSource::TabLifecycleUnit::TabLifecycleUnit(
@@ -438,11 +444,24 @@
return false;
}
+ auto intervention_policy = GetInterventionPolicyDatabase()->GetFreezingPolicy(
+ url::Origin::Create(GetWebContents()->GetLastCommittedURL()));
+
+ switch (intervention_policy) {
+ case OriginInterventions::OPT_IN:
+ decision_details->AddReason(DecisionSuccessReason::GLOBAL_WHITELIST);
+ break;
+ case OriginInterventions::OPT_OUT:
+ decision_details->AddReason(DecisionFailureReason::GLOBAL_BLACKLIST);
+ break;
+ case OriginInterventions::DEFAULT:
+ break;
+ }
+
if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
decision_details->AddReason(DecisionFailureReason::LIVE_STATE_VISIBLE);
- CheckIfTabIsUsedInBackground(decision_details,
- false /* urgent_intervention */);
+ CheckIfTabIsUsedInBackground(decision_details, InterventionType::kProactive);
if (decision_details->reasons().empty()) {
decision_details->AddReason(
@@ -504,9 +523,26 @@
#endif // defined(OS_CHROMEOS)
}
-// We deliberately run through all of the logic without early termination.
-// This ensures that the decision details lists all possible reasons that the
-// transition can be denied.
+ // We deliberately run through all of the logic without early termination.
+ // This ensures that the decision details lists all possible reasons that the
+ // transition can be denied.
+
+ if (reason == DiscardReason::kProactive) {
+ auto intervention_policy =
+ GetInterventionPolicyDatabase()->GetDiscardingPolicy(
+ url::Origin::Create(GetWebContents()->GetLastCommittedURL()));
+
+ switch (intervention_policy) {
+ case OriginInterventions::OPT_IN:
+ decision_details->AddReason(DecisionSuccessReason::GLOBAL_WHITELIST);
+ break;
+ case OriginInterventions::OPT_OUT:
+ decision_details->AddReason(DecisionFailureReason::GLOBAL_BLACKLIST);
+ break;
+ case OriginInterventions::DEFAULT:
+ break;
+ }
+ }
#if defined(OS_CHROMEOS)
if (GetWebContents()->GetVisibility() == content::Visibility::VISIBLE)
@@ -535,7 +571,9 @@
}
CheckIfTabIsUsedInBackground(decision_details,
- reason == DiscardReason::kUrgent);
+ reason == DiscardReason::kProactive
+ ? InterventionType::kProactive
+ : InterventionType::kExternalOrUrgent);
if (decision_details->reasons().empty()) {
decision_details->AddReason(
@@ -818,7 +856,7 @@
void TabLifecycleUnitSource::TabLifecycleUnit::CheckIfTabIsUsedInBackground(
DecisionDetails* decision_details,
- bool urgent_intervention) const {
+ InterventionType intervention_type) const {
DCHECK(decision_details);
// We deliberately run through all of the logic without early termination.
@@ -830,9 +868,9 @@
// Consult the local database to see if this tab could try to communicate with
// the user while in background (don't check for the visibility here as
- // there's already a check for that above). Skip this test if this is an
- // urgent intervention.
- if (!urgent_intervention) {
+ // there's already a check for that above). Only do this for proactive
+ // interventions.
+ if (intervention_type == InterventionType::kProactive) {
CheckIfTabCanCommunicateWithUserWhileInBackground(GetWebContents(),
decision_details);
}
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
index 01aca6c..33516c1 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -113,6 +113,12 @@
friend class TabLifecycleUnitSource;
private:
+ // Indicates if an intervention (freezing or discarding) is proactive or not.
+ enum class InterventionType {
+ kProactive,
+ kExternalOrUrgent,
+ };
+
// Determines if the tab is a media tab, and populates an optional
// |decision_details| with full details.
bool IsMediaTabImpl(DecisionDetails* decision_details) const;
@@ -146,11 +152,11 @@
// Indicates if freezing or discarding this tab would be noticeable by the
// user even if it isn't brought back to the foreground. Populates
- // |decision_details| with full details. |urgent_intervention| indicates if
- // this is for an urgent intervention, in which case some heuristics will be
- // skipped.
+ // |decision_details| with full details. If |intervention_type| indicates that
+ // this is a proactive intervention then more heuristics will be
+ // applied.
void CheckIfTabIsUsedInBackground(DecisionDetails* decision_details,
- bool urgent_intervention) const;
+ InterventionType intervention_type) const;
// List of observers to notify when the discarded state or the auto-
// discardable state of this tab changes.
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index 5830d42..2d30144 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -47,13 +47,16 @@
DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitHolder);
};
-TabLifecycleUnitSource::TabLifecycleUnitSource()
- : browser_tab_strip_tracker_(this, nullptr, this) {
+TabLifecycleUnitSource::TabLifecycleUnitSource(
+ InterventionPolicyDatabase* intervention_policy_database)
+ : browser_tab_strip_tracker_(this, nullptr, this),
+ intervention_policy_database_(intervention_policy_database) {
DCHECK(!instance_);
// In unit tests, tabs might already exist when TabLifecycleUnitSource is
// instantiated. No TabLifecycleUnit is created for these tabs.
+ DCHECK(intervention_policy_database_);
browser_tab_strip_tracker_.Init();
instance_ = this;
// TODO(chrisha): Create a ScopedPageSignalObserver helper class to clean up
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index 06b65fd..25ac393 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -23,6 +23,7 @@
namespace resource_coordinator {
+class InterventionPolicyDatabase;
class TabLifecycleObserver;
class TabLifecycleUnitExternal;
@@ -32,7 +33,8 @@
public PageSignalObserver,
public TabStripModelObserver {
public:
- TabLifecycleUnitSource();
+ explicit TabLifecycleUnitSource(
+ InterventionPolicyDatabase* intervention_policy_database);
~TabLifecycleUnitSource() override;
static TabLifecycleUnitSource* GetInstance();
@@ -50,6 +52,10 @@
// Pretend that |tab_strip| is the TabStripModel of the focused window.
void SetFocusedTabStripModelForTesting(TabStripModel* tab_strip);
+ InterventionPolicyDatabase* intervention_policy_database() const {
+ return intervention_policy_database_;
+ }
+
class TabLifecycleUnitHolder;
private:
@@ -129,6 +135,10 @@
// changes.
base::ObserverList<TabLifecycleObserver> tab_lifecycle_observers_;
+ // The intervention policy database used to assist freezing/discarding
+ // decisions.
+ InterventionPolicyDatabase* intervention_policy_database_;
+
DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSource);
};
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 8ecd2df..23c9853f21 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -10,6 +10,8 @@
#include "base/observer_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/resource_coordinator/intervention_policy_database.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
@@ -79,6 +81,9 @@
void SetUp() override {
ChromeTestHarnessWithLocalDB::SetUp();
+ // Force TabManager/TabLifecycleUnitSource creation.
+ g_browser_process->GetTabManager();
+
std::unique_ptr<content::WebContents> test_web_contents =
CreateTestWebContents();
web_contents_ = test_web_contents.get();
@@ -168,14 +173,14 @@
EXPECT_FALSE(decision_details.IsPositive());
EXPECT_EQ(failure_reason, decision_details.FailureReason());
}
+
+ // Heuristics shouldn't be considered for urgent or external tab discarding.
{
DecisionDetails decision_details;
- EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal,
- &decision_details));
- EXPECT_FALSE(decision_details.IsPositive());
- EXPECT_EQ(failure_reason, decision_details.FailureReason());
+ EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal,
+ &decision_details));
+ EXPECT_TRUE(decision_details.IsPositive());
}
- // Heuristics shouldn't be considered for urgent tab discarding.
{
DecisionDetails decision_details;
EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent,
@@ -390,6 +395,104 @@
DecisionFailureReason::HEURISTIC_INSUFFICIENT_OBSERVATION, nullptr);
}
+TEST_F(TabLifecycleUnitTest, CannotProactivelyDiscardTabIfOriginOptedOut) {
+ InterventionPolicyDatabase* policy_db =
+ TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+ policy_db->AddOriginPoliciesForTesting(
+ url::Origin::Create(web_contents_->GetLastCommittedURL()),
+ {OriginInterventions::OPT_OUT, OriginInterventions::DEFAULT});
+
+ TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+ tab_strip_model_.get());
+ // Proactive discarding shouldn't be possible, urgent and external discarding
+ // should still be possible.
+ {
+ DecisionDetails decision_details;
+ EXPECT_FALSE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive,
+ &decision_details));
+ EXPECT_FALSE(decision_details.IsPositive());
+ EXPECT_EQ(DecisionFailureReason::GLOBAL_BLACKLIST,
+ decision_details.FailureReason());
+ EXPECT_EQ(1U, decision_details.reasons().size());
+ }
+ {
+ DecisionDetails decision_details;
+ EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kUrgent,
+ &decision_details));
+ EXPECT_TRUE(decision_details.IsPositive());
+ }
+ {
+ DecisionDetails decision_details;
+ EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kExternal,
+ &decision_details));
+ EXPECT_TRUE(decision_details.IsPositive());
+ }
+}
+
+TEST_F(TabLifecycleUnitTest, CannotFreezeTabIfOriginOptedOut) {
+ auto* policy_db =
+ TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+ policy_db->AddOriginPoliciesForTesting(
+ url::Origin::Create(web_contents_->GetLastCommittedURL()),
+ InterventionPolicyDatabase::OriginInterventionPolicies(
+ OriginInterventions::DEFAULT, OriginInterventions::OPT_OUT));
+
+ TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+ tab_strip_model_.get());
+ TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
+ LoadingState::LOADED);
+ DecisionDetails decision_details;
+ EXPECT_FALSE(tab_lifecycle_unit.CanFreeze(&decision_details));
+ EXPECT_FALSE(decision_details.IsPositive());
+ EXPECT_EQ(DecisionFailureReason::GLOBAL_BLACKLIST,
+ decision_details.FailureReason());
+}
+
+TEST_F(TabLifecycleUnitTest, OptInTabsGetsDiscarded) {
+ auto* policy_db =
+ TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+ policy_db->AddOriginPoliciesForTesting(
+ url::Origin::Create(web_contents_->GetLastCommittedURL()),
+ InterventionPolicyDatabase::OriginInterventionPolicies(
+ OriginInterventions::OPT_IN, OriginInterventions::DEFAULT));
+
+ TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+ tab_strip_model_.get());
+
+ // Mark the tab as recently audible, this should protect it from being
+ // discarded.
+ tab_lifecycle_unit.SetRecentlyAudible(true);
+
+ DecisionDetails decision_details;
+ EXPECT_TRUE(tab_lifecycle_unit.CanDiscard(DiscardReason::kProactive,
+ &decision_details));
+ EXPECT_TRUE(decision_details.IsPositive());
+ EXPECT_EQ(DecisionSuccessReason::GLOBAL_WHITELIST,
+ decision_details.SuccessReason());
+}
+
+TEST_F(TabLifecycleUnitTest, CanFreezeOptedInTabs) {
+ auto* policy_db =
+ TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
+ policy_db->AddOriginPoliciesForTesting(
+ url::Origin::Create(web_contents_->GetLastCommittedURL()),
+ {OriginInterventions::DEFAULT, OriginInterventions::OPT_IN});
+
+ TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
+ tab_strip_model_.get());
+ TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
+ LoadingState::LOADED);
+
+ // Mark the tab as recently audible, this should protect it from being frozen.
+ tab_lifecycle_unit.SetRecentlyAudible(true);
+
+ DecisionDetails decision_details;
+ EXPECT_TRUE(tab_lifecycle_unit.CanFreeze(&decision_details));
+ EXPECT_TRUE(decision_details.IsPositive());
+ EXPECT_EQ(DecisionSuccessReason::GLOBAL_WHITELIST,
+ decision_details.SuccessReason());
+}
+
TEST_F(TabLifecycleUnitTest, CannotFreezeAFrozenTab) {
TabLifecycleUnit tab_lifecycle_unit(&observers_, web_contents_,
tab_strip_model_.get());
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index 1a946f8..1df7b45 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -432,7 +432,8 @@
if (!tab_manager_) {
tab_manager_ = std::make_unique<resource_coordinator::TabManager>();
tab_lifecycle_unit_source_ =
- std::make_unique<resource_coordinator::TabLifecycleUnitSource>();
+ std::make_unique<resource_coordinator::TabLifecycleUnitSource>(
+ tab_manager_->intervention_policy_database());
tab_lifecycle_unit_source_->AddObserver(tab_manager_.get());
}
return tab_manager_.get();