blob: 5f52f7997ac61fcedf803140fa5b1ae2945ad49e [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/ad_auction_service_impl.h"
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "content/browser/attribution_reporting/attribution_manager.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/fenced_frame/fenced_frame_reporter.h"
#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
#include "content/browser/interest_group/ad_auction_document_data.h"
#include "content/browser/interest_group/ad_auction_result_metrics.h"
#include "content/browser/interest_group/auction_runner.h"
#include "content/browser/interest_group/auction_worklet_manager.h"
#include "content/browser/interest_group/interest_group_manager_impl.h"
#include "content/browser/private_aggregation/private_aggregation_manager.h"
#include "content/browser/renderer_host/page_impl.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h"
#include "content/services/auction_worklet/public/mojom/private_aggregation_request.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/http/http_response_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/fenced_frame/fenced_frame_utils.h"
#include "third_party/blink/public/common/interest_group/auction_config.h"
#include "third_party/blink/public/common/interest_group/interest_group.h"
#include "third_party/blink/public/mojom/private_aggregation/aggregatable_report.mojom.h"
#include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/scheme_host_port.h"
#include "url/url_constants.h"
namespace content {
namespace {
constexpr base::TimeDelta kMaxExpiry = base::Days(30);
bool IsAdRequestValid(const blink::mojom::AdRequestConfig& config) {
// The ad_request_url origin has to be HTTPS.
if (config.ad_request_url.scheme() != url::kHttpsScheme) {
return false;
}
// At least one adProperties is required to request potential ads.
if (config.ad_properties.size() <= 0) {
return false;
}
// If a fallback source is specified it must be HTTPS.
if (config.fallback_source &&
(config.fallback_source->scheme() != url::kHttpsScheme)) {
return false;
}
return true;
}
} // namespace
// static
void AdAuctionServiceImpl::CreateMojoService(
RenderFrameHost* render_frame_host,
mojo::PendingReceiver<blink::mojom::AdAuctionService> receiver) {
CHECK(render_frame_host);
// The object is bound to the lifetime of `render_frame_host` and the mojo
// connection. See DocumentService for details.
new AdAuctionServiceImpl(*render_frame_host, std::move(receiver));
}
void AdAuctionServiceImpl::JoinInterestGroup(
const blink::InterestGroup& group,
JoinInterestGroupCallback callback) {
if (!JoinOrLeaveApiAllowedFromRenderer(group.owner))
return;
// If the interest group API is not allowed for this origin, report the result
// of the permissions check, but don't actually join the interest group.
// The return value of IsInterestGroupAPIAllowed() is potentially affected by
// a user's browser configuration, which shouldn't be leaked to sites to
// protect against fingerprinting.
bool report_result_only = !IsInterestGroupAPIAllowed(
ContentBrowserClient::InterestGroupApiOperation::kJoin, group.owner);
blink::InterestGroup updated_group = group;
base::Time max_expiry = base::Time::Now() + kMaxExpiry;
if (updated_group.expiry > max_expiry)
updated_group.expiry = max_expiry;
GetInterestGroupManager().CheckPermissionsAndJoinInterestGroup(
std::move(updated_group), main_frame_url_, origin(),
GetFrame()->GetNetworkIsolationKey(), report_result_only,
*GetFrameURLLoaderFactory(), std::move(callback));
}
void AdAuctionServiceImpl::LeaveInterestGroup(
const url::Origin& owner,
const std::string& name,
LeaveInterestGroupCallback callback) {
if (!JoinOrLeaveApiAllowedFromRenderer(owner))
return;
// If the interest group API is not allowed for this origin, report the result
// of the permissions check, but don't actually join the interest group.
// The return value of IsInterestGroupAPIAllowed() is potentially affected by
// a user's browser configuration, which shouldn't be leaked to sites to
// protect against fingerprinting.
bool report_result_only = !IsInterestGroupAPIAllowed(
ContentBrowserClient::InterestGroupApiOperation::kLeave, owner);
GetInterestGroupManager().CheckPermissionsAndLeaveInterestGroup(
blink::InterestGroupKey(owner, name), main_frame_origin_, origin(),
GetFrame()->GetNetworkIsolationKey(), report_result_only,
*GetFrameURLLoaderFactory(), std::move(callback));
}
void AdAuctionServiceImpl::LeaveInterestGroupForDocument() {
// Based on the spec, permission policy is bypassed for leaving implicit
// interest groups.
// If the interest group API is not allowed for this origin do nothing.
if (!IsInterestGroupAPIAllowed(
ContentBrowserClient::InterestGroupApiOperation::kLeave, origin())) {
return;
}
if (origin().scheme() != url::kHttpsScheme) {
ReportBadMessageAndDeleteThis(
"Unexpected request: LeaveInterestGroupForDocument only supported for "
"secure origins");
return;
}
if (!render_frame_host().IsNestedWithinFencedFrame()) {
ReportBadMessageAndDeleteThis(
"Unexpected request: LeaveInterestGroupForDocument only supported "
"within fenced frames");
return;
}
// Get interest group owner and name. AdAuctionDocumentData is created as
// part of navigation to a mapped URN URL. We need to find the top-level
// fenced frame, since only the top-level frame has the document data.
RenderFrameHost* rfh = &render_frame_host();
while (!rfh->IsFencedFrameRoot()) {
rfh = rfh->GetParentOrOuterDocument();
if (!rfh) {
return;
}
}
AdAuctionDocumentData* auction_data =
AdAuctionDocumentData::GetForCurrentDocument(rfh);
if (!auction_data) {
return;
}
if (auction_data->interest_group_owner() != origin()) {
// The ad page calling LeaveAdInterestGroup is not the owner of the group.
return;
}
GetInterestGroupManager().LeaveInterestGroup(
blink::InterestGroupKey(auction_data->interest_group_owner(),
auction_data->interest_group_name()),
main_frame_origin_);
}
void AdAuctionServiceImpl::UpdateAdInterestGroups() {
// If the interest group API is not allowed for this context by Permissions
// Policy, do nothing
if (!render_frame_host().IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
ReportBadMessageAndDeleteThis("Unexpected request");
return;
}
// If the interest group API is not allowed for this origin do nothing.
if (!IsInterestGroupAPIAllowed(
ContentBrowserClient::InterestGroupApiOperation::kUpdate, origin())) {
return;
}
GetInterestGroupManager().UpdateInterestGroupsOfOwner(
origin(), GetClientSecurityState());
}
void AdAuctionServiceImpl::RunAdAuction(
const blink::AuctionConfig& config,
mojo::PendingReceiver<blink::mojom::AbortableAdAuction> abort_receiver,
RunAdAuctionCallback callback) {
// If the run ad auction API is not allowed for this context by Permissions
// Policy, do nothing
if (!render_frame_host().IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kRunAdAuction)) {
ReportBadMessageAndDeleteThis("Unexpected request");
return;
}
auto* auction_result_metrics =
AdAuctionResultMetrics::GetOrCreateForPage(render_frame_host().GetPage());
if (!auction_result_metrics->ShouldRunAuction()) {
std::move(callback).Run(/*manually_aborted=*/false,
/*config=*/absl::nullopt);
return;
}
FencedFrameURLMapping& fenced_frame_urls_map =
GetFrame()->GetPage().fenced_frame_urls_map();
auto urn_uuid = fenced_frame_urls_map.GeneratePendingMappedURN();
// If pending mapped URN cannot be generated due to number of mappings has
// reached limit, stop the auction.
if (!urn_uuid.has_value()) {
std::move(callback).Run(/*manually_aborted=*/false,
/*config=*/absl::nullopt);
return;
}
std::unique_ptr<AuctionRunner> auction = AuctionRunner::CreateAndStart(
&auction_worklet_manager_, &GetInterestGroupManager(),
AttributionManager::FromBrowserContext(
render_frame_host().GetBrowserContext()),
private_aggregation_manager_,
// Unlike other callbacks, this needs to be safe to call after destruction
// of the AdAuctionServiceImpl, so that the reporter can outlive it.
base::BindRepeating(
&AdAuctionServiceImpl::MaybeLogPrivateAggregationFeatures,
weak_ptr_factory_.GetWeakPtr()),
config, main_frame_origin_, origin(),
render_frame_host().GetPageUkmSourceId(), GetClientSecurityState(),
GetRefCountedTrustedURLLoaderFactory(),
base::BindRepeating(&AdAuctionServiceImpl::IsInterestGroupAPIAllowed,
base::Unretained(this)),
std::move(abort_receiver),
base::BindOnce(&AdAuctionServiceImpl::OnAuctionComplete,
base::Unretained(this), std::move(callback),
std::move(urn_uuid.value()),
fenced_frame_urls_map.unique_id()));
AuctionRunner* raw_auction = auction.get();
auctions_.emplace(raw_auction, std::move(auction));
}
namespace {
// Helper class to retrieve the URL that a given URN is mapped to.
class FencedFrameURLMappingObserver
: public FencedFrameURLMapping::MappingResultObserver {
public:
// Retrieves the URL that `urn_url` is mapped to, if any. If `send_reports` is
// true, sends the reports associated with `urn_url`, if there are any.
static absl::optional<GURL> GetURL(RenderFrameHostImpl& render_frame_host,
const GURL& urn_url,
bool send_reports) {
absl::optional<GURL> mapped_url;
FencedFrameURLMappingObserver obs(&mapped_url, send_reports);
content::FencedFrameURLMapping& mapping =
render_frame_host.GetPage().fenced_frame_urls_map();
// FLEDGE URN URLs should already be mapped, so the observer will be called
// synchronously.
mapping.ConvertFencedFrameURNToURL(urn_url, &obs);
if (!obs.called_)
mapping.RemoveObserverForURN(urn_url, &obs);
return mapped_url;
}
private:
FencedFrameURLMappingObserver(absl::optional<GURL>* mapped_url,
bool send_reports)
: mapped_url_(mapped_url), send_reports_(send_reports) {}
~FencedFrameURLMappingObserver() override = default;
void OnFencedFrameURLMappingComplete(
const absl::optional<FencedFrameProperties>& properties) override {
if (properties) {
if (properties->mapped_url_) {
*mapped_url_ = properties->mapped_url_->GetValueIgnoringVisibility();
}
if (send_reports_ && properties->on_navigate_callback_) {
properties->on_navigate_callback_.Run();
}
}
called_ = true;
}
bool called_ = false;
raw_ptr<absl::optional<GURL>> mapped_url_;
bool send_reports_;
};
} // namespace
void AdAuctionServiceImpl::DeprecatedGetURLFromURN(
const GURL& urn_url,
bool send_reports,
DeprecatedGetURLFromURNCallback callback) {
if (!blink::IsValidUrnUuidURL(urn_url)) {
ReportBadMessageAndDeleteThis("Unexpected request: invalid URN");
return;
}
std::move(callback).Run(FencedFrameURLMappingObserver::GetURL(
static_cast<RenderFrameHostImpl&>(render_frame_host()), urn_url,
send_reports));
}
void AdAuctionServiceImpl::DeprecatedReplaceInURN(
const GURL& urn_url,
std::vector<blink::mojom::AdKeywordReplacementPtr> replacements,
DeprecatedReplaceInURNCallback callback) {
if (!blink::IsValidUrnUuidURL(urn_url)) {
ReportBadMessageAndDeleteThis("Unexpected request: invalid URN");
return;
}
std::vector<std::pair<std::string, std::string>> local_replacements;
for (const auto& replacement : replacements) {
if (!(base::StartsWith(replacement->match, "${") &&
base::EndsWith(replacement->match, "}")) &&
!(base::StartsWith(replacement->match, "%%") &&
base::EndsWith(replacement->match, "%%"))) {
ReportBadMessageAndDeleteThis("Unexpected request: bad replacement");
return;
}
local_replacements.emplace_back(std::move(replacement->match),
std::move(replacement->replacement));
}
content::FencedFrameURLMapping& mapping =
static_cast<RenderFrameHostImpl&>(render_frame_host())
.GetPage()
.fenced_frame_urls_map();
mapping.SubstituteMappedURL(urn_url, local_replacements);
std::move(callback).Run();
}
void AdAuctionServiceImpl::GetInterestGroupAdAuctionData(
const url::Origin& seller,
GetInterestGroupAdAuctionDataCallback callback) {
// TODO(behamilton): Implement this functionality.
std::move(callback).Run({});
}
void AdAuctionServiceImpl::CreateAdRequest(
blink::mojom::AdRequestConfigPtr config,
CreateAdRequestCallback callback) {
if (!IsAdRequestValid(*config)) {
std::move(callback).Run(absl::nullopt);
return;
}
// TODO(https://crbug.com/1249186): Actually request Ads and return a guid.
// For now just act like it failed.
std::move(callback).Run(absl::nullopt);
}
void AdAuctionServiceImpl::FinalizeAd(const std::string& ads_guid,
const blink::AuctionConfig& config,
FinalizeAdCallback callback) {
if (ads_guid.empty()) {
ReportBadMessageAndDeleteThis("GUID empty");
return;
}
// TODO(https://crbug.com/1249186): Actually finalize Ad and return an URL.
// For now just act like it failed.
std::move(callback).Run(absl::nullopt);
}
network::mojom::URLLoaderFactory*
AdAuctionServiceImpl::GetFrameURLLoaderFactory() {
if (!frame_url_loader_factory_ || !frame_url_loader_factory_.is_connected()) {
frame_url_loader_factory_.reset();
render_frame_host().CreateNetworkServiceDefaultFactory(
frame_url_loader_factory_.BindNewPipeAndPassReceiver());
}
return frame_url_loader_factory_.get();
}
network::mojom::URLLoaderFactory*
AdAuctionServiceImpl::GetTrustedURLLoaderFactory() {
if (!trusted_url_loader_factory_ ||
!trusted_url_loader_factory_.is_connected()) {
trusted_url_loader_factory_.reset();
mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory_receiver =
trusted_url_loader_factory_.BindNewPipeAndPassReceiver();
// TODO(mmenke): Should this have its own URLLoaderFactoryType? FLEDGE
// requests are very different from subresource requests.
//
// TODO(mmenke): Hook up devtools.
GetContentClient()->browser()->WillCreateURLLoaderFactory(
render_frame_host().GetSiteInstance()->GetBrowserContext(),
&render_frame_host(), render_frame_host().GetProcess()->GetID(),
ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource,
url::Origin(), /*navigation_id=*/absl::nullopt,
ukm::SourceIdObj::FromInt64(render_frame_host().GetPageUkmSourceId()),
&factory_receiver, /*header_client=*/nullptr,
/*bypass_redirect_checks=*/nullptr, /*disable_secure_dns=*/nullptr,
/*factory_override=*/nullptr,
/*navigation_response_task_runner=*/nullptr);
render_frame_host()
.GetStoragePartition()
->GetURLLoaderFactoryForBrowserProcess()
->Clone(std::move(factory_receiver));
mojo::Remote<network::mojom::URLLoaderFactory> shared_remote;
trusted_url_loader_factory_->Clone(
shared_remote.BindNewPipeAndPassReceiver());
ref_counted_trusted_url_loader_factory_ =
base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
std::move(shared_remote));
}
return trusted_url_loader_factory_.get();
}
void AdAuctionServiceImpl::PreconnectSocket(
const GURL& url,
const net::NetworkAnonymizationKey& network_anonymization_key) {
render_frame_host()
.GetStoragePartition()
->GetNetworkContext()
->PreconnectSockets(/*num_streams=*/1, url, /*allow_credentials=*/false,
network_anonymization_key);
}
scoped_refptr<network::WrapperSharedURLLoaderFactory>
AdAuctionServiceImpl::GetRefCountedTrustedURLLoaderFactory() {
GetTrustedURLLoaderFactory();
return ref_counted_trusted_url_loader_factory_;
}
RenderFrameHostImpl* AdAuctionServiceImpl::GetFrame() {
return static_cast<RenderFrameHostImpl*>(&render_frame_host());
}
scoped_refptr<SiteInstance> AdAuctionServiceImpl::GetFrameSiteInstance() {
return render_frame_host().GetSiteInstance();
}
network::mojom::ClientSecurityStatePtr
AdAuctionServiceImpl::GetClientSecurityState() {
return GetFrame()->BuildClientSecurityState();
}
AdAuctionServiceImpl::AdAuctionServiceImpl(
RenderFrameHost& render_frame_host,
mojo::PendingReceiver<blink::mojom::AdAuctionService> receiver)
: DocumentService(render_frame_host, std::move(receiver)),
main_frame_origin_(
render_frame_host.GetMainFrame()->GetLastCommittedOrigin()),
main_frame_url_(render_frame_host.GetMainFrame()->GetLastCommittedURL()),
auction_worklet_manager_(
&GetInterestGroupManager().auction_process_manager(),
GetTopWindowOrigin(),
origin(),
this),
private_aggregation_manager_(PrivateAggregationManager::GetManager(
*render_frame_host.GetBrowserContext())) {}
AdAuctionServiceImpl::~AdAuctionServiceImpl() {
while (!auctions_.empty()) {
// Need to fail all auctions rather than just deleting them, to ensure Mojo
// callbacks from the renderers are invoked. Uninvoked Mojo callbacks may
// not be destroyed before the Mojo pipe is, and the parent DocumentService
// class owns the pipe, so it may still be open at this point.
auctions_.begin()->first->FailAuction(/*manually_aborted=*/false);
}
}
bool AdAuctionServiceImpl::JoinOrLeaveApiAllowedFromRenderer(
const url::Origin& owner) {
// If the interest group API is not allowed for this context by Permissions
// Policy, do nothing
if (!render_frame_host().IsFeatureEnabled(
blink::mojom::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
ReportBadMessageAndDeleteThis("Unexpected request");
return false;
}
if (owner.scheme() != url::kHttpsScheme) {
ReportBadMessageAndDeleteThis(
"Unexpected request: Interest groups may only be owned by secure "
"origins");
return false;
}
if (origin().scheme() != url::kHttpsScheme) {
ReportBadMessageAndDeleteThis(
"Unexpected request: Interest groups may only be joined or left from "
"secure origins");
return false;
}
return true;
}
bool AdAuctionServiceImpl::IsInterestGroupAPIAllowed(
ContentBrowserClient::InterestGroupApiOperation
interest_group_api_operation,
const url::Origin& origin) const {
return GetContentClient()->browser()->IsInterestGroupAPIAllowed(
&render_frame_host(), interest_group_api_operation, main_frame_origin_,
origin);
}
void AdAuctionServiceImpl::OnAuctionComplete(
RunAdAuctionCallback callback,
GURL urn_uuid,
FencedFrameURLMapping::Id fenced_frame_urls_map_id,
AuctionRunner* auction,
bool manually_aborted,
absl::optional<blink::InterestGroupKey> winning_group_key,
absl::optional<blink::AdSize> requested_ad_size,
absl::optional<blink::AdDescriptor> ad_descriptor,
std::vector<blink::AdDescriptor> ad_component_descriptors,
std::vector<std::string> errors,
std::unique_ptr<InterestGroupAuctionReporter> reporter) {
// Remove `auction` from `auctions_` but temporarily keep it alive - on
// success, it owns a AuctionWorkletManager::WorkletHandle for the top-level
// auction, which `reporter` can reuse once started. Fine to delete after
// starting the reporter.
auto auction_it = auctions_.find(auction);
DCHECK(auction_it != auctions_.end());
std::unique_ptr<AuctionRunner> owned_auction = std::move(auction_it->second);
auctions_.erase(auction_it);
// Forward debug information to devtools.
for (const std::string& error : errors) {
devtools_instrumentation::LogWorkletMessage(
*GetFrame(), blink::mojom::ConsoleMessageLevel::kError,
base::StrCat({"Worklet error: ", error}));
}
auto* auction_result_metrics =
AdAuctionResultMetrics::GetForPage(render_frame_host().GetPage());
if (!ad_descriptor) {
DCHECK(!reporter);
std::move(callback).Run(manually_aborted, /*config=*/absl::nullopt);
if (auction_result_metrics) {
// `auction_result_metrics` can be null since PageUserData like
// AdAuctionResultMetrics isn't guaranteed to be destroyed after document
// services like `this`, even though this typically is the case for
// destruction of the RenderFrameHost (except for renderer crashes).
//
// So, we need to guard against this.
auction_result_metrics->ReportAuctionResult(
AdAuctionResultMetrics::AuctionResult::kFailed);
}
return;
}
DCHECK(reporter);
// Should always be present with a ad_descriptor->url.
DCHECK(winning_group_key);
DCHECK(blink::IsValidFencedFrameURL(ad_descriptor->url));
DCHECK(urn_uuid.is_valid());
GetContentClient()->browser()->OnAuctionComplete(
&render_frame_host(),
InterestGroupManager::InterestGroupDataKey{
reporter->winning_bid_info()
.storage_interest_group->interest_group.owner,
reporter->winning_bid_info().storage_interest_group->joining_origin,
});
content::AdAuctionData ad_auction_data{winning_group_key->owner,
winning_group_key->name};
FencedFrameURLMapping& current_fenced_frame_urls_map =
GetFrame()->GetPage().fenced_frame_urls_map();
// The auction must operate on the same fenced frame mapping.
CHECK_EQ(fenced_frame_urls_map_id, current_fenced_frame_urls_map.unique_id());
// Set up reporting for any fenced frame that's navigated to the winning bid's
// URL. Use a URLLoaderFactory that will automatically reconnect on network
// process crashes, and can outlive the frame.
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
render_frame_host()
.GetStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
blink::FencedFrame::RedactedFencedFrameConfig config =
current_fenced_frame_urls_map.AssignFencedFrameURLAndInterestGroupInfo(
urn_uuid, requested_ad_size, *ad_descriptor,
std::move(ad_auction_data), reporter->OnNavigateToWinningAdCallback(),
ad_component_descriptors, reporter->fenced_frame_reporter());
std::move(callback).Run(/*manually_aborted=*/false, std::move(config));
// Start the InterestGroupAuctionReporter. It will run reporting scripts, but
// nothing will be reported (nor the reporter deleted) until a fenced frame
// navigates to the winning ad, which will be signalled by invoking the
// callback returned by the InterestGroupAuctionReporter's
// OnNavitationToWinningAdCallback() method (invoked just above).
reporters_.emplace_front(std::move(reporter));
reporters_.front()->Start(
base::BindOnce(&AdAuctionServiceImpl::OnReporterComplete,
base::Unretained(this), reporters_.begin()));
if (auction_result_metrics) {
auction_result_metrics->ReportAuctionResult(
AdAuctionResultMetrics::AuctionResult::kSucceeded);
}
}
void AdAuctionServiceImpl::OnReporterComplete(
ReporterList::iterator reporter_it) {
// Forward debug information to devtools.
//
// TODO(https://crbug.com/1394777): Ideally this will share code with the
// handling of the errors from the earlier phases of the auction.
InterestGroupAuctionReporter* reporter = reporter_it->get();
for (const std::string& error : reporter->errors()) {
devtools_instrumentation::LogWorkletMessage(
*GetFrame(), blink::mojom::ConsoleMessageLevel::kError,
base::StrCat({"Worklet error: ", error}));
}
reporters_.erase(reporter_it);
}
void AdAuctionServiceImpl::MaybeLogPrivateAggregationFeatures(
const std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>&
private_aggregation_requests) {
// TODO(crbug.com/1356654): Improve coverage of these use counters, i.e.
// for API usage that does not result in a successful request.
if (private_aggregation_requests.empty()) {
return;
}
if (!has_logged_extended_private_aggregation_web_feature_ &&
base::ranges::any_of(
private_aggregation_requests, [](const auto& request) {
return request->contribution->is_for_event_contribution();
})) {
has_logged_extended_private_aggregation_web_feature_ = true;
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
&render_frame_host(),
blink::mojom::WebFeature::kPrivateAggregationApiFledgeExtensions);
}
if (!has_logged_private_aggregation_web_features_) {
has_logged_private_aggregation_web_features_ = true;
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
&render_frame_host(),
blink::mojom::WebFeature::kPrivateAggregationApiAll);
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
&render_frame_host(),
blink::mojom::WebFeature::kPrivateAggregationApiFledge);
}
}
InterestGroupManagerImpl& AdAuctionServiceImpl::GetInterestGroupManager()
const {
return *static_cast<InterestGroupManagerImpl*>(
render_frame_host().GetStoragePartition()->GetInterestGroupManager());
}
url::Origin AdAuctionServiceImpl::GetTopWindowOrigin() const {
if (!render_frame_host().GetParent())
return origin();
return render_frame_host().GetMainFrame()->GetLastCommittedOrigin();
}
} // namespace content