0

Protected Audience: separate frame destruction and abort results in UMA

These two scenarios are currently represented by one AuctionResult,
but we want to track them separately.

Change-Id: I11e86b52dd5a41febf2c9eb21a9040b074c71daf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6316940
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Noam Rosenthal <nrosenthal@chromium.org>
Commit-Queue: Abigail Katcoff <abigailkatcoff@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1430846}
This commit is contained in:
Abigail Katcoff
2025-03-11 07:45:19 -07:00
committed by Chromium LUCI CQ
parent 17e1e21d0a
commit 43d6a26d0a
12 changed files with 90 additions and 42 deletions

@ -3285,7 +3285,7 @@ TEST_P(AdsPageLoadMetricsObserverTest,
FirstContentfulPaintPostAbortedOnDeviceAuction_NotRecorded) {
SimulateFCPPostAuctions({CompleteAuctionResult(
/*is_server_auction=*/false,
/*is_on_device_auction=*/true, content::AuctionResult::kAborted)});
/*is_on_device_auction=*/true, content::AuctionResult::kAbortSignal)});
histogram_tester().ExpectUniqueSample(
SuffixedHistogram(

@ -40,7 +40,8 @@ void AggregateFrameData::OnAdAuctionComplete(bool is_server_auction,
// abort signal was sent, the caller likely was not waiting for the auction to
// finish.
if (!first_ad_fcp_after_main_nav_start_ &&
result != content::AuctionResult::kAborted) {
result != content::AuctionResult::kAbortSignal &&
result != content::AuctionResult::kDocumentDestruction) {
completed_fledge_server_auction_before_fcp_ |= is_server_auction;
completed_fledge_on_device_auction_before_fcp_ |= is_on_device_auction;
completed_only_winning_fledge_auctions_ &=

@ -12931,7 +12931,7 @@ TEST_F(AdAuctionServiceImplBAndATest, RunBAndAAuction) {
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTime", 1);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTimeNoWinner",
0);
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
AuctionResult::kSuccess, 1);
hist.ExpectUniqueSample(
"Ads.InterestGroup.ServerAuction.NonKAnonWinnerIsKAnon", true, 1);
@ -12948,7 +12948,7 @@ TEST_F(AdAuctionServiceImplBAndATest, RunBAndAAuction) {
// There should be no on-device metrics
hist.ExpectTotalCount("Ads.InterestGroup.Auction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.EndToEndTimeNoWinner", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.Result", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.Result2", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.AuctionWithWinnerTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.ReportDelay", 0);
@ -13110,7 +13110,7 @@ TEST_F(AdAuctionServiceImplBAndATest, RunBAndAAuctionNoBids) {
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTimeNoWinner",
1);
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
AuctionResult::kNoBids, 1);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.NonKAnonWinnerIsKAnon",
0);
@ -13120,7 +13120,7 @@ TEST_F(AdAuctionServiceImplBAndATest, RunBAndAAuctionNoBids) {
// There should be no on-device metrics
hist.ExpectTotalCount("Ads.InterestGroup.Auction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.EndToEndTimeNoWinner", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.Result", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.Result2", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.AuctionWithWinnerTime", 0);
}
@ -13200,7 +13200,7 @@ TEST_F(AdAuctionServiceImplBAndATest, RunBAndAAuctionServerError) {
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTimeNoWinner",
1);
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
AuctionResult::kNoBids, 1);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.NonKAnonWinnerIsKAnon",
0);
@ -13210,7 +13210,7 @@ TEST_F(AdAuctionServiceImplBAndATest, RunBAndAAuctionServerError) {
// There should be no on-device metrics
hist.ExpectTotalCount("Ads.InterestGroup.Auction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.EndToEndTimeNoWinner", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.Result", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.Result2", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", 0);
hist.ExpectTotalCount("Ads.InterestGroup.Auction.AuctionWithWinnerTime", 0);
}
@ -13291,7 +13291,7 @@ TEST_F(AdAuctionServiceImplBAndATest, HandlesBadResponseForBAndAAuction) {
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTimeNoWinner",
0);
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
AuctionResult::kInvalidServerResponse, 1);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.NonKAnonWinnerIsKAnon",
0);
@ -13377,7 +13377,7 @@ TEST_F(AdAuctionServiceImplBAndATest,
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTime", 0);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.EndToEndTimeNoWinner",
0);
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
AuctionResult::kInvalidServerResponse, 1);
hist.ExpectTotalCount("Ads.InterestGroup.ServerAuction.NonKAnonWinnerIsKAnon",
0);

