blob: 396b9d731c0417825f3f91c7178472cc9a83678c [file] [log] [blame]
// Copyright (c) 2012 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.
// Defines the Chrome Extensions BrowsingData API functions, which entail
// clearing browsing data, and clearing the browser's cache (which, let's be
// honest, are the same thing), as specified in the extension API JSON.
#include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
#include <string>
#include <utility>
#include "base/task/post_task.h"
#include "base/values.h"
#include "chrome/browser/browsing_data/browsing_data_helper.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
#include "chrome/browser/plugins/plugin_data_remover_helper.h"
#include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/browsing_data/core/pref_names.h"
#include "components/signin/core/browser/signin_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
using content::BrowserThread;
using browsing_data::ClearBrowsingDataTab;
using browsing_data::BrowsingDataType;
namespace extension_browsing_data_api_constants {
// Parameter name keys.
const char kDataRemovalPermittedKey[] = "dataRemovalPermitted";
const char kDataToRemoveKey[] = "dataToRemove";
const char kOptionsKey[] = "options";
// Type keys.
const char kAppCacheKey[] = "appcache";
const char kCacheKey[] = "cache";
const char kChannelIDsKey[] = "serverBoundCertificates";
const char kCookiesKey[] = "cookies";
const char kDownloadsKey[] = "downloads";
const char kFileSystemsKey[] = "fileSystems";
const char kFormDataKey[] = "formData";
const char kHistoryKey[] = "history";
const char kIndexedDBKey[] = "indexedDB";
const char kLocalStorageKey[] = "localStorage";
const char kPasswordsKey[] = "passwords";
const char kPluginDataKey[] = "pluginData";
const char kServiceWorkersKey[] = "serviceWorkers";
const char kCacheStorageKey[] = "cacheStorage";
const char kWebSQLKey[] = "webSQL";
// Option keys.
const char kExtensionsKey[] = "extension";
const char kOriginTypesKey[] = "originTypes";
const char kProtectedWebKey[] = "protectedWeb";
const char kSinceKey[] = "since";
const char kUnprotectedWebKey[] = "unprotectedWeb";
// Errors!
// The placeholder will be filled by the name of the affected data type (e.g.,
// "history").
const char kBadDataTypeDetails[] = "Invalid value for data type '%s'.";
const char kDeleteProhibitedError[] =
"Browsing history and downloads are not "
"permitted to be removed.";
} // namespace extension_browsing_data_api_constants
namespace {
int MaskForKey(const char* key) {
if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_APP_CACHE;
if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_CACHE;
if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0) {
return content::BrowsingDataRemover::DATA_TYPE_COOKIES;
}
if (strcmp(key, extension_browsing_data_api_constants::kDownloadsKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS;
if (strcmp(key, extension_browsing_data_api_constants::kFormDataKey) == 0)
return ChromeBrowsingDataRemoverDelegate::DATA_TYPE_FORM_DATA;
if (strcmp(key, extension_browsing_data_api_constants::kHistoryKey) == 0)
return ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY;
if (strcmp(key, extension_browsing_data_api_constants::kIndexedDBKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_INDEXED_DB;
if (strcmp(key, extension_browsing_data_api_constants::kLocalStorageKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE;
if (strcmp(key,
extension_browsing_data_api_constants::kChannelIDsKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_CHANNEL_IDS;
if (strcmp(key, extension_browsing_data_api_constants::kPasswordsKey) == 0)
return ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS;
if (strcmp(key, extension_browsing_data_api_constants::kPluginDataKey) == 0)
return ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA;
if (strcmp(key, extension_browsing_data_api_constants::kServiceWorkersKey) ==
0)
return content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS;
if (strcmp(key, extension_browsing_data_api_constants::kCacheStorageKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE;
if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0)
return content::BrowsingDataRemover::DATA_TYPE_WEB_SQL;
return 0;
}
// Returns false if any of the selected data types are not allowed to be
// deleted.
bool IsRemovalPermitted(int removal_mask, PrefService* prefs) {
// Enterprise policy or user preference might prohibit deleting browser or
// download history.
if ((removal_mask & ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY) ||
(removal_mask & content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS)) {
return prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory);
}
return true;
}
// Returns true if Sync is currently running (i.e. enabled and not in error).
bool IsSyncRunning(Profile* profile) {
syncer::SyncService* sync_service =
ProfileSyncServiceFactory::GetSyncServiceForBrowserContext(profile);
SigninManagerBase* signin_manager =
SigninManagerFactory::GetForProfile(profile);
sync_ui_util::MessageType sync_status =
sync_ui_util::GetStatus(profile, sync_service, *signin_manager);
return sync_status == sync_ui_util::SYNCED;
}
} // namespace
bool BrowsingDataSettingsFunction::isDataTypeSelected(
BrowsingDataType data_type,
ClearBrowsingDataTab tab) {
std::string pref_name;
bool success = GetDeletionPreferenceFromDataType(data_type, tab, &pref_name);
return success && prefs_->GetBoolean(pref_name);
}
ExtensionFunction::ResponseAction BrowsingDataSettingsFunction::Run() {
prefs_ = Profile::FromBrowserContext(browser_context())->GetPrefs();
ClearBrowsingDataTab tab = static_cast<ClearBrowsingDataTab>(
prefs_->GetInteger(browsing_data::prefs::kLastClearBrowsingDataTab));
// Fill origin types.
// The "cookies" and "hosted apps" UI checkboxes both map to
// REMOVE_SITE_DATA in browsing_data_remover.h, the former for the unprotected
// web, the latter for protected web data. There is no UI control for
// extension data.
std::unique_ptr<base::DictionaryValue> origin_types(
new base::DictionaryValue);
origin_types->SetBoolean(
extension_browsing_data_api_constants::kUnprotectedWebKey,
isDataTypeSelected(BrowsingDataType::COOKIES, tab));
origin_types->SetBoolean(
extension_browsing_data_api_constants::kProtectedWebKey,
isDataTypeSelected(BrowsingDataType::HOSTED_APPS_DATA, tab));
origin_types->SetBoolean(
extension_browsing_data_api_constants::kExtensionsKey, false);
// Fill deletion time period.
int period_pref =
prefs_->GetInteger(browsing_data::GetTimePeriodPreferenceName(tab));
browsing_data::TimePeriod period =
static_cast<browsing_data::TimePeriod>(period_pref);
double since = 0;
if (period != browsing_data::TimePeriod::ALL_TIME) {
base::Time time = browsing_data::CalculateBeginDeleteTime(period);
since = time.ToJsTime();
}
std::unique_ptr<base::DictionaryValue> options(new base::DictionaryValue);
options->Set(extension_browsing_data_api_constants::kOriginTypesKey,
std::move(origin_types));
options->SetDouble(extension_browsing_data_api_constants::kSinceKey, since);
// Fill dataToRemove and dataRemovalPermitted.
std::unique_ptr<base::DictionaryValue> selected(new base::DictionaryValue);
std::unique_ptr<base::DictionaryValue> permitted(new base::DictionaryValue);
bool delete_site_data =
isDataTypeSelected(BrowsingDataType::COOKIES, tab) ||
isDataTypeSelected(BrowsingDataType::HOSTED_APPS_DATA, tab);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kAppCacheKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kCookiesKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kFileSystemsKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kIndexedDBKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kLocalStorageKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kWebSQLKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kChannelIDsKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kServiceWorkersKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kCacheStorageKey,
delete_site_data);
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kPluginDataKey,
delete_site_data &&
prefs_->GetBoolean(prefs::kClearPluginLSODataEnabled));
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kHistoryKey,
isDataTypeSelected(BrowsingDataType::HISTORY, tab));
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kDownloadsKey,
isDataTypeSelected(BrowsingDataType::DOWNLOADS, tab));
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kCacheKey,
isDataTypeSelected(BrowsingDataType::CACHE, tab));
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kFormDataKey,
isDataTypeSelected(BrowsingDataType::FORM_DATA, tab));
SetDetails(selected.get(), permitted.get(),
extension_browsing_data_api_constants::kPasswordsKey,
isDataTypeSelected(BrowsingDataType::PASSWORDS, tab));
std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue);
result->Set(extension_browsing_data_api_constants::kOptionsKey,
std::move(options));
result->Set(extension_browsing_data_api_constants::kDataToRemoveKey,
std::move(selected));
result->Set(extension_browsing_data_api_constants::kDataRemovalPermittedKey,
std::move(permitted));
return RespondNow(OneArgument(std::move(result)));
}
void BrowsingDataSettingsFunction::SetDetails(
base::DictionaryValue* selected_dict,
base::DictionaryValue* permitted_dict,
const char* data_type,
bool is_selected) {
bool is_permitted = IsRemovalPermitted(MaskForKey(data_type), prefs_);
selected_dict->SetBoolean(data_type, is_selected && is_permitted);
permitted_dict->SetBoolean(data_type, is_permitted);
}
BrowsingDataRemoverFunction::BrowsingDataRemoverFunction() : observer_(this) {}
void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
synced_data_deletion_.reset();
observer_.RemoveAll();
this->SendResponse(true);
Release(); // Balanced in RunAsync.
}
bool BrowsingDataRemoverFunction::RunAsync() {
// If we don't have a profile, something's pretty wrong.
DCHECK(GetProfile());
// Grab the initial |options| parameter, and parse out the arguments.
base::DictionaryValue* options;
EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options));
DCHECK(options);
EXTENSION_FUNCTION_VALIDATE(
ParseOriginTypeMask(*options, &origin_type_mask_));
// If |ms_since_epoch| isn't set, default it to 0.
double ms_since_epoch;
if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
&ms_since_epoch))
ms_since_epoch = 0;
// base::Time takes a double that represents seconds since epoch. JavaScript
// gives developers milliseconds, so do a quick conversion before populating
// the object. Also, Time::FromDoubleT converts double time 0 to empty Time
// object. So we need to do special handling here.
remove_since_ = (ms_since_epoch == 0) ?
base::Time::UnixEpoch() :
base::Time::FromDoubleT(ms_since_epoch / 1000.0);
EXTENSION_FUNCTION_VALIDATE(GetRemovalMask(&removal_mask_));
// Check for prohibited data types.
if (!IsRemovalPermitted(removal_mask_, GetProfile()->GetPrefs())) {
error_ = extension_browsing_data_api_constants::kDeleteProhibitedError;
return false;
}
if (removal_mask_ &
ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA) {
// If we're being asked to remove plugin data, check whether it's actually
// supported.
PostTaskWithTraits(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
base::TaskPriority::USER_VISIBLE},
base::BindOnce(
&BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported,
this, PluginPrefs::GetForProfile(GetProfile())));
} else {
StartRemoving();
}
// Will finish asynchronously.
return true;
}
BrowsingDataRemoverFunction::~BrowsingDataRemoverFunction() {}
bool BrowsingDataRemoverFunction::IsPauseSyncAllowed() {
return true;
}
void BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported(
scoped_refptr<PluginPrefs> plugin_prefs) {
if (!PluginDataRemoverHelper::IsSupported(plugin_prefs.get()))
removal_mask_ &= ~ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA;
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&BrowsingDataRemoverFunction::StartRemoving, this));
}
void BrowsingDataRemoverFunction::StartRemoving() {
Profile* profile = GetProfile();
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(profile);
// Add a ref (Balanced in OnBrowsingDataRemoverDone)
AddRef();
// Prevent Sync from being paused, if required.
DCHECK(!synced_data_deletion_);
if (!IsPauseSyncAllowed() && IsSyncRunning(profile)) {
synced_data_deletion_ = AccountReconcilorFactory::GetForProfile(profile)
->GetScopedSyncDataDeletion();
}
// Create a BrowsingDataRemover, set the current object as an observer (so
// that we're notified after removal) and call remove() with the arguments
// we've generated above. We can use a raw pointer here, as the browsing data
// remover is responsible for deleting itself once data removal is complete.
observer_.Add(remover);
remover->RemoveAndReply(
remove_since_, base::Time::Max(),
removal_mask_, origin_type_mask_, this);
}
bool BrowsingDataRemoverFunction::ParseOriginTypeMask(
const base::DictionaryValue& options,
int* origin_type_mask) {
// Parse the |options| dictionary to generate the origin set mask. Default to
// UNPROTECTED_WEB if the developer doesn't specify anything.
*origin_type_mask = content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB;
const base::DictionaryValue* d = NULL;
if (options.HasKey(extension_browsing_data_api_constants::kOriginTypesKey)) {
if (!options.GetDictionary(
extension_browsing_data_api_constants::kOriginTypesKey, &d)) {
return false;
}
bool value;
// The developer specified something! Reset to 0 and parse the dictionary.
*origin_type_mask = 0;
// Unprotected web.
if (d->HasKey(extension_browsing_data_api_constants::kUnprotectedWebKey)) {
if (!d->GetBoolean(
extension_browsing_data_api_constants::kUnprotectedWebKey,
&value)) {
return false;
}
*origin_type_mask |=
value ? content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB : 0;
}
// Protected web.
if (d->HasKey(extension_browsing_data_api_constants::kProtectedWebKey)) {
if (!d->GetBoolean(
extension_browsing_data_api_constants::kProtectedWebKey,
&value)) {
return false;
}
*origin_type_mask |=
value ? content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB : 0;
}
// Extensions.
if (d->HasKey(extension_browsing_data_api_constants::kExtensionsKey)) {
if (!d->GetBoolean(extension_browsing_data_api_constants::kExtensionsKey,
&value)) {
return false;
}
*origin_type_mask |=
value ? ChromeBrowsingDataRemoverDelegate::ORIGIN_TYPE_EXTENSION : 0;
}
}
return true;
}
// Parses the |dataToRemove| argument to generate the removal mask.
// Returns false if parse was not successful, i.e. if 'dataToRemove' is not
// present or any data-type keys don't have supported (boolean) values.
bool BrowsingDataRemoveFunction::GetRemovalMask(int* removal_mask) {
base::DictionaryValue* data_to_remove;
if (!args_->GetDictionary(1, &data_to_remove))
return false;
*removal_mask = 0;
for (base::DictionaryValue::Iterator i(*data_to_remove);
!i.IsAtEnd();
i.Advance()) {
bool selected = false;
if (!i.value().GetAsBoolean(&selected))
return false;
if (selected)
*removal_mask |= MaskForKey(i.key().c_str());
}
return true;
}
bool BrowsingDataRemoveFunction::IsPauseSyncAllowed() {
return false;
}
bool BrowsingDataRemoveAppcacheFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_APP_CACHE;
return true;
}
bool BrowsingDataRemoveCacheFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_CACHE;
return true;
}
bool BrowsingDataRemoveCookiesFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_COOKIES |
content::BrowsingDataRemover::DATA_TYPE_CHANNEL_IDS;
return true;
}
bool BrowsingDataRemoveDownloadsFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS;
return true;
}
bool BrowsingDataRemoveFileSystemsFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS;
return true;
}
bool BrowsingDataRemoveFormDataFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_FORM_DATA;
return true;
}
bool BrowsingDataRemoveHistoryFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY;
return true;
}
bool BrowsingDataRemoveIndexedDBFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_INDEXED_DB;
return true;
}
bool BrowsingDataRemoveLocalStorageFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE;
return true;
}
bool BrowsingDataRemovePluginDataFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA;
return true;
}
bool BrowsingDataRemovePasswordsFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS;
return true;
}
bool BrowsingDataRemoveServiceWorkersFunction::GetRemovalMask(
int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS;
return true;
}
bool BrowsingDataRemoveCacheStorageFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE;
return true;
}
bool BrowsingDataRemoveWebSQLFunction::GetRemovalMask(int* removal_mask) {
*removal_mask = content::BrowsingDataRemover::DATA_TYPE_WEB_SQL;
return true;
}