blob: c108af6d6a1de8fd1b9dfa700b1b00eb005ef23c [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 "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h"
#include <stddef.h>
#include <vector>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/browser/browsing_data/browsing_data_helper.h"
#include "chrome/browser/browsing_data/browsing_data_important_sites_util.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
#include "chrome/browser/browsing_data/counters/browsing_data_counter_factory.h"
#include "chrome/browser/browsing_data/counters/browsing_data_counter_utils.h"
#include "chrome/browser/history/web_history_service_factory.h"
#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/browsing_data/core/history_notice_utils.h"
#include "components/browsing_data/core/pref_names.h"
#include "components/feature_engagement/buildflags.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "ui/base/text/bytes_formatting.h"
#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker.h"
#include "chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_factory.h"
#endif
using BrowsingDataType = browsing_data::BrowsingDataType;
namespace {
const int kMaxTimesHistoryNoticeShown = 1;
// TODO(msramek): Get the list of deletion preferences from the JS side.
const char* kCounterPrefsAdvanced[] = {
browsing_data::prefs::kDeleteBrowsingHistory,
browsing_data::prefs::kDeleteCache,
browsing_data::prefs::kDeleteCookies,
browsing_data::prefs::kDeleteDownloadHistory,
browsing_data::prefs::kDeleteFormData,
browsing_data::prefs::kDeleteHostedAppsData,
browsing_data::prefs::kDeletePasswords,
browsing_data::prefs::kDeleteSiteSettings,
};
// Additional counters for the basic tab of CBD.
const char* kCounterPrefsBasic[] = {
browsing_data::prefs::kDeleteCacheBasic,
};
} // namespace
namespace settings {
// ClearBrowsingDataHandler ----------------------------------------------------
ClearBrowsingDataHandler::ClearBrowsingDataHandler(content::WebUI* webui)
: profile_(Profile::FromWebUI(webui)),
sync_service_(ProfileSyncServiceFactory::GetForProfile(profile_)),
sync_service_observer_(this),
show_history_deletion_dialog_(false),
weak_ptr_factory_(this) {}
ClearBrowsingDataHandler::~ClearBrowsingDataHandler() {
}
void ClearBrowsingDataHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"clearBrowsingData",
base::BindRepeating(&ClearBrowsingDataHandler::HandleClearBrowsingData,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"initializeClearBrowsingData",
base::BindRepeating(&ClearBrowsingDataHandler::HandleInitialize,
base::Unretained(this)));
}
void ClearBrowsingDataHandler::OnJavascriptAllowed() {
if (sync_service_)
sync_service_observer_.Add(sync_service_);
DCHECK(counters_.empty());
for (const std::string& pref : kCounterPrefsBasic) {
AddCounter(BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
browsing_data::ClearBrowsingDataTab::BASIC);
}
for (const std::string& pref : kCounterPrefsAdvanced) {
AddCounter(BrowsingDataCounterFactory::GetForProfileAndPref(profile_, pref),
browsing_data::ClearBrowsingDataTab::ADVANCED);
}
PrefService* prefs = profile_->GetPrefs();
period_ = std::make_unique<IntegerPrefMember>();
period_->Init(
browsing_data::prefs::kDeleteTimePeriod, prefs,
base::BindRepeating(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
base::Unretained(this)));
periodBasic_ = std::make_unique<IntegerPrefMember>();
periodBasic_->Init(
browsing_data::prefs::kDeleteTimePeriodBasic, prefs,
base::BindRepeating(&ClearBrowsingDataHandler::HandleTimePeriodChanged,
base::Unretained(this)));
}
void ClearBrowsingDataHandler::OnJavascriptDisallowed() {
sync_service_observer_.RemoveAll();
weak_ptr_factory_.InvalidateWeakPtrs();
counters_.clear();
period_.reset();
periodBasic_.reset();
}
void ClearBrowsingDataHandler::HandleClearBrowsingDataForTest() {
// HandleClearBrowsingData takes in a ListValue as its only parameter. The
// ListValue must contain four values: web_ui callback ID, a list of data
// types that the user cleared from the clear browsing data UI and time period
// of the data to be cleared.
std::unique_ptr<base::ListValue> data_types =
std::make_unique<base::ListValue>();
data_types->AppendString("browser.clear_data.browsing_history");
base::ListValue list_args;
list_args.AppendString("webui_callback_id");
list_args.Append(std::move(data_types));
list_args.AppendInteger(1u);
HandleClearBrowsingData(&list_args);
}
void ClearBrowsingDataHandler::HandleClearBrowsingData(
const base::ListValue* args) {
CHECK_EQ(3U, args->GetSize());
std::string webui_callback_id;
CHECK(args->GetString(0, &webui_callback_id));
PrefService* prefs = profile_->GetPrefs();
int site_data_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA;
// Don't try to clear LSO data if it's not supported.
if (!prefs->GetBoolean(prefs::kClearPluginLSODataEnabled))
site_data_mask &= ~ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA;
int remove_mask = 0;
int origin_mask = 0;
std::vector<BrowsingDataType> data_type_vector;
const base::ListValue* data_type_list = nullptr;
CHECK(args->GetList(1, &data_type_list));
for (const base::Value& type : *data_type_list) {
std::string pref_name;
CHECK(type.GetAsString(&pref_name));
BrowsingDataType data_type =
browsing_data::GetDataTypeFromDeletionPreference(pref_name);
data_type_vector.push_back(data_type);
switch (data_type) {
case BrowsingDataType::HISTORY:
if (prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory))
remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY;
break;
case BrowsingDataType::DOWNLOADS:
if (prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory))
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
break;
case BrowsingDataType::CACHE:
remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CACHE;
break;
case BrowsingDataType::COOKIES:
remove_mask |= site_data_mask;
origin_mask |=
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
break;
case BrowsingDataType::PASSWORDS:
remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS;
break;
case BrowsingDataType::FORM_DATA:
remove_mask |= ChromeBrowsingDataRemoverDelegate::DATA_TYPE_FORM_DATA;
break;
case BrowsingDataType::SITE_SETTINGS:
remove_mask |=
ChromeBrowsingDataRemoverDelegate::DATA_TYPE_CONTENT_SETTINGS;
break;
case BrowsingDataType::HOSTED_APPS_DATA:
remove_mask |= site_data_mask;
origin_mask |= content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
break;
case BrowsingDataType::BOOKMARKS:
// Only implemented on Android.
NOTREACHED();
break;
case BrowsingDataType::NUM_TYPES:
NOTREACHED();
break;
}
}
base::flat_set<BrowsingDataType> data_types(std::move(data_type_vector));
// Record the deletion of cookies and cache.
content::BrowsingDataRemover::CookieOrCacheDeletionChoice choice =
content::BrowsingDataRemover::NEITHER_COOKIES_NOR_CACHE;
if (data_types.find(BrowsingDataType::COOKIES) != data_types.end()) {
choice = data_types.find(BrowsingDataType::CACHE) != data_types.end()
? content::BrowsingDataRemover::BOTH_COOKIES_AND_CACHE
: content::BrowsingDataRemover::ONLY_COOKIES;
} else if (data_types.find(BrowsingDataType::CACHE) != data_types.end()) {
choice = content::BrowsingDataRemover::ONLY_CACHE;
}
UMA_HISTOGRAM_ENUMERATION(
"History.ClearBrowsingData.UserDeletedCookieOrCacheFromDialog", choice,
content::BrowsingDataRemover::MAX_CHOICE_VALUE);
// Record the circumstances under which passwords are deleted.
if (data_types.find(BrowsingDataType::PASSWORDS) != data_types.end()) {
static const BrowsingDataType other_types[] = {
BrowsingDataType::HISTORY, BrowsingDataType::DOWNLOADS,
BrowsingDataType::CACHE, BrowsingDataType::COOKIES,
BrowsingDataType::FORM_DATA, BrowsingDataType::HOSTED_APPS_DATA,
};
static size_t num_other_types = base::size(other_types);
int checked_other_types =
std::count_if(other_types, other_types + num_other_types,
[&data_types](BrowsingDataType type) {
return data_types.find(type) != data_types.end();
});
base::UmaHistogramSparse(
"History.ClearBrowsingData.PasswordsDeletion.AdditionalDatatypesCount",
checked_other_types);
}
std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion>
scoped_data_deletion;
// If Sync is running, prevent it from being paused during the operation.
// However, if Sync is in error, clearing cookies should pause it.
if (!profile_->IsGuestSession() &&
sync_ui_util::GetStatus(profile_) == sync_ui_util::SYNCED) {
// Settings can not be opened in incognito windows.
DCHECK(!profile_->IsOffTheRecord());
scoped_data_deletion = AccountReconcilorFactory::GetForProfile(profile_)
->GetScopedSyncDataDeletion();
}
int period_selected;
CHECK(args->GetInteger(2, &period_selected));
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(profile_);
base::OnceClosure callback =
base::BindOnce(&ClearBrowsingDataHandler::OnClearingTaskFinished,
weak_ptr_factory_.GetWeakPtr(), webui_callback_id,
std::move(data_types), std::move(scoped_data_deletion));
browsing_data::TimePeriod time_period =
static_cast<browsing_data::TimePeriod>(period_selected);
browsing_data_important_sites_util::Remove(
remove_mask, origin_mask, time_period,
content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::BLACKLIST),
remover, std::move(callback));
#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
feature_engagement::IncognitoWindowTrackerFactory::GetInstance()
->GetForProfile(profile_)
->OnBrowsingDataCleared();
#endif
}
void ClearBrowsingDataHandler::OnClearingTaskFinished(
const std::string& webui_callback_id,
const base::flat_set<BrowsingDataType>& data_types,
std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion) {
PrefService* prefs = profile_->GetPrefs();
int notice_shown_times = prefs->GetInteger(
browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
// When the deletion is complete, we might show an additional dialog with
// a notice about other forms of browsing history. This is the case if
const bool show_notice =
// 1. The dialog is relevant for the user.
show_history_deletion_dialog_ &&
// 2. The notice has been shown less than |kMaxTimesHistoryNoticeShown|.
notice_shown_times < kMaxTimesHistoryNoticeShown &&
// 3. The selected data types contained browsing history.
data_types.find(BrowsingDataType::HISTORY) != data_types.end();
if (show_notice) {
// Increment the preference.
prefs->SetInteger(
browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes,
notice_shown_times + 1);
}
UMA_HISTOGRAM_BOOLEAN(
"History.ClearBrowsingData.ShownHistoryNoticeAfterClearing", show_notice);
ResolveJavascriptCallback(base::Value(webui_callback_id),
base::Value(show_notice));
}
void ClearBrowsingDataHandler::HandleInitialize(const base::ListValue* args) {
AllowJavascript();
const base::Value* callback_id;
CHECK(args->Get(0, &callback_id));
// Needed because WebUI doesn't handle renderer crashes. See crbug.com/610450.
weak_ptr_factory_.InvalidateWeakPtrs();
UpdateSyncState();
RefreshHistoryNotice();
// Restart the counters each time the dialog is reopened.
for (const auto& counter : counters_)
counter->Restart();
ResolveJavascriptCallback(*callback_id, base::Value() /* Promise<void> */);
}
void ClearBrowsingDataHandler::OnStateChanged(syncer::SyncService* sync) {
UpdateSyncState();
}
void ClearBrowsingDataHandler::UpdateSyncState() {
identity::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_);
FireWebUIListener(
"update-sync-state",
base::Value(identity_manager && identity_manager->HasPrimaryAccount()),
base::Value(sync_service_ && sync_service_->IsSyncFeatureActive() &&
sync_service_->GetActiveDataTypes().Has(
syncer::HISTORY_DELETE_DIRECTIVES)),
base::Value(
browsing_data_counter_utils::ShouldShowCookieException(profile_)));
}
void ClearBrowsingDataHandler::RefreshHistoryNotice() {
// If the dialog with history notice has been shown less than
// |kMaxTimesHistoryNoticeShown| times, we might have to show it when the
// user deletes history. Find out if the conditions are met.
int notice_shown_times = profile_->GetPrefs()->GetInteger(
browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes);
if (notice_shown_times < kMaxTimesHistoryNoticeShown) {
browsing_data::ShouldPopupDialogAboutOtherFormsOfBrowsingHistory(
sync_service_,
WebHistoryServiceFactory::GetForProfile(profile_),
chrome::GetChannel(),
base::Bind(&ClearBrowsingDataHandler::UpdateHistoryDeletionDialog,
weak_ptr_factory_.GetWeakPtr()));
}
}
void ClearBrowsingDataHandler::UpdateHistoryDeletionDialog(bool show) {
// This is used by OnClearingTaskFinished (when the deletion finishes).
show_history_deletion_dialog_ = show;
}
void ClearBrowsingDataHandler::AddCounter(
std::unique_ptr<browsing_data::BrowsingDataCounter> counter,
browsing_data::ClearBrowsingDataTab tab) {
DCHECK(counter);
counter->Init(profile_->GetPrefs(), tab,
base::Bind(&ClearBrowsingDataHandler::UpdateCounterText,
base::Unretained(this)));
counters_.push_back(std::move(counter));
}
void ClearBrowsingDataHandler::UpdateCounterText(
std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
FireWebUIListener(
"update-counter-text", base::Value(result->source()->GetPrefName()),
base::Value(browsing_data_counter_utils::GetChromeCounterTextFromResult(
result.get(), profile_)));
}
void ClearBrowsingDataHandler::HandleTimePeriodChanged(
const std::string& pref_name) {
PrefService* prefs = profile_->GetPrefs();
int period = prefs->GetInteger(pref_name);
browsing_data::TimePeriod time_period =
static_cast<browsing_data::TimePeriod>(period);
browsing_data::RecordTimePeriodChange(time_period);
}
} // namespace settings