blob: 3d5e8b4809c096d35d75d6edec996a99c38c275f [file] [log] [blame]
// Copyright 2016 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/browser/devtools/protocol/storage_handler.h"
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>
#include "base/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/notreached.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/string_view_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/attribution_reporting/aggregatable_debug_reporting_config.h"
#include "components/attribution_reporting/aggregatable_dedup_key.h"
#include "components/attribution_reporting/aggregatable_named_budget_candidate.h"
#include "components/attribution_reporting/aggregatable_named_budget_defs.h"
#include "components/attribution_reporting/aggregatable_trigger_data.h"
#include "components/attribution_reporting/aggregatable_values.h"
#include "components/attribution_reporting/aggregation_keys.h"
#include "components/attribution_reporting/attribution_scopes_data.h"
#include "components/attribution_reporting/attribution_scopes_set.h"
#include "components/attribution_reporting/debug_types.h"
#include "components/attribution_reporting/destination_set.h"
#include "components/attribution_reporting/event_trigger_data.h"
#include "components/attribution_reporting/filters.h"
#include "components/attribution_reporting/max_event_level_reports.h"
#include "components/attribution_reporting/parsing_utils.h"
#include "components/attribution_reporting/source_registration.h"
#include "components/attribution_reporting/source_type.h"
#include "components/attribution_reporting/suitable_origin.h"
#include "components/attribution_reporting/trigger_config.h"
#include "components/attribution_reporting/trigger_data_matching.mojom.h"
#include "components/services/storage/privileged/mojom/indexed_db_control.mojom.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
#include "content/browser/attribution_reporting/aggregatable_result.mojom.h"
#include "content/browser/attribution_reporting/attribution_debug_report.h"
#include "content/browser/attribution_reporting/attribution_manager.h"
#include "content/browser/attribution_reporting/attribution_observer.h"
#include "content/browser/attribution_reporting/attribution_report.h"
#include "content/browser/attribution_reporting/attribution_trigger.h"
#include "content/browser/attribution_reporting/common_source_info.h"
#include "content/browser/attribution_reporting/create_report_result.h"
#include "content/browser/attribution_reporting/event_level_result.mojom.h"
#include "content/browser/attribution_reporting/send_result.h"
#include "content/browser/attribution_reporting/storable_source.h"
#include "content/browser/attribution_reporting/store_source_result.mojom.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/protocol/browser_handler.h"
#include "content/browser/devtools/protocol/handler_helpers.h"
#include "content/browser/devtools/protocol/network.h"
#include "content/browser/devtools/protocol/network_handler.h"
#include "content/browser/devtools/protocol/storage.h"
#include "content/browser/interest_group/interest_group_manager_impl.h"
#include "content/browser/renderer_host/render_frame_host_impl.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/devtools_agent_host_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/net_errors.h"
#include "net/base/schemeful_site.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/browser/quota/quota_manager_impl.h"
#include "storage/browser/quota/quota_manager_observer.mojom-forward.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/quota/quota_override_handle.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
#include "third_party/blink/public/common/interest_group/devtools_serialization.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/buckets/bucket_manager_host.mojom-shared.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
namespace protocol {
using ClearCookiesCallback = Storage::Backend::ClearCookiesCallback;
using GetCookiesCallback = Storage::Backend::GetCookiesCallback;
using SetCookiesCallback = Storage::Backend::SetCookiesCallback;
struct UsageListInitializer {
const char* type;
int64_t blink::mojom::UsageBreakdown::*usage_member;
};
UsageListInitializer initializers[] = {
{Storage::StorageTypeEnum::File_systems,
&blink::mojom::UsageBreakdown::fileSystem},
{Storage::StorageTypeEnum::Indexeddb,
&blink::mojom::UsageBreakdown::indexedDatabase},
{Storage::StorageTypeEnum::Cache_storage,
&blink::mojom::UsageBreakdown::serviceWorkerCache},
{Storage::StorageTypeEnum::Service_workers,
&blink::mojom::UsageBreakdown::serviceWorker},
};
namespace {
void ReportUsageAndQuotaDataOnUIThread(
std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
blink::mojom::QuotaStatusCode code,
int64_t usage,
int64_t quota,
bool is_override_enabled,
blink::mojom::UsageBreakdownPtr usage_breakdown) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (code != blink::mojom::QuotaStatusCode::kOk) {
return callback->sendFailure(
Response::ServerError("Quota information is not available"));
}
auto usageList = std::make_unique<Array<Storage::UsageForType>>();
blink::mojom::UsageBreakdown* breakdown_ptr = usage_breakdown.get();
for (const auto initializer : initializers) {
std::unique_ptr<Storage::UsageForType> entry =
Storage::UsageForType::Create()
.SetStorageType(initializer.type)
.SetUsage(breakdown_ptr->*(initializer.usage_member))
.Build();
usageList->emplace_back(std::move(entry));
}
callback->sendSuccess(usage, quota, is_override_enabled,
std::move(usageList));
}
void GotUsageAndQuotaDataCallback(
std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
blink::mojom::QuotaStatusCode code,
int64_t usage,
int64_t quota,
bool is_override_enabled,
blink::mojom::UsageBreakdownPtr usage_breakdown) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(ReportUsageAndQuotaDataOnUIThread, std::move(callback),
code, usage, quota, is_override_enabled,
std::move(usage_breakdown)));
}
void GetUsageAndQuotaOnIOThread(
storage::QuotaManager* manager,
const blink::StorageKey& storage_key,
std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
manager->GetUsageAndQuotaForDevtools(
storage_key,
base::BindOnce(&GotUsageAndQuotaDataCallback, std::move(callback)));
}
std::unique_ptr<protocol::Storage::StorageBucketInfo> BuildBucketInfo(
const storage::BucketInfo& bucket) {
std::string durability_enum;
switch (bucket.durability) {
case blink::mojom::BucketDurability::kRelaxed:
durability_enum = Storage::StorageBucketsDurabilityEnum::Relaxed;
break;
case blink::mojom::BucketDurability::kStrict:
durability_enum = Storage::StorageBucketsDurabilityEnum::Strict;
break;
}
std::unique_ptr<protocol::Storage::StorageBucket> storage_bucket;
if (bucket.is_default()) {
storage_bucket = protocol::Storage::StorageBucket::Create()
.SetStorageKey(bucket.storage_key.Serialize())
.Build();
} else {
storage_bucket = protocol::Storage::StorageBucket::Create()
.SetStorageKey(bucket.storage_key.Serialize())
.SetName(bucket.name)
.Build();
}
return protocol::Storage::StorageBucketInfo::Create()
.SetBucket(std::move(storage_bucket))
.SetId(base::NumberToString(bucket.id.value()))
.SetExpiration(bucket.expiration.InSecondsFSinceUnixEpoch())
.SetQuota(bucket.quota)
.SetPersistent(bucket.persistent)
.SetDurability(durability_enum)
.Build();
}
} // namespace
// Observer that listens on the UI thread for cache storage notifications and
// informs the StorageHandler on the UI thread for origins of interest.
// Created and used exclusively on the UI thread.
class StorageHandler::CacheStorageObserver
: storage::mojom::CacheStorageObserver {
public:
CacheStorageObserver(
base::WeakPtr<StorageHandler> owner_storage_handler,
mojo::PendingReceiver<storage::mojom::CacheStorageObserver> observer)
: owner_(owner_storage_handler), receiver_(this, std::move(observer)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
CacheStorageObserver(const CacheStorageObserver&) = delete;
CacheStorageObserver& operator=(const CacheStorageObserver&) = delete;
~CacheStorageObserver() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
void TrackStorageKey(const blink::StorageKey& storage_key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (storage_keys_.find(storage_key) != storage_keys_.end()) {
return;
}
storage_keys_.insert(storage_key);
}
void UntrackStorageKey(const blink::StorageKey& storage_key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
storage_keys_.erase(storage_key);
}
void OnCacheListChanged(
const storage::BucketLocator& bucket_locator) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto found = storage_keys_.find(bucket_locator.storage_key);
if (found == storage_keys_.end()) {
return;
}
owner_->NotifyCacheStorageListChanged(bucket_locator);
}
void OnCacheContentChanged(const storage::BucketLocator& bucket_locator,
const std::string& cache_name) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (storage_keys_.find(bucket_locator.storage_key) == storage_keys_.end()) {
return;
}
owner_->NotifyCacheStorageContentChanged(bucket_locator, cache_name);
}
private:
// Maintained on the IO thread to avoid thread contention.
base::flat_set<blink::StorageKey> storage_keys_;
base::WeakPtr<StorageHandler> owner_;
mojo::Receiver<storage::mojom::CacheStorageObserver> receiver_;
};
// Observer that listens on the IDB thread for IndexedDB notifications and
// informs the StorageHandler on the UI thread for storage_keys of interest.
// Created on the UI thread but predominantly used and deleted on the IDB
// thread.
class StorageHandler::IndexedDBObserver
: public storage::mojom::IndexedDBObserver {
public:
explicit IndexedDBObserver(
base::WeakPtr<StorageHandler> owner_storage_handler)
: owner_(owner_storage_handler), receiver_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ReconnectObserver();
}
IndexedDBObserver(const IndexedDBObserver&) = delete;
IndexedDBObserver& operator=(const IndexedDBObserver&) = delete;
~IndexedDBObserver() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); }
void TrackStorageKey(const blink::StorageKey& storage_key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (storage_keys_.find(storage_key) != storage_keys_.end()) {
return;
}
storage_keys_.insert(storage_key);
}
void UntrackStorageKey(const blink::StorageKey& storage_key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
storage_keys_.erase(storage_key);
}
void OnIndexedDBListChanged(
const storage::BucketLocator& bucket_locator) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!owner_) {
return;
}
// TODO(crbug.com/40221733): Allow custom bucket names.
auto found = storage_keys_.find(bucket_locator.storage_key);
if (found == storage_keys_.end()) {
return;
}
owner_->NotifyIndexedDBListChanged(bucket_locator);
}
void OnIndexedDBContentChanged(
const storage::BucketLocator& bucket_locator,
const std::u16string& database_name,
const std::u16string& object_store_name) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!owner_) {
return;
}
// TODO(crbug.com/40221733): Allow custom bucket names.
auto found = storage_keys_.find(bucket_locator.storage_key);
if (found == storage_keys_.end()) {
return;
}
owner_->NotifyIndexedDBContentChanged(bucket_locator, database_name,
object_store_name);
}
private:
void ReconnectObserver() {
DCHECK(!receiver_.is_bound());
if (!owner_) {
return;
}
auto& control = owner_->storage_partition_->GetIndexedDBControl();
mojo::PendingRemote<storage::mojom::IndexedDBObserver> remote;
receiver_.Bind(remote.InitWithNewPipeAndPassReceiver());
receiver_.set_disconnect_handler(base::BindOnce(
[](IndexedDBObserver* observer) {
// If this observer disconnects because IndexedDB or the storage
// service goes away, reconnect again.
observer->ReconnectObserver();
},
this));
control.AddObserver(std::move(remote));
}
base::flat_set<blink::StorageKey> storage_keys_;
base::WeakPtr<StorageHandler> owner_;
mojo::Receiver<storage::mojom::IndexedDBObserver> receiver_;
};
class StorageHandler::QuotaManagerObserver
: storage::mojom::QuotaManagerObserver {
public:
QuotaManagerObserver(base::WeakPtr<StorageHandler> owner_storage_handler,
storage::QuotaManagerProxy* quota_manager_proxy)
: owner_(owner_storage_handler) {
quota_manager_proxy->AddObserver(receiver_.BindNewPipeAndPassRemote());
}
QuotaManagerObserver(const QuotaManagerObserver&) = delete;
QuotaManagerObserver& operator=(const QuotaManagerObserver&) = delete;
~QuotaManagerObserver() override = default;
void TrackStorageKey(const blink::StorageKey& storage_key,
storage::QuotaManagerProxy* manager) {
if (!storage_keys_.insert(storage_key).second) {
return;
}
manager->GetBucketsForStorageKey(
storage_key, /*delete_expired=*/false,
base::SingleThreadTaskRunner::GetCurrentDefault(),
base::BindOnce(
[](base::WeakPtr<StorageHandler> owner_storage_handler,
storage::QuotaErrorOr<std::set<storage::BucketInfo>> buckets) {
if (!owner_storage_handler || !buckets.has_value()) {
return;
}
for (const storage::BucketInfo& bucket : buckets.value()) {
owner_storage_handler->NotifyCreateOrUpdateBucket(bucket);
}
},
owner_));
}
void UntrackStorageKey(const blink::StorageKey& storage_key) {
storage_keys_.erase(storage_key);
}
private:
void OnCreateOrUpdateBucket(const storage::BucketInfo& bucket_info) override {
auto found = storage_keys_.find(bucket_info.storage_key);
if (found == storage_keys_.end()) {
return;
}
owner_->NotifyCreateOrUpdateBucket(bucket_info);
}
void OnDeleteBucket(const storage::BucketLocator& bucket_locator) override {
auto found = storage_keys_.find(bucket_locator.storage_key);
if (found == storage_keys_.end()) {
return;
}
owner_->NotifyDeleteBucket(bucket_locator);
}
base::flat_set<blink::StorageKey> storage_keys_;
base::WeakPtr<StorageHandler> owner_;
mojo::Receiver<storage::mojom::QuotaManagerObserver> receiver_{this};
};
StorageHandler::StorageHandler(DevToolsAgentHostClient* client)
: DevToolsDomainHandler(Storage::Metainfo::domainName), client_(client) {}
StorageHandler::~StorageHandler() {
DCHECK(!cache_storage_observer_);
DCHECK(!indexed_db_observer_);
}
// static
std::vector<StorageHandler*> StorageHandler::ForAgentHost(
DevToolsAgentHostImpl* host) {
return host->HandlersByName<StorageHandler>(Storage::Metainfo::domainName);
}
void StorageHandler::Wire(UberDispatcher* dispatcher) {
frontend_ = std::make_unique<Storage::Frontend>(dispatcher->channel());
Storage::Dispatcher::wire(dispatcher, this);
}
void StorageHandler::SetRenderer(int process_host_id,
RenderFrameHostImpl* frame_host) {
RenderProcessHost* process = RenderProcessHost::FromID(process_host_id);
StoragePartition* new_storage_partition =
process ? process->GetStoragePartition() : nullptr;
if (interest_group_tracking_enabled_) {
// Transfer observer registration from old frame's StoragePartition to new;
// SetInterestGroupTrackingInternal() will handle any nulls.
SetInterestGroupTrackingInternal(storage_partition_, false);
SetInterestGroupTrackingInternal(new_storage_partition, true);
}
storage_partition_ = new_storage_partition;
frame_host_ = frame_host;
}
Response StorageHandler::Disable() {
cache_storage_observer_.reset();
indexed_db_observer_.reset();
quota_override_handle_.reset();
SetInterestGroupTracking(false);
SetSharedStorageTracking(false);
quota_manager_observer_.reset();
ResetAttributionReporting();
return Response::Success();
}
void StorageHandler::GetCookies(std::optional<std::string> browser_context_id,
std::unique_ptr<GetCookiesCallback> callback) {
StoragePartition* storage_partition = nullptr;
Response response = StorageHandler::FindStoragePartition(browser_context_id,
&storage_partition);
if (!response.IsSuccess()) {
callback->sendFailure(std::move(response));
return;
}
storage_partition->GetCookieManagerForBrowserProcess()->GetAllCookies(
base::BindOnce(&StorageHandler::GotAllCookies,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void StorageHandler::GotAllCookies(
std::unique_ptr<GetCookiesCallback> callback,
const std::vector<net::CanonicalCookie>& cookies) {
bool is_webui = frame_host_ && frame_host_->web_ui();
std::vector<net::CanonicalCookie> filtered_cookies;
for (const auto& cookie : cookies) {
if (client_->MayAttachToURL(
GURL(base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator,
cookie.DomainWithoutDot()})),
is_webui) &&
client_->MayAttachToURL(
GURL(base::StrCat({url::kHttpScheme, url::kStandardSchemeSeparator,
cookie.DomainWithoutDot()})),
is_webui)) {
filtered_cookies.emplace_back(std::move(cookie));
}
}
callback->sendSuccess(NetworkHandler::BuildCookieArray(filtered_cookies));
}
void StorageHandler::SetCookies(
std::unique_ptr<protocol::Array<Network::CookieParam>> cookies,
std::optional<std::string> browser_context_id,
std::unique_ptr<SetCookiesCallback> callback) {
StoragePartition* storage_partition = nullptr;
Response response = StorageHandler::FindStoragePartition(browser_context_id,
&storage_partition);
if (!response.IsSuccess()) {
callback->sendFailure(std::move(response));
return;
}
NetworkHandler::SetCookies(
storage_partition, std::move(cookies),
base::BindOnce(
[](std::unique_ptr<SetCookiesCallback> callback, bool success) {
if (success) {
callback->sendSuccess();
} else {
callback->sendFailure(
Response::InvalidParams("Invalid cookie fields"));
}
},
std::move(callback)));
}
void StorageHandler::ClearCookies(
std::optional<std::string> browser_context_id,
std::unique_ptr<ClearCookiesCallback> callback) {
StoragePartition* storage_partition = nullptr;
Response response = StorageHandler::FindStoragePartition(browser_context_id,
&storage_partition);
if (!response.IsSuccess()) {
callback->sendFailure(std::move(response));
return;
}
storage_partition->GetCookieManagerForBrowserProcess()->DeleteCookies(
network::mojom::CookieDeletionFilter::New(),
base::BindOnce([](std::unique_ptr<ClearCookiesCallback> callback,
uint32_t) { callback->sendSuccess(); },
std::move(callback)));
}
Response StorageHandler::GetStorageKeyForFrame(
const std::string& frame_id,
std::string* serialized_storage_key) {
if (!frame_host_) {
return Response::InvalidParams("Frame host not found");
}
FrameTreeNode* node = protocol::FrameTreeNodeFromDevToolsFrameToken(
frame_host_->frame_tree_node(), frame_id);
if (!node) {
return Response::InvalidParams("Frame tree node for given frame not found");
}
RenderFrameHostImpl* rfh = node->current_frame_host();
if (rfh->GetStorageKey().origin().opaque()) {
return Response::ServerError(
"Frame corresponds to an opaque origin and its storage key cannot be "
"serialized");
}
*serialized_storage_key = rfh->GetStorageKey().Serialize();
return Response::Success();
}
namespace {
uint32_t GetRemoveDataMask(const std::string& storage_types) {
std::vector<std::string> types = base::SplitString(
storage_types, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::unordered_set<std::string> set(types.begin(), types.end());
uint32_t remove_mask = 0;
if (set.count(Storage::StorageTypeEnum::Cookies)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
}
if (set.count(Storage::StorageTypeEnum::File_systems)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
}
if (set.count(Storage::StorageTypeEnum::Indexeddb)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
}
if (set.count(Storage::StorageTypeEnum::Local_storage)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
}
if (set.count(Storage::StorageTypeEnum::Shader_cache)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
}
if (set.count(Storage::StorageTypeEnum::Service_workers)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
}
if (set.count(Storage::StorageTypeEnum::Cache_storage)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE;
}
if (set.count(Storage::StorageTypeEnum::Interest_groups)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_INTEREST_GROUPS;
}
if (set.count(Storage::StorageTypeEnum::Shared_storage)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_SHARED_STORAGE;
}
if (set.count(Storage::StorageTypeEnum::All)) {
remove_mask |= StoragePartition::REMOVE_DATA_MASK_ALL;
}
return remove_mask;
}
} // namespace
void StorageHandler::ClearDataForOrigin(
const std::string& origin,
const std::string& storage_types,
std::unique_ptr<ClearDataForOriginCallback> callback) {
if (!storage_partition_) {
return callback->sendFailure(Response::InternalError());
}
uint32_t remove_mask = GetRemoveDataMask(storage_types);
if (!remove_mask) {
return callback->sendFailure(
Response::InvalidParams("No valid storage type specified"));
}
storage_partition_->ClearData(
remove_mask, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
blink::StorageKey::CreateFirstParty(url::Origin::Create(GURL(origin))),
base::Time(), base::Time::Max(),
base::BindOnce(&ClearDataForOriginCallback::sendSuccess,
std::move(callback)));
}
void StorageHandler::ClearDataForStorageKey(
const std::string& storage_key,
const std::string& storage_types,
std::unique_ptr<ClearDataForStorageKeyCallback> callback) {
if (!storage_partition_) {
return callback->sendFailure(Response::InternalError());
}
uint32_t remove_mask = GetRemoveDataMask(storage_types);
if (!remove_mask) {
return callback->sendFailure(
Response::InvalidParams("No valid storage type specified"));
}
std::optional<blink::StorageKey> key =
blink::StorageKey::Deserialize(storage_key);
if (!key) {
return callback->sendFailure(
Response::InvalidParams("Unable to deserialize storage key"));
}
storage_partition_->ClearData(
remove_mask, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, *key,
base::Time(), base::Time::Max(),
base::BindOnce(&ClearDataForStorageKeyCallback::sendSuccess,
std::move(callback)));
}
void StorageHandler::GetUsageAndQuota(
const String& origin_string,
std::unique_ptr<GetUsageAndQuotaCallback> callback) {
if (!storage_partition_) {
return callback->sendFailure(Response::InternalError());
}
GURL origin_url(origin_string);
url::Origin origin = url::Origin::Create(origin_url);
if (!origin_url.is_valid() || origin.opaque()) {
return callback->sendFailure(
Response::ServerError(origin_string + " is not a valid URL"));
}
storage::QuotaManager* manager = storage_partition_->GetQuotaManager();
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&GetUsageAndQuotaOnIOThread, base::RetainedRef(manager),
blink::StorageKey::CreateFirstParty(origin),
std::move(callback)));
}
void StorageHandler::OverrideQuotaForOrigin(
const String& origin_string,
std::optional<double> quota_size,
std::unique_ptr<OverrideQuotaForOriginCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
GURL url(origin_string);
url::Origin origin = url::Origin::Create(url);
if (!url.is_valid() || origin.opaque()) {
callback->sendFailure(
Response::InvalidParams(origin_string + " is not a valid URL"));
return;
}
if (!quota_override_handle_) {
scoped_refptr<storage::QuotaManagerProxy> manager_proxy =
storage_partition_->GetQuotaManager()->proxy();
quota_override_handle_ = manager_proxy->GetQuotaOverrideHandle();
}
quota_override_handle_->OverrideQuotaForStorageKey(
blink::StorageKey::CreateFirstParty(origin),
quota_size.has_value() ? std::make_optional(quota_size.value())
: std::nullopt,
base::BindOnce(&OverrideQuotaForOriginCallback::sendSuccess,
std::move(callback)));
}
Response StorageHandler::TrackCacheStorageForOrigin(
const std::string& origin_string) {
if (!storage_partition_) {
return Response::InternalError();
}
GURL origin_url(origin_string);
url::Origin origin = url::Origin::Create(origin_url);
if (!origin_url.is_valid() || origin.opaque()) {
return Response::InvalidParams(origin_string + " is not a valid URL");
}
GetCacheStorageObserver()->TrackStorageKey(
blink::StorageKey::CreateFirstParty(origin));
return Response::Success();
}
Response StorageHandler::TrackCacheStorageForStorageKey(
const std::string& storage_key) {
if (!storage_partition_) {
return Response::InternalError();
}
std::optional<blink::StorageKey> key =
blink::StorageKey::Deserialize(storage_key);
if (!key) {
return Response::InvalidParams("Unable to deserialize storage key");
}
GetCacheStorageObserver()->TrackStorageKey(*key);
return Response::Success();
}
Response StorageHandler::UntrackCacheStorageForOrigin(
const std::string& origin_string) {
if (!storage_partition_) {
return Response::InternalError();
}
GURL origin_url(origin_string);
url::Origin origin = url::Origin::Create(origin_url);
if (!origin_url.is_valid() || origin.opaque()) {
return Response::InvalidParams(origin_string + " is not a valid URL");
}
GetCacheStorageObserver()->UntrackStorageKey(
blink::StorageKey::CreateFirstParty(origin));
return Response::Success();
}
Response StorageHandler::UntrackCacheStorageForStorageKey(
const std::string& storage_key) {
if (!storage_partition_) {
return Response::InternalError();
}
std::optional<blink::StorageKey> key =
blink::StorageKey::Deserialize(storage_key);
if (!key) {
return Response::InvalidParams("Unable to deserialize storage key");
}
GetCacheStorageObserver()->UntrackStorageKey(*key);
return Response::Success();
}
Response StorageHandler::TrackIndexedDBForOrigin(
const std::string& origin_string) {
if (!storage_partition_) {
return Response::InternalError();
}
GURL origin_url(origin_string);
url::Origin origin = url::Origin::Create(origin_url);
if (!origin_url.is_valid() || origin.opaque()) {
return Response::InvalidParams(origin_string + " is not a valid URL");
}
GetIndexedDBObserver()->TrackStorageKey(
blink::StorageKey::CreateFirstParty(origin));
return Response::Success();
}
Response StorageHandler::TrackIndexedDBForStorageKey(
const std::string& storage_key) {
if (!storage_partition_) {
return Response::InternalError();
}
std::optional<blink::StorageKey> key =
blink::StorageKey::Deserialize(storage_key);
if (!key) {
return Response::InvalidParams("Unable to deserialize storage key");
}
GetIndexedDBObserver()->TrackStorageKey(*key);
return Response::Success();
}
Response StorageHandler::UntrackIndexedDBForOrigin(
const std::string& origin_string) {
if (!storage_partition_) {
return Response::InternalError();
}
GURL origin_url(origin_string);
url::Origin origin = url::Origin::Create(origin_url);
if (!origin_url.is_valid() || origin.opaque()) {
return Response::InvalidParams(origin_string + " is not a valid URL");
}
GetIndexedDBObserver()->UntrackStorageKey(
blink::StorageKey::CreateFirstParty(origin));
return Response::Success();
}
Response StorageHandler::UntrackIndexedDBForStorageKey(
const std::string& storage_key) {
if (!storage_partition_) {
return Response::InternalError();
}
std::optional<blink::StorageKey> key =
blink::StorageKey::Deserialize(storage_key);
if (!key) {
return Response::InvalidParams("Unable to deserialize storage key");
}
GetIndexedDBObserver()->UntrackStorageKey(*key);
return Response::Success();
}
StorageHandler::CacheStorageObserver*
StorageHandler::GetCacheStorageObserver() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!cache_storage_observer_) {
mojo::PendingRemote<storage::mojom::CacheStorageObserver> observer;
cache_storage_observer_ = std::make_unique<CacheStorageObserver>(
weak_ptr_factory_.GetWeakPtr(),
observer.InitWithNewPipeAndPassReceiver());
storage_partition_->GetCacheStorageControl()->AddObserver(
std::move(observer));
}
return cache_storage_observer_.get();
}
StorageHandler::IndexedDBObserver* StorageHandler::GetIndexedDBObserver() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!indexed_db_observer_) {
indexed_db_observer_ =
std::make_unique<IndexedDBObserver>(weak_ptr_factory_.GetWeakPtr());
}
return indexed_db_observer_.get();
}
SharedStorageRuntimeManager* StorageHandler::GetSharedStorageRuntimeManager() {
DCHECK(storage_partition_);
return static_cast<StoragePartitionImpl*>(storage_partition_)
->GetSharedStorageRuntimeManager();
}
std::variant<protocol::Response, storage::SharedStorageManager*>
StorageHandler::GetSharedStorageManager() {
if (!storage_partition_) {
return Response::InternalError();
}
if (auto* manager = static_cast<StoragePartitionImpl*>(storage_partition_)
->GetSharedStorageManager()) {
return manager;
}
return Response::ServerError("Shared storage is disabled");
}
storage::QuotaManagerProxy* StorageHandler::GetQuotaManagerProxy() {
DCHECK(storage_partition_);
return static_cast<StoragePartitionImpl*>(storage_partition_)
->GetQuotaManagerProxy();
}
void StorageHandler::NotifyCacheStorageListChanged(
const storage::BucketLocator& bucket_locator) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
frontend_->CacheStorageListUpdated(
bucket_locator.storage_key.origin().Serialize(),
bucket_locator.storage_key.Serialize(),
base::NumberToString(bucket_locator.id.value()));
}
void StorageHandler::NotifyCacheStorageContentChanged(
const storage::BucketLocator& bucket_locator,
const std::string& name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
frontend_->CacheStorageContentUpdated(
bucket_locator.storage_key.origin().Serialize(),
bucket_locator.storage_key.Serialize(),
base::NumberToString(bucket_locator.id.value()), name);
}
void StorageHandler::NotifyIndexedDBListChanged(
storage::BucketLocator bucket_locator) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
frontend_->IndexedDBListUpdated(
bucket_locator.storage_key.origin().Serialize(),
bucket_locator.storage_key.Serialize(),
base::NumberToString(bucket_locator.id.value()));
}
void StorageHandler::NotifyIndexedDBContentChanged(
storage::BucketLocator bucket_locator,
const std::u16string& database_name,
const std::u16string& object_store_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
frontend_->IndexedDBContentUpdated(
bucket_locator.storage_key.origin().Serialize(),
bucket_locator.storage_key.Serialize(),
base::NumberToString(bucket_locator.id.value()),
base::UTF16ToUTF8(database_name), base::UTF16ToUTF8(object_store_name));
}
Response StorageHandler::FindStoragePartition(
const std::optional<std::string>& browser_context_id,
StoragePartition** storage_partition) {
BrowserContext* browser_context = nullptr;
Response response =
BrowserHandler::FindBrowserContext(browser_context_id, &browser_context);
if (!response.IsSuccess()) {
return response;
}
*storage_partition = browser_context->GetDefaultStoragePartition();
if (!*storage_partition) {
return Response::InternalError();
}
return Response::Success();
}
namespace {
void SendTrustTokens(
std::unique_ptr<StorageHandler::GetTrustTokensCallback> callback,
std::vector<::network::mojom::StoredTrustTokensForIssuerPtr> tokens) {
auto result =
std::make_unique<protocol::Array<protocol::Storage::TrustTokens>>();
for (auto const& token : tokens) {
auto protocol_token = protocol::Storage::TrustTokens::Create()
.SetIssuerOrigin(token->issuer.Serialize())
.SetCount(token->count)
.Build();
result->push_back(std::move(protocol_token));
}
callback->sendSuccess(std::move(result));
}
} // namespace
void StorageHandler::GetTrustTokens(
std::unique_ptr<GetTrustTokensCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
storage_partition_->GetNetworkContext()->GetStoredTrustTokenCounts(
base::BindOnce(&SendTrustTokens, std::move(callback)));
}
namespace {
void SendClearTrustTokensStatus(
std::unique_ptr<StorageHandler::ClearTrustTokensCallback> callback,
network::mojom::DeleteStoredTrustTokensStatus status) {
switch (status) {
case network::mojom::DeleteStoredTrustTokensStatus::kSuccessTokensDeleted:
callback->sendSuccess(/* didDeleteTokens */ true);
break;
case network::mojom::DeleteStoredTrustTokensStatus::kSuccessNoTokensDeleted:
callback->sendSuccess(/* didDeleteTokens */ false);
break;
case network::mojom::DeleteStoredTrustTokensStatus::kFailureFeatureDisabled:
callback->sendFailure(
Response::ServerError("The Trust Tokens feature is disabled."));
break;
case network::mojom::DeleteStoredTrustTokensStatus::kFailureInvalidOrigin:
callback->sendFailure(
Response::InvalidParams("The provided issuerOrigin is invalid. It "
"must be a HTTP/HTTPS trustworthy origin."));
break;
}
}
} // namespace
void StorageHandler::ClearTrustTokens(
const std::string& issuerOrigin,
std::unique_ptr<ClearTrustTokensCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
storage_partition_->GetNetworkContext()->DeleteStoredTrustTokens(
url::Origin::Create(GURL(issuerOrigin)),
base::BindOnce(&SendClearTrustTokensStatus, std::move(callback)));
}
void StorageHandler::OnInterestGroupAccessed(
base::optional_ref<const std::string> auction_id,
base::Time access_time,
InterestGroupManagerImpl::InterestGroupObserver::AccessType type,
const url::Origin& owner_origin,
const std::string& name,
base::optional_ref<const url::Origin> component_seller_origin,
std::optional<double> bid,
base::optional_ref<const std::string> bid_currency) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
using AccessType =
InterestGroupManagerImpl::InterestGroupObserver::AccessType;
std::string type_enum;
switch (type) {
case AccessType::kJoin:
type_enum = Storage::InterestGroupAccessTypeEnum::Join;
break;
case AccessType::kLeave:
type_enum = Storage::InterestGroupAccessTypeEnum::Leave;
break;
case AccessType::kUpdate:
type_enum = Storage::InterestGroupAccessTypeEnum::Update;
break;
case AccessType::kLoaded:
type_enum = Storage::InterestGroupAccessTypeEnum::Loaded;
break;
case AccessType::kBid:
type_enum = Storage::InterestGroupAccessTypeEnum::Bid;
break;
case AccessType::kAdditionalBid:
type_enum = Storage::InterestGroupAccessTypeEnum::AdditionalBid;
break;
case AccessType::kWin:
type_enum = Storage::InterestGroupAccessTypeEnum::Win;
break;
case AccessType::kAdditionalBidWin:
type_enum = Storage::InterestGroupAccessTypeEnum::AdditionalBidWin;
break;
case AccessType::kClear:
type_enum = Storage::InterestGroupAccessTypeEnum::Clear;
break;
case AccessType::kTopLevelBid:
type_enum = Storage::InterestGroupAccessTypeEnum::TopLevelBid;
break;
case AccessType::kTopLevelAdditionalBid:
type_enum = Storage::InterestGroupAccessTypeEnum::TopLevelAdditionalBid;
break;
};
frontend_->InterestGroupAccessed(
access_time.InSecondsFSinceUnixEpoch(), type_enum,
owner_origin.Serialize(), name,
component_seller_origin.has_value()
? std::optional<String>(component_seller_origin->Serialize())
: std::nullopt,
bid, bid_currency.CopyAsOptional(), auction_id.CopyAsOptional());
}
namespace {
void SendGetInterestGroup(
std::unique_ptr<StorageHandler::GetInterestGroupDetailsCallback> callback,
std::optional<SingleStorageInterestGroup> storage_group) {
if (!storage_group) {
callback->sendFailure(Response::ServerError("Interest group not found"));
return;
}
base::Value::Dict ig_serialization =
SerializeInterestGroupForDevtools(storage_group.value()->interest_group);
// "joiningOrigin" is in StorageInterestGroup, not InterestGroup, so it needs
// to be added in separately.
ig_serialization.Set("joiningOrigin",
storage_group.value()->joining_origin.Serialize());
callback->sendSuccess(
std::make_unique<base::Value::Dict>(std::move(ig_serialization)));
}
} // namespace
void StorageHandler::GetInterestGroupDetails(
const std::string& owner_origin_string,
const std::string& name,
std::unique_ptr<GetInterestGroupDetailsCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
InterestGroupManagerImpl* manager = static_cast<InterestGroupManagerImpl*>(
storage_partition_->GetInterestGroupManager());
if (!manager) {
callback->sendFailure(
Response::ServerError("Interest group storage is disabled"));
return;
}
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::ServerError("Invalid Owner Origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(GURL(owner_origin_string));
DCHECK(!owner_origin.opaque());
manager->GetInterestGroup(
owner_origin, name,
base::BindOnce(&SendGetInterestGroup, std::move(callback)));
}
Response StorageHandler::SetInterestGroupTracking(bool enable) {
interest_group_tracking_enabled_ = enable;
return SetInterestGroupTrackingInternal(storage_partition_, enable);
}
Response StorageHandler::SetInterestGroupTrackingInternal(
StoragePartition* storage_partition,
bool enable) {
if (!storage_partition) {
return Response::InternalError();
}
InterestGroupManagerImpl* manager = static_cast<InterestGroupManagerImpl*>(
storage_partition->GetInterestGroupManager());
if (!manager) {
return Response::ServerError("Interest group storage is disabled.");
}
if (enable) {
// Only add if we are not already registered as an observer. We only
// observe the interest group manager, so if we're observing anything then
// we are already registered.
if (!InterestGroupManagerImpl::InterestGroupObserver::IsInObserverList()) {
manager->AddInterestGroupObserver(this);
}
} else {
// Removal doesn't care if we are not registered.
manager->RemoveInterestGroupObserver(this);
}
return Response::Success();
}
Response StorageHandler::SetInterestGroupAuctionTracking(bool enable) {
interest_group_auction_tracking_enabled_ = enable;
return Response::Success();
}
namespace {
void SendSharedStorageMetadata(
std::unique_ptr<StorageHandler::GetSharedStorageMetadataCallback> callback,
storage::SharedStorageManager::MetadataResult metadata) {
if (metadata.time_result ==
storage::SharedStorageManager::OperationResult::kNotFound) {
callback->sendFailure(Response::ServerError("Origin not found."));
return;
}
std::string error_message;
if (metadata.length == -1) {
error_message += "Unable to retrieve `length`. ";
}
if (metadata.time_result !=
storage::SharedStorageManager::OperationResult::kSuccess) {
error_message += "Unable to retrieve `creationTime`. ";
}
if (metadata.budget_result !=
storage::SharedStorageManager::OperationResult::kSuccess) {
error_message += "Unable to retrieve `remainingBudget`. ";
}
if (metadata.bytes_used == -1) {
error_message += "Unable to retrieve `bytes_used`. ";
}
if (!error_message.empty()) {
callback->sendFailure(Response::ServerError(error_message));
return;
}
auto protocol_metadata =
protocol::Storage::SharedStorageMetadata::Create()
.SetLength(metadata.length)
.SetCreationTime(metadata.creation_time.InSecondsFSinceUnixEpoch())
.SetRemainingBudget(metadata.remaining_budget)
.SetBytesUsed(metadata.bytes_used)
.Build();
callback->sendSuccess(std::move(protocol_metadata));
}
} // namespace
void StorageHandler::GetSharedStorageMetadata(
const std::string& owner_origin_string,
std::unique_ptr<GetSharedStorageMetadataCallback> callback) {
auto manager_or_response = GetSharedStorageManager();
if (std::holds_alternative<protocol::Response>(manager_or_response)) {
callback->sendFailure(std::get<protocol::Response>(manager_or_response));
return;
}
storage::SharedStorageManager* manager =
std::get<storage::SharedStorageManager*>(manager_or_response);
DCHECK(manager);
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::InvalidParams("Invalid owner origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(owner_origin_url);
DCHECK(!owner_origin.opaque());
manager->GetMetadata(
std::move(owner_origin),
base::BindOnce(&SendSharedStorageMetadata, std::move(callback)));
}
namespace {
void RetrieveSharedStorageEntries(
std::unique_ptr<StorageHandler::GetSharedStorageEntriesCallback> callback,
storage::SharedStorageManager::EntriesResult entries_result) {
if (entries_result.result !=
storage::SharedStorageManager::OperationResult::kSuccess) {
callback->sendFailure(Response::ServerError("Database error"));
return;
}
auto entries = std::make_unique<
protocol::Array<protocol::Storage::SharedStorageEntry>>();
for (const auto& entry : entries_result.entries) {
auto protocol_entry = protocol::Storage::SharedStorageEntry::Create()
.SetKey(entry.first)
.SetValue(entry.second)
.Build();
entries->push_back(std::move(protocol_entry));
}
callback->sendSuccess(std::move(entries));
}
} // namespace
void StorageHandler::GetSharedStorageEntries(
const std::string& owner_origin_string,
std::unique_ptr<GetSharedStorageEntriesCallback> callback) {
auto manager_or_response = GetSharedStorageManager();
if (std::holds_alternative<protocol::Response>(manager_or_response)) {
callback->sendFailure(std::get<protocol::Response>(manager_or_response));
return;
}
storage::SharedStorageManager* manager =
std::get<storage::SharedStorageManager*>(manager_or_response);
DCHECK(manager);
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::InvalidParams("Invalid owner origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(owner_origin_url);
DCHECK(!owner_origin.opaque());
manager->GetEntriesForDevTools(
owner_origin,
base::BindOnce(&RetrieveSharedStorageEntries, std::move(callback)));
}
namespace {
void DispatchSharedStorageSetCallback(
std::unique_ptr<Storage::Backend::SetSharedStorageEntryCallback> callback,
storage::SharedStorageManager::OperationResult result) {
if (result != storage::SharedStorageManager::OperationResult::kSet &&
result != storage::SharedStorageManager::OperationResult::kIgnored) {
callback->sendFailure(Response::ServerError("Database error"));
return;
}
callback->sendSuccess();
}
} // namespace
void StorageHandler::SetSharedStorageEntry(
const std::string& owner_origin_string,
const std::string& key,
const std::string& value,
std::optional<bool> ignore_if_present,
std::unique_ptr<SetSharedStorageEntryCallback> callback) {
auto manager_or_response = GetSharedStorageManager();
if (std::holds_alternative<protocol::Response>(manager_or_response)) {
callback->sendFailure(std::get<protocol::Response>(manager_or_response));
return;
}
storage::SharedStorageManager* manager =
std::get<storage::SharedStorageManager*>(manager_or_response);
DCHECK(manager);
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::InvalidParams("Invalid owner origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(owner_origin_url);
DCHECK(!owner_origin.opaque());
auto set_behavior =
ignore_if_present.value_or(false)
? storage::SharedStorageManager::SetBehavior::kIgnoreIfPresent
: storage::SharedStorageManager::SetBehavior::kDefault;
manager->Set(
owner_origin, base::UTF8ToUTF16(key), base::UTF8ToUTF16(value),
base::BindOnce(&DispatchSharedStorageSetCallback, std::move(callback)),
set_behavior);
}
namespace {
template <typename CallbackType>
void DispatchSharedStorageCallback(
std::unique_ptr<CallbackType> callback,
storage::SharedStorageManager::OperationResult result) {
if (result != storage::SharedStorageManager::OperationResult::kSuccess) {
callback->sendFailure(Response::ServerError("Database error"));
return;
}
callback->sendSuccess();
}
} // namespace
void StorageHandler::DeleteSharedStorageEntry(
const std::string& owner_origin_string,
const std::string& key,
std::unique_ptr<DeleteSharedStorageEntryCallback> callback) {
auto manager_or_response = GetSharedStorageManager();
if (std::holds_alternative<protocol::Response>(manager_or_response)) {
callback->sendFailure(std::get<protocol::Response>(manager_or_response));
return;
}
storage::SharedStorageManager* manager =
std::get<storage::SharedStorageManager*>(manager_or_response);
DCHECK(manager);
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::InvalidParams("Invalid owner origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(owner_origin_url);
DCHECK(!owner_origin.opaque());
manager->Delete(
owner_origin, base::UTF8ToUTF16(key),
base::BindOnce(
&DispatchSharedStorageCallback<DeleteSharedStorageEntryCallback>,
std::move(callback)));
}
void StorageHandler::ClearSharedStorageEntries(
const std::string& owner_origin_string,
std::unique_ptr<ClearSharedStorageEntriesCallback> callback) {
auto manager_or_response = GetSharedStorageManager();
if (std::holds_alternative<protocol::Response>(manager_or_response)) {
callback->sendFailure(std::get<protocol::Response>(manager_or_response));
return;
}
storage::SharedStorageManager* manager =
std::get<storage::SharedStorageManager*>(manager_or_response);
DCHECK(manager);
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::InvalidParams("Invalid owner origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(owner_origin_url);
DCHECK(!owner_origin.opaque());
manager->Clear(
owner_origin,
base::BindOnce(
&DispatchSharedStorageCallback<ClearSharedStorageEntriesCallback>,
std::move(callback)));
}
Response StorageHandler::SetSharedStorageTracking(bool enable) {
if (enable) {
auto* manager = GetSharedStorageRuntimeManager();
if (!manager) {
return Response::ServerError("Shared storage is disabled.");
}
// Only enable tracking if this handler is associated with a main render
// frame host, and if tracking isn't already enabled.
if (frame_host_ && frame_host_->IsOutermostMainFrame() &&
!shared_storage_observation_.IsObserving()) {
shared_storage_observation_.Observe(manager);
}
} else {
shared_storage_observation_.Reset();
}
return Response::Success();
}
void StorageHandler::ResetSharedStorageBudget(
const std::string& owner_origin_string,
std::unique_ptr<ResetSharedStorageBudgetCallback> callback) {
auto manager_or_response = GetSharedStorageManager();
if (std::holds_alternative<protocol::Response>(manager_or_response)) {
callback->sendFailure(std::get<protocol::Response>(manager_or_response));
return;
}
storage::SharedStorageManager* manager =
std::get<storage::SharedStorageManager*>(manager_or_response);
DCHECK(manager);
GURL owner_origin_url(owner_origin_string);
if (!owner_origin_url.is_valid()) {
callback->sendFailure(Response::InvalidParams("Invalid owner origin"));
return;
}
url::Origin owner_origin = url::Origin::Create(owner_origin_url);
DCHECK(!owner_origin.opaque());
manager->ResetBudgetForDevTools(
owner_origin,
base::BindOnce(
&DispatchSharedStorageCallback<ResetSharedStorageBudgetCallback>,
std::move(callback)));
}
GlobalRenderFrameHostId StorageHandler::AssociatedFrameHostId() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return frame_host_ ? frame_host_->GetGlobalId() : GlobalRenderFrameHostId();
}
bool StorageHandler::ShouldReceiveAllSharedStorageReports() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return false;
}
namespace {
std::string GetFrameTokenFromGlobalRenderFrameHostId(
GlobalRenderFrameHostId frame_id) {
auto* rfh = frame_id ? RenderFrameHostImpl::FromID(frame_id) : nullptr;
return rfh ? rfh->devtools_frame_token().ToString() : std::string();
}
const char* GetSharedStorageAccessMethodEnum(
SharedStorageRuntimeManager::SharedStorageObserverInterface::AccessMethod
method) {
using AccessMethod =
SharedStorageRuntimeManager::SharedStorageObserverInterface::AccessMethod;
switch (method) {
case AccessMethod::kAddModule:
return Storage::SharedStorageAccessMethodEnum::AddModule;
case AccessMethod::kCreateWorklet:
return Storage::SharedStorageAccessMethodEnum::CreateWorklet;
case AccessMethod::kSelectURL:
return Storage::SharedStorageAccessMethodEnum::SelectURL;
case AccessMethod::kRun:
return Storage::SharedStorageAccessMethodEnum::Run;
case AccessMethod::kBatchUpdate:
return Storage::SharedStorageAccessMethodEnum::BatchUpdate;
case AccessMethod::kSet:
return Storage::SharedStorageAccessMethodEnum::Set;
case AccessMethod::kAppend:
return Storage::SharedStorageAccessMethodEnum::Append;
case AccessMethod::kDelete:
return Storage::SharedStorageAccessMethodEnum::Delete;
case AccessMethod::kClear:
return Storage::SharedStorageAccessMethodEnum::Clear;
case AccessMethod::kGet:
return Storage::SharedStorageAccessMethodEnum::Get;
case AccessMethod::kKeys:
return Storage::SharedStorageAccessMethodEnum::Keys;
case AccessMethod::kValues:
return Storage::SharedStorageAccessMethodEnum::Values;
case AccessMethod::kEntries:
return Storage::SharedStorageAccessMethodEnum::Entries;
case AccessMethod::kLength:
return Storage::SharedStorageAccessMethodEnum::Length;
case AccessMethod::kRemainingBudget:
return Storage::SharedStorageAccessMethodEnum::RemainingBudget;
};
NOTREACHED();
}
} // namespace
void StorageHandler::OnSharedStorageAccessed(
base::Time access_time,
blink::SharedStorageAccessScope scope,
SharedStorageRuntimeManager::SharedStorageObserverInterface::AccessMethod
method,
GlobalRenderFrameHostId main_frame_id,
const std::string& owner_origin,
const SharedStorageEventParams& params) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
using AccessScope = blink::SharedStorageAccessScope;
std::string scope_enum;
switch (scope) {
case AccessScope::kWindow:
scope_enum = Storage::SharedStorageAccessScopeEnum::Window;
break;
case AccessScope::kSharedStorageWorklet:
scope_enum = Storage::SharedStorageAccessScopeEnum::SharedStorageWorklet;
break;
case AccessScope::kProtectedAudienceWorklet:
scope_enum =
Storage::SharedStorageAccessScopeEnum::ProtectedAudienceWorklet;
break;
case AccessScope::kHeader:
scope_enum = Storage::SharedStorageAccessScopeEnum::Header;
break;
};
auto protocol_params =
protocol::Storage::SharedStorageAccessParams::Create().Build();
if (params.script_source_url) {
protocol_params->SetScriptSourceUrl(*params.script_source_url);
}
if (params.data_origin) {
protocol_params->SetDataOrigin(*params.data_origin);
}
if (params.operation_name) {
protocol_params->SetOperationName(*params.operation_name);
}
if (params.operation_id) {
protocol_params->SetOperationId(base::NumberToString(*params.operation_id));
}
if (params.keep_alive) {
protocol_params->SetKeepAlive(*params.keep_alive);
}
if (params.serialized_data) {
protocol_params->SetSerializedData(*params.serialized_data);
}
if (params.urn_uuid) {
protocol_params->SetUrnUuid(*params.urn_uuid);
}
if (params.key) {
protocol_params->SetKey(*params.key);
}
if (params.value) {
protocol_params->SetValue(*params.value);
}
if (params.ignore_if_present) {
protocol_params->SetIgnoreIfPresent(*params.ignore_if_present);
}
if (params.worklet_ordinal) {
protocol_params->SetWorkletOrdinal(*params.worklet_ordinal);
}
if (!params.worklet_devtools_token.is_empty()) {
protocol_params->SetWorkletTargetId(
params.worklet_devtools_token.ToString());
}
if (params.with_lock) {
protocol_params->SetWithLock(*params.with_lock);
}
if (params.batch_update_id) {
protocol_params->SetBatchUpdateId(
base::NumberToString(*params.batch_update_id));
}
if (params.batch_size) {
protocol_params->SetBatchSize(*params.batch_size);
}
if (params.private_aggregation_config) {
auto protocol_private_aggregation_config =
protocol::Storage::SharedStoragePrivateAggregationConfig::Create()
.SetFilteringIdMaxBytes(params.private_aggregation_config->config
->filtering_id_max_bytes)
.Build();
if (params.private_aggregation_config->config
->aggregation_coordinator_origin) {
protocol_private_aggregation_config->SetAggregationCoordinatorOrigin(
params.private_aggregation_config->config
->aggregation_coordinator_origin->Serialize());
}
if (params.private_aggregation_config->config->context_id) {
protocol_private_aggregation_config->SetContextId(
params.private_aggregation_config->config->context_id.value());
}
if (params.private_aggregation_config->config->max_contributions) {
protocol_private_aggregation_config->SetMaxContributions(
params.private_aggregation_config->config->max_contributions.value());
}
protocol_params->SetPrivateAggregationConfig(
std::move(protocol_private_aggregation_config));
}
if (params.urls_with_metadata) {
auto protocol_urls = std::make_unique<
protocol::Array<protocol::Storage::SharedStorageUrlWithMetadata>>();
for (const auto& url_with_metadata : *params.urls_with_metadata) {
auto reporting_metadata = std::make_unique<
protocol::Array<protocol::Storage::SharedStorageReportingMetadata>>();
for (const auto& metadata_pair : url_with_metadata.reporting_metadata) {
auto reporting_pair =
protocol::Storage::SharedStorageReportingMetadata::Create()
.SetEventType(metadata_pair.first)
.SetReportingUrl(metadata_pair.second)
.Build();
reporting_metadata->push_back(std::move(reporting_pair));
}
auto protocol_url =
protocol::Storage::SharedStorageUrlWithMetadata::Create()
.SetUrl(url_with_metadata.url)
.SetReportingMetadata(std::move(reporting_metadata))
.Build();
protocol_urls->push_back(std::move(protocol_url));
}
protocol_params->SetUrlsWithMetadata(std::move(protocol_urls));
}
frontend_->SharedStorageAccessed(
access_time.InSecondsFSinceUnixEpoch(), scope_enum,
GetSharedStorageAccessMethodEnum(method),
GetFrameTokenFromGlobalRenderFrameHostId(main_frame_id), owner_origin,
net::SchemefulSite(GURL(owner_origin)).Serialize(),
std::move(protocol_params));
}
void StorageHandler::OnSharedStorageSelectUrlUrnUuidGenerated(
const GURL& urn_uuid) {}
void StorageHandler::OnSharedStorageSelectUrlConfigPopulated(
const std::optional<FencedFrameConfig>& config) {}
void StorageHandler::OnSharedStorageWorkletOperationExecutionFinished(
base::Time finished_time,
base::TimeDelta execution_time,
SharedStorageRuntimeManager::SharedStorageObserverInterface::AccessMethod
method,
int operation_id,
const base::UnguessableToken& worklet_devtools_token,
GlobalRenderFrameHostId main_frame_id,
const std::string& owner_origin) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
frontend_->SharedStorageWorkletOperationExecutionFinished(
finished_time.InSecondsFSinceUnixEpoch(), execution_time.InMicroseconds(),
GetSharedStorageAccessMethodEnum(method),
base::NumberToString(operation_id), worklet_devtools_token.ToString(),
GetFrameTokenFromGlobalRenderFrameHostId(main_frame_id), owner_origin);
}
DispatchResponse StorageHandler::SetStorageBucketTracking(
const std::string& serialized_storage_key,
bool enable) {
auto storage_key = blink::StorageKey::Deserialize(serialized_storage_key);
if (!storage_key.has_value()) {
return Response::InvalidParams("Invalid Storage Key given.");
}
if (enable) {
storage::QuotaManagerProxy* manager = GetQuotaManagerProxy();
if (!quota_manager_observer_) {
quota_manager_observer_ =
std::make_unique<StorageHandler::QuotaManagerObserver>(
weak_ptr_factory_.GetWeakPtr(), manager);
}
quota_manager_observer_->TrackStorageKey(storage_key.value(), manager);
} else if (quota_manager_observer_) {
quota_manager_observer_->UntrackStorageKey(storage_key.value());
}
return Response::Success();
}
DispatchResponse StorageHandler::DeleteStorageBucket(
std::unique_ptr<protocol::Storage::StorageBucket> bucket) {
storage::QuotaManagerProxy* manager = GetQuotaManagerProxy();
DCHECK(manager);
if (!bucket->HasName()) {
return Response::InvalidParams("Can't delete the default bucket.");
}
auto storage_key = blink::StorageKey::Deserialize(bucket->GetStorageKey());
if (!storage_key.has_value()) {
return Response::InvalidParams("Invalid Storage Key given.");
}
manager->DeleteBucket(storage_key.value(), bucket->GetName(""),
base::SingleThreadTaskRunner::GetCurrentDefault(),
base::DoNothing());
return Response::Success();
}
void StorageHandler::NotifyCreateOrUpdateBucket(
const storage::BucketInfo& bucket_info) {
frontend_->StorageBucketCreatedOrUpdated(BuildBucketInfo(bucket_info));
}
void StorageHandler::NotifyDeleteBucket(
const storage::BucketLocator& bucket_locator) {
frontend_->StorageBucketDeleted(
base::NumberToString(bucket_locator.id.value()));
}
AttributionManager* StorageHandler::GetAttributionManager() {
if (!storage_partition_) {
return nullptr;
}
return static_cast<StoragePartitionImpl*>(storage_partition_)
->GetAttributionManager();
}
void StorageHandler::SetAttributionReportingLocalTestingMode(
bool enabled,
std::unique_ptr<SetAttributionReportingLocalTestingModeCallback> callback) {
auto* manager = GetAttributionManager();
if (!manager) {
callback->sendFailure(Response::InternalError());
return;
}
manager->SetDebugMode(
enabled,
base::BindOnce(
&SetAttributionReportingLocalTestingModeCallback::sendSuccess,
std::move(callback)));
}
void StorageHandler::SendPendingAttributionReports(
std::unique_ptr<SendPendingAttributionReportsCallback> callback) {
auto* manager = GetAttributionManager();
if (!manager) {
callback->sendFailure(Response::InternalError());
return;
}
manager->GetPendingReportsForInternalUse(
/*limit=*/-1,
base::BindOnce(
[](base::WeakPtr<StorageHandler> storage_handler,
std::unique_ptr<SendPendingAttributionReportsCallback> callback,
std::vector<AttributionReport> reports) {
if (!storage_handler) {
callback->sendFailure(Response::InternalError());
return;
}
auto* manager = storage_handler->GetAttributionManager();
if (!manager) {
callback->sendFailure(Response::InternalError());
return;
}
auto barrier = base::BarrierClosure(
reports.size(),
base::BindOnce(
&SendPendingAttributionReportsCallback::sendSuccess,
std::move(callback), reports.size()));
for (const auto& report : reports) {
manager->SendReportForWebUI(report.id(), barrier);
}
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void StorageHandler::ResetAttributionReporting() {
attribution_observation_.Reset();
auto* manager = GetAttributionManager();
if (!manager) {
return;
}
manager->SetDebugMode(/*enabled=*/std::nullopt, base::DoNothing());
}
namespace {
using ::attribution_reporting::mojom::AggregatableResult;
using ::attribution_reporting::mojom::EventLevelResult;
using ::attribution_reporting::mojom::StoreSourceResult;
Storage::AttributionReportingSourceRegistrationResult
ToSourceRegistrationResult(StoreSourceResult result) {
switch (result) {
case StoreSourceResult::kSuccess:
return Storage::AttributionReportingSourceRegistrationResultEnum::Success;
case StoreSourceResult::kInternalError:
return Storage::AttributionReportingSourceRegistrationResultEnum::
InternalError;
case StoreSourceResult::kInsufficientSourceCapacity:
return Storage::AttributionReportingSourceRegistrationResultEnum::
InsufficientSourceCapacity;
case StoreSourceResult::kInsufficientUniqueDestinationCapacity:
return Storage::AttributionReportingSourceRegistrationResultEnum::
InsufficientUniqueDestinationCapacity;
case StoreSourceResult::kExcessiveReportingOrigins:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ExcessiveReportingOrigins;
case StoreSourceResult::kProhibitedByBrowserPolicy:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ProhibitedByBrowserPolicy;
case StoreSourceResult::kSuccessNoised:
return Storage::AttributionReportingSourceRegistrationResultEnum::
SuccessNoised;
case StoreSourceResult::kDestinationReportingLimitReached:
return Storage::AttributionReportingSourceRegistrationResultEnum::
DestinationReportingLimitReached;
case StoreSourceResult::kDestinationGlobalLimitReached:
return Storage::AttributionReportingSourceRegistrationResultEnum::
DestinationGlobalLimitReached;
case StoreSourceResult::kDestinationBothLimitsReached:
return Storage::AttributionReportingSourceRegistrationResultEnum::
DestinationBothLimitsReached;
case StoreSourceResult::kReportingOriginsPerSiteLimitReached:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ReportingOriginsPerSiteLimitReached;
case StoreSourceResult::kExceedsMaxChannelCapacity:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ExceedsMaxChannelCapacity;
case StoreSourceResult::kExceedsMaxScopesChannelCapacity:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ExceedsMaxScopesChannelCapacity;
case StoreSourceResult::kExceedsMaxTriggerStateCardinality:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ExceedsMaxTriggerStateCardinality;
case StoreSourceResult::kExceedsMaxEventStatesLimit:
return Storage::AttributionReportingSourceRegistrationResultEnum::
ExceedsMaxEventStatesLimit;
case StoreSourceResult::kDestinationPerDayReportingLimitReached:
return Storage::AttributionReportingSourceRegistrationResultEnum::
DestinationPerDayReportingLimitReached;
}
}
Storage::AttributionReportingEventLevelResult ToEventLevelResult(
EventLevelResult result) {
switch (result) {
case EventLevelResult::kSuccess:
return Storage::AttributionReportingEventLevelResultEnum::Success;
case EventLevelResult::kSuccessDroppedLowerPriority:
return Storage::AttributionReportingEventLevelResultEnum::
SuccessDroppedLowerPriority;
case EventLevelResult::kInternalError:
return Storage::AttributionReportingEventLevelResultEnum::InternalError;
case EventLevelResult::kNoCapacityForConversionDestination:
return Storage::AttributionReportingEventLevelResultEnum::
NoCapacityForAttributionDestination;
case EventLevelResult::kNoMatchingImpressions:
return Storage::AttributionReportingEventLevelResultEnum::
NoMatchingSources;
case EventLevelResult::kDeduplicated:
return Storage::AttributionReportingEventLevelResultEnum::Deduplicated;
case EventLevelResult::kExcessiveAttributions:
return Storage::AttributionReportingEventLevelResultEnum::
ExcessiveAttributions;
case EventLevelResult::kPriorityTooLow:
return Storage::AttributionReportingEventLevelResultEnum::PriorityTooLow;
case EventLevelResult::kNeverAttributedSource:
return Storage::AttributionReportingEventLevelResultEnum::
NeverAttributedSource;
case EventLevelResult::kExcessiveReportingOrigins:
return Storage::AttributionReportingEventLevelResultEnum::
ExcessiveReportingOrigins;
case EventLevelResult::kNoMatchingSourceFilterData:
return Storage::AttributionReportingEventLevelResultEnum::
NoMatchingSourceFilterData;
case EventLevelResult::kProhibitedByBrowserPolicy:
return Storage::AttributionReportingEventLevelResultEnum::
ProhibitedByBrowserPolicy;
case EventLevelResult::kNoMatchingConfigurations:
return Storage::AttributionReportingEventLevelResultEnum::
NoMatchingConfigurations;
case EventLevelResult::kExcessiveReports:
return Storage::AttributionReportingEventLevelResultEnum::
ExcessiveReports;
case EventLevelResult::kFalselyAttributedSource:
return Storage::AttributionReportingEventLevelResultEnum::
FalselyAttributedSource;
case EventLevelResult::kReportWindowPassed:
return Storage::AttributionReportingEventLevelResultEnum::
ReportWindowPassed;
case EventLevelResult::kNotRegistered:
return Storage::AttributionReportingEventLevelResultEnum::NotRegistered;
case EventLevelResult::kReportWindowNotStarted:
return Storage::AttributionReportingEventLevelResultEnum::
ReportWindowNotStarted;
case EventLevelResult::kNoMatchingTriggerData:
return Storage::AttributionReportingEventLevelResultEnum::
NoMatchingTriggerData;
}
}
Storage::AttributionReportingAggregatableResult ToAggregatableResult(
AggregatableResult result) {
switch (result) {
case AggregatableResult::kSuccess:
return Storage::AttributionReportingAggregatableResultEnum::Success;
case AggregatableResult::kInternalError:
return Storage::AttributionReportingAggregatableResultEnum::InternalError;
case AggregatableResult::kNoCapacityForConversionDestination:
return Storage::AttributionReportingAggregatableResultEnum::
NoCapacityForAttributionDestination;
case AggregatableResult::kNoMatchingImpressions:
return Storage::AttributionReportingAggregatableResultEnum::
NoMatchingSources;
case AggregatableResult::kExcessiveAttributions:
return Storage::AttributionReportingAggregatableResultEnum::
ExcessiveAttributions;
case AggregatableResult::kExcessiveReportingOrigins:
return Storage::AttributionReportingAggregatableResultEnum::
ExcessiveReportingOrigins;
case AggregatableResult::kNoHistograms:
return Storage::AttributionReportingAggregatableResultEnum::NoHistograms;
case AggregatableResult::kInsufficientBudget:
return Storage::AttributionReportingAggregatableResultEnum::
InsufficientBudget;
case AggregatableResult::kInsufficientNamedBudget:
return Storage::AttributionReportingAggregatableResultEnum::
InsufficientNamedBudget;
case AggregatableResult::kNoMatchingSourceFilterData:
return Storage::AttributionReportingAggregatableResultEnum::
NoMatchingSourceFilterData;
case AggregatableResult::kNotRegistered:
return Storage::AttributionReportingAggregatableResultEnum::NotRegistered;
case AggregatableResult::kProhibitedByBrowserPolicy:
return Storage::AttributionReportingAggregatableResultEnum::
ProhibitedByBrowserPolicy;
case AggregatableResult::kDeduplicated:
return Storage::AttributionReportingAggregatableResultEnum::Deduplicated;
case AggregatableResult::kReportWindowPassed:
return Storage::AttributionReportingAggregatableResultEnum::
ReportWindowPassed;
case AggregatableResult::kExcessiveReports:
return Storage::AttributionReportingAggregatableResultEnum::
ExcessiveReports;
}
}
std::unique_ptr<Array<Storage::AttributionReportingFilterDataEntry>>
ToFilterDataEntries(const attribution_reporting::FilterData& filter_data) {
auto out =
std::make_unique<Array<Storage::AttributionReportingFilterDataEntry>>();
for (const auto& [key, values] : filter_data.filter_values()) {
out->emplace_back(Storage::AttributionReportingFilterDataEntry::Create()
.SetKey(key)
.SetValues(std::make_unique<Array<String>>(values))
.Build());
}
return out;
}
std::unique_ptr<Array<Storage::AttributionReportingFilterConfig>>
ToFilterConfigs(
const std::vector<attribution_reporting::FilterConfig>& filter_configs) {
auto out =
std::make_unique<Array<Storage::AttributionReportingFilterConfig>>();
for (const auto& config : filter_configs) {
auto filter_data =
std::make_unique<Array<Storage::AttributionReportingFilterDataEntry>>();
for (const auto& [key, values] : config.filter_values()) {
filter_data->emplace_back(
Storage::AttributionReportingFilterDataEntry::Create()
.SetKey(key)
.SetValues(std::make_unique<Array<String>>(values))
.Build());
}
auto config_entry = Storage::AttributionReportingFilterConfig::Create()
.SetFilterValues(std::move(filter_data))
.Build();
if (auto lookback_window = config.lookback_window();
lookback_window.has_value()) {
config_entry->SetLookbackWindow(lookback_window->InSeconds());
}
out->emplace_back(std::move(config_entry));
}
return out;
}
std::unique_ptr<Storage::AttributionReportingFilterPair> ToFilterPair(
const attribution_reporting::FilterPair& filters) {
return Storage::AttributionReportingFilterPair::Create()
.SetFilters(ToFilterConfigs(filters.positive))
.SetNotFilters(ToFilterConfigs(filters.negative))
.Build();
}
std::unique_ptr<Array<Storage::AttributionReportingAggregationKeysEntry>>
ToAggregationKeysEntries(const attribution_reporting::AggregationKeys& keys) {
auto out = std::make_unique<
Array<Storage::AttributionReportingAggregationKeysEntry>>();
for (const auto& [key, value] : keys.keys()) {
out->emplace_back(
Storage::AttributionReportingAggregationKeysEntry::Create()
.SetKey(key)
.SetValue(attribution_reporting::HexEncodeAggregationKey(value))
.Build());
}
return out;
}
std::unique_ptr<Storage::AttributionReportingEventReportWindows>
ToEventReportWindows(const attribution_reporting::EventReportWindows& windows) {
auto end_times = std::make_unique<Array<int>>();
for (base::TimeDelta end_time : windows.end_times()) {
end_times->emplace_back(end_time.InSeconds());
}
return Storage::AttributionReportingEventReportWindows::Create()
.SetStart(windows.start_time().InSeconds())
.SetEnds(std::move(end_times))
.Build();
}
std::unique_ptr<Array<double>> ToTriggerData(
const attribution_reporting::TriggerDataSet::TriggerData& trigger_data) {
return std::make_unique<Array<double>>(trigger_data.begin(),
trigger_data.end());
}
Storage::AttributionReportingTriggerDataMatching ToTriggerDataMatching(
attribution_reporting::mojom::TriggerDataMatching value) {
switch (value) {
case attribution_reporting::mojom::TriggerDataMatching::kExact:
return Storage::AttributionReportingTriggerDataMatchingEnum::Exact;
case attribution_reporting::mojom::TriggerDataMatching::kModulus:
return Storage::AttributionReportingTriggerDataMatchingEnum::Modulus;
}
}
std::unique_ptr<Array<Storage::AttributionReportingAggregatableDedupKey>>
ToAggregatableDedupKeys(
const std::vector<attribution_reporting::AggregatableDedupKey>&
dedup_keys) {
auto out = std::make_unique<
Array<Storage::AttributionReportingAggregatableDedupKey>>();
for (const auto& dedup_key : dedup_keys) {
auto dedup_key_entry =
Storage::AttributionReportingAggregatableDedupKey::Create()
.SetFilters(ToFilterPair(dedup_key.filters))
.Build();
if (dedup_key.dedup_key.has_value()) {
dedup_key_entry->SetDedupKey(base::NumberToString(*dedup_key.dedup_key));
}
out->push_back(std::move(dedup_key_entry));
}
return out;
}
std::unique_ptr<Array<Storage::AttributionReportingEventTriggerData>>
ToEventTriggerData(const std::vector<attribution_reporting::EventTriggerData>&
event_triggers) {
auto out =
std::make_unique<Array<Storage::AttributionReportingEventTriggerData>>();
for (const auto& event_trigger : event_triggers) {
auto event_trigger_entry =
Storage::AttributionReportingEventTriggerData::Create()
.SetData(base::NumberToString(event_trigger.data))
.SetPriority(base::NumberToString(event_trigger.priority))
.SetFilters(ToFilterPair(event_trigger.filters))
.Build();
if (event_trigger.dedup_key.has_value()) {
event_trigger_entry->SetDedupKey(
base::NumberToString(*event_trigger.dedup_key));
}
out->push_back(std::move(event_trigger_entry));
}
return out;
}
std::unique_ptr<Array<Storage::AttributionReportingAggregatableTriggerData>>
ToAggregatableTriggerData(
const std::vector<attribution_reporting::AggregatableTriggerData>&
aggregatable_triggers) {
auto out = std::make_unique<
Array<Storage::AttributionReportingAggregatableTriggerData>>();
for (const auto& aggregatable_trigger : aggregatable_triggers) {
out->emplace_back(
Storage::AttributionReportingAggregatableTriggerData::Create()
.SetKeyPiece(attribution_reporting::HexEncodeAggregationKey(
aggregatable_trigger.key_piece()))
.SetSourceKeys(std::make_unique<Array<String>>(
aggregatable_trigger.source_keys().begin(),
aggregatable_trigger.source_keys().end()))
.SetFilters(ToFilterPair(aggregatable_trigger.filters()))
.Build());
}
return out;
}
std::unique_ptr<Array<Storage::AttributionReportingAggregatableValueDictEntry>>
ToAggregatableValueDictEntries(
const attribution_reporting::AggregatableValues::Values&
aggregatable_value) {
auto out = std::make_unique<
Array<Storage::AttributionReportingAggregatableValueDictEntry>>();
out->reserve(aggregatable_value.size());
for (const auto& [key, value] : aggregatable_value) {
out->emplace_back(
Storage::AttributionReportingAggregatableValueDictEntry::Create()
.SetKey(key)
.SetValue(value.value())
.SetFilteringId(base::NumberToString(value.filtering_id()))
.Build());
}
return out;
}
std::unique_ptr<Array<Storage::AttributionReportingAggregatableValueEntry>>
ToAggregatableValueEntries(
const std::vector<attribution_reporting::AggregatableValues>&
aggregatable_values) {
auto out = std::make_unique<
Array<Storage::AttributionReportingAggregatableValueEntry>>();
out->reserve(aggregatable_values.size());
for (const auto& aggregatable_value : aggregatable_values) {
out->emplace_back(
Storage::AttributionReportingAggregatableValueEntry::Create()
.SetValues(
ToAggregatableValueDictEntries(aggregatable_value.values()))
.SetFilters(ToFilterPair(aggregatable_value.filters()))
.Build());
}
return out;
}
Storage::AttributionReportingSourceRegistrationTimeConfig
ToSourceRegistrationTimeConfig(
attribution_reporting::mojom::SourceRegistrationTimeConfig
source_registration_time_config) {
switch (source_registration_time_config) {
case attribution_reporting::mojom::SourceRegistrationTimeConfig::kInclude:
return Storage::AttributionReportingSourceRegistrationTimeConfigEnum::
Include;
case attribution_reporting::mojom::SourceRegistrationTimeConfig::kExclude:
return Storage::AttributionReportingSourceRegistrationTimeConfigEnum::
Exclude;
}
}
std::unique_ptr<
Array<Storage::AttributionReportingAggregatableDebugReportingData>>
ToAggregatableDebugReportingDataArray(
const attribution_reporting::AggregatableDebugReportingConfig::DebugData&
data) {
auto out = std::make_unique<
Array<Storage::AttributionReportingAggregatableDebugReportingData>>();
for (const auto& [type, contribution] : data) {
auto types = std::make_unique<Array<String>>();
types->emplace_back(attribution_reporting::SerializeDebugDataType(type));
out->emplace_back(
Storage::AttributionReportingAggregatableDebugReportingData::Create()
.SetKeyPiece(attribution_reporting::HexEncodeAggregationKey(
contribution.key_piece()))
.SetValue(contribution.value())
.SetTypes(std::move(types))
.Build());
}
return out;
}
std::unique_ptr<Storage::AttributionReportingAggregatableDebugReportingConfig>
ToAggregatableDebugReportingConfig(
std::optional<double> budget,
const attribution_reporting::AggregatableDebugReportingConfig& config) {
auto out_config =
Storage::AttributionReportingAggregatableDebugReportingConfig::Create()
.SetKeyPiece(
attribution_reporting::HexEncodeAggregationKey(config.key_piece))
.SetDebugData(
ToAggregatableDebugReportingDataArray(config.debug_data))
.Build();
if (budget.has_value()) {
out_config->SetBudget(*budget);
}
if (config.aggregation_coordinator_origin.has_value()) {
out_config->SetAggregationCoordinatorOrigin(
config.aggregation_coordinator_origin->Serialize());
}
return out_config;
}
std::unique_ptr<Array<Storage::AttributionReportingNamedBudgetDef>>
ToNamedBudgetDefs(
const attribution_reporting::AggregatableNamedBudgetDefs& budgets) {
auto out =
std::make_unique<Array<Storage::AttributionReportingNamedBudgetDef>>();
for (const auto& [name, budget] : budgets.budgets()) {
out->emplace_back(Storage::AttributionReportingNamedBudgetDef::Create()
.SetName(name)
.SetBudget(budget)
.Build());
}
return out;
}
std::unique_ptr<Array<Storage::AttributionReportingNamedBudgetCandidate>>
ToNamedBudgetCandidates(
const std::vector<attribution_reporting::AggregatableNamedBudgetCandidate>&
candidates) {
auto out = std::make_unique<
Array<Storage::AttributionReportingNamedBudgetCandidate>>();
for (const auto& candidate : candidates) {
auto& out_candidate = out->emplace_back(
Storage::AttributionReportingNamedBudgetCandidate::Create()
.SetFilters(ToFilterPair(candidate.filters()))
.Build());
if (const std::optional<std::string>& name = candidate.name()) {
out_candidate->SetName(*name);
}
}
return out;
}
} // namespace
void StorageHandler::OnSourceHandled(
const StorableSource& source,
base::Time source_time,
std::optional<uint64_t> cleared_debug_key,
attribution_reporting::mojom::StoreSourceResult result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto& registration = source.registration();
auto destination_sites = std::make_unique<Array<String>>();
for (const auto& site : registration.destination_set.destinations()) {
destination_sites->emplace_back(site.Serialize());
}
const auto& common_info = source.common_info();
const auto& aggregatable_debug_reporting_config =
registration.aggregatable_debug_reporting_config;
auto out_source =
Storage::AttributionReportingSourceRegistration::Create()
.SetTime(source_time.InSecondsFSinceUnixEpoch())
.SetType(
attribution_reporting::SourceTypeName(common_info.source_type()))
.SetSourceOrigin(common_info.source_origin()->Serialize())
.SetReportingOrigin(common_info.reporting_origin()->Serialize())
.SetDestinationSites(std::move(destination_sites))
.SetEventId(base::NumberToString(registration.source_event_id))
.SetPriority(base::NumberToString(registration.priority))
.SetFilterData(ToFilterDataEntries(registration.filter_data))
.SetAggregationKeys(
ToAggregationKeysEntries(registration.aggregation_keys))
.SetExpiry(registration.expiry.InSeconds())
.SetTriggerData(
ToTriggerData(registration.trigger_data.trigger_data()))
.SetEventReportWindows(
ToEventReportWindows(registration.event_report_windows))
.SetAggregatableReportWindow(
registration.aggregatable_report_window.InSeconds())
.SetTriggerDataMatching(
ToTriggerDataMatching(registration.trigger_data_matching))
.SetDestinationLimitPriority(
base::NumberToString(registration.destination_limit_priority))
.SetAggregatableDebugReportingConfig(
ToAggregatableDebugReportingConfig(
aggregatable_debug_reporting_config.budget(),
aggregatable_debug_reporting_config.config()))
.SetMaxEventLevelReports(registration.max_event_level_reports)
.SetNamedBudgets(
ToNamedBudgetDefs(registration.aggregatable_named_budget_defs))
.SetDebugReporting(registration.debug_reporting)
.SetEventLevelEpsilon(registration.event_level_epsilon)
.Build();
if (registration.debug_key.has_value()) {
out_source->SetDebugKey(base::NumberToString(*registration.debug_key));
}
if (const std::optional<attribution_reporting::AttributionScopesData>&
attribution_scopes_data = registration.attribution_scopes_data) {
out_source->SetScopesData(
Storage::AttributionScopesData::Create()
.SetValues(std::make_unique<Array<String>>(
attribution_scopes_data->attribution_scopes_set()
.scopes()
.begin(),
attribution_scopes_data->attribution_scopes_set()
.scopes()
.end()))
.SetLimit(attribution_scopes_data->attribution_scope_limit())
.SetMaxEventStates(attribution_scopes_data->max_event_states())
.Build());
}
frontend_->AttributionReportingSourceRegistered(
std::move(out_source), ToSourceRegistrationResult(result));
}
void StorageHandler::OnTriggerHandled(std::optional<uint64_t> cleared_debug_key,
const CreateReportResult& result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const auto& registration = result.trigger().registration();
auto out_trigger =
Storage::AttributionReportingTriggerRegistration::Create()
.SetFilters(ToFilterPair(registration.filters))
.SetAggregatableDedupKeys(
ToAggregatableDedupKeys(registration.aggregatable_dedup_keys))
.SetEventTriggerData(ToEventTriggerData(registration.event_triggers))
.SetAggregatableTriggerData(
ToAggregatableTriggerData(registration.aggregatable_trigger_data))
.SetAggregatableValues(
ToAggregatableValueEntries(registration.aggregatable_values))
.SetAggregatableFilteringIdMaxBytes(
registration.aggregatable_trigger_config
.aggregatable_filtering_id_max_bytes()
.value())
.SetDebugReporting(registration.debug_reporting)
.SetSourceRegistrationTimeConfig(ToSourceRegistrationTimeConfig(
registration.aggregatable_trigger_config
.source_registration_time_config()))
.SetAggregatableDebugReportingConfig(
ToAggregatableDebugReportingConfig(
/*budget=*/std::nullopt,
registration.aggregatable_debug_reporting_config))
.SetScopes(std::make_unique<Array<String>>(
registration.attribution_scopes.scopes().begin(),
registration.attribution_scopes.scopes().end()))
.SetNamedBudgets(ToNamedBudgetCandidates(
registration.aggregatable_named_budget_candidates))
.Build();
if (registration.debug_key.has_value()) {
out_trigger->SetDebugKey(base::NumberToString(*registration.debug_key));
}
if (registration.aggregation_coordinator_origin.has_value()) {
out_trigger->SetAggregationCoordinatorOrigin(
registration.aggregation_coordinator_origin->Serialize());
}
if (registration.aggregatable_trigger_config.trigger_context_id()
.has_value()) {
out_trigger->SetTriggerContextId(
*registration.aggregatable_trigger_config.trigger_context_id());
}
frontend_->AttributionReportingTriggerRegistered(
std::move(out_trigger), ToEventLevelResult(result.event_level_status()),
ToAggregatableResult(result.aggregatable_status()));
}
void StorageHandler::OnReportSent(const AttributionReport& report,
bool is_debug_report,
const SendResult& result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::optional<int> net_error;
std::optional<std::string> net_error_name;
std::optional<int> http_status_code;
Storage::AttributionReportingReportResult out_result = std::visit(
absl::Overload{
[&](SendResult::Sent result) {
if (result.status > 0) {
http_status_code = result.status;
} else {
net_error = result.status;
net_error_name = net::ErrorToShortString(result.status);
}
return Storage::AttributionReportingReportResultEnum::Sent;
},
[](SendResult::Dropped) {
return Storage::AttributionReportingReportResultEnum::Prohibited;
},
[](SendResult::Expired) {
return Storage::AttributionReportingReportResultEnum::Expired;
},
[](SendResult::AssemblyFailure) {
return Storage::AttributionReportingReportResultEnum::
FailedToAssemble;
},
},
result.result);
frontend_->AttributionReportingReportSent(
report.ReportURL(is_debug_report).spec(),
std::make_unique<base::Value::Dict>(report.ReportBody()), out_result,
net_error, std::move(net_error_name), http_status_code);
}
void StorageHandler::OnDebugReportSent(const AttributionDebugReport& report,
int status,
base::Time time) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::optional<int> net_error;
std::optional<std::string> net_error_name;
std::optional<int> http_status_code;
if (status > 0) {
http_status_code = status;
} else {
net_error = status;
net_error_name = net::ErrorToShortString(status);
}
auto body = std::make_unique<Array<Value::Dict>>();
for (const auto& item : report.ReportBody()) {
const auto* as_dict = item.GetIfDict();
CHECK(as_dict);
body->push_back(std::make_unique<Value::Dict>(as_dict->Clone()));
}
frontend_->AttributionReportingVerboseDebugReportSent(
report.ReportUrl().spec(), std::move(body), net_error,
std::move(net_error_name), http_status_code);
}
Response StorageHandler::SetAttributionReportingTracking(bool enable) {
if (enable) {
auto* manager = GetAttributionManager();
if (!manager) {
return Response::ServerError("Attribution Reporting is disabled.");
}
// Prevent `DCHECK` crashes in `base::ScopedObservation::Observe()` when we
// are already observing.
if (!attribution_observation_.IsObserving()) {
attribution_observation_.Observe(manager);
}
} else {
attribution_observation_.Reset();
}
return Response::Success();
}
void StorageHandler::NotifyInterestGroupAuctionEventOccurred(
base::Time event_time,
content::InterestGroupAuctionEventType type,
const std::string& unique_auction_id,
base::optional_ref<const std::string> parent_auction_id,
const base::Value::Dict& auction_config) {
if (!interest_group_auction_tracking_enabled_) {
return;
}
std::string type_enum;
switch (type) {
case content::InterestGroupAuctionEventType::kStarted:
type_enum = Storage::InterestGroupAuctionEventTypeEnum::Started;
break;
case content::InterestGroupAuctionEventType::kConfigResolved:
type_enum = Storage::InterestGroupAuctionEventTypeEnum::ConfigResolved;
break;
};
frontend_->InterestGroupAuctionEventOccurred(
event_time.InSecondsFSinceUnixEpoch(), type_enum, unique_auction_id,
parent_auction_id.CopyAsOptional(),
std::make_unique<base::Value::Dict>(auction_config.Clone()));
}
void StorageHandler::NotifyInterestGroupAuctionNetworkRequestCreated(
content::InterestGroupAuctionFetchType type,
const std::string& request_id,
const std::vector<std::string>& devtools_auction_ids) {
if (!interest_group_auction_tracking_enabled_) {
return;
}
std::string type_enum;
switch (type) {
case content::InterestGroupAuctionFetchType::kBidderJs:
type_enum = Storage::InterestGroupAuctionFetchTypeEnum::BidderJs;
break;
case content::InterestGroupAuctionFetchType::kBidderWasm:
type_enum = Storage::InterestGroupAuctionFetchTypeEnum::BidderWasm;
break;
case content::InterestGroupAuctionFetchType::kSellerJs:
type_enum = Storage::InterestGroupAuctionFetchTypeEnum::SellerJs;
break;
case content::InterestGroupAuctionFetchType::kBidderTrustedSignals:
type_enum =
Storage::InterestGroupAuctionFetchTypeEnum::BidderTrustedSignals;
break;
case content::InterestGroupAuctionFetchType::kSellerTrustedSignals:
type_enum =
Storage::InterestGroupAuctionFetchTypeEnum::SellerTrustedSignals;
break;
};
frontend_->InterestGroupAuctionNetworkRequestCreated(
type_enum, request_id,
std::make_unique<std::vector<std::string>>(devtools_auction_ids));
}
Response StorageHandler::SetProtectedAudienceKAnonymity(
const std::string& in_owner_origin,
const std::string& in_group_name,
std::unique_ptr<std::vector<Binary>> in_hashes) {
url::Origin owner_origin = url::Origin::Create(GURL(in_owner_origin));
// Ensure we are in "test" mode.
// For now we just make sure the interest group owner is a .test domain.
if (!base::EndsWith(owner_origin.host(), ".test")) {
return Response::ServerError("owner origin must be on a .test domain");
}
std::vector<std::string> hashes;
for (const auto& in_hash : *in_hashes) {
hashes.emplace_back(base::as_string_view(in_hash));
}
InterestGroupManagerImpl* manager = static_cast<InterestGroupManagerImpl*>(
storage_partition_->GetInterestGroupManager());
if (!manager) {
return Response::ServerError("Protected Audience not enabled");
}
manager->UpdateKAnonymity(
blink::InterestGroupKey(std::move(owner_origin), in_group_name),
/*positive_hashed_keys=*/std::move(hashes),
/*update_time=*/base::Time::Now(),
/*replace_existing_values=*/true);
return Response::Success();
}
} // namespace protocol
} // namespace content