blob: 9e1394bb8bcb87a0f678840460ac5559d150ab45 [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 "chrome/browser/browsing_data/chrome_browsing_data_lifetime_manager.h"
#include <algorithm>
#include <limits>
#include <string>
#include <utility>
#include "base/containers/flat_set.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/scoped_observation.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "components/browsing_data/core/pref_names.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/web_contents.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"
#include "url/origin.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#else
#include "chrome/browser/android/tab_android.h"
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#endif
namespace {
constexpr int kInitialCleanupDelayInSeconds = 15;
constexpr int kDefaultCleanupPeriodInHours = 1;
using ScheduledRemovalSettings =
ChromeBrowsingDataLifetimeManager::ScheduledRemovalSettings;
// An observer of all the browsing data removal tasks that are started by the
// ChromeBrowsingDataLifetimeManager that records the the tasks starts and
// completed states as well as their durations.
class BrowsingDataRemoverObserver
: public content::BrowsingDataRemover::Observer {
public:
~BrowsingDataRemoverObserver() override = default;
// Creates an instance of BrowsingDataRemoverObserver that
// manages its own lifetime. The instance will be deleted after
// |OnBrowsingDataRemoverDone| is called. |keep_alive| is an optional
// parameter to pass to ensure that the browser does not initiates a shutdown
// before the browsing data clearing is complete.
static content::BrowsingDataRemover::Observer* Create(
content::BrowsingDataRemover* remover,
bool filterable_deletion,
Profile* profile,
bool keep_browser_alive = false) {
return new BrowsingDataRemoverObserver(remover, filterable_deletion,
profile, keep_browser_alive);
}
// content::BrowsingDataRemover::Observer:
void OnBrowsingDataRemoverDone(uint64_t failed_data_types) override {
base::UmaHistogramMediumTimes(duration_histogram(),
base::TimeTicks::Now() - start_time_);
// Having |keep_browser_alive_| being true means that the deletion that just
// finished was happening at the browser exit, therefore
// |kClearBrowsingDataOnExitDeletionPending| is no more necessary;
if (keep_browser_alive_) {
profile_->GetPrefs()->ClearPref(
browsing_data::prefs::kClearBrowsingDataOnExitDeletionPending);
}
base::UmaHistogramBoolean(state_histogram(),
/*BooleanStartedCompleted.Completed*/ true);
// The profile and browser should not be shutting down yet.
DCHECK(!keep_browser_alive_ || !profile_->ShutdownStarted());
delete this;
}
private:
BrowsingDataRemoverObserver(content::BrowsingDataRemover* remover,
bool filterable_deletion,
Profile* profile,
bool keep_browser_alive)
: start_time_(base::TimeTicks::Now()),
filterable_deletion_(filterable_deletion),
profile_(profile),
keep_browser_alive_(keep_browser_alive) {
#if !BUILDFLAG(IS_ANDROID)
if (keep_browser_alive) {
keep_alive_ = std::make_unique<ScopedKeepAlive>(
KeepAliveOrigin::BROWSING_DATA_LIFETIME_MANAGER,
KeepAliveRestartOption::DISABLED);
}
#endif
browsing_data_remover_observer_.Observe(remover);
base::UmaHistogramBoolean(state_histogram(),
/*BooleanStartedCompleted.Started*/ false);
}
const char* duration_histogram() const {
static constexpr char kDurationScheduledFilterableDeletion[] =
"History.BrowsingDataLifetime.Duration.ScheduledFilterableDeletion";
static constexpr char kDurationScheduledUnfilterableDeletion[] =
"History.BrowsingDataLifetime.Duration.ScheduledUnfilterableDeletion";
static constexpr char kDurationBrowserShutdownDeletion[] =
"History.BrowsingDataLifetime.Duration.BrowserShutdownDeletion";
return keep_browser_alive_
? kDurationBrowserShutdownDeletion
: filterable_deletion_ ? kDurationScheduledFilterableDeletion
: kDurationScheduledUnfilterableDeletion;
}
const char* state_histogram() const {
static constexpr char kStateScheduledFilterableDeletion[] =
"History.BrowsingDataLifetime.State.ScheduledFilterableDeletion";
static constexpr char kStateScheduledUnfilterableDeletion[] =
"History.BrowsingDataLifetime.State.ScheduledUnfilterableDeletion";
static constexpr char kStateBrowserShutdownDeletion[] =
"History.BrowsingDataLifetime.State.BrowserShutdownDeletion";
return keep_browser_alive_
? kStateBrowserShutdownDeletion
: filterable_deletion_ ? kStateScheduledFilterableDeletion
: kStateScheduledUnfilterableDeletion;
}
base::ScopedObservation<content::BrowsingDataRemover,
content::BrowsingDataRemover::Observer>
browsing_data_remover_observer_{this};
const base::TimeTicks start_time_;
const bool filterable_deletion_;
const raw_ptr<Profile> profile_;
bool keep_browser_alive_;
#if !BUILDFLAG(IS_ANDROID)
std::unique_ptr<ScopedKeepAlive> keep_alive_;
#endif
};
uint64_t GetOriginTypeMask(const base::Value::List& data_types) {
uint64_t result = 0;
for (const auto& data_type : data_types) {
std::string data_type_str = data_type.GetString();
if (data_type_str ==
browsing_data::policy_data_types::kCookiesAndOtherSiteData) {
result |= content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
} else if (data_type_str ==
browsing_data::policy_data_types::kHostedAppData) {
result |= content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
}
}
return result;
}
uint64_t GetRemoveMask(const base::Value::List& data_types) {
uint64_t result = 0;
for (const auto& data_type : data_types) {
std::string data_type_str = data_type.GetString();
if (data_type_str == browsing_data::policy_data_types::kBrowsingHistory) {
result |= chrome_browsing_data_remover::DATA_TYPE_HISTORY;
} else if (data_type_str ==
browsing_data::policy_data_types::kDownloadHistory) {
result |= content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
} else if (data_type_str ==
browsing_data::policy_data_types::kCookiesAndOtherSiteData) {
result |= chrome_browsing_data_remover::DATA_TYPE_SITE_DATA;
} else if (data_type_str ==
browsing_data::policy_data_types::kCachedImagesAndFiles) {
result |= content::BrowsingDataRemover::DATA_TYPE_CACHE;
} else if (data_type_str ==
browsing_data::policy_data_types::kPasswordSignin) {
result |= chrome_browsing_data_remover::DATA_TYPE_PASSWORDS;
} else if (data_type_str == browsing_data::policy_data_types::kAutofill) {
result |= chrome_browsing_data_remover::DATA_TYPE_FORM_DATA;
} else if (data_type_str ==
browsing_data::policy_data_types::kSiteSettings) {
result |= chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS;
} else if (data_type_str ==
browsing_data::policy_data_types::kHostedAppData) {
result |= chrome_browsing_data_remover::DATA_TYPE_SITE_DATA;
}
}
return result;
}
std::vector<ScheduledRemovalSettings> ConvertToScheduledRemovalSettings(
const base::Value::List& browsing_data_settings) {
std::vector<ScheduledRemovalSettings> scheduled_removals_settings;
for (const auto& setting : browsing_data_settings) {
const auto* data_types =
setting.FindListKey(browsing_data::policy_fields::kDataTypes);
const auto time_to_live_in_hours =
setting.FindIntKey(browsing_data::policy_fields::kTimeToLiveInHours);
scheduled_removals_settings.push_back(
{GetRemoveMask(data_types->GetList()),
GetOriginTypeMask(data_types->GetList()), *time_to_live_in_hours});
}
return scheduled_removals_settings;
}
base::flat_set<GURL> GetOpenedUrls(Profile* profile) {
base::flat_set<GURL> result;
// TODO (crbug/1288416): Enable this for android.
#if !BUILDFLAG(IS_ANDROID)
for (auto* browser : *BrowserList::GetInstance()) {
if (browser->profile() != profile) {
continue;
}
for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
result.insert(browser->tab_strip_model()->GetWebContentsAt(i)->GetURL());
}
}
#else
for (const TabModel* model : TabModelList::models()) {
for (int index = 0; index < model->GetTabCount(); ++index) {
TabAndroid* tab = model->GetTabAt(index);
if (tab)
result.insert(tab->GetURL());
}
}
#endif
return result;
}
} // namespace
namespace browsing_data {
namespace policy_data_types {
const char kBrowsingHistory[] = "browsing_history";
const char kDownloadHistory[] = "download_history";
const char kCookiesAndOtherSiteData[] = "cookies_and_other_site_data";
const char kCachedImagesAndFiles[] = "cached_images_and_files";
const char kPasswordSignin[] = "password_signin";
const char kAutofill[] = "autofill";
const char kSiteSettings[] = "site_settings";
const char kHostedAppData[] = "hosted_app_data";
} // namespace policy_data_types
namespace policy_fields {
const char kTimeToLiveInHours[] = "time_to_live_in_hours";
const char kDataTypes[] = "data_types";
} // namespace policy_fields
} // namespace browsing_data
ChromeBrowsingDataLifetimeManager::ChromeBrowsingDataLifetimeManager(
content::BrowserContext* browser_context)
: profile_(Profile::FromBrowserContext(browser_context)) {
DCHECK(!profile_->IsGuestSession() || profile_->IsOffTheRecord());
pref_change_registrar_.Init(profile_->GetPrefs());
pref_change_registrar_.Add(
browsing_data::prefs::kBrowsingDataLifetime,
base::BindRepeating(
&ChromeBrowsingDataLifetimeManager::UpdateScheduledRemovalSettings,
base::Unretained(this)));
// When the service is instantiated, wait a few minutes after Chrome startup
// to start deleting data.
content::GetUIThreadTaskRunner(
{
base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
})
->PostDelayedTask(FROM_HERE,
base::BindOnce(&ChromeBrowsingDataLifetimeManager::
UpdateScheduledRemovalSettings,
weak_ptr_factory_.GetWeakPtr()),
base::Seconds(kInitialCleanupDelayInSeconds));
}
ChromeBrowsingDataLifetimeManager::~ChromeBrowsingDataLifetimeManager() =
default;
void ChromeBrowsingDataLifetimeManager::Shutdown() {
pref_change_registrar_.RemoveAll();
weak_ptr_factory_.InvalidateWeakPtrs();
}
void ChromeBrowsingDataLifetimeManager::ClearBrowsingDataForOnExitPolicy(
bool keep_browser_alive) {
const base::Value::List& data_types = profile_->GetPrefs()->GetList(
browsing_data::prefs::kClearBrowsingDataOnExitList);
if (!data_types.empty() && !SyncServiceFactory::IsSyncAllowed(profile_)) {
profile_->GetPrefs()->SetBoolean(
browsing_data::prefs::kClearBrowsingDataOnExitDeletionPending, true);
auto* remover = profile_->GetBrowsingDataRemover();
// Add a ScopedKeepAlive to hold the browser shutdown until the browsing
// data is deleted and the profile is destroyed.
#if DCHECK_IS_ON()
if (browser_shutdown::HasShutdownStarted())
DCHECK(keep_browser_alive);
#endif
remover->RemoveAndReply(base::Time(), base::Time::Max(),
GetRemoveMask(data_types),
GetOriginTypeMask(data_types),
BrowsingDataRemoverObserver::Create(
remover, /*filterable_deletion=*/true, profile_,
keep_browser_alive));
} else {
profile_->GetPrefs()->ClearPref(
browsing_data::prefs::kClearBrowsingDataOnExitDeletionPending);
}
}
void ChromeBrowsingDataLifetimeManager::UpdateScheduledRemovalSettings() {
weak_ptr_factory_.InvalidateWeakPtrs();
scheduled_removals_settings_ =
ConvertToScheduledRemovalSettings(profile_->GetPrefs()->GetList(
browsing_data::prefs::kBrowsingDataLifetime));
if (!scheduled_removals_settings_.empty())
StartScheduledBrowsingDataRemoval();
}
void ChromeBrowsingDataLifetimeManager::StartScheduledBrowsingDataRemoval() {
content::BrowsingDataRemover* remover = profile_->GetBrowsingDataRemover();
for (auto& removal_settings : scheduled_removals_settings_) {
if (removal_settings.time_to_live_in_hours <= 0)
continue;
if (SyncServiceFactory::IsSyncAllowed(profile_))
continue;
auto deletion_end_time = end_time_for_testing_.value_or(
base::Time::Now() -
base::Hours(removal_settings.time_to_live_in_hours));
auto filterable_remove_mask =
removal_settings.remove_mask &
chrome_browsing_data_remover::FILTERABLE_DATA_TYPES;
if (filterable_remove_mask) {
auto filter_builder = content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::Mode::kPreserve);
for (const auto& url : GetOpenedUrls(profile_)) {
std::string domain = GetDomainAndRegistry(
url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (domain.empty())
domain = url.host(); // IP address or internal hostname.
filter_builder->AddRegisterableDomain(domain);
}
remover->RemoveWithFilterAndReply(
base::Time::Min(), deletion_end_time, filterable_remove_mask,
removal_settings.origin_type_mask, std::move(filter_builder),
testing_data_remover_observer_
? testing_data_remover_observer_.get()
: BrowsingDataRemoverObserver::Create(
remover, /*filterable_deletion=*/true, profile_));
}
auto unfilterable_remove_mask =
removal_settings.remove_mask &
~chrome_browsing_data_remover::FILTERABLE_DATA_TYPES;
if (unfilterable_remove_mask) {
remover->RemoveAndReply(
base::Time::Min(), deletion_end_time, unfilterable_remove_mask,
removal_settings.origin_type_mask,
testing_data_remover_observer_
? testing_data_remover_observer_.get()
: BrowsingDataRemoverObserver::Create(
remover, /*filterable_deletion=*/false, profile_));
}
}
content::GetUIThreadTaskRunner(
{
base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
})
->PostDelayedTask(FROM_HERE,
base::BindOnce(&ChromeBrowsingDataLifetimeManager::
StartScheduledBrowsingDataRemoval,
weak_ptr_factory_.GetWeakPtr()),
base::Hours(kDefaultCleanupPeriodInHours));
}