@ -439,6 +439,7 @@ void AuctionRunner::Abort() {
base::UmaHistogramMediumTimes(
uma_prefix + "SignaledAbortTime",
base::TimeTicks::Now() - auction_.creation_time());
auction_.SetReceivedAbortSignal();
FailAuction(/*aborted_by_script=*/true);
}
@ -503,17 +504,19 @@ void AuctionRunner::FailAuction(
auto [contained_server_auction, contained_on_device_auction] =
IncludesServerAndOnDeviceAuctions();
AuctionResult auction_result = auction_.final_auction_result().value_or(
aborted_by_script ? AuctionResult::kAbortSignal
: AuctionResult::kDocumentDestruction);
// When the auction fails, private aggregation requests of non-reserved event
// types cannot be triggered anyway, so no need to pass it along.
std::move(callback_).Run(
this, aborted_by_script,
/*winning_group_key=*/std::nullopt,
/*requested_ad_size=*/std::nullopt,
/*ad_descriptor=*/std::nullopt,
/*ad_component_descriptors=*/{}, auction_.TakeErrors(),
/*reporter=*/nullptr, contained_server_auction,
contained_on_device_auction,
auction_.final_auction_result().value_or(AuctionResult::kAborted));
std::move(callback_).Run(this, aborted_by_script,
/*winning_group_key=*/std::nullopt,
/*requested_ad_size=*/std::nullopt,
/*ad_descriptor=*/std::nullopt,
/*ad_component_descriptors=*/{},
auction_.TakeErrors(),
/*reporter=*/nullptr, contained_server_auction,
contained_on_device_auction, auction_result);
}
AuctionRunner::AuctionRunner(
@ -690,7 +693,8 @@ void AuctionRunner::OnBidsGeneratedAndScored(base::TimeTicks start_time,
std::move(component_ad_descriptors_with_replacements), std::move(errors),
std::move(reporter), contained_server_auction,
contained_on_device_auction,
auction_.final_auction_result().value_or(AuctionResult::kAborted));
auction_.final_auction_result().value_or(
AuctionResult::kDocumentDestruction));
}
void AuctionRunner::UpdateInterestGroupsPostAuction() {

@ -3301,7 +3301,7 @@ class AuctionRunnerTest : public RenderViewHostTestHarness,
SCOPED_TRACE(location.ToString());
using UkmEntry = ukm::builders::AdsInterestGroup_AuctionLatency_V2;
ukm::TestUkmRecorder::HumanReadableUkmMetrics ukm_metrics = GetUkmMetrics();
histogram_tester_->ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
histogram_tester_->ExpectUniqueSample("Ads.InterestGroup.Auction.Result2",
expectations.result, 1);
EXPECT_THAT(ukm_metrics,
HasMetricWithValue(UkmEntry::kResultName,
@ -3391,7 +3391,8 @@ class AuctionRunnerTest : public RenderViewHostTestHarness,
histogram_tester_->ExpectTotalCount(
"Ads.InterestGroup.Auction.AbortTime",
expectations.result == AuctionResult::kAborted);
expectations.result == AuctionResult::kDocumentDestruction ||
expectations.result == AuctionResult::kAbortSignal);
histogram_tester_->ExpectTotalCount(
"Ads.InterestGroup.Auction.CompletedWithoutWinnerTime",
expectations.result == AuctionResult::kNoBids ||
@ -25403,7 +25404,7 @@ TEST_P(AuctionRunnerKAnonTest, FailureHandling) {
testing::ElementsAre());
histogram_tester_->ExpectUniqueSample(
"Ads.InterestGroup.Auction.NonKAnonWinnerIsKAnon", false, 0);
MetricsExpectations expectations(AuctionResult::kAborted);
MetricsExpectations expectations(AuctionResult::kDocumentDestruction);
expectations.SetNumInterestGroups(2)
.SetNumOwnersAndDistinctOwners(2)
.SetNumOwnersWithoutInterestGroups(0)
@ -26287,7 +26288,7 @@ TEST_F(AuctionRunnerTest, ServerResponseLogsErrors) {
request_id.AsLowercaseString() + "'");
}
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
test_case.result, 1);
}
}
@ -26435,7 +26436,7 @@ TEST_F(AuctionRunnerTest, MatchedSelectedReportingIdInServerResponse) {
auction_run_loop_->Run();
EXPECT_THAT(result_.errors, testing::ElementsAreArray(test_case.errors));
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
test_case.result, 1);
}
}
@ -26656,7 +26657,7 @@ TEST_F(AuctionRunnerTest, MatchedReportingIdsInServerResponse) {
auction_run_loop_->Run();
EXPECT_THAT(result_.errors, testing::ElementsAreArray(test_case.errors));
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result",
hist.ExpectUniqueSample("Ads.InterestGroup.ServerAuction.Result2",
test_case.result, 1);
}
}

