Skip to content

Commit f38fbdc

Browse files
Matt MenkeChromium LUCI CQ
Matt Menke
authored and
Chromium LUCI CQ
committed
[FLEDGE] Component auctions CL10: Add component reportResult() fields.
In FLEDGE, the reportResult() methods of nested component sellers need two additional parameters: The modified bid, if the component seller's scoreAd() method modified the bid, and the output of the top-level sellers reportResult() method. These fields are called modifiedBid and topLevelSellerSignals respectively, and appear in reportResult's browserSignals input. This CL adds support for both these fields. Bug: 1288865 Change-Id: I11afd961f1f5b684fb1a84037e4be8590bf40ac2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3499152 Reviewed-by: Maks Orlovich <[email protected]> Reviewed-by: Daniel Cheng <[email protected]> Commit-Queue: Matt Menke <[email protected]> Cr-Commit-Position: refs/heads/main@{#981985}
1 parent ada2066 commit f38fbdc

9 files changed

+367
-62
lines changed

content/browser/interest_group/auction_runner.cc

+40-23
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ void AuctionRunner::Auction::StartBiddingAndScoringPhase(
283283
}
284284

285285
void AuctionRunner::Auction::StartReportingPhase(
286+
absl::optional<std::string> top_seller_signals,
286287
AuctionPhaseCompletionCallback reporting_phase_callback) {
287288
DCHECK(reporting_phase_callback);
288289
DCHECK(!load_interest_groups_phase_callback_);
@@ -297,18 +298,21 @@ void AuctionRunner::Auction::StartReportingPhase(
297298
// reload the seller worklet in the case of a component auction.
298299
if (!seller_worklet_handle_) {
299300
DCHECK(parent_);
301+
DCHECK(top_seller_signals);
300302
if (!auction_worklet_manager_->RequestSellerWorklet(
301303
config_->decision_logic_url, config_->trusted_scoring_signals_url,
302-
base::BindOnce(&Auction::ReportSellerResult,
303-
base::Unretained(this)),
304+
base::BindOnce(&Auction::ReportSellerResult, base::Unretained(this),
305+
top_seller_signals),
304306
base::BindOnce(&Auction::OnWinningComponentSellerWorkletFatalError,
305307
base::Unretained(this)),
306308
seller_worklet_handle_)) {
307309
return;
308310
}
311+
} else {
312+
DCHECK(!parent_);
313+
DCHECK(!top_seller_signals);
309314
}
310-
311-
ReportSellerResult();
315+
ReportSellerResult(std::move(top_seller_signals));
312316
}
313317

314318
void AuctionRunner::Auction::ClosePipes() {
@@ -984,14 +988,30 @@ void AuctionRunner::Auction::OnBiddingAndScoringComplete(
984988
std::move(bidding_and_scoring_phase_callback_).Run(success);
985989
}
986990

987-
void AuctionRunner::Auction::ReportSellerResult() {
991+
void AuctionRunner::Auction::ReportSellerResult(
992+
absl::optional<std::string> top_seller_signals) {
988993
DCHECK(seller_worklet_handle_);
989994
DCHECK(reporting_phase_callback_);
990995

996+
auction_worklet::mojom::ComponentAuctionReportResultParamsPtr
997+
browser_signals_component_auction_report_result_params;
998+
if (parent_) {
999+
DCHECK(top_seller_signals);
1000+
DCHECK(top_bid_->component_auction_modified_bid_params);
1001+
browser_signals_component_auction_report_result_params =
1002+
auction_worklet::mojom::ComponentAuctionReportResultParams::New(
1003+
/*top_level_seller_signals=*/std::move(top_seller_signals).value(),
1004+
/*modified_bid=*/
1005+
top_bid_->component_auction_modified_bid_params->bid,
1006+
/*has_modified_bid=*/
1007+
top_bid_->component_auction_modified_bid_params->has_bid);
1008+
}
1009+
9911010
seller_worklet_handle_->GetSellerWorklet()->ReportResult(
9921011
config_->auction_ad_config_non_shared_params.Clone(),
9931012
GetOtherSellerParam(*top_bid_->bid), top_bid_->bid->interest_group->owner,
9941013
top_bid_->bid->render_url, top_bid_->bid->bid, top_bid_->score,
1014+
std::move(browser_signals_component_auction_report_result_params),
9951015
top_bid_->scoring_signals_data_version.value_or(0),
9961016
top_bid_->scoring_signals_data_version.has_value(),
9971017
base::BindOnce(&Auction::OnReportSellerResultComplete,
@@ -1023,21 +1043,28 @@ void AuctionRunner::Auction::OnReportSellerResultComplete(
10231043

10241044
errors_.insert(errors_.end(), errors.begin(), errors.end());
10251045

1046+
// Treat a null `signals_for_winner` value as a null JS response.
1047+
//
1048+
// TODO(mmenke): Consider making `signals_for_winner` itself non-optional, and
1049+
// clean this up.
1050+
std::string fixed_up_signals_for_winner = signals_for_winner.value_or("null");
1051+
10261052
// If a the winning bid is from a nested component auction, need to call into
10271053
// that Auction's report logic (which will invoke both that seller's
10281054
// ReportResult() method, and the bidder's ReportWin()).
10291055
if (top_bid_->bid->auction != this) {
10301056
top_bid_->bid->auction->StartReportingPhase(
1057+
std::move(fixed_up_signals_for_winner),
10311058
base::BindOnce(&Auction::OnComponentAuctionReportingPhaseComplete,
10321059
base::Unretained(this)));
10331060
return;
10341061
}
10351062

1036-
LoadBidderWorkletToReportBidWin(signals_for_winner);
1063+
LoadBidderWorkletToReportBidWin(std::move(fixed_up_signals_for_winner));
10371064
}
10381065

10391066
void AuctionRunner::Auction::LoadBidderWorkletToReportBidWin(
1040-
const absl::optional<std::string>& signals_for_winner) {
1067+
const std::string& signals_for_winner) {
10411068
// Worklet handle should have been destroyed once the bid was generated.
10421069
DCHECK(!top_bid_->bid->bid_state->worklet_handle);
10431070

@@ -1052,25 +1079,13 @@ void AuctionRunner::Auction::LoadBidderWorkletToReportBidWin(
10521079
}
10531080

10541081
void AuctionRunner::Auction::ReportBidWin(
1055-
const absl::optional<std::string>& signals_for_winner) {
1082+
const std::string& signals_for_winner) {
10561083
DCHECK(top_bid_);
10571084

1058-
std::string signals_for_winner_arg;
1059-
if (signals_for_winner) {
1060-
signals_for_winner_arg = *signals_for_winner;
1061-
} else {
1062-
// `signals_for_winner_arg` is passed as JSON, so need to pass "null" when
1063-
// it's not provided. Pass in "null" instead of making the API take an
1064-
// optional to limit the information provided to the untrusted BidderWorklet
1065-
// process that's not part of the FLEDGE API. Unlikely to matter, but best
1066-
// to be safe.
1067-
signals_for_winner_arg = "null";
1068-
}
1069-
10701085
top_bid_->bid->bid_state->worklet_handle->GetBidderWorklet()->ReportWin(
10711086
top_bid_->bid->interest_group->name,
10721087
config_->auction_ad_config_non_shared_params->auction_signals,
1073-
PerBuyerSignals(top_bid_->bid->bid_state), signals_for_winner_arg,
1088+
PerBuyerSignals(top_bid_->bid->bid_state), signals_for_winner,
10741089
top_bid_->bid->render_url, top_bid_->bid->bid, config_->seller,
10751090
parent_ ? parent_->config_->seller : absl::optional<url::Origin>(),
10761091
top_bid_->bid->bidding_signals_data_version.value_or(0),
@@ -1358,8 +1373,10 @@ void AuctionRunner::OnBidsGeneratedAndScored(bool success) {
13581373
return;
13591374
}
13601375

1361-
auction_.StartReportingPhase(base::BindOnce(
1362-
&AuctionRunner::OnReportingPhaseComplete, base::Unretained(this)));
1376+
auction_.StartReportingPhase(
1377+
/*top_seller_signals=*/absl::nullopt,
1378+
base::BindOnce(&AuctionRunner::OnReportingPhaseComplete,
1379+
base::Unretained(this)));
13631380
}
13641381

13651382
void AuctionRunner::OnReportingPhaseComplete(bool success) {

content/browser/interest_group/auction_runner.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,11 @@ class CONTENT_EXPORT AuctionRunner {
380380
// when all reporting URLs (if any) have been retrieved from the applicable
381381
// worklets. `success` is true if the final status of the auction is
382382
// `kSuccess`.
383+
//
384+
// If this is a component auction, `top_seller_signals` must populated and
385+
// be the output from the top-level seller's reportResult() method.
383386
void StartReportingPhase(
387+
absl::optional<std::string> top_seller_signals,
384388
AuctionPhaseCompletionCallback reporting_phase_callback);
385389

386390
// Close all Mojo pipes and release all weak pointers. Called when an
@@ -572,14 +576,13 @@ class CONTENT_EXPORT AuctionRunner {
572576
// Sequence of asynchronous methods to call into the seller and then bidder
573577
// worklet to report a a win. Will ultimately invoke
574578
// `reporting_phase_callback_`, which will delete the auction.
575-
void ReportSellerResult();
579+
void ReportSellerResult(absl::optional<std::string> top_seller_signals);
576580
void OnReportSellerResultComplete(
577581
const absl::optional<std::string>& signals_for_winner,
578582
const absl::optional<GURL>& seller_report_url,
579583
const std::vector<std::string>& error_msgs);
580-
void LoadBidderWorkletToReportBidWin(
581-
const absl::optional<std::string>& signals_for_winner);
582-
void ReportBidWin(const absl::optional<std::string>& signals_for_winner);
584+
void LoadBidderWorkletToReportBidWin(const std::string& signals_for_winner);
585+
void ReportBidWin(const std::string& signals_for_winner);
583586
void OnReportBidWinComplete(const absl::optional<GURL>& bidder_report_url,
584587
const std::vector<std::string>& error_msgs);
585588

content/browser/interest_group/auction_runner_unittest.cc

+125-18
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,24 @@ std::string MakeBidScript(const url::Origin& seller,
211211
throw new Error("wrong renderUrl");
212212
if (sellerSignals.bid !== bid)
213213
throw new Error("wrong bid");
214+
// `sellerSignals` is the `browserSignals` for the seller that's
215+
// associated with the bid. If it's the top-level seller, the seller's
216+
// `browserSignals` should have no `componentSeller`, since the bid
217+
// was made directly to the top-level seller. If it's the component
218+
// seller, the seller's `browserSignals` should have a `topLevelSeller`
219+
// instead of a `componentSeller`, so `componentSeller` should never
220+
// be present in `sellerSignals` here.
221+
if ("componentSeller" in sellerSignals)
222+
throw new Error("wrong componentSeller in sellerSignals");
223+
if (browserSignals.seller === topLevelSeller) {
224+
if ("topLevelSeller" in sellerSignals)
225+
throw new Error("wrong topLevelSeller in sellerSignals");
226+
} else {
227+
// If the seller is a component seller, then then the seller's
228+
// `browserSignals` should have the top-level seller.
229+
if (sellerSignals.topLevelSeller !== topLevelSeller)
230+
throw new Error("wrong topLevelSeller in browserSignals");
231+
}
214232
215233
if (browserSignals.topWindowHostname !== 'publisher1.com')
216234
throw new Error("wrong browserSignals.topWindowHostname");
@@ -393,25 +411,39 @@ std::string MakeDecisionScript(
393411
if (decisionLogicUrl.startsWith(topLevelSeller)) {
394412
// Top-level sellers should receive component sellers, but only for
395413
// bids received from component auctions.
396-
if ("topLevelSeller" in browserSignals)
397-
throw new Error("Expected no topLevelSeller in browserSignals.");
414+
if ("topLevelSeller" in browserSignals)
415+
throw new Error("Expected no topLevelSeller in browserSignals.");
398416
if (bidFromComponentAuctionWins) {
399417
if (!browserSignals.componentSeller.includes("component"))
400418
throw new Error("Incorrect componentSeller in browserSignals.");
401419
} else {
402420
if ("componentSeller" in browserSignals)
403421
throw new Error("Expected no componentSeller in browserSignals.");
404422
}
423+
424+
if ("topLevelSellerSignals" in browserSignals)
425+
throw new Error("Unexpected browserSignals.topLevelSellerSignals");
405426
} else {
406427
// Component sellers should receive only the top-level seller.
407428
if (browserSignals.topLevelSeller !== topLevelSeller)
408429
throw new Error("Incorrect topLevelSeller in browserSignals.");
409430
if ("componentSeller" in browserSignals)
410431
throw new Error("Expected no componentSeller in browserSignals.");
432+
433+
// Component sellers should get the return value of the top-level
434+
// seller's `reportResult()` call, which is, in this case, the
435+
// `browserSignals` of the top-level seller.
436+
if (browserSignals.topLevelSellerSignals.componentSeller !=
437+
auctionConfig.seller) {
438+
throw new Error("Unexpected browserSignals.topLevelSellerSignals");
439+
}
411440
}
412441
413442
if (browserSignals.desirability != computeScore(browserSignals.bid))
414443
throw new Error("wrong bid or desirability in browserSignals");
444+
// The default scoreAd() script does not modify bids.
445+
if ("modifiedBid" in browserSignals)
446+
throw new Error("modifiedBid unexpectedly in browserSignals");
415447
if (browserSignals.dataVersion !== undefined)
416448
throw new Error(`wrong dataVersion (${browserSignals.dataVersion})`);
417449
if (sendReportUrl)
@@ -766,17 +798,20 @@ class MockSellerWorklet : public auction_worklet::mojom::SellerWorklet {
766798
send_pending_signals_requests_called_ = true;
767799
}
768800

769-
void ReportResult(blink::mojom::AuctionAdConfigNonSharedParamsPtr
770-
auction_ad_config_non_shared_params,
771-
auction_worklet::mojom::ComponentAuctionOtherSellerPtr
772-
browser_signals_other_seller,
773-
const url::Origin& browser_signal_interest_group_owner,
774-
const GURL& browser_signal_render_url,
775-
double browser_signal_bid,
776-
double browser_signal_desirability,
777-
uint32_t browser_signal_data_version,
778-
bool browser_signal_has_data_version,
779-
ReportResultCallback report_result_callback) override {
801+
void ReportResult(
802+
blink::mojom::AuctionAdConfigNonSharedParamsPtr
803+
auction_ad_config_non_shared_params,
804+
auction_worklet::mojom::ComponentAuctionOtherSellerPtr
805+
browser_signals_other_seller,
806+
const url::Origin& browser_signal_interest_group_owner,
807+
const GURL& browser_signal_render_url,
808+
double browser_signal_bid,
809+
double browser_signal_desirability,
810+
auction_worklet::mojom::ComponentAuctionReportResultParamsPtr
811+
browser_signals_component_auction_report_result_params,
812+
uint32_t browser_signal_data_version,
813+
bool browser_signal_has_data_version,
814+
ReportResultCallback report_result_callback) override {
780815
report_result_callback_ = std::move(report_result_callback);
781816
if (report_result_run_loop_)
782817
report_result_run_loop_->Quit();
@@ -2495,6 +2530,80 @@ TEST_F(AuctionRunnerTest, ComponentAuctionOneComponentTwoBidders) {
24952530
/*expected_interest_groups=*/2, /*expected_owners=*/2);
24962531
}
24972532

2533+
// Test the case a top-level seller returns no signals in its reportResult
2534+
// method. The default scripts return signals, so only need to individually test
2535+
// the no-value case.
2536+
TEST_F(AuctionRunnerTest, ComponentAuctionNoTopLevelReportResultSignals) {
2537+
// Basic bid script.
2538+
const char kBidScript[] = R"(
2539+
function generateBid(interestGroup, auctionSignals, perBuyerSignals,
2540+
trustedBiddingSignals, browserSignals) {
2541+
return {ad: [], bid: 2, render: interestGroup.ads[0].renderUrl,
2542+
allowComponentAuction: true};
2543+
}
2544+
2545+
function reportWin(auctionSignals, perBuyerSignals, sellerSignals,
2546+
browserSignals) {
2547+
sendReportTo("https://buyer-reporting.example.com/" + browserSignals.bid);
2548+
}
2549+
)";
2550+
2551+
// Component seller script that makes a report to a URL based on whether the
2552+
// top-level seller signals are null.
2553+
const std::string kComponentSellerScript = R"(
2554+
function scoreAd(adMetadata, bid, auctionConfig, browserSignals) {
2555+
return {desirability: 10, allowComponentAuction: true};
2556+
}
2557+
2558+
function reportResult(auctionConfig, browserSignals) {
2559+
sendReportTo(auctionConfig.seller + "/" +
2560+
(browserSignals.topLevelSellerSignals === null));
2561+
}
2562+
)";
2563+
2564+
// Top-level seller script with a reportResult method that has no return
2565+
// value.
2566+
const std::string kTopLevelSellerScript = R"(
2567+
function scoreAd(adMetadata, bid, auctionConfig, browserSignals) {
2568+
return {desirability: 10, allowComponentAuction: true};
2569+
}
2570+
2571+
function reportResult(auctionConfig, browserSignals) {
2572+
sendReportTo(auctionConfig.seller + "/" + browserSignals.bid);
2573+
// Note that there's no return value here.
2574+
}
2575+
)";
2576+
2577+
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kBidder1Url,
2578+
kBidScript);
2579+
auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
2580+
kTopLevelSellerScript);
2581+
auction_worklet::AddJavascriptResponse(
2582+
&url_loader_factory_, kComponentSeller1Url, kComponentSellerScript);
2583+
2584+
interest_group_buyers_.reset();
2585+
component_auctions_.emplace_back(
2586+
CreateAuctionConfig(kComponentSeller1Url, {{kBidder1}}));
2587+
2588+
std::vector<StorageInterestGroup> bidders;
2589+
bidders.emplace_back(MakeInterestGroup(
2590+
kBidder1, kBidder1Name, kBidder1Url,
2591+
/*trusted_bidding_signals_url=*/absl::nullopt,
2592+
/*trusted_bidding_signals_keys=*/{}, GURL("https://ad1.com")));
2593+
2594+
StartAuction(kSellerUrl, std::move(bidders));
2595+
auction_run_loop_->Run();
2596+
EXPECT_EQ(GURL("https://ad1.com"), result_.ad_url);
2597+
EXPECT_THAT(result_.errors, testing::ElementsAre());
2598+
EXPECT_THAT(result_.report_urls,
2599+
testing::UnorderedElementsAre(
2600+
GURL("https://buyer-reporting.example.com/2"),
2601+
GURL("https://component.seller1.test/true"),
2602+
GURL("https://adstuff.publisher1.com/2")));
2603+
CheckHistograms(AuctionRunner::AuctionResult::kSuccess,
2604+
/*expected_interest_groups=*/1, /*expected_owners=*/1);
2605+
}
2606+
24982607
TEST_F(AuctionRunnerTest, ComponentAuctionModifiesBid) {
24992608
// Basic bid script.
25002609
const char kBidScript[] = R"(
@@ -2517,7 +2626,8 @@ TEST_F(AuctionRunnerTest, ComponentAuctionModifiesBid) {
25172626
}
25182627
25192628
function reportResult(auctionConfig, browserSignals) {
2520-
sendReportTo(auctionConfig.seller + "/" + browserSignals.bid);
2629+
sendReportTo(auctionConfig.seller + "/" + browserSignals.bid +
2630+
"_" + browserSignals.modifiedBid);
25212631
}
25222632
)";
25232633

@@ -2558,13 +2668,10 @@ TEST_F(AuctionRunnerTest, ComponentAuctionModifiesBid) {
25582668
EXPECT_THAT(result_.errors, testing::ElementsAre());
25592669
// The reporting URLs contain the bids - the top-level seller report should
25602670
// see the modified bid, the other worklets see the original bid.
2561-
//
2562-
// TODO(https://crbug.com/1288865): The component seller worklet should also
2563-
// get the modified bid, in another field.
25642671
EXPECT_THAT(result_.report_urls,
25652672
testing::UnorderedElementsAre(
25662673
GURL("https://buyer-reporting.example.com/2"),
2567-
GURL("https://component.seller1.test/2"),
2674+
GURL("https://component.seller1.test/2_3"),
25682675
GURL("https://adstuff.publisher1.com/3")));
25692676
CheckHistograms(AuctionRunner::AuctionResult::kSuccess,
25702677
/*expected_interest_groups=*/1, /*expected_owners=*/1);

0 commit comments

Comments
 (0)