blob: bae0a64e41bd259779b9f07fff6c2ca8e81413cb [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/clear_site_data_utils.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "content/browser/browser_context_impl.h"
#include "content/browser/browsing_data/browsing_data_remover_impl.h"
#include "content/public/browser/browser_context.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/storage_partition_config.h"
#include "content/public/browser/web_contents.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
namespace content {
namespace {
// Finds the BrowserContext associated with the request and requests
// the actual clearing of data for `origin`. The data types to be deleted
// are determined by `clear_site_data_types` and `storage_buckets_to_remove`.
// `web_contents_getter` identifies the WebContents from which the request
// originated. Must be run on the UI thread. The `callback` will be executed
// on the IO thread.
class SiteDataClearer : public BrowsingDataRemover::Observer {
public:
SiteDataClearer(
BrowserContext* browser_context,
std::optional<StoragePartitionConfig> storage_partition_config,
const url::Origin& origin,
const ClearSiteDataTypeSet clear_site_data_types,
const std::set<std::string>& storage_buckets_to_remove,
bool avoid_closing_connections,
std::optional<net::CookiePartitionKey> cookie_partition_key,
std::optional<blink::StorageKey> storage_key,
bool partitioned_state_allowed_only,
base::OnceClosure callback)
: storage_partition_config_(std::move(storage_partition_config)),
origin_(origin),
clear_site_data_types_(clear_site_data_types),
storage_buckets_to_remove_(storage_buckets_to_remove),
avoid_closing_connections_(avoid_closing_connections),
cookie_partition_key_(std::move(cookie_partition_key)),
storage_key_(std::move(storage_key)),
partitioned_state_allowed_only_(partitioned_state_allowed_only),
callback_(std::move(callback)),
pending_task_count_(0),
remover_(nullptr) {
remover_ =
BrowserContextImpl::From(browser_context)->GetBrowsingDataRemover();
DCHECK(remover_);
scoped_observation_.Observe(remover_.get());
}
~SiteDataClearer() override {
// This SiteDataClearer class is self-owned, and the only way for it to be
// destroyed should be the "delete this" part in
// OnBrowsingDataRemoverDone() function, and it invokes the |callback_|. So
// when this destructor is called, the |callback_| should be null.
DCHECK(!callback_);
}
void RunAndDestroySelfWhenDone() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Cookies and channel IDs are scoped to
// a) eTLD+1 of |origin|'s host if |origin|'s host is a registrable domain
// or a subdomain thereof
// b) |origin|'s host exactly if it is an IP address or an internal hostname
// (e.g. "localhost" or "fileserver").
if (clear_site_data_types_.Has(ClearSiteDataType::kCookies)) {
std::string domain = GetDomainAndRegistry(
origin_,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (domain.empty()) {
domain = origin_.host(); // IP address or internal hostname.
}
std::unique_ptr<BrowsingDataFilterBuilder> cookie_filter_builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
cookie_filter_builder->AddRegisterableDomain(domain);
cookie_filter_builder->SetCookiePartitionKeyCollection(
net::CookiePartitionKeyCollection(cookie_partition_key_));
cookie_filter_builder->SetPartitionedCookiesOnly(
partitioned_state_allowed_only_);
if (storage_partition_config_.has_value()) {
cookie_filter_builder->SetStoragePartitionConfig(
storage_partition_config_.value());
}
pending_task_count_++;
uint64_t remove_mask =
BrowsingDataRemover::DATA_TYPE_COOKIES |
BrowsingDataRemover::DATA_TYPE_DEVICE_BOUND_SESSIONS;
if (avoid_closing_connections_) {
remove_mask |= BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
}
remover_->RemoveWithFilterAndReply(
base::Time(), base::Time::Max(), remove_mask,
BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB,
std::move(cookie_filter_builder), this);
}
// Storage buckets
if (!storage_buckets_to_remove_.empty()) {
pending_task_count_++;
// For storage buckets, no mask is being passed per se. Therefore, when
// the storage buckets are successfully removed, the `failed_data_types`
// arg should be set to 0 to align with existing behaviour in this class.
remover_->RemoveStorageBucketsAndReply(
storage_partition_config_,
storage_key_.value_or(blink::StorageKey::CreateFirstParty(origin_)),
storage_buckets_to_remove_,
base::BindOnce(&SiteDataClearer::OnBrowsingDataRemoverDone,
weak_factory_.GetWeakPtr(), 0));
}
// Delete origin-scoped data.
uint64_t remove_mask = 0;
if (clear_site_data_types_.Has(ClearSiteDataType::kStorage)) {
remove_mask |= BrowsingDataRemover::DATA_TYPE_DOM_STORAGE;
remove_mask |= BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX;
remove_mask |= BrowsingDataRemover::DATA_TYPE_DEVICE_BOUND_SESSIONS;
// Internal data should not be removed by site-initiated deletions.
remove_mask &= ~BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX_INTERNAL;
// Some deletions should also be more narrow for Clear-Site-Data, to avoid
// sites from hostilely interfering with each other where a user-initiated
// deletion would be conservative.
remove_mask &= ~BrowsingDataRemover::DATA_TYPE_INTEREST_GROUPS_USER_CLEAR;
}
if (clear_site_data_types_.Has(ClearSiteDataType::kCache)) {
remove_mask |= BrowsingDataRemover::DATA_TYPE_CACHE;
}
if (clear_site_data_types_.Has(ClearSiteDataType::kPrefetchCache)) {
remove_mask |= BrowsingDataRemover::DATA_TYPE_PREFETCH_CACHE;
}
if (clear_site_data_types_.Has(ClearSiteDataType::kPrerenderCache)) {
remove_mask |= BrowsingDataRemover::DATA_TYPE_PRERENDER_CACHE;
}
if (remove_mask) {
std::unique_ptr<BrowsingDataFilterBuilder> origin_filter_builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
origin_filter_builder->AddOrigin(origin_);
origin_filter_builder->SetStorageKey(storage_key_);
if (storage_partition_config_.has_value()) {
origin_filter_builder->SetStoragePartitionConfig(
storage_partition_config_.value());
}
pending_task_count_++;
remover_->RemoveWithFilterAndReply(
base::Time(), base::Time::Max(), remove_mask,
BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB,
std::move(origin_filter_builder), this);
}
// We clear client hints for both cookie and cache clears.
if (clear_site_data_types_.HasAny({ClearSiteDataType::kCookies,
ClearSiteDataType::kCache,
ClearSiteDataType::kClientHints})) {
pending_task_count_++;
// For client hints, no mask is being passed per se. Therefore, when
// the client hints are successfully removed, the `failed_data_types`
// arg should be set to 0 to align with existing behaviour in this class.
remover_->ClearClientHintCacheAndReply(
origin_, base::BindOnce(&SiteDataClearer::OnBrowsingDataRemoverDone,
weak_factory_.GetWeakPtr(), 0));
}
DCHECK_GT(pending_task_count_, 0);
}
private:
// BrowsingDataRemover::Observer:
void OnBrowsingDataRemoverDone(uint64_t failed_data_types) override {
DCHECK(pending_task_count_);
if (--pending_task_count_) {
return;
}
std::move(callback_).Run();
delete this;
}
const std::optional<StoragePartitionConfig> storage_partition_config_;
const url::Origin origin_;
const ClearSiteDataTypeSet clear_site_data_types_;
const std::set<std::string> storage_buckets_to_remove_;
const bool avoid_closing_connections_;
const std::optional<net::CookiePartitionKey> cookie_partition_key_;
const std::optional<blink::StorageKey> storage_key_;
const bool partitioned_state_allowed_only_;
base::OnceClosure callback_;
int pending_task_count_ = 0;
raw_ptr<BrowsingDataRemoverImpl> remover_ = nullptr;
base::ScopedObservation<BrowsingDataRemover, BrowsingDataRemover::Observer>
scoped_observation_{this};
base::WeakPtrFactory<SiteDataClearer> weak_factory_{this};
};
} // namespace
void ClearSiteData(
base::WeakPtr<BrowserContext> browser_context,
std::optional<StoragePartitionConfig> storage_partition_config,
const url::Origin& origin,
const ClearSiteDataTypeSet clear_site_data_types,
const std::set<std::string>& storage_buckets_to_remove,
bool avoid_closing_connections,
std::optional<net::CookiePartitionKey> cookie_partition_key,
std::optional<blink::StorageKey> storage_key,
bool partitioned_state_allowed_only,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// It's not possible to clear all storage and also only specific buckets.
DCHECK(!clear_site_data_types.Has(ClearSiteDataType::kStorage) ||
storage_buckets_to_remove.empty());
if (!browser_context) {
std::move(callback).Run();
return;
}
(new SiteDataClearer(browser_context.get(), storage_partition_config, origin,
clear_site_data_types, storage_buckets_to_remove,
avoid_closing_connections, cookie_partition_key,
storage_key, partitioned_state_allowed_only,
std::move(callback)))
->RunAndDestroySelfWhenDone();
}
} // namespace content