@ -3126,7 +3126,11 @@ InterestGroupAuction::~InterestGroupAuction() {
}
if (!final_auction_result_) {
final_auction_result_ = AuctionResult::kAborted;
if (received_abort_signal_) {
final_auction_result_ = AuctionResult::kAbortSignal;
} else {
final_auction_result_ = AuctionResult::kDocumentDestruction;
}
}
std::string uma_prefix = "Ads.InterestGroup.Auction.";
@ -3136,7 +3140,13 @@ InterestGroupAuction::~InterestGroupAuction() {
// TODO(mmenke): Record histograms for component auctions.
if (!parent_) {
base::UmaHistogramEnumeration(uma_prefix + "Result",
base::UmaHistogramEnumeration(
uma_prefix + "Result",
*final_auction_result_ == AuctionResult::kAbortSignal ||
*final_auction_result_ == AuctionResult::kDocumentDestruction
? AuctionResult::kAborted
: *final_auction_result_);
base::UmaHistogramEnumeration(uma_prefix + "Result2",
*final_auction_result_);
if (HasNonKAnonWinner()) {
@ -3147,7 +3157,8 @@ InterestGroupAuction::~InterestGroupAuction() {
// Only record time of full auctions and aborts.
base::TimeTicks now = base::TimeTicks::Now();
switch (*final_auction_result_) {
case AuctionResult::kAborted:
case AuctionResult::kDocumentDestruction:
case AuctionResult::kAbortSignal:
base::UmaHistogramMediumTimes(uma_prefix + "AbortTime",
now - creation_time_);
break;
@ -4133,6 +4144,8 @@ bool InterestGroupAuction::HasNonKAnonWinner() const {
case AuctionResult::kNoBids:
case AuctionResult::kAllBidsRejected:
return top_non_kanon_enforced_bid() != nullptr;
case AuctionResult::kAbortSignal:
case AuctionResult::kDocumentDestruction:
case AuctionResult::kAborted:
case AuctionResult::kBadMojoMessage:
case AuctionResult::kNoInterestGroups:

@ -819,6 +819,8 @@ class CONTENT_EXPORT InterestGroupAuction
return final_auction_result_;
}
void SetReceivedAbortSignal() { received_abort_signal_ = true; }
// Gets the buyer experiment ID in `config` for buyer. Public so that
// InterestGroupAuctionReporter can use it.
static std::optional<uint16_t> GetBuyerExperimentId(
@ -1652,6 +1654,11 @@ class CONTENT_EXPORT InterestGroupAuction
size_t interest_groups_bytes_for_metrics_ = 0u;
size_t ads_and_ad_components_bytes_for_metrics_ = 0u;
// If true, indicates that this auction received an abort signal. Used for UMA
// only, to differentiate frame destruction from receiving an abort signal in
// Auction.Result.
bool received_abort_signal_ = false;
base::WeakPtrFactory<InterestGroupAuction> weak_ptr_factory_{this};
};

@ -24633,7 +24633,7 @@ IN_PROC_BROWSER_TEST_F(UsesAnticipatoryProcessesTest,
{
base::HistogramTester histogram_tester;
auto result = RunAuctionAndWait(auction_config);
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result2",
AuctionResult::kNoBids, 1);
histogram_tester.ExpectUniqueSample(
"Ads.InterestGroup.Auction.Seller.RequestWorkletServiceOutcome",
@ -24664,7 +24664,7 @@ IN_PROC_BROWSER_TEST_F(UsesAnticipatoryProcessesTest,
{
base::HistogramTester histogram_tester;
auto result = RunAuctionAndWait(auction_config);
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result2",
AuctionResult::kNoBids, 1);
histogram_tester.ExpectUniqueSample(
"Ads.InterestGroup.Auction.Seller.RequestWorkletServiceOutcome",
@ -26994,7 +26994,7 @@ IN_PROC_BROWSER_TEST_P(InterestGroupPreconnectOwnerAndSignalsOriginsTest,
// We have no interest groups & nothing cached at this point.
base::HistogramTester histogram_tester;
auto result1 = RunAuctionAndWait(auction_config);
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result2",
AuctionResult::kNoInterestGroups, 1);
histogram_tester.ExpectUniqueSample(
"Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect", 0, 1);
@ -27029,7 +27029,7 @@ IN_PROC_BROWSER_TEST_P(InterestGroupPreconnectOwnerAndSignalsOriginsTest,
EXPECT_EQ(cached_signals_origin, signals_origin);
auto result2 = RunAuctionAndWait(auction_config);
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result2",
AuctionResult::kNoInterestGroups, 2);
histogram_tester.ExpectBucketCount(
"Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect", 1, 1);
@ -27049,8 +27049,8 @@ IN_PROC_BROWSER_TEST_P(InterestGroupPreconnectOwnerAndSignalsOriginsTest,
interest_group.owner, "interest_group"}});
auto result3 = RunAuctionAndWait(auction_config);
histogram_tester.ExpectTotalCount("Ads.InterestGroup.Auction.Result", 3);
histogram_tester.ExpectBucketCount("Ads.InterestGroup.Auction.Result",
histogram_tester.ExpectTotalCount("Ads.InterestGroup.Auction.Result2", 3);
histogram_tester.ExpectBucketCount("Ads.InterestGroup.Auction.Result2",
AuctionResult::kNoInterestGroups, 2);
histogram_tester.ExpectBucketCount(
"Ads.InterestGroup.Auction.NumOwnerOriginsCachedForPreconnect", 1, 2);

@ -478,7 +478,7 @@ IN_PROC_BROWSER_TEST_F(AdjustableAuction, RunAdjustableAuction) {
histogram_tester.ExpectBucketCount(
"Ads.InterestGroup.Auction.NumInterestGroups",
kInterestGroupsPerOwner * kOwners * kSellers, kAuctions);
histogram_tester.ExpectTotalCount("Ads.InterestGroup.Auction.Result",
histogram_tester.ExpectTotalCount("Ads.InterestGroup.Auction.Result2",
kAuctions);
histogram_tester.ExpectTotalCount(
"Ads.InterestGroup.Auction.AuctionWithWinnerTime", kAuctions);

@ -16,8 +16,11 @@ enum class AuctionResult {
// The auction succeeded, with a winning bidder.
kSuccess = 0,
// The auction was aborted, due to either navigating away from the frame
// that started the auction or browser shutdown.
// TODO(abigailkatcoff): This will be made obsolete and replaced by
// kDocumentDestruction and kAbortSignal. It's only around so that
// InterestGroup.Auction.Result can coexist with
// InterestGroup.Auction.Result2 for a short while before we replace
// InterestGroup.Auction.Result.
kAborted = 1,
// Bad message received over Mojo. This is potentially a security error.
@ -60,7 +63,14 @@ enum class AuctionResult {
// prior call to createAuctionNonce.
kInvalidAuctionNonce = 13,
kMaxValue = kInvalidAuctionNonce
// The auction's abort signal was called.
kAbortSignal = 14,
// The auction was aborted due to either navigating away from the document
// that started the auction or browser shutdown.
kDocumentDestruction = 15,
kMaxValue = kDocumentDestruction
};
} // namespace content

@ -657,6 +657,8 @@ uploading your change for review. These are checked by presubmit scripts.
<int value="10" label="Component lost top-level auction"/>
<int value="11" label="(obsolete) Winning component seller worklet crashed"/>
<int value="12" label="Invalid server response"/>
<int value="13" label="Abort signal"/>
<int value="14" label="Document destruction"/>
</enum>
<enum name="AuctionState">

@ -2426,13 +2426,13 @@ chromium-metrics-reviews@google.com.
</token>
</histogram>
<histogram name="Ads.InterestGroup.{AuctionType}.Result" enum="AuctionResult"
expires_after="2025-09-07">
<histogram name="Ads.InterestGroup.{AuctionType}.Result{Version}"
enum="AuctionResult" expires_after="2025-09-07">
<owner>mmenke@chromium.org</owner>
<owner>morlovich@chromium.org</owner>
<summary>
The final result type of running a {AuctionType} auction. Recorded on
auction completion/cancellation.
auction completion/cancellation. {Version}
See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
version of the FLEDGE explainer.
@ -2445,6 +2445,16 @@ chromium-metrics-reviews@google.com.
<owner>privacy-sandbox-dev@chromium.org</owner>
</variant>
</token>
<token key="Version">
<variant name=""
summary="Warning: Ads.InterestGroup.{AuctionType}.Result is
deprecated and will be removed. Use
Ads.InterestGroup.{AuctionType}.Result2 instead."/>
<variant name="2" summary="">
<owner>abigailkatcoff@google.com</owner>
<owner>privacy-sandbox-dev@chromium.org</owner>
</variant>
</token>
</histogram>
<histogram name="Ads.InterestGroup.{AuctionType}.SignaledAbortTime" units="ms"