[go: nahoru, domu]

blob: daee2338b983d1c4652f6d30e8df150aa4f28389 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/browsing_data/content/browsing_data_model.h"
#include <set>
#include <string>
#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/containers/enum_set.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/overloaded.h"
#include "base/memory/weak_ptr.h"
#include "components/attribution_reporting/features.h"
#include "components/browsing_data/content/browsing_data_quota_helper.h"
#include "components/browsing_data/content/shared_worker_info.h"
#include "components/browsing_data/core/features.h"
#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
#include "components/services/storage/shared_storage/shared_storage_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/dom_storage_context.h"
#include "content/public/browser/private_aggregation_data_model.h"
#include "content/public/browser/session_storage_usage_info.h"
#include "content/public/browser/shared_worker_service.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/browser/storage_usage_info.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_util.h"
#include "services/network/network_context.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/interest_group/interest_group.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/origin.h"
#include "url/url_util.h"
namespace {
// A number of bytes used to represent data which takes up a practically
// imperceptible, but non-0 amount of space, such as Trust Tokens.
constexpr int kSmallAmountOfDataInBytes = 100;
// An estimate of storage size of an Interest Group object.
constexpr int kModerateAmountOfDataInBytes = 1024;
// Visitor which returns the appropriate data owner for a given `data_key`
// and `storage_type`.
struct GetDataOwner {
GetDataOwner(BrowsingDataModel::Delegate* delegate,
BrowsingDataModel::StorageType storage_type)
: delegate_(delegate), storage_type_(storage_type) {}
template <class T>
BrowsingDataModel::DataOwner operator()(const T& data_key) const {
if (delegate_) {
std::optional<BrowsingDataModel::DataOwner> owner =
delegate_->GetDataOwner(data_key, storage_type_);
if (owner.has_value()) {
return *owner;
}
}
return GetOwningOriginOrHost(data_key);
}
private:
template <class T>
BrowsingDataModel::DataOwner GetOwningOriginOrHost(const T& data_key) const;
// Returns the origin's host if the URL scheme is `http` or `https` otherwise
// returns the origin.
BrowsingDataModel::DataOwner GetOwnerBasedOnScheme(
const url::Origin origin) const {
if (origin.GetURL().SchemeIsHTTPOrHTTPS()) {
return origin.host();
}
return origin;
}
raw_ptr<BrowsingDataModel::Delegate> delegate_;
BrowsingDataModel::StorageType storage_type_;
};
template <>
BrowsingDataModel::DataOwner GetDataOwner::GetOwningOriginOrHost<url::Origin>(
const url::Origin& data_key) const {
if (storage_type_ == BrowsingDataModel::StorageType::kTrustTokens) {
return GetOwnerBasedOnScheme(data_key);
}
NOTREACHED() << "Unexpected StorageType: " << static_cast<int>(storage_type_);
return "";
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<blink::StorageKey>(
const blink::StorageKey& data_key) const {
// TODO(crbug.com/1271155): This logic is useful for testing during the
// implementation of the model, but ultimately these storage types may not
// coexist.
switch (storage_type_) {
case BrowsingDataModel::StorageType::kQuotaStorage:
case BrowsingDataModel::StorageType::kSharedStorage:
case BrowsingDataModel::StorageType::kLocalStorage:
return GetOwnerBasedOnScheme(data_key.origin());
default:
NOTREACHED() << "Unexpected StorageType: "
<< static_cast<int>(storage_type_);
return "";
}
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<content::SessionStorageUsageInfo>(
const content::SessionStorageUsageInfo& session_storage_usage_info) const {
DCHECK_EQ(BrowsingDataModel::StorageType::kSessionStorage, storage_type_);
return GetOwnerBasedOnScheme(session_storage_usage_info.storage_key.origin());
}
template <>
BrowsingDataModel::DataOwner GetDataOwner::GetOwningOriginOrHost<
content::InterestGroupManager::InterestGroupDataKey>(
const content::InterestGroupManager::InterestGroupDataKey& data_key) const {
CHECK_EQ(BrowsingDataModel::StorageType::kInterestGroup, storage_type_);
return GetOwnerBasedOnScheme(data_key.owner);
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<content::AttributionDataModel::DataKey>(
const content::AttributionDataModel::DataKey& data_key) const {
CHECK_EQ(BrowsingDataModel::StorageType::kAttributionReporting,
storage_type_);
return GetOwnerBasedOnScheme(data_key.reporting_origin());
}
template <>
BrowsingDataModel::DataOwner GetDataOwner::GetOwningOriginOrHost<
content::PrivateAggregationDataModel::DataKey>(
const content::PrivateAggregationDataModel::DataKey& data_key) const {
CHECK_EQ(BrowsingDataModel::StorageType::kPrivateAggregation, storage_type_);
return GetOwnerBasedOnScheme(data_key.reporting_origin());
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<net::SharedDictionaryIsolationKey>(
const net::SharedDictionaryIsolationKey& isolation_key) const {
DCHECK_EQ(BrowsingDataModel::StorageType::kSharedDictionary, storage_type_);
return GetOwnerBasedOnScheme(isolation_key.frame_origin());
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<browsing_data::SharedWorkerInfo>(
const browsing_data::SharedWorkerInfo& shared_worker_info) const {
DCHECK_EQ(BrowsingDataModel::StorageType::kSharedWorker, storage_type_);
return GetOwnerBasedOnScheme(shared_worker_info.storage_key.origin());
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<net::CanonicalCookie>(
const net::CanonicalCookie& cookie) const {
DCHECK_EQ(BrowsingDataModel::StorageType::kCookie, storage_type_);
return cookie.DomainWithoutDot();
}
template <>
BrowsingDataModel::DataOwner
GetDataOwner::GetOwningOriginOrHost<webid::FederatedIdentityDataModel::DataKey>(
const webid::FederatedIdentityDataModel::DataKey& data_key) const {
// Getting owning origin or host also handled by GetDataOwner in
// ChromeBrowsingDataModelDelegate.
return GetOwnerBasedOnScheme(data_key.relying_party_embedder());
}
// Helper which allows the lifetime management of a deletion action to occur
// separately from the BrowsingDataModel itself.
struct StorageRemoverHelper {
explicit StorageRemoverHelper(
content::StoragePartition* storage_partition,
scoped_refptr<BrowsingDataQuotaHelper> quota_helper,
BrowsingDataModel::Delegate* delegate
// TODO(crbug.com/1271155): Inject other dependencies.
)
: storage_partition_(storage_partition),
quota_helper_(quota_helper),
delegate_(delegate ? delegate->AsWeakPtr() : nullptr) {}
void RemoveDataKeyEntries(
const BrowsingDataModel::DataKeyEntries& data_key_entries,
base::OnceClosure completed);
private:
// Visitor struct to hold information used for deletion. absl::visit doesn't
// support multiple arguments elegantly.
struct Visitor {
raw_ptr<StorageRemoverHelper> helper;
BrowsingDataModel::StorageTypeSet types;
template <class T>
void operator()(const T& data_key);
};
// Returns a OnceClosure which can be passed to a storage backend for calling
// on deletion completion.
base::OnceClosure GetCompleteCallback();
void BackendFinished();
bool removing_ = false;
base::OnceClosure completed_;
size_t callbacks_expected_ = 0;
size_t callbacks_seen_ = 0;
raw_ptr<content::StoragePartition> storage_partition_;
scoped_refptr<BrowsingDataQuotaHelper> quota_helper_;
base::WeakPtr<BrowsingDataModel::Delegate> delegate_;
base::WeakPtrFactory<StorageRemoverHelper> weak_ptr_factory_{this};
};
template <>
void StorageRemoverHelper::Visitor::operator()<url::Origin>(
const url::Origin& origin) {
if (types.Has(BrowsingDataModel::StorageType::kTrustTokens)) {
helper->storage_partition_->GetNetworkContext()->DeleteStoredTrustTokens(
origin, base::BindOnce(
[](base::OnceClosure complete_callback,
::network::mojom::DeleteStoredTrustTokensStatus status) {
std::move(complete_callback).Run();
},
helper->GetCompleteCallback()));
}
}
template <>
void StorageRemoverHelper::Visitor::operator()<blink::StorageKey>(
const blink::StorageKey& storage_key) {
if (types.Has(BrowsingDataModel::StorageType::kSharedStorage)) {
helper->storage_partition_->GetSharedStorageManager()->Clear(
storage_key.origin(),
base::BindOnce(
[](base::OnceClosure complete_callback,
storage::SharedStorageDatabase::OperationResult result) {
std::move(complete_callback).Run();
},
helper->GetCompleteCallback()));
}
if (types.Has(BrowsingDataModel::StorageType::kQuotaStorage)) {
const blink::mojom::StorageType quota_types[] = {
blink::mojom::StorageType::kTemporary,
blink::mojom::StorageType::kSyncable};
for (auto type : quota_types) {
helper->quota_helper_->DeleteStorageKeyData(
storage_key, type, helper->GetCompleteCallback());
}
}
if (types.Has(BrowsingDataModel::StorageType::kLocalStorage)) {
helper->storage_partition_->GetDOMStorageContext()->DeleteLocalStorage(
storage_key, helper->GetCompleteCallback());
}
}
template <>
void StorageRemoverHelper::Visitor::operator()<
content::SessionStorageUsageInfo>(
const content::SessionStorageUsageInfo& session_storage_usage_info) {
if (types.Has(BrowsingDataModel::StorageType::kSessionStorage)) {
helper->storage_partition_->GetDOMStorageContext()->DeleteSessionStorage(
session_storage_usage_info, helper->GetCompleteCallback());
}
}
template <>
void StorageRemoverHelper::Visitor::operator()<browsing_data::SharedWorkerInfo>(
const browsing_data::SharedWorkerInfo& shared_worker_info) {
if (types.Has(BrowsingDataModel::StorageType::kSharedWorker)) {
helper->storage_partition_->GetSharedWorkerService()->TerminateWorker(
shared_worker_info.worker, shared_worker_info.name,
shared_worker_info.storage_key, shared_worker_info.same_site_cookies);
}
}
template <>
void StorageRemoverHelper::Visitor::operator()<
content::InterestGroupManager::InterestGroupDataKey>(
const content::InterestGroupManager::InterestGroupDataKey& data_key) {
CHECK(types.Has(BrowsingDataModel::StorageType::kInterestGroup));
helper->storage_partition_->GetInterestGroupManager()
->RemoveInterestGroupsByDataKey(
data_key, base::BindOnce(
[](base::OnceClosure complete_callback) {
std::move(complete_callback).Run();
},
helper->GetCompleteCallback()));
}
template <>
void StorageRemoverHelper::Visitor::operator()<
content::AttributionDataModel::DataKey>(
const content::AttributionDataModel::DataKey& data_key) {
CHECK(types.Has(BrowsingDataModel::StorageType::kAttributionReporting));
helper->storage_partition_->GetAttributionDataModel()
->RemoveAttributionDataByDataKey(data_key, helper->GetCompleteCallback());
}
template <>
void StorageRemoverHelper::Visitor::operator()<
content::PrivateAggregationDataModel::DataKey>(
const content::PrivateAggregationDataModel::DataKey& data_key) {
CHECK(types.Has(BrowsingDataModel::StorageType::kPrivateAggregation));
helper->storage_partition_->GetPrivateAggregationDataModel()
->RemovePendingDataKey(data_key, helper->GetCompleteCallback());
}
template <>
void StorageRemoverHelper::Visitor::operator()<
net::SharedDictionaryIsolationKey>(
const net::SharedDictionaryIsolationKey& isolation_key) {
if (types.Has(BrowsingDataModel::StorageType::kSharedDictionary)) {
helper->storage_partition_->GetNetworkContext()
->ClearSharedDictionaryCacheForIsolationKey(
isolation_key, helper->GetCompleteCallback());
} else {
NOTREACHED();
}
}
template <>
void StorageRemoverHelper::Visitor::operator()<net::CanonicalCookie>(
const net::CanonicalCookie& cookie) {
if (types.Has(BrowsingDataModel::StorageType::kCookie)) {
if (helper->delegate_ &&
helper->delegate_->IsCookieDeletionDisabled(
net::cookie_util::CookieOriginToURL(cookie.Domain(),
cookie.SecureAttribute()))) {
// TODO(crbug.com/1500256): Expand test coverage for this block.
return;
}
helper->storage_partition_->GetCookieManagerForBrowserProcess()
->DeleteCanonicalCookie(
cookie,
base::BindOnce([](base::OnceClosure callback,
bool deleted) { std::move(callback).Run(); },
helper->GetCompleteCallback()));
} else {
NOTREACHED();
}
}
template <>
void StorageRemoverHelper::Visitor::operator()<
webid::FederatedIdentityDataModel::DataKey>(
const webid::FederatedIdentityDataModel::DataKey& data_key) {
// Storage removal handled by RemoveDataKey in
// ChromeBrowsingDataModelDelegate.
}
void StorageRemoverHelper::RemoveDataKeyEntries(
const BrowsingDataModel::DataKeyEntries& data_key_entries,
base::OnceClosure completed) {
// At a helper level, only a single deletion may occur at a time. However
// multiple helpers may be associated with a single model.
DCHECK(!removing_);
removing_ = true;
completed_ = std::move(completed);
// Creating a synchronous callback to hold off running `completed_` callback
// until the loop has completed visiting all its entries whether deletion is
// synchronous or asynchronous.
auto sync_completion = GetCompleteCallback();
for (const auto& [key, details] : data_key_entries) {
absl::visit(Visitor{this, details.storage_types}, key);
if (delegate_) {
delegate_->RemoveDataKey(key, details.storage_types,
GetCompleteCallback());
}
}
std::move(sync_completion).Run();
}
base::OnceClosure StorageRemoverHelper::GetCompleteCallback() {
callbacks_expected_++;
return base::BindOnce(&StorageRemoverHelper::BackendFinished,
weak_ptr_factory_.GetWeakPtr());
}
void StorageRemoverHelper::BackendFinished() {
DCHECK(callbacks_expected_ > callbacks_seen_);
callbacks_seen_++;
if (callbacks_seen_ == callbacks_expected_)
std::move(completed_).Run();
}
// Only websafe state is considered browsing data.
bool HasStorageScheme(const url::Origin& origin) {
return base::Contains(url::GetWebStorageSchemes(), origin.scheme());
}
void OnTrustTokenIssuanceInfoLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
std::vector<::network::mojom::StoredTrustTokensForIssuerPtr> tokens) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (const auto& token : tokens) {
if (token->count == 0)
continue;
model->AddBrowsingData(token->issuer,
BrowsingDataModel::StorageType::kTrustTokens,
kSmallAmountOfDataInBytes);
}
std::move(loaded_callback).Run();
}
void OnSharedStorageLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
std::vector<::storage::mojom::StorageUsageInfoPtr> storage_usage_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (const auto& info : storage_usage_info) {
model->AddBrowsingData(info->storage_key,
BrowsingDataModel::StorageType::kSharedStorage,
info->total_size_bytes);
}
std::move(loaded_callback).Run();
}
void OnInterestGroupsLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
std::vector<content::InterestGroupManager::InterestGroupDataKey>
interest_groups) {
for (const auto& data_key : interest_groups) {
model->AddBrowsingData(data_key,
BrowsingDataModel::StorageType::kInterestGroup,
kModerateAmountOfDataInBytes);
}
std::move(loaded_callback).Run();
}
void OnAttributionReportingLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
std::set<content::AttributionDataModel::DataKey> attribution_reporting) {
for (const auto& data_key : attribution_reporting) {
model->AddBrowsingData(
data_key, BrowsingDataModel::StorageType::kAttributionReporting,
kSmallAmountOfDataInBytes);
}
std::move(loaded_callback).Run();
}
void OnPrivateAggregationLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
std::set<content::PrivateAggregationDataModel::DataKey>
private_aggregation) {
for (const auto& data_key : private_aggregation) {
model->AddBrowsingData(data_key,
BrowsingDataModel::StorageType::kPrivateAggregation,
kSmallAmountOfDataInBytes);
}
std::move(loaded_callback).Run();
}
void OnQuotaStorageLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
const std::list<BrowsingDataQuotaHelper::QuotaInfo>& quota_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
for (const auto& entry : quota_info) {
model->AddBrowsingData(entry.storage_key,
BrowsingDataModel::StorageType::kQuotaStorage,
entry.syncable_usage + entry.temporary_usage);
}
std::move(loaded_callback).Run();
}
void OnLocalStorageLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
const std::vector<content::StorageUsageInfo>& storage_usage_info) {
for (const auto& info : storage_usage_info) {
if (HasStorageScheme(info.storage_key.origin())) {
model->AddBrowsingData(info.storage_key,
BrowsingDataModel::StorageType::kLocalStorage,
info.total_size_bytes);
}
}
std::move(loaded_callback).Run();
}
void OnSharedDictionaryUsageLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
const std::vector<net::SharedDictionaryUsageInfo>& usage_info) {
for (const auto& info : usage_info) {
model->AddBrowsingData(info.isolation_key,
BrowsingDataModel::StorageType::kSharedDictionary,
info.total_size_bytes);
}
std::move(loaded_callback).Run();
}
void OnCookiesLoaded(BrowsingDataModel* model,
base::OnceClosure loaded_callback,
const net::CookieList& cookie_list) {
for (const auto& cookie : cookie_list) {
model->AddBrowsingData(cookie, BrowsingDataModel::StorageType::kCookie, 0,
1);
}
std::move(loaded_callback).Run();
}
void OnDelegateDataLoaded(
BrowsingDataModel* model,
base::OnceClosure loaded_callback,
std::vector<BrowsingDataModel::Delegate::DelegateEntry> delegated_entries) {
for (const auto& entry : delegated_entries) {
model->AddBrowsingData(entry.data_key, entry.storage_type,
entry.storage_size);
}
std::move(loaded_callback).Run();
}
// If `data_key` represents a non-1P partition, returns the site on which it
// is partitioned, std::nullopt otherwise.
std::optional<net::SchemefulSite> GetThirdPartyPartitioningSite(
const BrowsingDataModel::DataKey& data_key) {
std::optional<net::SchemefulSite> top_level_site = std::nullopt;
absl::visit(
base::Overloaded{
[&](const url::Origin&) {},
[&](const content::InterestGroupManager::InterestGroupDataKey) {},
[&](const content::AttributionDataModel::DataKey) {},
[&](const content::PrivateAggregationDataModel::DataKey) {},
[&](const blink::StorageKey& storage_key) {
if (storage_key.IsThirdPartyContext()) {
top_level_site = storage_key.top_level_site();
}
},
[&](const content::SessionStorageUsageInfo& info) {
if (info.storage_key.IsThirdPartyContext()) {
top_level_site = info.storage_key.top_level_site();
}
},
[&](const browsing_data::SharedWorkerInfo& info) {
if (info.storage_key.IsThirdPartyContext()) {
top_level_site = info.storage_key.top_level_site();
}
},
[&](const net::SharedDictionaryIsolationKey& key) {
if (net::SchemefulSite(key.frame_origin()) !=
key.top_frame_site()) {
top_level_site = key.top_frame_site();
}
},
[&](const net::CanonicalCookie& cookie) {
if (cookie.IsThirdPartyPartitioned()) {
top_level_site = cookie.PartitionKey()->site();
}
},
[&](const webid::FederatedIdentityDataModel::DataKey& data_key) {
if (data_key.relying_party_requester() !=
data_key.relying_party_embedder()) {
top_level_site =
net::SchemefulSite(data_key.relying_party_embedder());
}
},
},
data_key);
return top_level_site;
}
} // namespace
BrowsingDataModel::DataDetails::~DataDetails() = default;
bool BrowsingDataModel::DataDetails::operator==(
const DataDetails& other) const {
return storage_types == other.storage_types &&
storage_size == other.storage_size &&
cookie_count == other.cookie_count;
}
BrowsingDataModel::BrowsingDataEntryView::BrowsingDataEntryView(
const DataOwner& data_owner,
const DataKey& data_key,
const DataDetails& data_details)
: data_owner(data_owner), data_key(data_key), data_details(data_details) {}
BrowsingDataModel::BrowsingDataEntryView::~BrowsingDataEntryView() = default;
// static
const std::string BrowsingDataModel::GetHost(const DataOwner& data_owner) {
return absl::visit(
base::Overloaded{
[&](const std::string& host) { return host; },
[&](const url::Origin& origin) { return origin.host(); }},
data_owner);
}
bool BrowsingDataModel::BrowsingDataEntryView::Matches(
const url::Origin& origin) const {
return absl::visit(base::Overloaded{[&](const std::string& entry_host) {
return entry_host == origin.host();
},
[&](const url::Origin& entry_origin) {
return entry_origin == origin;
}},
*data_owner);
}
std::optional<net::SchemefulSite>
BrowsingDataModel::BrowsingDataEntryView::GetThirdPartyPartitioningSite()
const {
// Partition information is only dependent on it's `data_key`.
return ::GetThirdPartyPartitioningSite(data_key.get());
}
BrowsingDataModel::Delegate::DelegateEntry::DelegateEntry(
const DataKey& data_key,
StorageType storage_type,
uint64_t storage_size)
: data_key(data_key),
storage_type(storage_type),
storage_size(storage_size) {}
BrowsingDataModel::Delegate::DelegateEntry::DelegateEntry(
const DelegateEntry& other) = default;
BrowsingDataModel::Delegate::DelegateEntry::~DelegateEntry() = default;
BrowsingDataModel::Iterator::Iterator(const Iterator& iterator) = default;
BrowsingDataModel::Iterator::~Iterator() = default;
bool BrowsingDataModel::Iterator::operator==(const Iterator& other) const {
if (outer_iterator_ == outer_end_iterator_ &&
other.outer_iterator_ == other.outer_end_iterator_) {
// Special case the == .end() scenario, because the inner iterators may
// not have been set.
return outer_iterator_ == other.outer_iterator_;
}
return outer_iterator_ == other.outer_iterator_ &&
inner_iterator_ == other.inner_iterator_;
}
bool BrowsingDataModel::Iterator::operator!=(const Iterator& other) const {
return !operator==(other);
}
BrowsingDataModel::BrowsingDataEntryView
BrowsingDataModel::Iterator::operator*() const {
DCHECK(outer_iterator_ != outer_end_iterator_);
DCHECK(inner_iterator_ != outer_iterator_->second.end());
return BrowsingDataEntryView(outer_iterator_->first, inner_iterator_->first,
inner_iterator_->second);
}
BrowsingDataModel::Iterator& BrowsingDataModel::Iterator::operator++() {
if (inner_iterator_ != outer_iterator_->second.end()) {
inner_iterator_++;
}
if (inner_iterator_ == outer_iterator_->second.end()) {
outer_iterator_++;
if (outer_iterator_ != outer_end_iterator_)
inner_iterator_ = outer_iterator_->second.begin();
}
return *this;
}
BrowsingDataModel::Iterator::Iterator(
BrowsingDataEntries::const_iterator outer_iterator,
BrowsingDataEntries::const_iterator outer_end_iterator)
: outer_iterator_(outer_iterator), outer_end_iterator_(outer_end_iterator) {
if (outer_iterator_ != outer_end_iterator_) {
inner_iterator_ = outer_iterator_->second.begin();
}
}
BrowsingDataModel::Iterator BrowsingDataModel::begin() const {
return Iterator(browsing_data_entries_.begin(), browsing_data_entries_.end());
}
BrowsingDataModel::Iterator BrowsingDataModel::end() const {
return Iterator(browsing_data_entries_.end(), browsing_data_entries_.end());
}
BrowsingDataModel::~BrowsingDataModel() = default;
void BrowsingDataModel::BuildFromDisk(
content::BrowserContext* browser_context,
std::unique_ptr<Delegate> delegate,
base::OnceCallback<void(std::unique_ptr<BrowsingDataModel>)>
complete_callback) {
BuildFromStoragePartition(browser_context->GetDefaultStoragePartition(),
std::move(delegate), std::move(complete_callback));
}
void BrowsingDataModel::BuildFromNonDefaultStoragePartition(
content::StoragePartition* storage_partition,
std::unique_ptr<Delegate> delegate,
base::OnceCallback<void(std::unique_ptr<BrowsingDataModel>)>
complete_callback) {
DCHECK(!storage_partition->GetConfig().is_default());
BuildFromStoragePartition(storage_partition, std::move(delegate),
std::move(complete_callback));
}
void BrowsingDataModel::BuildFromStoragePartition(
content::StoragePartition* storage_partition,
std::unique_ptr<Delegate> delegate,
base::OnceCallback<void(std::unique_ptr<BrowsingDataModel>)>
complete_callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto model = BuildEmpty(storage_partition, std::move(delegate));
auto* model_pointer = model.get();
// This functor will own the unique_ptr for the model during construction,
// after which it hands it to the initial caller. This ownership semantic
// ensures that raw `this` pointers provided to backends for fetching remain
// valid.
base::OnceClosure completion = base::BindOnce(
[](std::unique_ptr<BrowsingDataModel> model,
base::OnceCallback<void(std::unique_ptr<BrowsingDataModel>)>
callback) { std::move(callback).Run(std::move(model)); },
std::move(model), std::move(complete_callback));
model_pointer->PopulateFromDisk(std::move(completion));
}
void BrowsingDataModel::RemoveBrowsingDataEntriesFromDisk(
const BrowsingDataModel::DataKeyEntries& browsing_data_entries,
base::OnceClosure completed) {
// Bind the lifetime of the helper to the lifetime of the callback.
auto helper = std::make_unique<StorageRemoverHelper>(
storage_partition_, quota_helper_, delegate_.get());
auto* helper_pointer = helper.get();
base::OnceClosure wrapped_completed = base::BindOnce(
[](std::unique_ptr<StorageRemoverHelper> storage_remover,
base::OnceClosure completed) { std::move(completed).Run(); },
std::move(helper), std::move(completed));
helper_pointer->RemoveDataKeyEntries(browsing_data_entries,
std::move(wrapped_completed));
}
std::unique_ptr<BrowsingDataModel> BrowsingDataModel::BuildEmpty(
content::StoragePartition* storage_partition,
std::unique_ptr<Delegate> delegate) {
return base::WrapUnique(new BrowsingDataModel(
storage_partition, std::move(delegate))); // Private constructor
}
void BrowsingDataModel::AddBrowsingData(const DataKey& data_key,
StorageType storage_type,
uint64_t storage_size,
uint64_t cookie_count) {
DataOwner data_owner =
absl::visit(GetDataOwner(delegate_.get(), storage_type), data_key);
// Find the existing entry if it exists, constructing any missing components.
auto& entry = browsing_data_entries_[data_owner][data_key];
entry.storage_size += storage_size;
entry.cookie_count += cookie_count;
entry.storage_types.Put(storage_type);
}
void BrowsingDataModel::RemoveBrowsingData(const DataOwner& data_owner,
base::OnceClosure completed) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
RemoveBrowsingDataEntriesFromDisk(browsing_data_entries_[data_owner],
std::move(completed));
// Immediately remove the affected entries from the in-memory model. Different
// UI elements have different sync vs. async expectations. Exposing a
// completed callback, but updating the model synchronously, serves both.
browsing_data_entries_.erase(data_owner);
}
void BrowsingDataModel::RemovePartitionedBrowsingData(
const DataOwner& data_owner,
const net::SchemefulSite& top_level_site,
base::OnceClosure completed) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DataKeyEntries affected_data_key_entries;
GetAffectedDataKeyEntriesForRemovePartitionedBrowsingData(
data_owner, top_level_site, affected_data_key_entries);
RemoveBrowsingDataEntriesFromDisk(affected_data_key_entries,
std::move(completed));
// Immediately remove the affected entries from the in-memory model.
// Different UI elements have different sync vs. async expectations.
// Exposing a completed callback, but updating the model synchronously,
// serves both.
auto& data_owner_entries = browsing_data_entries_[data_owner];
for (auto& entry : affected_data_key_entries) {
data_owner_entries.erase(entry.first);
}
if (data_owner_entries.empty()) {
browsing_data_entries_.erase(data_owner);
}
}
void BrowsingDataModel::RemoveUnpartitionedBrowsingData(
const DataOwner& data_owner,
base::OnceClosure completed) {
DataKeyEntries affected_data_key_entries;
for (const auto& entry : browsing_data_entries_[data_owner]) {
if (!GetThirdPartyPartitioningSite(entry.first).has_value()) {
affected_data_key_entries.insert(entry);
}
}
RemoveBrowsingDataEntriesFromDisk(affected_data_key_entries,
std::move(completed));
auto& data_owner_entries = browsing_data_entries_[data_owner];
for (auto& entry : affected_data_key_entries) {
data_owner_entries.erase(entry.first);
}
if (data_owner_entries.empty()) {
browsing_data_entries_.erase(data_owner);
}
}
bool BrowsingDataModel::IsBlockedByThirdPartyCookieBlocking(
const DataKey& data_key,
StorageType type) const {
if (GetThirdPartyPartitioningSite(data_key).has_value()) {
return false;
}
if (delegate_) {
auto delegate_response =
delegate_->IsBlockedByThirdPartyCookieBlocking(data_key, type);
if (delegate_response.has_value()) {
return delegate_response.value();
}
}
switch (type) {
case BrowsingDataModel::StorageType::kTrustTokens:
case BrowsingDataModel::StorageType::kInterestGroup:
case BrowsingDataModel::StorageType::kAttributionReporting:
case BrowsingDataModel::StorageType::kPrivateAggregation:
case BrowsingDataModel::StorageType::kSharedDictionary:
return false;
case BrowsingDataModel::StorageType::kSharedStorage:
case BrowsingDataModel::StorageType::kLocalStorage:
case BrowsingDataModel::StorageType::kQuotaStorage:
case BrowsingDataModel::StorageType::kSessionStorage:
case BrowsingDataModel::StorageType::kSharedWorker:
case BrowsingDataModel::StorageType::kCookie:
return true;
case BrowsingDataModel::StorageType::kExtendedDelegateRange:
NOTREACHED_NORETURN();
}
}
void BrowsingDataModel::PopulateFromDisk(base::OnceClosure finished_callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
bool is_shared_storage_enabled =
base::FeatureList::IsEnabled(blink::features::kSharedStorageAPI);
bool is_shared_dictionary_enabled = base::FeatureList::IsEnabled(
network::features::kCompressionDictionaryTransportBackend);
bool is_interest_group_enabled =
base::FeatureList::IsEnabled(blink::features::kAdInterestGroupAPI);
bool is_attribution_reporting_enabled = base::FeatureList::IsEnabled(
attribution_reporting::features::kConversionMeasurement);
bool is_private_aggregation_enabled =
base::FeatureList::IsEnabled(blink::features::kPrivateAggregationApi);
bool is_cookies_tree_model_deprecated = base::FeatureList::IsEnabled(
browsing_data::features::kDeprecateCookiesTreeModel);
base::RepeatingClosure completion =
base::BindRepeating([](const base::OnceClosure&) {},
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(finished_callback)));
// The public build interfaces for the model ensure that `this` remains valid
// until `finished_callback` has been run. Thus, it's safe to pass raw `this`
// to backend callbacks.
// Issued Trust Tokens
storage_partition_->GetNetworkContext()->GetStoredTrustTokenCounts(
base::BindOnce(&OnTrustTokenIssuanceInfoLoaded, this, completion));
// Quota Storage
quota_helper_->StartFetching(
base::BindOnce(&OnQuotaStorageLoaded, this, completion));
storage_partition_->GetDOMStorageContext()->GetLocalStorageUsage(
base::BindOnce(&OnLocalStorageLoaded, this, completion));
// Shared storage origins
if (is_shared_storage_enabled) {
storage_partition_->GetSharedStorageManager()->FetchOrigins(
base::BindOnce(&OnSharedStorageLoaded, this, completion));
}
// Shared Dictionaries
if (is_shared_dictionary_enabled) {
storage_partition_->GetNetworkContext()->GetSharedDictionaryUsageInfo(
base::BindOnce(&OnSharedDictionaryUsageLoaded, this, completion));
}
// Interest Groups
if (is_interest_group_enabled) {
storage_partition_->GetInterestGroupManager()->GetAllInterestGroupDataKeys(
base::BindOnce(&OnInterestGroupsLoaded, this, completion));
}
// Attribution Reporting
if (is_attribution_reporting_enabled) {
storage_partition_->GetAttributionDataModel()->GetAllDataKeys(
base::BindOnce(&OnAttributionReportingLoaded, this, completion));
}
// Private Aggregation
if (is_private_aggregation_enabled) {
storage_partition_->GetPrivateAggregationDataModel()->GetAllDataKeys(
base::BindOnce(&OnPrivateAggregationLoaded, this, completion));
}
// Cookies
if (is_cookies_tree_model_deprecated) {
storage_partition_->GetCookieManagerForBrowserProcess()->GetAllCookies(
base::BindOnce(&OnCookiesLoaded, this, completion));
}
// Data loaded from non-components storage types via the delegate.
if (delegate_) {
delegate_->GetAllDataKeys(
base::BindOnce(&OnDelegateDataLoaded, this, completion));
}
}
BrowsingDataModel::BrowsingDataModel(
content::StoragePartition* storage_partition,
std::unique_ptr<Delegate> delegate)
: storage_partition_(storage_partition), delegate_(std::move(delegate)) {
if (storage_partition_) {
quota_helper_ = BrowsingDataQuotaHelper::Create(storage_partition_);
}
}
void BrowsingDataModel::
GetAffectedDataKeyEntriesForRemovePartitionedBrowsingData(
const DataOwner& data_owner,
const net::SchemefulSite& top_level_site,
DataKeyEntries& affected_data_key_entries) {
for (const auto& entry : browsing_data_entries_[data_owner]) {
if (GetThirdPartyPartitioningSite(entry.first) == top_level_site) {
affected_data_key_entries.insert(entry);
}
}
}