0

Provide bid rejected reason to losing bidders.

Allow scoreAd() return an extra string field 'rejectReason', which
tells why a bid is rejected. Its value is case sensitive. A bidder's
script can use placeholder ${rejectReason} in its debugging loss report
URL's query string, to get the reject reason. If score > 0, the
'rejectReason' value will be ignored (set to default 'not-available')
even if there is one from scoreAd().
Currently component sellers which participate in a top level auction
and act as bidders do not get top level's reject reasons.

Fixed: b/232945640
Bug: b/232945640
Change-Id: Idc0a270252241475863f28e0c212453fef9b29dc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3661960
Reviewed-by: Matt Menke <mmenke@chromium.org>
Commit-Queue: Qingxin Wu <qingxinwu@google.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1049741}
This commit is contained in:
Qingxin Wu
2022-09-21 18:02:11 +00:00
committed by Chromium LUCI CQ
parent 4ed53038e0
commit d225f2c3dd
7 changed files with 735 additions and 178 deletions

@ -169,7 +169,8 @@ std::string MakeBidScript(const url::Origin& seller,
const std::string& signal_val = "",
bool report_post_auction_signals = false,
const std::string& debug_loss_report_url = "",
const std::string& debug_win_report_url = "") {
const std::string& debug_win_report_url = "",
bool report_reject_reason = false) {
// TODO(morlovich): Use JsReplace.
constexpr char kBidScript[] = R"(
const seller = "%s";
@ -180,6 +181,7 @@ std::string MakeBidScript(const url::Origin& seller,
const interestGroupName = "%s";
const hasSignals = %s;
const reportPostAuctionSignals = %s;
const reportRejectReason = %s;
const postAuctionSignalsPlaceholder = "%s";
let debugLossReportUrl = "%s";
let debugWinReportUrl = "%s";
@ -284,6 +286,10 @@ std::string MakeBidScript(const url::Origin& seller,
if (debugLossReportUrl) {
if (reportPostAuctionSignals)
debugLossReportUrl += postAuctionSignalsPlaceholder;
if (reportRejectReason) {
debugLossReportUrl += reportPostAuctionSignals ? '&' : '?';
debugLossReportUrl += 'rejectReason=${rejectReason}';
}
forDebuggingOnly.reportAdAuctionLoss(debugLossReportUrl);
}
if (debugWinReportUrl) {
@ -387,8 +393,9 @@ std::string MakeBidScript(const url::Origin& seller,
num_ad_components, interest_group_owner.Serialize().c_str(),
interest_group_name.c_str(), has_signals ? "true" : "false",
report_post_auction_signals ? "true" : "false",
kPostAuctionSignalsPlaceholder, debug_loss_report_url.c_str(),
debug_win_report_url.c_str(), signal_key.c_str(), signal_val.c_str());
report_reject_reason ? "true" : "false", kPostAuctionSignalsPlaceholder,
debug_loss_report_url.c_str(), debug_win_report_url.c_str(),
signal_key.c_str(), signal_val.c_str());
}
// This can be appended to the standard script to override the function.
@ -537,8 +544,7 @@ std::string MakeDecisionScript(
if (reportPostAuctionSignals)
debugReportUrl += postAuctionSignalsPlaceholder;
if (reportTopLevelPostAuctionSignals) {
if (reportPostAuctionSignals)
debugReportUrl += "&";
debugReportUrl += reportPostAuctionSignals ? '&' : '?';
debugReportUrl += topLevelPostAuctionSignalsPlaceholder;
}
// Only add key "bid=" to the report URL when report post auction signals
@ -655,23 +661,13 @@ std::string MakeAuctionScriptNoReportUrl(
bool report_post_auction_signals = false,
const std::string& debug_loss_report_url = "",
const std::string& debug_win_report_url = "") {
return MakeDecisionScript(
decision_logic_url,
/*send_report_url=*/absl::nullopt,
/*bid_from_component_auction_wins=*/false,
/*report_post_auction_signals=*/report_post_auction_signals,
debug_loss_report_url, debug_win_report_url);
return MakeDecisionScript(decision_logic_url,
/*send_report_url=*/absl::nullopt,
/*bid_from_component_auction_wins=*/false,
report_post_auction_signals, debug_loss_report_url,
debug_win_report_url);
}
const char kAuctionScriptRejects2[] = R"(
function scoreAd(adMetadata, bid, auctionConfig, browserSignals) {
privateAggregation.sendHistogramReport({bucket: 5, value: 6});
if (bid === 2)
return -1;
return bid + 1;
}
)";
const char kBasicReportResult[] = R"(
function reportResult(auctionConfig, browserSignals) {
privateAggregation.sendHistogramReport({bucket: 7, value: 8});
@ -683,8 +679,18 @@ const char kBasicReportResult[] = R"(
}
)";
std::string MakeAuctionScriptReject2() {
return std::string(kAuctionScriptRejects2) + kBasicReportResult;
std::string MakeAuctionScriptReject2(
const std::string& reject_reason = "not-available") {
constexpr char kAuctionScriptRejects2[] = R"(
function scoreAd(adMetadata, bid, auctionConfig, browserSignals) {
privateAggregation.sendHistogramReport({bucket: 5, value: 6});
if (bid === 2)
return {desirability: -1, rejectReason: '%s'};
return bid + 1;
}
)";
return base::StringPrintf(kAuctionScriptRejects2, reject_reason.c_str()) +
kBasicReportResult;
}
std::string MakeAuctionScriptReject1And2WithDebugReporting(
@ -695,15 +701,26 @@ std::string MakeAuctionScriptReject1And2WithDebugReporting(
const debugWinReportUrl = "%s";
function scoreAd(adMetadata, bid, auctionConfig, browserSignals) {
let result = bid + 1;
if (bid === 1 || bid === 2)
let rejectReason = "not-available";
if (bid === 1) {
result = -1;
rejectReason = 'invalid-bid';
} else if (bid === 2) {
result = -1;
rejectReason = 'bid-below-auction-floor';
}
if (debugLossReportUrl) {
forDebuggingOnly.reportAdAuctionLoss(
debugLossReportUrl + '&bid=' + bid);
}
if (debugWinReportUrl)
forDebuggingOnly.reportAdAuctionWin(debugWinReportUrl + "&bid=" + bid);
return result;
return {
desirability: result,
allowComponentAuction: true,
rejectReason: rejectReason
};
}
)";
return base::StringPrintf(kReject1And2WithDebugReporting,
@ -819,9 +836,11 @@ const GURL ReportWinUrl(
// Returns a report URL with given parameters for forDebuggingOnly win/loss
// report APIs, with post auction signals included in the URL.
const GURL DebugReportUrl(const std::string& url,
const PostAuctionSignals& signals,
absl::optional<double> bid = absl::nullopt) {
const GURL DebugReportUrl(
const std::string& url,
const PostAuctionSignals& signals,
absl::optional<double> bid = absl::nullopt,
absl::optional<std::string> reject_reason = absl::nullopt) {
// Post auction signals needs to be consistent with
// `kPostAuctionSignalsPlaceholder`. Only keeps integer part of bid values for
// simplicity for now.
@ -834,6 +853,11 @@ const GURL DebugReportUrl(const std::string& url,
signals.made_winning_bid ? "true" : "false",
signals.highest_scoring_other_bid,
signals.made_highest_scoring_other_bid ? "true" : "false");
if (reject_reason.has_value()) {
report_url_string.append(
base::StringPrintf("&rejectReason=%s", reject_reason.value().c_str()));
}
if (bid.has_value()) {
return GURL(base::StringPrintf("%s&bid=%.0f", report_url_string.c_str(),
bid.value()));
@ -5938,6 +5962,8 @@ TEST_F(AuctionRunnerTest, BidderCrashBeforeBidding) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/11,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6036,6 +6062,8 @@ TEST_F(AuctionRunnerTest, WinningBidderCrashWhileReporting) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/11,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6055,6 +6083,8 @@ TEST_F(AuctionRunnerTest, WinningBidderCrashWhileReporting) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6131,8 +6161,7 @@ TEST_F(AuctionRunnerTest, ForDebuggingOnlyReporting) {
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kSellerUrl,
MakeAuctionScript(/*report_post_auction_signals=*/false,
GURL("https://adstuff.publisher1.com/auction.js"),
MakeAuctionScript(/*report_post_auction_signals=*/false, kSellerUrl,
kSellerDebugLossReportBaseUrl,
kSellerDebugWinReportBaseUrl));
@ -6202,6 +6231,8 @@ TEST_F(AuctionRunnerTest, SellerCrash) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6216,6 +6247,8 @@ TEST_F(AuctionRunnerTest, SellerCrash) {
std::move(score_ad_params2.score_ad_client))
->OnScoreAdComplete(
/*score=*/11,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6330,6 +6363,8 @@ TEST_F(AuctionRunnerTest, ComponentAuctionOneBidderCrashesBeforeBidding) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/3,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
/*ad=*/"null",
/*bid=*/0,
@ -6351,6 +6386,8 @@ TEST_F(AuctionRunnerTest, ComponentAuctionOneBidderCrashesBeforeBidding) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/4,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6449,6 +6486,8 @@ TEST_F(AuctionRunnerTest, ComponentAuctionComponentSellersReportResultFails) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/3,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
/*ad=*/"null",
/*bid=*/0,
@ -6470,6 +6509,8 @@ TEST_F(AuctionRunnerTest, ComponentAuctionComponentSellersReportResultFails) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/4,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6676,7 +6717,10 @@ TEST_F(AuctionRunnerTest, ComponentAuctionComponentSellerBadBidParams) {
EXPECT_EQ(2, score_ad_params.bid);
mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(/*score=*/3, test_case.params.Clone(),
->OnScoreAdComplete(/*score=*/3,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
test_case.params.Clone(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
/*debug_loss_report_url=*/absl::nullopt,
@ -6735,6 +6779,8 @@ TEST_F(AuctionRunnerTest, TopLevelSellerBadBidParams) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/3,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
/*ad=*/"null",
/*bid=*/0,
@ -6805,6 +6851,8 @@ TEST_F(AuctionRunnerTest, NullAdComponents) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/11,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -6902,6 +6950,8 @@ TEST_F(AuctionRunnerTest, AdComponentsLimit) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/11,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7139,6 +7189,8 @@ TEST_F(AuctionRunnerTest, BadSellerReportUrl) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7198,6 +7250,8 @@ TEST_F(AuctionRunnerTest, BadSellerBeaconUrl) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7267,6 +7321,8 @@ TEST_F(AuctionRunnerTest, BadComponentSellerReportUrl) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
/*ad=*/"null",
/*bid=*/0,
@ -7285,6 +7341,8 @@ TEST_F(AuctionRunnerTest, BadComponentSellerReportUrl) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7354,6 +7412,8 @@ TEST_F(AuctionRunnerTest, BadBidderReportUrl) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7417,6 +7477,8 @@ TEST_F(AuctionRunnerTest, BadBidderBeaconUrl) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7485,6 +7547,8 @@ TEST_F(AuctionRunnerTest, DestroyBidderWorkletWithoutBid) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/11,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7550,6 +7614,8 @@ TEST_F(AuctionRunnerTest, Tie) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7567,6 +7633,8 @@ TEST_F(AuctionRunnerTest, Tie) {
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -7701,6 +7769,8 @@ TEST_F(AuctionRunnerTest, WorkletOrder) {
std::move(score_ad_params1.score_ad_client))
->OnScoreAdComplete(
/*score=*/bidder1_wins ? 11 : 9,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::
ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
@ -7714,15 +7784,18 @@ TEST_F(AuctionRunnerTest, WorkletOrder) {
case Event::kBid2Scored:
mojo::Remote<auction_worklet::mojom::ScoreAdClient>(
std::move(score_ad_params2.score_ad_client))
->OnScoreAdComplete(/*score=*/10,
auction_worklet::mojom::
ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
/*debug_loss_report_url=*/absl::nullopt,
/*debug_win_report_url=*/absl::nullopt,
/*pa_requests=*/{},
/*errors=*/{});
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::
ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
/*debug_loss_report_url=*/absl::nullopt,
/*debug_win_report_url=*/absl::nullopt,
/*pa_requests=*/{},
/*errors=*/{});
// Wait for the AuctionRunner to receive the score.
task_environment_.RunUntilIdle();
break;
@ -9546,14 +9619,16 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
kBidder1, kBidder1Name,
/*has_signals=*/false, "k1", "a",
/*report_post_auction_signals=*/true,
kBidder1DebugLossReportUrl, kBidder1DebugWinReportUrl));
kBidder1DebugLossReportUrl, kBidder1DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kBidder2Url,
MakeBidScript(kSeller, "2", "https://ad2.com/", /*num_ad_components=*/2,
kBidder2, kBidder2Name,
/*has_signals=*/false, "l2", "b",
/*report_post_auction_signals=*/true,
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl));
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kSellerUrl,
MakeAuctionScriptReject1And2WithDebugReporting(
@ -9571,8 +9646,10 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
EXPECT_THAT(
res.debug_loss_report_urls,
testing::UnorderedElementsAre(
DebugReportUrl(kBidder1DebugLossReportUrl, PostAuctionSignals()),
DebugReportUrl(kBidder2DebugLossReportUrl, PostAuctionSignals()),
DebugReportUrl(kBidder1DebugLossReportUrl, PostAuctionSignals(),
/*bid=*/absl::nullopt, "invalid-bid"),
DebugReportUrl(kBidder2DebugLossReportUrl, PostAuctionSignals(),
/*bid=*/absl::nullopt, "bid-below-auction-floor"),
DebugReportUrl(kSellerDebugLossReportBaseUrl, PostAuctionSignals(),
/*bid=*/1),
DebugReportUrl(kSellerDebugLossReportBaseUrl, PostAuctionSignals(),
@ -9706,7 +9783,10 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
/*bid=*/2)));
}
// Like above test, but top-level seller rejects all bidders.
// Test debug loss reporting in an auction with no winner. Component bidder 1 is
// rejected by component seller, and component bidder 2 is rejected by top-level
// seller. Component bidders get component auction's reject reason but not the
// top-level auction's.
TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
ForDebuggingOnlyReportingComponentAuctionNoWinner) {
interest_group_buyers_.emplace();
@ -9715,21 +9795,17 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
CreateAuctionConfig(kComponentSeller1Url, {{kBidder1}}));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kComponentSeller1Url,
MakeDecisionScript(
kComponentSeller1Url,
/*send_report_url=*/GURL("https://component1-report.test/"),
/*bid_from_component_auction_wins=*/false,
/*report_post_auction_signals=*/true,
/*debug_loss_report_url=*/"https://component1-loss-reporting.test/",
/*debug_win_report_url=*/"https://component1-win-reporting.test/",
/*report_top_level_post_auction_signals*/ true));
MakeAuctionScriptReject1And2WithDebugReporting(
"https://component1-loss-reporting.test/?",
"https://component1-win-reporting.test/?"));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kBidder1Url,
MakeBidScript(kComponentSeller1, "1", "https://ad1.com/",
/*num_ad_components=*/2, kBidder1, kBidder1Name,
/*has_signals=*/true, "k1", "a",
/*report_post_auction_signals=*/true,
kBidder1DebugLossReportUrl, kBidder1DebugWinReportUrl));
kBidder1DebugLossReportUrl, kBidder1DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddBidderJsonResponse(
&url_loader_factory_,
GURL(kBidder1TrustedSignalsUrl.spec() +
@ -9755,7 +9831,8 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
/*num_ad_components=*/2, kBidder2, kBidder2Name,
/*has_signals=*/true, "l2", "b",
/*report_post_auction_signals=*/true,
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl));
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddBidderJsonResponse(
&url_loader_factory_,
GURL(kBidder2TrustedSignalsUrl.spec() +
@ -9775,7 +9852,11 @@ function scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals,
// While not setting `allowComponentAuction` will also reject the ad, it
// also prevents loss reports and adds an error message, so need to set
// it to true.
return {desirability: 0, allowComponentAuction: true};
return {
desirability: 0,
allowComponentAuction: true,
rejectReason: "bid-below-auction-floor"
};
}
)",
kPostAuctionSignalsPlaceholder,
@ -9787,34 +9868,27 @@ function scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals,
// No interest group won the auction.
EXPECT_FALSE(result_.ad_url);
// Component bidder 1 rejected by component auction gets its reject reason
// "invalid-bid". Component bidders don't get the top-level auction's reject
// reason.
EXPECT_THAT(
result_.debug_loss_report_urls,
testing::UnorderedElementsAre(
DebugReportUrl(kBidder1DebugLossReportUrl,
PostAuctionSignals(
/*winning_bid=*/1,
/*made_winning_bid=*/true,
/*winning_bid=*/0,
/*made_winning_bid=*/false,
/*highest_scoring_other_bid=*/0,
/*made_highest_scoring_other_bid=*/false)),
ComponentSellerDebugReportUrl(
"https://component1-loss-reporting.test/",
/*signals=*/
PostAuctionSignals(/*winning_bid=*/1,
/*made_winning_bid=*/true,
/*highest_scoring_other_bid=*/0,
/*made_highest_scoring_other_bid=*/false),
/*top_level_signals=*/
PostAuctionSignals(),
/*bid=*/1),
DebugReportUrl("https://top-seller-loss-reporting.test/",
PostAuctionSignals(),
/*bid=*/1),
/*made_highest_scoring_other_bid=*/false),
/*bid=*/absl::nullopt, "invalid-bid"),
GURL("https://component1-loss-reporting.test/?&bid=1"),
DebugReportUrl(kBidder2DebugLossReportUrl,
PostAuctionSignals(
/*winning_bid=*/2,
/*made_winning_bid=*/true,
/*highest_scoring_other_bid=*/0,
/*made_highest_scoring_other_bid=*/false)),
/*made_highest_scoring_other_bid=*/false),
/*bid=*/absl::nullopt, "not-available"),
ComponentSellerDebugReportUrl(
"https://component2-loss-reporting.test/",
/*signals=*/
@ -10097,6 +10171,8 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -10160,6 +10236,8 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
std::move(score_ad_params.score_ad_client))
->OnScoreAdComplete(
/*score=*/10,
/*reject_reason=*/
auction_worklet::mojom::RejectReason::kNotAvailable,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr(),
/*scoring_signals_data_version=*/0,
/*has_scoring_signals_data_version=*/false,
@ -10343,8 +10421,231 @@ TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
GURL("https://seller0.test/win/11")));
}
// Enable and test forDebuggingOnly.reportAdAuctionLoss() and
// forDebuggingOnly.reportAdAuctionWin() APIs.
// Reject reason returned by scoreAd() for a rejected bid can be reported to the
// bidder through its debug loss report URL.
TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
RejectedBidGetsRejectReason) {
for (const std::string& reject_reason :
{"not-available", "invalid-bid", "bid-below-auction-floor",
"pending-approval-by-exchange", "disapproved-by-exchange",
"blocked-by-publisher", "language-exclusions", "category-exclusions"}) {
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kBidder1Url,
MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/2,
kBidder1, kBidder1Name,
/*has_signals=*/false, "k1", "a",
/*report_post_auction_signals=*/false,
kBidder1DebugLossReportUrl, kBidder1DebugWinReportUrl,
/*report_reject_reason=*/true));
// Bidder 2 will get a negative score from scoreAd().
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kBidder2Url,
MakeBidScript(kSeller, "2", "https://ad2.com/", /*num_ad_components=*/2,
kBidder2, kBidder2Name,
/*has_signals=*/false, "l2", "b",
/*report_post_auction_signals=*/false,
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kSellerUrl,
MakeAuctionScriptReject2(reject_reason));
const Result& res = RunStandardAuction();
// Bidder 1 won the auction.
EXPECT_EQ(InterestGroupKey(kBidder1, kBidder1Name),
result_.winning_group_id);
EXPECT_EQ(GURL("https://ad1.com/"), res.ad_url);
EXPECT_EQ(1u, res.debug_loss_report_urls.size());
// Seller rejected bidder 2 and returned the reject reason which were then
// reported to bidder 2 through its loss report URL.
EXPECT_THAT(res.debug_loss_report_urls,
testing::UnorderedElementsAre(base::StringPrintf(
"https://bidder2-debug-loss-reporting.com/"
"?rejectReason=%s",
reject_reason.c_str())));
EXPECT_EQ(1u, res.debug_win_report_urls.size());
EXPECT_THAT(res.debug_win_report_urls,
testing::UnorderedElementsAre(kBidder1DebugWinReportUrl));
}
}
// Reject reason returned by scoreAd() for a bid whose score is positive is
// ignored and will not be reported to the bidder.
TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
rejectReasonIgnoredForPositiveBid) {
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kBidder1Url,
MakeBidScript(kSeller, "1", "https://ad1.com/", /*num_ad_components=*/2,
kBidder1, kBidder1Name,
/*has_signals=*/false, "k1", "a",
/*report_post_auction_signals=*/false,
kBidder1DebugLossReportUrl, kBidder1DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddJavascriptResponse(
&url_loader_factory_, kBidder2Url,
MakeBidScript(kSeller, "3", "https://ad2.com/", /*num_ad_components=*/2,
kBidder2, kBidder2Name,
/*has_signals=*/false, "l2", "b",
/*report_post_auction_signals=*/false,
kBidder2DebugLossReportUrl, kBidder2DebugWinReportUrl,
/*report_reject_reason=*/true));
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
MakeAuctionScriptReject2());
const Result& res = RunStandardAuction();
// Bidder 2 won the auction.
EXPECT_EQ(InterestGroupKey(kBidder2, kBidder2Name), result_.winning_group_id);
EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
EXPECT_EQ(1u, res.debug_loss_report_urls.size());
// Reject reason returned by scoreAd() for bidder 1 should be ignored and
// reported as "not-available" in debug loss report URL, because the bid gets
// a positive score thus not rejected by seller.
EXPECT_THAT(
res.debug_loss_report_urls,
testing::UnorderedElementsAre("https://bidder1-debug-loss-reporting.com/"
"?rejectReason=not-available"));
EXPECT_EQ(1u, res.debug_win_report_urls.size());
EXPECT_THAT(res.debug_win_report_urls,
testing::UnorderedElementsAre(kBidder2DebugWinReportUrl));
}
// Only bidders' debug loss report URLs support macro ${rejectReason}.
// Bidders' debug win report URLs and sellers' debug loss/win report URLs does
// not.
TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
rejectReasonInBidderDebugLossReportOnly) {
const char kBidder1Script[] = R"(
function generateBid(interestGroup, auctionSignals, perBuyerSignals,
trustedBiddingSignals, browserSignals) {
forDebuggingOnly.reportAdAuctionLoss(
'https://bidder1-debug-loss-reporting.com/?reason=${rejectReason}');
forDebuggingOnly.reportAdAuctionWin(
'https://bidder1-debug-win-reporting.com/?reason=${rejectReason}');
return {
bid: 1,
render: interestGroup.ads[0].renderUrl
};
}
// Prevent an error about this method not existing.
function reportWin() {}
)";
const char kBidder2Script[] = R"(
function generateBid(interestGroup, auctionSignals, perBuyerSignals,
trustedBiddingSignals, browserSignals) {
forDebuggingOnly.reportAdAuctionLoss(
'https://bidder2-debug-loss-reporting.com/?reason=${rejectReason}');
forDebuggingOnly.reportAdAuctionWin(
'https://bidder2-debug-win-reporting.com/?reason=${rejectReason}');
return {
bid: 2,
render: interestGroup.ads[0].renderUrl
};
}
// Prevent an error about this method not existing.
function reportWin() {}
)";
// Desirability is -1 if bid is 1, otherwise is bid.
const char kSellerScript[] = R"(
function scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals,
browserSignals) {
forDebuggingOnly.reportAdAuctionLoss(
'https://seller-debug-loss-reporting.com/?reason=${rejectReason}');
forDebuggingOnly.reportAdAuctionWin(
'https://seller-debug-win-reporting.com/?reason=${rejectReason}');
if (bid == 1) {
return {desirability: -1, rejectReason: 'invalid-bid'}
} else {
return bid;
}
}
// Prevent an error about this method not existing.
function reportResult() {}
)";
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kBidder1Url,
kBidder1Script);
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kBidder2Url,
kBidder2Script);
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
kSellerScript);
const Result& res = RunStandardAuction();
// Bidder 2 won the auction.
EXPECT_EQ(InterestGroupKey(kBidder2, kBidder2Name), result_.winning_group_id);
EXPECT_EQ(GURL("https://ad2.com/"), res.ad_url);
// Only bidder's debug loss report supports macro ${rejectReason}.
EXPECT_EQ(2u, res.debug_loss_report_urls.size());
EXPECT_THAT(
res.debug_loss_report_urls,
testing::UnorderedElementsAre(
"https://bidder1-debug-loss-reporting.com/?reason=invalid-bid",
"https://seller-debug-loss-reporting.com/"
"?reason=${rejectReason}"));
EXPECT_EQ(2u, res.debug_win_report_urls.size());
EXPECT_THAT(
res.debug_win_report_urls,
testing::UnorderedElementsAre("https://bidder2-debug-win-reporting.com/"
"?reason=${rejectReason}",
"https://seller-debug-win-reporting.com/"
"?reason=${rejectReason}"));
}
// When scoreAd() does not return a reject reason, report it as "not-available"
// in bidder's loss report URL as default.
TEST_F(AuctionRunnerBiddingAndScoringDebugReportingAPIEnabledTest,
SellerNotReturningRejectReason) {
const char kBidderScript[] = R"(
function generateBid(interestGroup, auctionSignals, perBuyerSignals,
trustedBiddingSignals, browserSignals) {
forDebuggingOnly.reportAdAuctionLoss(
'https://bidder-debug-loss-reporting.com/?reason=${rejectReason}');
return {
bid: 1,
render: interestGroup.ads[0].renderUrl
};
}
// Prevent an error about this method not existing.
function reportWin() {}
)";
const char kSellerScript[] = R"(
function scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals,
browserSignals) {
return {desirability: -1};
}
// Prevent an error about this method not existing.
function reportResult() {}
)";
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kBidder1Url,
kBidderScript);
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
kSellerScript);
const Result& res = RunStandardAuction();
EXPECT_EQ(1u, res.debug_loss_report_urls.size());
EXPECT_THAT(
res.debug_loss_report_urls,
testing::UnorderedElementsAre("https://bidder-debug-loss-reporting.com/"
"?reason=not-available"));
EXPECT_EQ(0u, res.debug_win_report_urls.size());
}
// Disable private aggregation API.
class AuctionRunnerPrivateAggregationAPIDisabledTest
: public AuctionRunnerTest {
public:

@ -410,13 +410,15 @@ class InterestGroupAuction::BuyerHelper
continue;
}
if (bid_state->bidder_debug_loss_report_url.has_value()) {
// Losing bidders should not get highest_scoring_other_bid and
// made_highest_scoring_other_bid signals.
// Losing and rejected bidders should not get highest_scoring_other_bid
// and made_highest_scoring_other_bid signals.
debug_loss_report_urls.emplace_back(FillPostAuctionSignals(
std::move(bid_state->bidder_debug_loss_report_url).value(),
PostAuctionSignals(signals.winning_bid, signals.made_winning_bid,
0.0, false)));
0.0, false),
/*top_level_signals=*/absl::nullopt, bid_state->reject_reason));
}
// TODO(qingxinwu): Add reject reason to seller debug loss report as well.
if (bid_state->seller_debug_loss_report_url.has_value()) {
debug_loss_report_urls.emplace_back(FillPostAuctionSignals(
std::move(bid_state->seller_debug_loss_report_url).value(), signals,
@ -762,10 +764,10 @@ class InterestGroupAuction::BuyerHelper
maybe_bidding_signals_data_version,
debug_loss_report_url, debug_win_report_url);
if (bid)
state->bidder_debug_loss_report_url = std::move(debug_loss_report_url);
state->bidder_debug_loss_report_url = debug_loss_report_url;
} else {
// Bidders who do not bid are allowed to get loss report.
state->bidder_debug_loss_report_url = std::move(debug_loss_report_url);
state->bidder_debug_loss_report_url = debug_loss_report_url;
}
// Release the worklet. If it wins the auction, it will be requested again
@ -775,7 +777,7 @@ class InterestGroupAuction::BuyerHelper
if (!bid) {
state->EndTracing();
} else {
state->bidder_debug_win_report_url = std::move(debug_win_report_url);
state->bidder_debug_win_report_url = debug_win_report_url;
state->made_bid = true;
auction_->ScoreBidIfReady(std::move(bid));
}
@ -1180,10 +1182,43 @@ void InterestGroupAuction::GetInterestGroupsThatBid(
}
}
base::StringPiece GetRejectReasonString(
const auction_worklet::mojom::RejectReason reject_reason) {
base::StringPiece reject_reason_str;
switch (reject_reason) {
case auction_worklet::mojom::RejectReason::kNotAvailable:
reject_reason_str = "not-available";
break;
case auction_worklet::mojom::RejectReason::kInvalidBid:
reject_reason_str = "invalid-bid";
break;
case auction_worklet::mojom::RejectReason::kBidBelowAuctionFloor:
reject_reason_str = "bid-below-auction-floor";
break;
case auction_worklet::mojom::RejectReason::kPendingApprovalByExchange:
reject_reason_str = "pending-approval-by-exchange";
break;
case auction_worklet::mojom::RejectReason::kDisapprovedByExchange:
reject_reason_str = "disapproved-by-exchange";
break;
case auction_worklet::mojom::RejectReason::kBlockedByPublisher:
reject_reason_str = "blocked-by-publisher";
break;
case auction_worklet::mojom::RejectReason::kLanguageExclusions:
reject_reason_str = "language-exclusions";
break;
case auction_worklet::mojom::RejectReason::kCategoryExclusions:
reject_reason_str = "category-exclusions";
break;
}
return reject_reason_str;
}
GURL InterestGroupAuction::FillPostAuctionSignals(
const GURL& url,
const PostAuctionSignals& signals,
const absl::optional<PostAuctionSignals>& top_level_signals) {
const absl::optional<PostAuctionSignals>& top_level_signals,
const absl::optional<auction_worklet::mojom::RejectReason> reject_reason) {
// TODO(qingxinwu): Round `winning_bid` and `highest_scoring_other_bid` to two
// most-significant digits. Maybe same to corresponding browser signals of
// reportWin()/reportResult().
@ -1217,6 +1252,12 @@ GURL InterestGroupAuction::FillPostAuctionSignals(
top_level_signals->made_winning_bid ? "true" : "false");
}
if (reject_reason.has_value()) {
base::ReplaceSubstringsAfterOffset(
&query_string, 0, "${rejectReason}",
GetRejectReasonString(reject_reason.value()));
}
GURL::Replacements replacements;
replacements.SetQueryStr(query_string);
return url.ReplaceComponents(replacements);
@ -1671,6 +1712,7 @@ bool InterestGroupAuction::ValidateScoreBidCompleteResult(
void InterestGroupAuction::OnScoreAdComplete(
double score,
auction_worklet::mojom::RejectReason reject_reason,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t data_version,
@ -1719,6 +1761,9 @@ void InterestGroupAuction::OnScoreAdComplete(
std::move(debug_loss_report_url);
bid->bid_state->seller_debug_win_report_url =
std::move(debug_win_report_url);
// Ignores reject reason if score > 0.
if (score <= 0)
bid->bid_state->reject_reason = reject_reason;
} else {
bid->bid_state->top_level_seller_debug_loss_report_url =
std::move(debug_loss_report_url);

@ -243,6 +243,11 @@ class CONTENT_EXPORT InterestGroupAuction
// auction, won it, and was then scored by the top-level seller.
absl::optional<GURL> top_level_seller_debug_win_report_url;
absl::optional<GURL> top_level_seller_debug_loss_report_url;
// The reason this bid was rejected by the auction (i.e., reason why score
// was non-positive).
auction_worklet::mojom::RejectReason reject_reason =
auction_worklet::mojom::RejectReason::kNotAvailable;
};
// Result of generated a bid. Contains information that needs to score a bid
@ -541,6 +546,7 @@ class CONTENT_EXPORT InterestGroupAuction
// auction_worklet::mojom::ScoreAdClient implementation:
void OnScoreAdComplete(
double score,
auction_worklet::mojom::RejectReason reject_reason,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
@ -638,10 +644,13 @@ class CONTENT_EXPORT InterestGroupAuction
// Replaces `${}` placeholders in a debug report URL's query string for post
// auction signals if exist. Only replaces unescaped placeholder ${}, but
// not escaped placeholder (i.e., %24%7B%7D).
static GURL FillPostAuctionSignals(const GURL& url,
const PostAuctionSignals& signals,
const absl::optional<PostAuctionSignals>&
top_level_signals = absl::nullopt);
static GURL FillPostAuctionSignals(
const GURL& url,
const PostAuctionSignals& signals,
const absl::optional<PostAuctionSignals>& top_level_signals =
absl::nullopt,
const absl::optional<auction_worklet::mojom::RejectReason> reject_reason =
absl::nullopt);
// Tracing ID associated with the Auction. A nestable
// async "Auction" trace

@ -54,6 +54,18 @@ struct ComponentAuctionReportResultParams {
bool has_modified_bid;
};
// The reason a bid was rejected by an auction.
enum RejectReason {
kNotAvailable = 0,
kInvalidBid = 1,
kBidBelowAuctionFloor = 2,
kPendingApprovalByExchange = 3,
kDisapprovedByExchange = 4,
kBlockedByPublisher = 5,
kLanguageExclusions = 6,
kCategoryExclusions = 7,
};
// Interface for returning ScoreAd results. The advantage of having an interface
// is that it makes ScoreAd() calls cancellable, and allows callbacks passed
// over the Mojo pipe to be deleted when the Mojo pipe is, to avoid setting off
@ -66,6 +78,10 @@ interface ScoreAdClient {
// of 0 indicates either an error running the script, or that the script
// indicated the bid should not be used.
//
// `reject_reason` The reason this bid was rejected by the auction (i.e., the
// reason why `score` was non-positive). Default to kNotAvailable if not set.
// Will be ignored if `score` is positive.
//
// `component_auction_modified_bid_params` If this is a component seller
// worklet, contains parameters to pass to the top-level seller worklet
// in place of values from the original bidder worklet's BidderWorkletBid.
@ -96,6 +112,7 @@ interface ScoreAdClient {
// empty if `score` is positive, nor should it be assumed to be non-empty if
// `score` is 0.
OnScoreAdComplete(double score,
RejectReason reject_reason,
ComponentAuctionModifiedBidParams?
component_auction_modified_bid_params,
uint32 scoring_signals_data_version,
@ -160,7 +177,7 @@ interface SellerWorklet {
// to use with tracing calls.
//
// `score_ad_client` When the ScoreAd completes, successfully or not, its
// OnScoreAdComplete() method will invoked be invoked with the results.
// OnScoreAdComplete() method will be invoked with the results.
ScoreAd(string ad_metadata_json,
double bid,
blink.mojom.AuctionAdConfigNonSharedParams

@ -6,6 +6,7 @@
#include <stdint.h>
#include <cmath>
#include <list>
#include <memory>
#include <string>
@ -261,6 +262,30 @@ bool AddOtherSeller(
browser_signals_other_seller->get_component_seller().Serialize());
}
// Converts reject reason string to corresponding mojom enum.
absl::optional<mojom::RejectReason> RejectReasonStringToEnum(
const std::string& reason) {
if (reason == "not-available") {
return mojom::RejectReason::kNotAvailable;
} else if (reason == "invalid-bid") {
return mojom::RejectReason::kInvalidBid;
} else if (reason == "bid-below-auction-floor") {
return mojom::RejectReason::kBidBelowAuctionFloor;
} else if (reason == "pending-approval-by-exchange") {
return mojom::RejectReason::kPendingApprovalByExchange;
} else if (reason == "disapproved-by-exchange") {
return mojom::RejectReason::kDisapprovedByExchange;
} else if (reason == "blocked-by-publisher") {
return mojom::RejectReason::kBlockedByPublisher;
} else if (reason == "language-exclusions") {
return mojom::RejectReason::kLanguageExclusions;
} else if (reason == "category-exclusions") {
return mojom::RejectReason::kCategoryExclusions;
}
// Invalid (out of range) reject reason.
return absl::nullopt;
}
} // namespace
SellerWorklet::SellerWorklet(
@ -595,6 +620,7 @@ void SellerWorklet::V8State::ScoreAd(
// `scoreAd()` might use them to detect script timeout or failures.
PostScoreAdCallbackToUserThread(
std::move(callback), /*score=*/0,
/*reject_reason=*/mojom::RejectReason::kNotAvailable,
/*component_auction_modified_bid_params=*/nullptr,
/*scoring_signals_data_version=*/absl::nullopt,
/*debug_loss_report_url=*/
@ -607,13 +633,14 @@ void SellerWorklet::V8State::ScoreAd(
}
double score;
mojom::RejectReason reject_reason = mojom::RejectReason::kNotAvailable;
bool allow_component_auction = false;
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params;
// Try to parse the result as a number. On success, it's the desirability
// score.
if (!gin::ConvertFromV8(isolate, score_ad_result, &score)) {
// Otherwise, try it must be an object with the desireability score, and
// Otherwise, it must be an object with the desireability score, and
// potentially other fields as well.
if (!score_ad_result->IsObject()) {
errors_out.push_back(
@ -642,6 +669,30 @@ void SellerWorklet::V8State::ScoreAd(
if (!result_dict.Get("allowComponentAuction", &allow_component_auction))
allow_component_auction = false;
v8::Local<v8::Value> reject_reason_value;
if (score_ad_object
->Get(context, v8_helper_->CreateStringFromLiteral("rejectReason"))
.ToLocal(&reject_reason_value) &&
!reject_reason_value->IsUndefined()) {
if (!reject_reason_value->IsString()) {
errors_out.push_back(base::StrCat(
{decision_logic_url_.spec(),
" rejectReason returned by scoreAd() must be a string."}));
} else {
std::string reject_reason_str;
result_dict.Get("rejectReason", &reject_reason_str);
auto reject_reason_opt = RejectReasonStringToEnum(reject_reason_str);
if (!reject_reason_opt.has_value()) {
errors_out.push_back(
base::StrCat({decision_logic_url_.spec(),
" scoreAd() returned an invalid reject reason."}));
} else {
reject_reason = reject_reason_opt.value();
}
}
}
// If this is the seller in a component auction (and thus it was passed a
// top-level seller), need to return a
// mojom::ComponentAuctionModifiedBidParams.
@ -695,7 +746,7 @@ void SellerWorklet::V8State::ScoreAd(
// Keep debug report URLs because we want to send debug loss reports if
// seller rejected all bids.
PostScoreAdCallbackToUserThread(
std::move(callback), /*score=*/0,
std::move(callback), /*score=*/0, reject_reason,
/*component_auction_modified_bid_params=*/nullptr,
scoring_signals_data_version,
context_recycler.for_debugging_only_bindings()->TakeLossReportUrl(),
@ -726,6 +777,7 @@ void SellerWorklet::V8State::ScoreAd(
PostScoreAdCallbackToUserThread(
std::move(callback), score,
/*reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(component_auction_modified_bid_params),
scoring_signals_data_version,
context_recycler.for_debugging_only_bindings()->TakeLossReportUrl(),
@ -904,6 +956,7 @@ void SellerWorklet::V8State::PostScoreAdCallbackToUserThreadOnError(
PrivateAggregationRequests pa_requests) {
PostScoreAdCallbackToUserThread(
std::move(callback), /*score=*/0,
/*reject_reason=*/mojom::RejectReason::kNotAvailable,
/*component_auction_modified_bid_params=*/nullptr,
/*scoring_signals_data_version=*/absl::nullopt,
/*debug_loss_report_url=*/absl::nullopt,
@ -914,6 +967,7 @@ void SellerWorklet::V8State::PostScoreAdCallbackToUserThreadOnError(
void SellerWorklet::V8State::PostScoreAdCallbackToUserThread(
ScoreAdCallbackInternal callback,
double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
absl::optional<uint32_t> scoring_signals_data_version,
@ -924,7 +978,7 @@ void SellerWorklet::V8State::PostScoreAdCallbackToUserThread(
DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
user_thread_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), score,
base::BindOnce(std::move(callback), score, reject_reason,
std::move(component_auction_modified_bid_params),
scoring_signals_data_version,
std::move(debug_loss_report_url),
@ -1081,6 +1135,7 @@ void SellerWorklet::ScoreAdIfReady(ScoreAdTaskList::iterator task) {
void SellerWorklet::DeliverScoreAdCallbackOnUserThread(
ScoreAdTaskList::iterator task,
double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
absl::optional<uint32_t> scoring_signals_data_version,
@ -1089,7 +1144,6 @@ void SellerWorklet::DeliverScoreAdCallbackOnUserThread(
PrivateAggregationRequests pa_requests,
std::vector<std::string> errors) {
DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
if (load_script_error_msg_)
errors.insert(errors.begin(), load_script_error_msg_.value());
if (task->trusted_scoring_signals_error_msg)
@ -1102,7 +1156,7 @@ void SellerWorklet::DeliverScoreAdCallbackOnUserThread(
// it does. Only useful if the SellerWorklet object is still in use, so
// unclear how useful it would be.
task->score_ad_client->OnScoreAdComplete(
score, std::move(component_auction_modified_bid_params),
score, reject_reason, std::move(component_auction_modified_bid_params),
scoring_signals_data_version.value_or(0),
scoring_signals_data_version.has_value(), debug_loss_report_url,
debug_win_report_url, std::move(pa_requests), std::move(errors));

@ -195,6 +195,7 @@ class CONTENT_EXPORT SellerWorklet : public mojom::SellerWorklet {
// V8State, and avoids having to make a copy of the errors vector.
using ScoreAdCallbackInternal = base::OnceCallback<void(
double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
absl::optional<uint32_t> scoring_signals_data_version,
@ -270,6 +271,7 @@ class CONTENT_EXPORT SellerWorklet : public mojom::SellerWorklet {
void PostScoreAdCallbackToUserThread(
ScoreAdCallbackInternal callback,
double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
absl::optional<uint32_t> scoring_signals_data_version,
@ -333,6 +335,7 @@ class CONTENT_EXPORT SellerWorklet : public mojom::SellerWorklet {
void DeliverScoreAdCallbackOnUserThread(
ScoreAdTaskList::iterator task,
double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
absl::optional<uint32_t> scoring_signals_data_version,

@ -47,7 +47,7 @@ namespace auction_worklet {
namespace {
using PrivateAggregationRequests =
std::vector<auction_worklet::mojom::PrivateAggregationRequestPtr>;
std::vector<mojom::PrivateAggregationRequestPtr>;
// Very short time used by some tests that want to wait until just before a
// timer triggers.
@ -106,16 +106,17 @@ std::string CreateReportToScript(const std::string& raw_return_value,
// A ScoreAdClient that takes a callback to call in OnScoreAdComplete().
class TestScoreAdClient : public mojom::ScoreAdClient {
public:
using ScoreAdCompleteCallback = base::OnceCallback<void(
double score,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
bool has_scoring_signals_data_version,
const absl::optional<GURL>& debug_loss_report_url,
const absl::optional<GURL>& debug_win_report_url,
PrivateAggregationRequests pa_requests,
const std::vector<std::string>& errors)>;
using ScoreAdCompleteCallback =
base::OnceCallback<void(double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
bool has_scoring_signals_data_version,
const absl::optional<GURL>& debug_loss_report_url,
const absl::optional<GURL>& debug_win_report_url,
PrivateAggregationRequests pa_requests,
const std::vector<std::string>& errors)>;
explicit TestScoreAdClient(ScoreAdCompleteCallback score_ad_complete_callback)
: score_ad_complete_callback_(std::move(score_ad_complete_callback)) {}
@ -133,25 +134,26 @@ class TestScoreAdClient : public mojom::ScoreAdClient {
}
// mojom::ScoreAdClient implementation:
void OnScoreAdComplete(
double score,
auction_worklet::mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
bool has_scoring_signals_data_version,
const absl::optional<GURL>& debug_loss_report_url,
const absl::optional<GURL>& debug_win_report_url,
PrivateAggregationRequests pa_requests,
const std::vector<std::string>& errors) override {
void OnScoreAdComplete(double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
bool has_scoring_signals_data_version,
const absl::optional<GURL>& debug_loss_report_url,
const absl::optional<GURL>& debug_win_report_url,
PrivateAggregationRequests pa_requests,
const std::vector<std::string>& errors) override {
std::move(score_ad_complete_callback_)
.Run(score, std::move(component_auction_modified_bid_params),
.Run(score, reject_reason,
std::move(component_auction_modified_bid_params),
scoring_signals_data_version, has_scoring_signals_data_version,
debug_loss_report_url, debug_win_report_url,
std::move(pa_requests), errors);
}
static ScoreAdCompleteCallback ScoreAdNeverInvokedCallback() {
return base::BindOnce([](double score,
return base::BindOnce([](double score, mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
@ -241,12 +243,15 @@ class SellerWorkletTest : public testing::Test {
const absl::optional<GURL>& expected_debug_loss_report_url =
absl::nullopt,
const absl::optional<GURL>& expected_debug_win_report_url = absl::nullopt,
mojom::RejectReason expected_reject_reason =
mojom::RejectReason::kNotAvailable,
PrivateAggregationRequests expected_pa_requests = {}) {
RunScoreAdWithJavascriptExpectingResult(
CreateScoreAdScript(raw_return_value), expected_score, expected_errors,
std::move(expected_component_auction_modified_bid_params),
expected_data_version, expected_debug_loss_report_url,
expected_debug_win_report_url, std::move(expected_pa_requests));
expected_debug_win_report_url, expected_reject_reason,
std::move(expected_pa_requests));
}
// Behaves just like RunScoreAdWithReturnValueExpectingResult(), but
@ -267,6 +272,8 @@ class SellerWorkletTest : public testing::Test {
const absl::optional<GURL>& expected_debug_loss_report_url =
absl::nullopt,
const absl::optional<GURL>& expected_debug_win_report_url = absl::nullopt,
mojom::RejectReason expected_reject_reason =
mojom::RejectReason::kNotAvailable,
PrivateAggregationRequests expected_pa_requests = {}) {
AddJavascriptResponse(&url_loader_factory_, decision_logic_url_,
CreateScoreAdScript(raw_return_value));
@ -277,8 +284,8 @@ class SellerWorkletTest : public testing::Test {
seller_worklet.get(), expected_score, expected_errors,
std::move(expected_component_auction_modified_bid_params),
expected_data_version, expected_debug_loss_report_url,
expected_debug_win_report_url, std::move(expected_pa_requests),
run_loop.QuitClosure());
expected_debug_win_report_url, expected_reject_reason,
std::move(expected_pa_requests), run_loop.QuitClosure());
task_environment_.FastForwardBy(expected_duration - kTinyTime);
EXPECT_FALSE(run_loop.AnyQuitCalled());
task_environment_.FastForwardBy(kTinyTime);
@ -299,6 +306,8 @@ class SellerWorkletTest : public testing::Test {
const absl::optional<GURL>& expected_debug_loss_report_url =
absl::nullopt,
const absl::optional<GURL>& expected_debug_win_report_url = absl::nullopt,
mojom::RejectReason expected_reject_reason =
mojom::RejectReason::kNotAvailable,
PrivateAggregationRequests expected_pa_requests = {}) {
SCOPED_TRACE(javascript);
AddJavascriptResponse(&url_loader_factory_, decision_logic_url_,
@ -307,7 +316,8 @@ class SellerWorkletTest : public testing::Test {
expected_score, expected_errors,
std::move(expected_component_auction_modified_bid_params),
expected_data_version, expected_debug_loss_report_url,
expected_debug_win_report_url, std::move(expected_pa_requests));
expected_debug_win_report_url, expected_reject_reason,
std::move(expected_pa_requests));
}
// Runs score_ad() script, checking result and invoking provided closure
@ -321,6 +331,7 @@ class SellerWorkletTest : public testing::Test {
absl::optional<uint32_t> expected_data_version,
const absl::optional<GURL>& expected_debug_loss_report_url,
const absl::optional<GURL>& expected_debug_win_report_url,
mojom::RejectReason expected_reject_reason,
PrivateAggregationRequests expected_pa_requests,
base::OnceClosure done_closure) {
seller_worklet->ScoreAd(
@ -332,6 +343,7 @@ class SellerWorkletTest : public testing::Test {
/*trace_id=*/1,
TestScoreAdClient::Create(base::BindOnce(
[](double expected_score,
mojom::RejectReason expected_reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
expected_component_auction_modified_bid_params,
absl::optional<uint32_t> expected_data_version,
@ -340,6 +352,7 @@ class SellerWorkletTest : public testing::Test {
PrivateAggregationRequests expected_pa_requests,
std::vector<std::string> expected_errors,
base::OnceClosure done_closure, double score,
mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t data_version, bool has_data_version,
@ -351,6 +364,8 @@ class SellerWorkletTest : public testing::Test {
if (has_data_version)
maybe_data_version = data_version;
EXPECT_EQ(expected_score, score);
EXPECT_EQ(static_cast<int>(expected_reject_reason),
static_cast<int>(reject_reason));
EXPECT_EQ(
expected_component_auction_modified_bid_params.is_null(),
component_auction_modified_bid_params.is_null());
@ -373,7 +388,7 @@ class SellerWorkletTest : public testing::Test {
EXPECT_EQ(expected_errors, errors);
std::move(done_closure).Run();
},
expected_score,
expected_score, expected_reject_reason,
std::move(expected_component_auction_modified_bid_params),
expected_data_version, expected_debug_loss_report_url,
expected_debug_win_report_url, std::move(expected_pa_requests),
@ -406,14 +421,16 @@ class SellerWorkletTest : public testing::Test {
const absl::optional<GURL>& expected_debug_loss_report_url =
absl::nullopt,
const absl::optional<GURL>& expected_debug_win_report_url = absl::nullopt,
mojom::RejectReason expected_reject_reason =
mojom::RejectReason::kNotAvailable,
PrivateAggregationRequests expected_pa_requests = {}) {
base::RunLoop run_loop;
RunScoreAdOnWorkletAsync(
seller_worklet, expected_score, expected_errors,
std::move(expected_component_auction_modified_bid_params),
expected_data_version, expected_debug_loss_report_url,
expected_debug_win_report_url, std::move(expected_pa_requests),
run_loop.QuitClosure());
expected_debug_win_report_url, expected_reject_reason,
std::move(expected_pa_requests), run_loop.QuitClosure());
run_loop.Run();
}
@ -429,6 +446,8 @@ class SellerWorkletTest : public testing::Test {
const absl::optional<GURL>& expected_debug_loss_report_url =
absl::nullopt,
const absl::optional<GURL>& expected_debug_win_report_url = absl::nullopt,
mojom::RejectReason expected_reject_reason =
mojom::RejectReason::kNotAvailable,
PrivateAggregationRequests expected_pa_requests = {}) {
auto seller_worklet = CreateWorklet();
ASSERT_TRUE(seller_worklet);
@ -436,7 +455,8 @@ class SellerWorkletTest : public testing::Test {
seller_worklet.get(), expected_score, expected_errors,
std::move(expected_component_auction_modified_bid_params),
expected_data_version, expected_debug_loss_report_url,
expected_debug_win_report_url, std::move(expected_pa_requests));
expected_debug_win_report_url, expected_reject_reason,
std::move(expected_pa_requests));
}
// Configures `url_loader_factory_` to return a report_result() script created
@ -658,7 +678,7 @@ class SellerWorkletTest : public testing::Test {
uint32_t browser_signal_bidding_duration_msecs_;
double browser_signal_desireability_;
double browser_signal_highest_scoring_other_bid_;
auction_worklet::mojom::ComponentAuctionReportResultParamsPtr
mojom::ComponentAuctionReportResultParamsPtr
browser_signals_component_auction_report_result_params_;
absl::optional<uint32_t> browser_signal_data_version_;
absl::optional<base::TimeDelta> seller_timeout_;
@ -901,6 +921,80 @@ TEST_F(SellerWorkletTest, ScoreAdAd) {
/*ad=*/"[[35]]", /*bid=*/0, /*has_bid=*/false));
}
// Test the `rejectReason` output field of scoreAd().
TEST_F(SellerWorkletTest, ScoreAdRejectReason) {
const struct {
std::string reason_str;
mojom::RejectReason reason_enum;
} kTestCases[] = {
{"not-available", mojom::RejectReason::kNotAvailable},
{"invalid-bid", mojom::RejectReason::kInvalidBid},
{"bid-below-auction-floor", mojom::RejectReason::kBidBelowAuctionFloor},
{"pending-approval-by-exchange",
mojom::RejectReason::kPendingApprovalByExchange},
{"disapproved-by-exchange", mojom::RejectReason::kDisapprovedByExchange},
{"blocked-by-publisher", mojom::RejectReason::kBlockedByPublisher},
{"language-exclusions", mojom::RejectReason::kLanguageExclusions},
{"category-exclusions", mojom::RejectReason::kCategoryExclusions},
};
for (const auto& test_case : kTestCases) {
RunScoreAdWithReturnValueExpectingResult(
base::StringPrintf(R"({desirability:-1, rejectReason: '%s'})",
test_case.reason_str.c_str()),
0,
/*expected_errors=*/{}, mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt, test_case.reason_enum);
}
// Default reject reason is mojom::RejectReason::kNotAvailable, if scoreAd()
// does not return one.
RunScoreAdWithReturnValueExpectingResult(
"{desirability:-1}", 0,
/*expected_errors=*/{}, mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
mojom::RejectReason::kNotAvailable);
// Reject reason is ignored when desirability is positive.
RunScoreAdWithReturnValueExpectingResult(
"{desirability:3, rejectReason: 'invalid-bid'}", 3,
/*expected_errors=*/{}, mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason*/ mojom::RejectReason::kNotAvailable);
}
// Invalid `rejectReason` output of scoreAd() results in error.
TEST_F(SellerWorkletTest, ScoreAdInvalidRejectReason) {
// Reject reason string returned by seller script is case sensitive.
RunScoreAdWithReturnValueExpectingResult(
"{desirability:-1, rejectReason: 'INVALID-BID'}", 0,
/*expected_errors=*/
{"https://url.test/ scoreAd() returned an invalid reject reason."},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason*/ mojom::RejectReason::kNotAvailable);
// Reject reason returned by seller script must be a string.
RunScoreAdWithReturnValueExpectingResult(
"{desirability:-1, rejectReason: 2}", 0,
/*expected_errors=*/
{"https://url.test/ rejectReason returned by scoreAd() must be a "
"string."},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason*/ mojom::RejectReason::kNotAvailable);
}
// Test the `bid` output field of scoreAd().
TEST_F(SellerWorkletTest, ScoreAdModifiesBid) {
// When `browser_signals_other_seller_` is not top a top-level auction (i.e.
@ -1296,6 +1390,8 @@ TEST_F(SellerWorkletTest, ScoreAdParallelBeforeLoadComplete) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
base::BindLambdaForTesting([&]() {
++num_completed_worklets;
@ -1338,6 +1434,8 @@ TEST_F(SellerWorkletTest, ScoreAdParallelAfterLoadComplete) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
base::BindLambdaForTesting([&]() {
++num_completed_worklets;
@ -1402,6 +1500,8 @@ TEST_F(SellerWorkletTest, ScoreAdParallelTrustedScoringSignalsNotBatched) {
/*expected_data_version=*/i,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
base::BindLambdaForTesting([&]() {
++num_completed_worklets;
@ -1469,6 +1569,8 @@ TEST_F(SellerWorkletTest, ScoreAdParallelTrustedScoringSignalsBatched1) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
base::BindLambdaForTesting([&]() {
++num_completed_worklets;
@ -1527,6 +1629,8 @@ TEST_F(SellerWorkletTest, ScoreAdParallelTrustedScoringSignalsBatched2) {
/*expected_data_version=*/10,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
base::BindLambdaForTesting([&]() {
++num_completed_worklets;
@ -1594,6 +1698,8 @@ TEST_F(SellerWorkletTest, ScoreAdParallelTrustedScoringSignalsBatched3) {
/*expected_data_version=*/10,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
base::BindLambdaForTesting([&]() {
++num_completed_worklets;
@ -1849,7 +1955,7 @@ TEST_F(SellerWorkletTest, ReportResultTopLevelSeller) {
mojom::ComponentAuctionOtherSeller::NewTopLevelSeller(
url::Origin::Create(GURL("https://top.seller.test")));
browser_signals_component_auction_report_result_params_ =
auction_worklet::mojom::ComponentAuctionReportResultParams::New(
mojom::ComponentAuctionReportResultParams::New(
/*top_level_seller_signals=*/"null",
/*modified_bid=*/0,
/*has_modified_bid=*/false);
@ -1879,7 +1985,7 @@ TEST_F(SellerWorkletTest, ReportResultComponentSeller) {
mojom::ComponentAuctionOtherSeller::NewTopLevelSeller(
url::Origin::Create(GURL("https://top.seller.test")));
browser_signals_component_auction_report_result_params_ =
auction_worklet::mojom::ComponentAuctionReportResultParams::New(
mojom::ComponentAuctionReportResultParams::New(
/*top_level_seller_signals=*/"null",
/*modified_bid=*/0,
/*has_modified_bid=*/false);
@ -1923,7 +2029,7 @@ TEST_F(SellerWorkletTest, ReportResultTopLevelSellerSignals) {
mojom::ComponentAuctionOtherSeller::NewTopLevelSeller(
url::Origin::Create(GURL("https://top.seller.test")));
browser_signals_component_auction_report_result_params_ =
auction_worklet::mojom::ComponentAuctionReportResultParams::New(
mojom::ComponentAuctionReportResultParams::New(
/*top_level_seller_signals=*/"null",
/*modified_bid=*/0,
/*has_modified_bid=*/false);
@ -1965,7 +2071,7 @@ TEST_F(SellerWorkletTest, ReportResultModifiedBid) {
mojom::ComponentAuctionOtherSeller::NewTopLevelSeller(
url::Origin::Create(GURL("https://top.seller.test")));
browser_signals_component_auction_report_result_params_ =
auction_worklet::mojom::ComponentAuctionReportResultParams::New(
mojom::ComponentAuctionReportResultParams::New(
/*top_level_seller_signals=*/"null",
/*modified_bid=*/4,
/*has_modified_bid=*/true);
@ -2365,7 +2471,7 @@ TEST_F(SellerWorkletTest, ScriptIsolation) {
seller_timeout_,
/*trace_id=*/1,
TestScoreAdClient::Create(base::BindLambdaForTesting(
[&run_loop](double score,
[&run_loop](double score, mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
@ -2483,6 +2589,8 @@ TEST_F(SellerWorkletTest, PauseOnStart) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, run_loop.QuitClosure());
// Give it a chance to fetch.
@ -2565,6 +2673,8 @@ TEST_F(SellerWorkletTest, BasicV8Debug) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, run_loop1.QuitClosure());
decision_logic_url_ = kUrl2;
@ -2578,6 +2688,8 @@ TEST_F(SellerWorkletTest, BasicV8Debug) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, run_loop2.QuitClosure());
int id1 = worklet_impl1->context_group_id_for_testing();
@ -2693,22 +2805,27 @@ TEST_F(SellerWorkletTest, BasicDevToolsDebug) {
auto worklet1 = CreateWorklet(/*pause_for_debugger_on_start=*/true);
base::RunLoop run_loop1;
RunScoreAdOnWorkletAsync(
worklet1.get(), 100.5, {}, mojom::ComponentAuctionModifiedBidParamsPtr(),
worklet1.get(), /*expected_score=*/100.5, /*expected_errors=*/{},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, run_loop1.QuitClosure());
decision_logic_url_ = GURL(kUrl2);
auto worklet2 = CreateWorklet(/*pause_for_debugger_on_start=*/true);
base::RunLoop run_loop2;
RunScoreAdOnWorkletAsync(worklet2.get(), 0,
RunScoreAdOnWorkletAsync(worklet2.get(), /*expected_score=*/0,
{"http://example.org/second.js scoreAd() did not "
"return an object or a number."},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{},
run_loop2.QuitClosure());
@ -2848,11 +2965,14 @@ TEST_F(SellerWorkletTest, InstrumentationBreakpoints) {
decision_logic_url_ = GURL(kUrl);
auto worklet = CreateWorklet(/*pause_for_debugger_on_start=*/true);
base::RunLoop run_loop;
RunScoreAdOnWorkletAsync(worklet.get(), 1.0, {},
RunScoreAdOnWorkletAsync(worklet.get(), /*expected_score=*/1.0,
/*expected_errors=*/{},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, run_loop.QuitClosure());
mojo::AssociatedRemote<blink::mojom::DevToolsAgent> agent;
@ -2926,10 +3046,13 @@ TEST_F(SellerWorkletTest, InstrumentationBreakpoints) {
// remove it.
base::RunLoop run_loop3;
RunScoreAdOnWorkletAsync(
worklet.get(), 1.0, {}, mojom::ComponentAuctionModifiedBidParamsPtr(),
worklet.get(), /*expected_score=*/1.0, /*expected_errors=*/{},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, run_loop3.QuitClosure());
TestDevToolsAgentClient::Event breakpoint_hit3 =
@ -2988,10 +3111,13 @@ TEST_F(SellerWorkletTest, UnloadWhilePaused) {
R"({"id":4,"method":"Runtime.runIfWaitingForDebugger","params":{}})");
RunScoreAdOnWorkletAsync(
worklet.get(), 1.0, {}, mojom::ComponentAuctionModifiedBidParamsPtr(),
worklet.get(), /*expected_score=*/1.0, /*expected_errors=*/{},
mojom::ComponentAuctionModifiedBidParamsPtr(),
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/
mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{}, base::BindOnce([]() {
ADD_FAILURE() << "scoreAd shouldn't actually get to finish.";
}));
@ -3399,7 +3525,7 @@ TEST_F(SellerWorkletBiddingAndScoringDebugReportingAPIEnabledTest,
seller_timeout_,
/*trace_id=*/1,
TestScoreAdClient::Create(base::BindLambdaForTesting(
[&run_loop](double score,
[&run_loop](double score, mojom::RejectReason reject_reason,
mojom::ComponentAuctionModifiedBidParamsPtr
component_auction_modified_bid_params,
uint32_t scoring_signals_data_version,
@ -3436,15 +3562,15 @@ class SellerWorkletPrivateAggregationEnabledTest : public SellerWorkletTest {
};
TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest1 =
auction_worklet::mojom::PrivateAggregationRequest::New(
mojom::PrivateAggregationRequestPtr kExpectedRequest1 =
mojom::PrivateAggregationRequest::New(
content::mojom::AggregatableReportHistogramContribution::New(
/*bucket=*/123,
/*value=*/45),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New());
auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
auction_worklet::mojom::PrivateAggregationRequest::New(
mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
mojom::PrivateAggregationRequest::New(
content::mojom::AggregatableReportHistogramContribution::New(
/*bucket=*/absl::MakeInt128(/*high=*/1, /*low=*/0),
/*value=*/1),
@ -3464,6 +3590,7 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
@ -3481,6 +3608,7 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
@ -3498,6 +3626,7 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
@ -3518,6 +3647,7 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
@ -3537,18 +3667,18 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
// Debug mode enabled with debug key
{
PrivateAggregationRequests expected_pa_requests;
expected_pa_requests.push_back(
auction_worklet::mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
expected_pa_requests.push_back(mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
RunScoreAdWithJavascriptExpectingResult(
CreateScoreAdScript("5",
@ -3561,24 +3691,23 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
// Debug mode enabled without debug key, but with multiple requests
{
PrivateAggregationRequests expected_pa_requests;
expected_pa_requests.push_back(
auction_worklet::mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
expected_pa_requests.push_back(
auction_worklet::mojom::PrivateAggregationRequest::New(
kExpectedRequest2->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
expected_pa_requests.push_back(mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
expected_pa_requests.push_back(mojom::PrivateAggregationRequest::New(
kExpectedRequest2->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
RunScoreAdWithJavascriptExpectingResult(
CreateScoreAdScript("5",
@ -3593,20 +3722,21 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
std::move(expected_pa_requests));
}
}
TEST_F(SellerWorkletPrivateAggregationEnabledTest, ReportResult) {
auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest1 =
auction_worklet::mojom::PrivateAggregationRequest::New(
mojom::PrivateAggregationRequestPtr kExpectedRequest1 =
mojom::PrivateAggregationRequest::New(
content::mojom::AggregatableReportHistogramContribution::New(
/*bucket=*/123,
/*value=*/45),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New());
auction_worklet::mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
auction_worklet::mojom::PrivateAggregationRequest::New(
mojom::PrivateAggregationRequestPtr kExpectedRequest2 =
mojom::PrivateAggregationRequest::New(
content::mojom::AggregatableReportHistogramContribution::New(
/*bucket=*/absl::MakeInt128(/*high=*/1, /*low=*/0),
/*value=*/1),
@ -3696,12 +3826,11 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ReportResult) {
// Debug mode enabled with debug key
{
PrivateAggregationRequests expected_pa_requests;
expected_pa_requests.push_back(
auction_worklet::mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
expected_pa_requests.push_back(mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, content::mojom::DebugKey::New(1234u))));
RunReportResultCreatedScriptExpectingResult(
"5",
@ -3718,18 +3847,16 @@ TEST_F(SellerWorkletPrivateAggregationEnabledTest, ReportResult) {
// Debug mode enabled without debug key, but with multiple requests
{
PrivateAggregationRequests expected_pa_requests;
expected_pa_requests.push_back(
auction_worklet::mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
expected_pa_requests.push_back(
auction_worklet::mojom::PrivateAggregationRequest::New(
kExpectedRequest2->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
expected_pa_requests.push_back(mojom::PrivateAggregationRequest::New(
kExpectedRequest1->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
expected_pa_requests.push_back(mojom::PrivateAggregationRequest::New(
kExpectedRequest2->contribution->Clone(),
content::mojom::AggregationServiceMode::kDefault,
content::mojom::DebugModeDetails::New(
/*is_enabled=*/true, /*debug_key=*/nullptr)));
RunReportResultCreatedScriptExpectingResult(
"5",
@ -3784,6 +3911,7 @@ TEST_F(SellerWorkletPrivateAggregationDisabledTest, ScoreAd) {
/*expected_data_version=*/absl::nullopt,
/*expected_debug_loss_report_url=*/absl::nullopt,
/*expected_debug_win_report_url=*/absl::nullopt,
/*expected_reject_reason=*/mojom::RejectReason::kNotAvailable,
/*expected_pa_requests=*/{});
}