blob: 12f64c3b28fa4cd25242b730fd0b1641a5453266 [file] [log] [blame]
// Copyright 2021 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/interest_group/interest_group_manager_impl.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/observer_list.h"
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/strings/to_string.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/types/optional_ref.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#include "components/cbor/values.h"
#include "components/cbor/writer.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/devtools/network_service_devtools_observer.h"
#include "content/browser/interest_group/ad_auction_page_data.h"
#include "content/browser/interest_group/for_debugging_only_report_util.h"
#include "content/browser/interest_group/interest_group_caching_storage.h"
#include "content/browser/interest_group/interest_group_features.h"
#include "content/browser/interest_group/interest_group_real_time_report_util.h"
#include "content/browser/interest_group/interest_group_storage.h"
#include "content/browser/interest_group/interest_group_update.h"
#include "content/browser/interest_group/protected_audience_network_util.h"
#include "content/browser/interest_group/trusted_signals_cache_impl.h"
#include "content/browser/navigation_or_document_handle.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/services/auction_worklet/public/cpp/real_time_reporting.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/interest_group/interest_group.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
class BrowserContext;
namespace {
// The maximum number of active report requests at a time.
constexpr int kMaxActiveReportRequests = 5;
// The maximum number of report URLs that can be stored in `report_requests_`
// queue.
constexpr int kMaxReportQueueLength = 1000;
// The maximum amount of time allowed to fetch report requests in the queue.
constexpr base::TimeDelta kMaxReportingRoundDuration = base::Minutes(10);
// The time interval to wait before sending the next report after sending one.
constexpr base::TimeDelta kReportingInterval = base::Milliseconds(50);
// Version of real time report.
constexpr int kRealTimeReportDataVersion = 1;
constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("auction_report_sender", R"(
semantics {
sender: "Interest group based Ad Auction report"
description:
"Facilitates reporting the result of an in-browser interest group "
"based ad auction to an auction participant. "
"See https://github.com/WICG/turtledove/blob/main/FLEDGE.md"
trigger:
"Requested after running a in-browser interest group based ad "
"auction to report the auction result back to auction participants."
data: "URL associated with an interest group or seller."
destination: WEBSITE
}
policy {
cookies_allowed: NO
setting:
"These requests are controlled by a feature flag that is off by "
"default now. When enabled, they can be disabled by the Privacy"
" Sandbox setting."
policy_exception_justification:
"These requests are triggered by a website."
})");
mojo::PendingRemote<network::mojom::DevToolsObserver> CreateDevtoolsObserver(
FrameTreeNodeId frame_tree_node_id) {
if (frame_tree_node_id) {
FrameTreeNode* initiator_frame_tree_node =
FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (initiator_frame_tree_node) {
return NetworkServiceDevToolsObserver::MakeSelfOwned(
initiator_frame_tree_node);
}
}
return mojo::PendingRemote<network::mojom::DevToolsObserver>();
}
// Creates an uncredentialed request to use for the SimpleURLLoader and
// reporting to devtools.
std::unique_ptr<network::ResourceRequest> BuildUncredentialedRequest(
GURL url,
const url::Origin& frame_origin,
FrameTreeNodeId frame_tree_node_id,
const network::mojom::ClientSecurityState& client_security_state,
const std::optional<std::string>& user_agent_override,
bool is_post_method) {
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = std::move(url);
if (is_post_method) {
resource_request->method = net::HttpRequestHeaders::kPostMethod;
}
resource_request->devtools_request_id =
base::UnguessableToken::Create().ToString();
resource_request->redirect_mode = network::mojom::RedirectMode::kError;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
resource_request->request_initiator = frame_origin;
resource_request->trusted_params = network::ResourceRequest::TrustedParams();
resource_request->trusted_params->isolation_info =
net::IsolationInfo::CreateTransient(/*nonce=*/std::nullopt);
resource_request->trusted_params->client_security_state =
client_security_state.Clone();
if (user_agent_override.has_value()) {
resource_request->headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
std::move(user_agent_override.value()));
}
bool network_instrumentation_enabled = false;
if (frame_tree_node_id) {
FrameTreeNode* frame_tree_node =
FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (frame_tree_node != nullptr) {
devtools_instrumentation::ApplyAuctionNetworkRequestOverrides(
frame_tree_node, resource_request.get(),
&network_instrumentation_enabled);
}
}
if (network_instrumentation_enabled) {
resource_request->enable_load_timing = true;
resource_request->trusted_params->devtools_observer =
CreateDevtoolsObserver(frame_tree_node_id);
}
return resource_request;
}
std::vector<uint8_t> BuildRealTimeReport(
const std::vector<uint8_t>& real_time_histogram,
double flip_probability) {
size_t num_user_buckets =
blink::features::kFledgeRealTimeReportingNumBuckets.Get();
size_t num_platform_buckets =
auction_worklet::RealTimeReportingPlatformError::kNumValues;
CHECK_EQ(real_time_histogram.size(), num_user_buckets + num_platform_buckets);
std::vector<uint8_t> histogram_list;
std::vector<uint8_t> platform_histogram_list;
for (size_t i = 0; i < real_time_histogram.size(); i++) {
if (i < num_user_buckets) {
histogram_list.push_back(real_time_histogram[i]);
} else {
platform_histogram_list.push_back(real_time_histogram[i]);
}
}
cbor::Value::MapValue histogram_map;
histogram_map.emplace("length", static_cast<int64_t>(histogram_list.size()));
histogram_map.emplace("buckets", BitPacking(std::move(histogram_list)));
cbor::Value::MapValue platform_histogram_map;
platform_histogram_map.emplace(
"length", static_cast<int64_t>(platform_histogram_list.size()));
platform_histogram_map.emplace(
"buckets", BitPacking(std::move(platform_histogram_list)));
cbor::Value::MapValue report;
if (base::FeatureList::IsEnabled(
features::kFledgeEnableUnNoisedRealTimeReport)) {
report.emplace("version", 2);
report.emplace("flipProbability", flip_probability);
} else {
report.emplace("version", kRealTimeReportDataVersion);
}
report.emplace("histogram", std::move(histogram_map));
report.emplace("platformHistogram", std::move(platform_histogram_map));
std::optional<std::vector<uint8_t>> report_cbor =
cbor::Writer::Write(cbor::Value(std::move(report)));
if (!report_cbor.has_value()) {
return {};
}
return *report_cbor;
}
// Makes a SimpleURLLoader for a given request. Returns the SimpleURLLoader
// which will be used to report the result of an in-browser interest group based
// ad auction to an auction participant.
std::unique_ptr<network::SimpleURLLoader> BuildSimpleUrlLoader(
std::unique_ptr<network::ResourceRequest> resource_request,
std::optional<std::vector<uint8_t>> real_time_histogram,
std::optional<double> real_time_report_flip_probability) {
auto simple_url_loader = network::SimpleURLLoader::Create(
std::move(resource_request), kTrafficAnnotation);
simple_url_loader->SetTimeoutDuration(base::Seconds(30));
simple_url_loader->SetAllowHttpErrorResults(true);
if (real_time_histogram.has_value()) {
CHECK(real_time_report_flip_probability.has_value());
auto report = BuildRealTimeReport(*real_time_histogram,
*real_time_report_flip_probability);
simple_url_loader->AttachStringForUpload(
std::string(report.begin(), report.end()), "application/cbor");
}
return simple_url_loader;
}
std::vector<InterestGroupManager::InterestGroupDataKey>
ConvertOwnerJoinerPairsToDataKeys(
std::vector<std::pair<url::Origin, url::Origin>> owner_joiner_pairs) {
std::vector<InterestGroupManager::InterestGroupDataKey> data_keys;
for (auto& key : owner_joiner_pairs) {
data_keys.emplace_back(
InterestGroupManager::InterestGroupDataKey{key.first, key.second});
}
return data_keys;
}
double GetRealTimeReportingQuota(
std::optional<std::pair<base::TimeTicks, double>> quota,
base::TimeTicks now,
double max_real_time_reports,
base::TimeDelta rate_limit_window) {
if (!quota.has_value()) {
return max_real_time_reports;
}
double recovered_quota = max_real_time_reports *
(now - quota->first).InMillisecondsF() /
rate_limit_window.InMilliseconds();
double new_quota = quota->second + recovered_quota;
return std::min(new_quota, max_real_time_reports);
}
void RecordNumberOfSelectableBuyerAndSellerReportingIds(
base::span<const blink::InterestGroup::Ad> ads) {
for (const blink::InterestGroup::Ad& ad : ads) {
if (ad.selectable_buyer_and_seller_reporting_ids) {
UMA_HISTOGRAM_COUNTS_1000(
"Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds",
ad.selectable_buyer_and_seller_reporting_ids->size());
}
}
}
// Returns true if `origin` is allowed to use interest group operation
// `operation`, and false otherwise.
bool IsInterestGroupAPIAllowed(
BrowserContext& browser_context,
const NavigationOrDocumentHandle* navigation_or_document_handle,
ContentBrowserClient::InterestGroupApiOperation operation,
const url::Origin& origin,
const url::Origin& top_frame_origin) {
auto* rfh = navigation_or_document_handle
? navigation_or_document_handle->GetDocument()
: nullptr;
return GetContentClient()->browser()->IsInterestGroupAPIAllowed(
&browser_context, rfh, operation, top_frame_origin, origin);
}
} // namespace
InterestGroupManagerImpl::ReportRequest::ReportRequest() = default;
InterestGroupManagerImpl::ReportRequest::~ReportRequest() = default;
InterestGroupManagerImpl::AdAuctionDataLoaderState::AdAuctionDataLoaderState()
: start_time(base::TimeTicks::Now()) {}
InterestGroupManagerImpl::AdAuctionDataLoaderState::AdAuctionDataLoaderState(
AdAuctionDataLoaderState&& state) = default;
InterestGroupManagerImpl::AdAuctionDataLoaderState::
~AdAuctionDataLoaderState() = default;
InterestGroupManagerImpl::InterestGroupManagerImpl(
const base::FilePath& path,
bool in_memory,
ProcessMode process_mode,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
GetKAnonymityServiceDelegateCallback k_anonymity_service_callback)
: caching_storage_(path, in_memory),
trusted_signals_cache_(
base::FeatureList::IsEnabled(
blink::features::kFledgeTrustedSignalsKVv2Support) &&
base::FeatureList::IsEnabled(
features::kFledgeUseKVv2SignalsCache)
? std::make_unique<TrustedSignalsCacheImpl>(
&data_decoder_manager_,
base::BindRepeating(
&InterestGroupManagerImpl::GetTrustedServerKey,
base::Unretained(this),
TrustedServerAPIType::kTrustedKeyValue))
: nullptr),
auction_process_manager_(
base::WrapUnique(process_mode == ProcessMode::kDedicated
? static_cast<AuctionProcessManager*>(
new DedicatedAuctionProcessManager(
trusted_signals_cache_.get()))
: new InRendererAuctionProcessManager(
trusted_signals_cache_.get()))),
update_manager_(this, url_loader_factory),
k_anonymity_manager_(std::make_unique<InterestGroupKAnonymityManager>(
this,
&caching_storage_,
std::move(k_anonymity_service_callback))),
max_active_report_requests_(kMaxActiveReportRequests),
max_report_queue_length_(kMaxReportQueueLength),
reporting_interval_(kReportingInterval),
max_reporting_round_duration_(kMaxReportingRoundDuration),
real_time_reporting_window_(
blink::features::kFledgeRealTimeReportingWindow.Get()),
max_real_time_reports_(static_cast<double>(
blink::features::kFledgeRealTimeReportingMaxReports.Get())),
ba_key_fetcher_(this, std::move(url_loader_factory)) {}
InterestGroupManagerImpl::~InterestGroupManagerImpl() = default;
void InterestGroupManagerImpl::GetAllInterestGroupJoiningOrigins(
base::OnceCallback<void(std::vector<url::Origin>)> callback) {
caching_storage_.GetAllInterestGroupJoiningOrigins(std::move(callback));
}
void InterestGroupManagerImpl::GetAllInterestGroupDataKeys(
base::OnceCallback<void(std::vector<InterestGroupDataKey>)> callback) {
caching_storage_.GetAllInterestGroupOwnerJoinerPairs(
base::BindOnce(&ConvertOwnerJoinerPairsToDataKeys)
.Then(std::move(callback)));
}
void InterestGroupManagerImpl::RemoveInterestGroupsByDataKey(
InterestGroupDataKey data_key,
base::OnceClosure callback) {
caching_storage_.RemoveInterestGroupsMatchingOwnerAndJoiner(
data_key.owner, data_key.joining_origin, std::move(callback));
}
void InterestGroupManagerImpl::AddTrustedServerKeysDebugOverride(
TrustedServerAPIType api,
const url::Origin& coordinator,
std::string serialized_keys,
base::OnceCallback<void(std::optional<std::string>)> callback) {
ba_key_fetcher_.AddKeysDebugOverride(
api, coordinator, std::move(serialized_keys), std::move(callback));
}
void InterestGroupManagerImpl::CheckPermissionsAndJoinInterestGroup(
blink::InterestGroup group,
const GURL& joining_url,
const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
bool report_result_only,
network::mojom::URLLoaderFactory& url_loader_factory,
AreReportingOriginsAttestedCallback attestation_callback,
blink::mojom::AdAuctionService::JoinInterestGroupCallback callback) {
url::Origin interest_group_owner = group.owner;
permissions_checker_.CheckPermissions(
InterestGroupPermissionsChecker::Operation::kJoin, frame_origin,
interest_group_owner, network_isolation_key, url_loader_factory,
base::BindOnce(
&InterestGroupManagerImpl::OnJoinInterestGroupPermissionsChecked,
base::Unretained(this), std::move(group), joining_url,
report_result_only, std::move(attestation_callback),
std::move(callback)));
}
void InterestGroupManagerImpl::CheckPermissionsAndLeaveInterestGroup(
const blink::InterestGroupKey& group_key,
const url::Origin& main_frame,
const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
bool report_result_only,
network::mojom::URLLoaderFactory& url_loader_factory,
blink::mojom::AdAuctionService::LeaveInterestGroupCallback callback) {
permissions_checker_.CheckPermissions(
InterestGroupPermissionsChecker::Operation::kLeave, frame_origin,
group_key.owner, network_isolation_key, url_loader_factory,
base::BindOnce(
&InterestGroupManagerImpl::OnLeaveInterestGroupPermissionsChecked,
base::Unretained(this), group_key, main_frame, report_result_only,
std::move(callback)));
}
void InterestGroupManagerImpl::
CheckPermissionsAndClearOriginJoinedInterestGroups(
const url::Origin& owner,
const std::vector<std::string>& interest_groups_to_keep,
const url::Origin& main_frame_origin,
const url::Origin& frame_origin,
const net::NetworkIsolationKey& network_isolation_key,
bool report_result_only,
network::mojom::URLLoaderFactory& url_loader_factory,
blink::mojom::AdAuctionService::LeaveInterestGroupCallback callback) {
permissions_checker_.CheckPermissions(
InterestGroupPermissionsChecker::Operation::kLeave, frame_origin, owner,
network_isolation_key, url_loader_factory,
base::BindOnce(&InterestGroupManagerImpl::
OnClearOriginJoinedInterestGroupsPermissionsChecked,
base::Unretained(this), owner,
std::set<std::string>(interest_groups_to_keep.begin(),
interest_groups_to_keep.end()),
main_frame_origin, report_result_only,
std::move(callback)));
}
void InterestGroupManagerImpl::JoinInterestGroup(blink::InterestGroup group,
const GURL& joining_url) {
// Create notify callback first.
base::OnceClosure notify_callback = CreateNotifyInterestGroupAccessedCallback(
InterestGroupObserver::kJoin, group.owner, group.name);
if (group.ads) {
RecordNumberOfSelectableBuyerAndSellerReportingIds(*group.ads);
}
blink::InterestGroupKey group_key(group.owner, group.name);
caching_storage_.JoinInterestGroup(
group, joining_url,
base::BindOnce(
&InterestGroupManagerImpl::QueueKAnonymityUpdateForInterestGroup,
weak_factory_.GetWeakPtr(), group_key)
.Then(std::move(notify_callback)));
}
void InterestGroupManagerImpl::LeaveInterestGroup(
const blink::InterestGroupKey& group_key,
const ::url::Origin& main_frame) {
caching_storage_.LeaveInterestGroup(
group_key, main_frame,
CreateNotifyInterestGroupAccessedCallback(
InterestGroupObserver::kLeave, group_key.owner, group_key.name));
}
void InterestGroupManagerImpl::ClearOriginJoinedInterestGroups(
const url::Origin& owner,
std::set<std::string> interest_groups_to_keep,
url::Origin main_frame_origin) {
caching_storage_.ClearOriginJoinedInterestGroups(
owner, interest_groups_to_keep, main_frame_origin,
base::BindOnce(
&InterestGroupManagerImpl::OnClearOriginJoinedInterestGroupsComplete,
weak_factory_.GetWeakPtr(), owner));
}
void InterestGroupManagerImpl::OnClearOriginJoinedInterestGroupsComplete(
const url::Origin& owner,
std::vector<std::string> left_interest_group_names) {
for (const auto& name : left_interest_group_names) {
NotifyInterestGroupAccessed(
/*devtools_auction_id=*/std::nullopt, InterestGroupObserver::kClear,
owner, name, /*component_seller_origin=*/std::nullopt,
/*bid=*/std::nullopt, /*bid_currency=*/std::nullopt);
}
}
void InterestGroupManagerImpl::UpdateInterestGroupsOfOwners(
std::vector<url::Origin> owners,
network::mojom::ClientSecurityStatePtr client_security_state,
std::optional<std::string> user_agent_override,
AreReportingOriginsAttestedCallback callback) {
update_manager_.UpdateInterestGroupsOfOwners(
owners, std::move(client_security_state), std::move(user_agent_override),
std::move(callback));
if (k_anonymity_manager_ &&
base::FeatureList::IsEnabled(features::kAlwaysUpdateKAnon)) {
k_anonymity_manager_->QueryKAnonymityOfOwners(owners);
}
}
void InterestGroupManagerImpl::UpdateInterestGroupsOfOwnersWithDelay(
std::vector<url::Origin> owners,
network::mojom::ClientSecurityStatePtr client_security_state,
std::optional<std::string> user_agent_override,
AreReportingOriginsAttestedCallback callback,
const base::TimeDelta& delay) {
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&InterestGroupManagerImpl::UpdateInterestGroupsOfOwners,
weak_factory_.GetWeakPtr(), std::move(owners),
std::move(client_security_state),
std::move(user_agent_override), std::move(callback)),
delay);
}
void InterestGroupManagerImpl::AllowUpdateIfOlderThan(
blink::InterestGroupKey group_key,
base::TimeDelta update_if_older_than) {
caching_storage_.AllowUpdateIfOlderThan(std::move(group_key),
update_if_older_than);
}
void InterestGroupManagerImpl::RecordInterestGroupBids(
const blink::InterestGroupSet& group_keys) {
if (group_keys.empty()) {
return;
}
caching_storage_.RecordInterestGroupBids(group_keys);
}
void InterestGroupManagerImpl::RecordInterestGroupWin(
const blink::InterestGroupKey& group_key,
const std::string& ad_json) {
caching_storage_.RecordInterestGroupWin(group_key, ad_json);
}
void InterestGroupManagerImpl::RecordDebugReportLockout(
base::Time starting_time,
base::TimeDelta duration) {
caching_storage_.RecordDebugReportLockout(starting_time, duration);
}
void InterestGroupManagerImpl::RecordRandomDebugReportLockout(
base::Time starting_time) {
int random = base::RandInt(1, 90);
caching_storage_.RecordDebugReportLockout(starting_time, base::Days(random));
}
void InterestGroupManagerImpl::RecordDebugReportCooldown(
const url::Origin& origin,
base::Time cooldown_start,
DebugReportCooldownType cooldown_type) {
caching_storage_.RecordDebugReportCooldown(origin, cooldown_start,
cooldown_type);
}
void InterestGroupManagerImpl::RecordViewClick(
BrowserContext& browser_context,
const NavigationOrDocumentHandle* navigation_or_document_handle,
const std::optional<url::Origin>& maybe_top_frame_origin,
network::AdAuctionEventRecord event_record) {
bool had_top_frame_origin = maybe_top_frame_origin.has_value();
url::Origin top_frame_origin =
maybe_top_frame_origin ? *maybe_top_frame_origin : url::Origin();
base::UmaHistogramBoolean(
"Storage.InterestGroup.HeaderObserver.CreatedOpaqueOriginForPrefsCheck",
!had_top_frame_origin);
if (!IsInterestGroupAPIAllowed(browser_context, navigation_or_document_handle,
InterestGroupApiOperation::kJoin,
event_record.providing_origin,
top_frame_origin)) {
return;
}
std::vector<url::Origin> allowed_eligible_origins;
for (url::Origin& eligible_origin : event_record.eligible_origins) {
if (IsInterestGroupAPIAllowed(browser_context,
navigation_or_document_handle,
InterestGroupApiOperation::kJoin,
eligible_origin, top_frame_origin)) {
allowed_eligible_origins.push_back(std::move(eligible_origin));
}
}
if (allowed_eligible_origins.empty()) {
return;
}
event_record.eligible_origins = std::move(allowed_eligible_origins);
caching_storage_.RecordViewClick(std::move(event_record));
}
void InterestGroupManagerImpl::RecordViewClickForTesting(
network::AdAuctionEventRecord event_record) {
caching_storage_.RecordViewClick(std::move(event_record));
}
void InterestGroupManagerImpl::CheckViewClickInfoInDbForTesting(
url::Origin provider_origin,
url::Origin eligible_origin,
base::OnceCallback<void(std::optional<bool>)> callback) {
caching_storage_.CheckViewClickInfoInDbForTesting(std::move(provider_origin),
std::move(eligible_origin),
std::move(callback));
}
void InterestGroupManagerImpl::RegisterAdKeysAsJoined(
base::flat_set<std::string> hashed_keys) {
k_anonymity_manager_->RegisterAdKeysAsJoined(std::move(hashed_keys));
}
void InterestGroupManagerImpl::GetInterestGroup(
const url::Origin& owner,
const std::string& name,
base::OnceCallback<void(std::optional<SingleStorageInterestGroup>)>
callback) {
GetInterestGroup(blink::InterestGroupKey(owner, name), std::move(callback));
}
void InterestGroupManagerImpl::GetInterestGroup(
const blink::InterestGroupKey& group_key,
base::OnceCallback<void(std::optional<SingleStorageInterestGroup>)>
callback) {
caching_storage_.GetInterestGroup(group_key, std::move(callback));
}
void InterestGroupManagerImpl::GetAllInterestGroupOwners(
base::OnceCallback<void(std::vector<url::Origin>)> callback) {
caching_storage_.GetAllInterestGroupOwners(std::move(callback));
}
void InterestGroupManagerImpl::GetInterestGroupsForOwner(
const std::optional<std::string>& devtools_auction_id,
const url::Origin& owner,
base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)> callback) {
caching_storage_.GetInterestGroupsForOwner(
owner,
base::BindOnce(&InterestGroupManagerImpl::OnGetInterestGroupsComplete,
weak_factory_.GetWeakPtr(), std::move(callback),
devtools_auction_id));
}
bool InterestGroupManagerImpl::GetCachedOwnerAndSignalsOrigins(
const url::Origin& owner,
std::optional<url::Origin>& signals_origin) {
return caching_storage_.GetCachedOwnerAndSignalsOrigins(owner,
signals_origin);
}
void InterestGroupManagerImpl::UpdateCachedOriginsIfEnabled(
const url::Origin& owner) {
caching_storage_.UpdateCachedOriginsIfEnabled(owner);
}
void InterestGroupManagerImpl::DeleteInterestGroupData(
StoragePartition::StorageKeyMatcherFunction storage_key_matcher,
bool user_initiated_deletion,
base::OnceClosure completion_callback) {
caching_storage_.DeleteInterestGroupData(std::move(storage_key_matcher),
user_initiated_deletion,
std::move(completion_callback));
}
void InterestGroupManagerImpl::DeleteAllInterestGroupData(
base::OnceClosure completion_callback) {
caching_storage_.DeleteAllInterestGroupData(std::move(completion_callback));
}
void InterestGroupManagerImpl::GetLastMaintenanceTimeForTesting(
base::RepeatingCallback<void(base::Time)> callback) const {
caching_storage_.GetLastMaintenanceTimeForTesting( // IN-TEST
std::move(callback));
}
void InterestGroupManagerImpl::EnqueueReports(
ReportType report_type,
std::vector<GURL> report_urls,
FrameTreeNodeId frame_tree_node_id,
const url::Origin& frame_origin,
const network::mojom::ClientSecurityState& client_security_state,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
if (report_urls.empty()) {
return;
}
// For memory usage reasons, purge the queue if it has at least
// `max_report_queue_length_` entries at the time we're about to add new
// entries.
if (report_requests_.size() >=
static_cast<unsigned int>(max_report_queue_length_)) {
report_requests_.clear();
}
const char* report_type_name;
switch (report_type) {
case ReportType::kSendReportTo:
report_type_name = "SendReportToReport";
break;
case ReportType::kDebugWin:
report_type_name = "DebugWinReport";
break;
case ReportType::kDebugLoss:
report_type_name = "DebugLossReport";
break;
}
for (GURL& report_url : report_urls) {
auto report_request = std::make_unique<ReportRequest>();
report_request->request_url_size_bytes = report_url.spec().size();
report_request->report_url = std::move(report_url);
report_request->frame_origin = frame_origin;
report_request->client_security_state = client_security_state;
report_request->name = report_type_name;
report_request->url_loader_factory = url_loader_factory;
report_request->frame_tree_node_id = frame_tree_node_id;
report_request->user_agent_override =
GetUserAgentOverrideForProtectedAudience(frame_tree_node_id);
report_requests_.emplace_back(std::move(report_request));
}
while (!report_requests_.empty() &&
num_active_ < max_active_report_requests_) {
++num_active_;
TrySendingOneReport();
}
}
void InterestGroupManagerImpl::EnqueueRealTimeReports(
std::map<url::Origin, RealTimeReportingContributions> contributions,
AdAuctionPageDataCallback ad_auction_page_data_callback,
FrameTreeNodeId frame_tree_node_id,
const url::Origin& frame_origin,
const network::mojom::ClientSecurityState& client_security_state,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
if (contributions.empty()) {
return;
}
AdAuctionPageData* ad_auction_page_data = ad_auction_page_data_callback.Run();
if (!ad_auction_page_data) {
// The page is destroyed. Don't enqueue the real time reports.
return;
}
// For memory usage reasons, purge the queue if it has at least
// `max_report_queue_length_` entries at the time we're about to add new
// entries.
if (report_requests_.size() >=
static_cast<unsigned int>(max_report_queue_length_)) {
report_requests_.clear();
}
// TODO(crbug.com/392148872): Set `add_noise` based on cookie setting.
bool add_noise = true;
double flip_probability =
add_noise ? CalculateFlipProbability(
blink::features::kFledgeRealTimeReportingEpsilon.Get())
: 0;
std::map<url::Origin, std::vector<uint8_t>> histograms =
CalculateRealTimeReportingHistograms(std::move(contributions),
flip_probability);
std::optional<std::string> user_agent_override =
GetUserAgentOverrideForProtectedAudience(frame_tree_node_id);
base::TimeTicks now = base::TimeTicks::Now();
for (auto& [origin, histogram] : histograms) {
double quota = GetRealTimeReportingQuota(
ad_auction_page_data->GetRealTimeReportingQuota(origin), now,
max_real_time_reports_, real_time_reporting_window_);
if (quota < 1) {
continue;
}
ad_auction_page_data->UpdateRealTimeReportingQuota(origin,
{now, quota - 1});
auto report_request = std::make_unique<ReportRequest>();
GURL report_url = GetRealTimeReportDestination(origin);
report_request->request_url_size_bytes = report_url.spec().size();
report_request->report_url = std::move(report_url);
report_request->real_time_histogram = std::move(histogram);
report_request->real_time_report_flip_probability = flip_probability;
report_request->frame_origin = frame_origin;
report_request->client_security_state = client_security_state;
report_request->name = "RealTimeReport";
report_request->url_loader_factory = url_loader_factory;
report_request->frame_tree_node_id = frame_tree_node_id;
report_request->user_agent_override = user_agent_override;
report_requests_.emplace_back(std::move(report_request));
}
while (!report_requests_.empty() &&
num_active_ < max_active_report_requests_) {
++num_active_;
TrySendingOneReport();
}
}
void InterestGroupManagerImpl::SetInterestGroupPriority(
const blink::InterestGroupKey& group_key,
double priority) {
caching_storage_.SetInterestGroupPriority(group_key, priority);
}
void InterestGroupManagerImpl::UpdateInterestGroupPriorityOverrides(
const blink::InterestGroupKey& group_key,
base::flat_map<std::string,
auction_worklet::mojom::PrioritySignalsDoublePtr>
update_priority_signals_overrides) {
caching_storage_.UpdateInterestGroupPriorityOverrides(
group_key, std::move(update_priority_signals_overrides));
}
void InterestGroupManagerImpl::SetBiddingAndAuctionServerKeys(
const url::Origin& coordinator,
std::string serialized_keys,
base::Time expiration) {
caching_storage_.SetBiddingAndAuctionServerKeys(
coordinator, std::move(serialized_keys), expiration);
}
void InterestGroupManagerImpl::GetBiddingAndAuctionServerKeys(
const url::Origin& coordinator,
base::OnceCallback<void(std::pair<base::Time, std::string>)> callback) {
caching_storage_.GetBiddingAndAuctionServerKeys(coordinator,
std::move(callback));
}
void InterestGroupManagerImpl::ClearPermissionsCache() {
permissions_checker_.ClearCache();
}
void InterestGroupManagerImpl::set_trusted_signals_cache_for_testing(
std::unique_ptr<TrustedSignalsCacheImpl> trusted_signals_cache) {
trusted_signals_cache_ = std::move(trusted_signals_cache);
}
void InterestGroupManagerImpl::QueueKAnonymityUpdateForInterestGroup(
const blink::InterestGroupKey& group_key,
const std::optional<InterestGroupKanonUpdateParameter> update_parameter) {
if (update_parameter) {
k_anonymity_manager_->QueryKAnonymityData(group_key,
update_parameter.value());
}
}
void InterestGroupManagerImpl::UpdateKAnonymity(
const blink::InterestGroupKey& interest_group_key,
const std::vector<std::string>& positive_hashed_keys,
const base::Time update_time,
bool replace_existing_values) {
caching_storage_.UpdateKAnonymity(interest_group_key, positive_hashed_keys,
update_time, replace_existing_values);
}
void InterestGroupManagerImpl::GetLastKAnonymityReported(
const std::string& hashed_key,
base::OnceCallback<void(std::optional<base::Time>)> callback) {
caching_storage_.GetLastKAnonymityReported(hashed_key, std::move(callback));
}
void InterestGroupManagerImpl::UpdateLastKAnonymityReported(
const std::string& hashed_key) {
caching_storage_.UpdateLastKAnonymityReported(hashed_key);
}
void InterestGroupManagerImpl::GetInterestGroupAdAuctionData(
url::Origin top_level_origin,
base::Uuid generation_id,
base::Time timestamp,
blink::mojom::AuctionDataConfigPtr config,
std::vector<url::Origin> sellers,
base::OnceCallback<void(BiddingAndAuctionData)> callback) {
AdAuctionDataLoaderState state;
state.serializer.SetPublisher(top_level_origin.host());
state.serializer.SetGenerationId(std::move(generation_id));
state.serializer.SetTimestamp(timestamp);
state.sellers = std::move(sellers);
state.callback = std::move(callback);
if (config->per_buyer_configs.size() == 0) {
state.serializer.SetConfig(std::move(config));
GetAllInterestGroupOwners(
base::BindOnce(&InterestGroupManagerImpl::
ShuffleOwnersThenLoadInterestGroupAdAuctionData,
weak_factory_.GetWeakPtr(), std::move(state)));
} else {
std::vector<url::Origin> owners;
owners.reserve(config->per_buyer_configs.size());
std::vector<url::Origin> sized_owners;
for (const auto& buyer_config : config->per_buyer_configs) {
if (buyer_config.second->target_size) {
sized_owners.push_back(buyer_config.first);
} else {
owners.push_back(buyer_config.first);
}
}
// Shuffle the owners. The algorithm for serializing interest groups is
// slightly unfair in that owners that are serialize first can't take
// advantage of space left over from when subsequent owners don't use all
// their assigned space. Randomizing the order avoids always penalizing the
// same owner.
base::RandomShuffle(owners.begin(), owners.end());
base::RandomShuffle(sized_owners.begin(), sized_owners.end());
// Move sized owners to the end. We load groups in reverse order and then
// serialize them in order, so this means we process the sized owners first.
// Unsized owners share the remaining space so we want to process them last.
std::move(sized_owners.begin(), sized_owners.end(),
std::back_inserter(owners));
state.serializer.SetConfig(std::move(config));
LoadNextInterestGroupAdAuctionData(std::move(state), std::move(owners));
}
}
void InterestGroupManagerImpl::ShuffleOwnersThenLoadInterestGroupAdAuctionData(
AdAuctionDataLoaderState state,
std::vector<url::Origin> owners) {
// Shuffle the owners. The algorithm for serializing interest groups is
// slightly unfair in that owners that are serialize first can't take
// advantage of space left over from when subsequent owners don't use all
// their assigned space. Randomizing the order avoids always penalizing the
// same owner.
base::RandomShuffle(owners.begin(), owners.end());
LoadNextInterestGroupAdAuctionData(std::move(state), std::move(owners));
}
void InterestGroupManagerImpl::LoadNextInterestGroupAdAuctionData(
AdAuctionDataLoaderState state,
std::vector<url::Origin> owners) {
if (!owners.empty()) {
url::Origin next_owner = std::move(owners.back());
owners.pop_back();
// Since a single B&A blob can be associated with multiple auctions, we
// can't link these loads to a specific one.
GetInterestGroupsForOwner(
/*devtools_auction_id=*/std::nullopt, next_owner,
base::BindOnce(
&InterestGroupManagerImpl::OnLoadedNextInterestGroupAdAuctionData,
weak_factory_.GetWeakPtr(), std::move(state), std::move(owners),
next_owner));
return;
}
// Loading is finished.
OnInterestGroupAdAuctionDataLoadComplete(std::move(state));
}
void InterestGroupManagerImpl::OnLoadedNextInterestGroupAdAuctionData(
AdAuctionDataLoaderState state,
std::vector<url::Origin> owners,
url::Origin owner,
scoped_refptr<StorageInterestGroups> groups) {
state.serializer.AddGroups(std::move(owner), std::move(groups));
LoadNextInterestGroupAdAuctionData(std::move(state), std::move(owners));
}
void InterestGroupManagerImpl::OnInterestGroupAdAuctionDataLoadComplete(
AdAuctionDataLoaderState state) {
if (base::FeatureList::IsEnabled(
blink::features::kFledgeSampleDebugReports)) {
caching_storage_.GetDebugReportLockoutAndAllCooldowns(
base::BindOnce(&InterestGroupManagerImpl::OnAdAuctionDataLoadComplete,
weak_factory_.GetWeakPtr(), std::move(state)));
} else {
OnAdAuctionDataLoadComplete(std::move(state), std::nullopt);
}
}
void InterestGroupManagerImpl::OnAdAuctionDataLoadComplete(
AdAuctionDataLoaderState state,
std::optional<DebugReportLockoutAndCooldowns> lockoutAndCooldowns) {
base::Time now = base::Time::Now();
bool in_debug_report_lockout = false;
if (lockoutAndCooldowns.has_value()) {
in_debug_report_lockout =
IsInDebugReportLockout(lockoutAndCooldowns->lockout, now);
state.serializer.SetDebugReportInLockout(in_debug_report_lockout);
state.serializer.SetDebugReportCooldownsMap(
lockoutAndCooldowns->debug_report_cooldown_map);
}
std::optional<BiddingAndAuctionData> data = state.serializer.Build();
if (data.has_value()) {
for (const auto& seller : state.sellers) {
std::optional<std::vector<uint8_t>> request =
state.serializer.BuildRequestFromMessage(seller, now);
if (request.has_value()) {
data->requests[seller] = std::move(*request);
} else {
data = std::nullopt;
break;
}
}
}
base::UmaHistogramTimes(
"Ads.InterestGroup.ServerAuction.AdAuctionDataLoadTime",
base::TimeTicks::Now() - state.start_time);
if (data.has_value()) {
std::move(state.callback).Run(*std::move(data));
} else {
std::move(state.callback).Run({});
}
}
void InterestGroupManagerImpl::GetTrustedServerKey(
TrustedServerAPIType api,
const url::Origin& seller,
const std::optional<url::Origin>& coordinator,
base::OnceCallback<void(
base::expected<BiddingAndAuctionServerKey, std::string>)> callback) {
ba_key_fetcher_.GetOrFetchKey(api, seller, coordinator, std::move(callback));
}
void InterestGroupManagerImpl::OnJoinInterestGroupPermissionsChecked(
blink::InterestGroup group,
const GURL& joining_url,
bool report_result_only,
AreReportingOriginsAttestedCallback attestation_callback,
blink::mojom::AdAuctionService::JoinInterestGroupCallback callback,
bool can_join) {
// Invoke callback before calling JoinInterestGroup(), which posts a task to
// another thread. Any FLEDGE call made from the renderer will need to pass
// through the UI thread and then bounce over the database thread, so it will
// see the new InterestGroup, so it's not necessary to actually wait for the
// database to be updated before invoking the callback. Waiting before
// invoking the callback may potentially leak whether the user was previously
// in the InterestGroup through timing differences.
std::move(callback).Run(/*failed_well_known_check=*/!can_join);
if (!report_result_only && can_join) {
// All ads' allowed reporting origins must be attested. Otherwise don't
// join.
if (group.ads) {
for (auto& ad : *group.ads) {
if (ad.allowed_reporting_origins) {
// Sort and de-duplicate by passing it through a flat_set.
ad.allowed_reporting_origins =
base::flat_set<url::Origin>(
std::move(ad.allowed_reporting_origins.value()))
.extract();
if (!attestation_callback.Run(ad.allowed_reporting_origins.value())) {
return;
}
}
}
}
JoinInterestGroup(std::move(group), joining_url);
}
ba_key_fetcher_.MaybePrefetchKeys();
}
void InterestGroupManagerImpl::OnLeaveInterestGroupPermissionsChecked(
const blink::InterestGroupKey& group_key,
const url::Origin& main_frame,
bool report_result_only,
blink::mojom::AdAuctionService::LeaveInterestGroupCallback callback,
bool can_leave) {
// Invoke callback before calling LeaveInterestGroup(), which posts a task to
// another thread. Any FLEDGE call made from the renderer will need to pass
// through the UI thread and then bounce over the database thread, so it will
// see the new InterestGroup, so it's not necessary to actually wait for the
// database to be updated before invoking the callback. Waiting before
// invoking the callback may potentially leak whether the user was previously
// in the InterestGroup through timing differences.
std::move(callback).Run(/*failed_well_known_check=*/!can_leave);
if (!report_result_only && can_leave) {
LeaveInterestGroup(group_key, main_frame);
}
}
void InterestGroupManagerImpl::
OnClearOriginJoinedInterestGroupsPermissionsChecked(
url::Origin owner,
std::set<std::string> interest_groups_to_keep,
url::Origin main_frame_origin,
bool report_result_only,
blink::mojom::AdAuctionService::LeaveInterestGroupCallback callback,
bool can_leave) {
// Invoke callback before calling ClearOriginJoinedInterestGroups(), which
// posts a task to another thread. Any FLEDGE call made from the renderer will
// need to pass through the UI thread and then bounce over the database
// thread, so it will see the new InterestGroup, so it's not necessary to
// actually wait for the database to be updated before invoking the callback.
// Waiting before invoking the callback may potentially leak whether the user
// was previously in the InterestGroup through timing differences.
std::move(callback).Run(/*failed_well_known_check=*/!can_leave);
if (!report_result_only && can_leave) {
ClearOriginJoinedInterestGroups(std::move(owner),
std::move(interest_groups_to_keep),
std::move(main_frame_origin));
}
}
void InterestGroupManagerImpl::GetInterestGroupsForUpdate(
const url::Origin& owner,
int groups_limit,
base::OnceCallback<void(std::vector<InterestGroupUpdateParameter>)>
callback) {
caching_storage_.GetInterestGroupsForUpdate(owner, groups_limit,
std::move(callback));
}
void InterestGroupManagerImpl::GetDebugReportLockoutAndCooldowns(
base::flat_set<url::Origin> origins,
base::OnceCallback<void(std::optional<DebugReportLockoutAndCooldowns>)>
callback) {
caching_storage_.GetDebugReportLockoutAndCooldowns(std::move(origins),
std::move(callback));
}
void InterestGroupManagerImpl::GetDebugReportLockoutAndAllCooldowns(
base::OnceCallback<void(std::optional<DebugReportLockoutAndCooldowns>)>
callback) {
caching_storage_.GetDebugReportLockoutAndAllCooldowns(std::move(callback));
}
void InterestGroupManagerImpl::UpdateInterestGroup(
const blink::InterestGroupKey& group_key,
InterestGroupUpdate update,
base::OnceCallback<void(bool)> callback) {
if (update.ads) {
RecordNumberOfSelectableBuyerAndSellerReportingIds(*update.ads);
}
caching_storage_.UpdateInterestGroup(
group_key, std::move(update),
base::BindOnce(&InterestGroupManagerImpl::OnUpdateComplete,
weak_factory_.GetWeakPtr(), group_key,
std::move(callback)));
}
void InterestGroupManagerImpl::OnUpdateComplete(
const blink::InterestGroupKey& group_key,
base::OnceCallback<void(bool)> callback,
std::optional<InterestGroupKanonUpdateParameter> kanon_update_parameter) {
NotifyInterestGroupAccessed(
/*devtools_auction_id=*/std::nullopt, InterestGroupObserver::kUpdate,
group_key.owner, group_key.name, /*component_seller_origin=*/std::nullopt,
/*bid=*/std::nullopt,
/*bid_currency=*/std::nullopt);
std::move(callback).Run(kanon_update_parameter.has_value());
QueueKAnonymityUpdateForInterestGroup(group_key,
std::move(kanon_update_parameter));
}
void InterestGroupManagerImpl::ReportUpdateFailed(
const blink::InterestGroupKey& group_key,
bool parse_failure) {
caching_storage_.ReportUpdateFailed(group_key, parse_failure);
}
void InterestGroupManagerImpl::OnGetInterestGroupsComplete(
base::OnceCallback<void(scoped_refptr<StorageInterestGroups>)> callback,
const std::optional<std::string>& devtools_auction_id,
scoped_refptr<StorageInterestGroups> groups) {
for (const SingleStorageInterestGroup& group : groups->GetInterestGroups()) {
NotifyInterestGroupAccessed(
devtools_auction_id, InterestGroupObserver::kLoaded,
group->interest_group.owner, group->interest_group.name,
/*component_seller_origin=*/std::nullopt,
/*bid=*/std::nullopt, /*bid_currency=*/std::nullopt);
}
std::move(callback).Run(std::move(groups));
}
void InterestGroupManagerImpl::NotifyInterestGroupAccessed(
base::optional_ref<const std::string> devtools_auction_id,
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) {
// Don't bother getting the time if there are no observers.
if (observers_.empty()) {
return;
}
base::Time now = base::Time::Now();
for (InterestGroupObserver& observer : observers_) {
observer.OnInterestGroupAccessed(
devtools_auction_id, now, type, owner_origin, name,
component_seller_origin, bid, bid_currency);
}
}
void InterestGroupManagerImpl::TrySendingOneReport() {
DCHECK_GT(num_active_, 0);
if (report_requests_.empty()) {
--num_active_;
if (num_active_ == 0) {
timeout_timer_.Stop();
}
return;
}
if (!timeout_timer_.IsRunning()) {
timeout_timer_.Start(
FROM_HERE, max_reporting_round_duration_,
base::BindOnce(&InterestGroupManagerImpl::TimeoutReports,
base::Unretained(this)));
}
std::unique_ptr<ReportRequest> report_request =
std::move(report_requests_.front());
report_requests_.pop_front();
FrameTreeNodeId frame_tree_node_id = report_request->frame_tree_node_id;
base::UmaHistogramCounts100000(
base::StrCat(
{"Ads.InterestGroup.Net.RequestUrlSizeBytes.", report_request->name}),
report_request->request_url_size_bytes);
base::UmaHistogramCounts100(
base::StrCat(
{"Ads.InterestGroup.Net.ResponseSizeBytes.", report_request->name}),
0);
std::unique_ptr<network::ResourceRequest> resource_request =
BuildUncredentialedRequest(
report_request->report_url, report_request->frame_origin,
report_request->frame_tree_node_id,
report_request->client_security_state,
report_request->user_agent_override,
/*is_post_method=*/report_request->real_time_histogram.has_value()
? true
: false);
std::string devtools_request_id =
resource_request->devtools_request_id.value();
devtools_instrumentation::OnAuctionWorkletNetworkRequestWillBeSent(
report_request->frame_tree_node_id, *resource_request,
base::TimeTicks::Now());
std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
BuildSimpleUrlLoader(std::move(resource_request),
report_request->real_time_histogram,
report_request->real_time_report_flip_probability);
// Pass simple_url_loader to keep it alive until the request fails or succeeds
// to prevent cancelling the request.
network::SimpleURLLoader* simple_url_loader_ptr = simple_url_loader.get();
simple_url_loader_ptr->DownloadHeadersOnly(
report_request->url_loader_factory.get(),
base::BindOnce(&InterestGroupManagerImpl::OnOneReportSent,
weak_factory_.GetWeakPtr(), std::move(simple_url_loader),
frame_tree_node_id, std::move(devtools_request_id)));
}
void InterestGroupManagerImpl::OnOneReportSent(
std::unique_ptr<network::SimpleURLLoader> simple_url_loader,
FrameTreeNodeId frame_tree_node_id,
const std::string& devtools_request_id,
scoped_refptr<net::HttpResponseHeaders> response_headers) {
DCHECK_GT(num_active_, 0);
network::URLLoaderCompletionStatus completion_status =
network::URLLoaderCompletionStatus(simple_url_loader->NetError());
if (simple_url_loader->CompletionStatus()) {
completion_status = simple_url_loader->CompletionStatus().value();
}
if (simple_url_loader->ResponseInfo() != nullptr) {
devtools_instrumentation::OnAuctionWorkletNetworkResponseReceived(
frame_tree_node_id, devtools_request_id, devtools_request_id,
simple_url_loader->GetFinalURL(), *simple_url_loader->ResponseInfo());
}
devtools_instrumentation::OnAuctionWorkletNetworkRequestComplete(
frame_tree_node_id, devtools_request_id, completion_status);
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&InterestGroupManagerImpl::TrySendingOneReport,
weak_factory_.GetWeakPtr()),
reporting_interval_);
}
void InterestGroupManagerImpl::TimeoutReports() {
// TODO(qingxinwu): maybe add UMA metrics to learn how often this happens.
report_requests_.clear();
}
base::OnceClosure
InterestGroupManagerImpl::CreateNotifyInterestGroupAccessedCallback(
InterestGroupObserver::AccessType type,
const url::Origin& owner_origin,
const std::string& name) {
// This is only used for join/leave, so no auction ID associated.
DCHECK(type == InterestGroupObserver::kJoin ||
type == InterestGroupObserver::kLeave);
return base::BindOnce(&InterestGroupManagerImpl::NotifyInterestGroupAccessed,
weak_factory_.GetWeakPtr(),
/*devtools_auction_id=*/std::nullopt, type,
owner_origin, name,
/*component_seller_origin=*/std::nullopt,
/*bid=*/std::nullopt, /*bid_currency=*/std::nullopt);
}
} // namespace content