summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fledge
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:13:33 +0000
commit086c044dc34dfc0f74fbe41f4ecb402b2cd34884 (patch)
treea4f824bd33cb075dd5aa3eb5a0a94af221bbe83a /testing/web-platform/tests/fledge
parentAdding debian version 124.0.1-1. (diff)
downloadfirefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.tar.xz
firefox-086c044dc34dfc0f74fbe41f4ecb402b2cd34884.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/fledge')
-rw-r--r--testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js146
-rw-r--r--testing/web-platform/tests/fledge/tentative/auction-config.https.window.js77
-rw-r--r--testing/web-platform/tests/fledge/tentative/component-auction.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js4
-rw-r--r--testing/web-platform/tests/fledge/tentative/direct-from-seller-signals.https.window.js56
-rw-r--r--testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js946
-rw-r--r--testing/web-platform/tests/fledge/tentative/generate-bid-recency.https.window.js34
-rw-r--r--testing/web-platform/tests/fledge/tentative/join-leave-ad-interest-group.https.window.js12
-rw-r--r--testing/web-platform/tests/fledge/tentative/kanon-status-below-threshold.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/kanon-status-not-calculated.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/register-ad-beacon.https.window.js48
-rw-r--r--testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js8
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/additional-bids.py59
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/bidding-logic.sub.py126
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/decision-logic.sub.py4
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js125
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/permissions.py54
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/request-tracker.py2
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py4
-rw-r--r--testing/web-platform/tests/fledge/tentative/round-a-value.https.window.js18
-rw-r--r--testing/web-platform/tests/fledge/tentative/send-report-to.https.window.js20
-rw-r--r--testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js20
22 files changed, 1563 insertions, 206 deletions
diff --git a/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js b/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js
new file mode 100644
index 0000000000..0e1d22c261
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js
@@ -0,0 +1,146 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.sub.js
+// META: script=/common/subset-tests.js
+// META: timeout=long
+// META: variant=?1-last
+
+"use strict;"
+
+// This file contains tests for additional bids and negative targeting.
+//
+// TODO:
+// - test that an negatively targeted additional bid is suppressed.
+// - test that an incorrectly signed additional bid is not negative targeted.
+// - test that an missing-signature additional bid is not negative targeted.
+// - test that an additional bid with some correct signatures can be negative.
+// negative targeted for those negative interest groups whose signatures
+// match.
+// - test an additional bid with multiple negative interest groups.
+// - test that multiple negative interest groups with mismatched joining origins
+// is not negative targeted.
+// - test that additional bids can be fetched using an iframe navigation.
+// - test that additional bids are not fetched using an iframe navigation for
+// which the `adAuctionHeaders=true` attribute is not specified.
+// - test that additional bids are not fetched using a Fetch request for which
+// `adAuctionHeaders: true` is not specified.
+// - test that an additional bid with an incorrect auction nonce is not used
+// included in an auction. Same for seller and top-level seller.
+// - test that correctly formatted additional bids are included in an auction
+// when fetched alongside malformed additional bid headers by a Fetch
+// request.
+// - test that correctly formatted additional bids are included in an auction
+// when fetched alongside malformed additional bid headers by an iframe
+// navigation.
+// - test that reportWin is not used for reporting an additional bid win.
+// - test that additional bids can *not* be fetched from iframe subresource
+// requests.
+// - test that an auction nonce can only be used once, and a second auction
+// trying to reuse an auction immediately fails.
+// - test that an auction nonce must be created in the same window/tab as the
+// call to runAdAuction.
+// - test reportAdditionalBidWin with each of no metadata, null metadata, and
+// an object metadata.
+// - test that an auction running in one tab can't see an additional bid loaded
+// in a new tab.
+// - test that two auctions running with different nonces only get the
+// additional bids fetched with their auction nonce.
+// - test that two auctions running with different nonces only get the
+// additional bids fetched with their auction nonce, when both additional
+// bids are retrieved with one fetch.
+// - test that a multiseller auction with two component auctions can direct
+// additional bids to the correct component auctions.
+// - test that two auctions running with different nonces only get the
+// additional bids fetched with their auction nonce.
+// - test that two auctions running with different nonces only get the
+// additional bids fetched with their auction nonce, when both additional
+// bids are retrieved with one fetch.
+// - test that an additional bid can compete against an interest group bid and
+// lose.
+// - test that an additional bid can compete against an interest group bid and
+// win.
+// - test that a malformed additional bid causes that one additional bid to be
+// ignored, but the rest of the auction (and other additional bids, even
+// from the same fetch) continue on.
+// - test (in join-leave-ad-interest-group.https.window.js) that an IG that
+// provides `additionalBidKey` fails if the key fails to decode, or if
+// that IG also provides `ads`, or if it provides `updateURL`.
+// - test that an IG update cannot cause a regular interest group (one that
+// does not provide `additionalBidKey`) to become a negative interest
+// group (one that does provide `additionalBidKey`).
+// - test (in auction-config-passed-to-worklets.https.window.js) that a
+// multi-seller auction fails if the top-level auction provides
+// a value for `additionalBids`.
+// - test (in auction-config-passed-to-worklets.https.window.js) that an auction
+// fails if it provides `additionalBids` but not `auctionNonce`, or if it
+// provides `additionalBids` but not `interestGroupBuyers`.
+
+// The auction is run with the seller being the same as the document origin.
+// The request to fetch additional bids must be issued to the seller's origin
+// for ad auction headers interception to associate it with this auction.
+const SINGLE_SELLER_AUCTION_SELLER = window.location.origin;
+
+// Single-seller auction with a single buyer who places a single additional
+// bid. As the only bid, this wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const buyer = OTHER_ORIGIN1;
+ const additionalBid = createAdditionalBid(
+ uuid, auctionNonce, seller, buyer, 'horses', 1.99);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer], auctionNonce,
+ fetchAdditionalBids(seller, [additionalBid]),
+ /*highestScoringOtherBid=*/0,
+ /*winningAdditionalBidId=*/'horses');
+}, 'single valid additional bid');
+
+// Single-seller auction with a two buyers competing with additional bids.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ fetchAdditionalBids(seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'two valid additional bids');
+
+// Same as the test above, except that this uses two Fetch requests instead of
+// one to retrieve the additional bids.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ Promise.all([
+ fetchAdditionalBids(seller, [additionalBid1]),
+ fetchAdditionalBids(seller, [additionalBid2])
+ ]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'two valid additional bids from two distinct Fetch requests');
diff --git a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
index 3b5814b5d4..8fbdc95dfc 100644
--- a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
@@ -390,3 +390,80 @@ makeTest({
[{width: '100', height: '100'},
{width: '200furlongs', height: '200'}]}
});
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // The renderURL / report URLs for the first/second iterations of the auction.
+ let renderURL = createRenderURL(uuid);
+ let bidderReportURL1 = createBidderReportURL(uuid, /*id=*/ 1);
+ let bidderReportURL2 = createBidderReportURL(uuid, /*id=*/ 2);
+ let bidderDebugReportURL =
+ createBidderReportURL(uuid, /*id=*/ 'forDebuggingOnly');
+ let sellerReportURL1 = createSellerReportURL(uuid, /*id=*/ 1);
+ let sellerReportURL2 = createSellerReportURL(uuid, /*id=*/ 2);
+ let sellerDebugReportURL =
+ createSellerReportURL(uuid, /*id=*/ 'forDebuggingOnly');
+
+ // reportWin() sends "bidderReportURL1" if
+ // browserSignals.forDebuggingOnlyInCooldownOrLockout is true,
+ // "bidderReportURL2" otherwise.
+ await joinInterestGroup(test, uuid, {
+ ads: [{renderURL: renderURL}],
+ biddingLogicURL: createBiddingScriptURL({
+ generateBid: `
+ forDebuggingOnly.reportAdAuctionWin('${bidderDebugReportURL}');
+ if (!browserSignals.hasOwnProperty(
+ 'forDebuggingOnlyInCooldownOrLockout')) {
+ throw "Missing forDebuggingOnlyInCooldownOrLockout in browserSignals";
+ }
+ let bid = browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
+ return {bid: bid, render: '${renderURL}'};`,
+ reportWin: `
+ if (browserSignals.bid == 1)
+ sendReportTo('${bidderReportURL1}');
+ if (browserSignals.bid == 2)
+ sendReportTo('${bidderReportURL2}');`
+
+ })
+ });
+
+ // reportResult() sends "sellerReportURL1" if
+ // browserSignals.forDebuggingOnlyInCooldownOrLockout in scoreAd() is true,
+ // "sellerReportURL2" otherwise.
+ const auctionConfigOverrides = {
+ decisionLogicURL: createDecisionScriptURL(uuid, {
+ scoreAd: `
+ forDebuggingOnly.reportAdAuctionWin('${sellerDebugReportURL}');
+ if (!browserSignals.hasOwnProperty(
+ 'forDebuggingOnlyInCooldownOrLockout')) {
+ throw "Missing forDebuggingOnlyInCooldownOrLockout in browserSignals";
+ }
+ let desirability =
+ browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
+ return {desirability: desirability};`,
+ reportResult: `
+ if (browserSignals.desirability == 1)
+ sendReportTo('${sellerReportURL1}');
+ if (browserSignals.desirability == 2)
+ sendReportTo('${sellerReportURL2}');`
+ })
+ };
+
+ // In the first auction, browserSignals.forDebuggingOnlyInCooldownOrLockout in
+ // generateBid() and scoreAd() should both be false. After the auction,
+ // lockout and cooldowns should be updated.
+ await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+ await waitForObservedRequestsIgnoreDebugOnlyReports(
+ uuid, [bidderReportURL2, sellerReportURL2]);
+
+ // In the second auction, browserSignals.forDebuggingOnlyInCooldownOrLockout
+ // in generateBid() and scoreAd() should both be true, since both the buyer
+ // and seller called forDebuggingOnly API in the first auction, so they are in
+ // cooldowns at least (and also in lockout if a debug report is allowed to be
+ // sent).
+ await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+ await waitForObservedRequestsIgnoreDebugOnlyReports(
+ uuid,
+ [bidderReportURL2, sellerReportURL2, bidderReportURL1, sellerReportURL1]);
+}, `forDebuggingOnly lockout and cooldowns updating in one auction, read in another's.`);
diff --git a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
index 63771d42b8..c70532024c 100644
--- a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
@@ -671,7 +671,7 @@ subsetTest(promise_test, async test => {
browserSignals.prevWinsMs[0][1].renderURL === "${renderURL1}") {
return {bid: 1, allowComponentAuction: true, render: "${renderURL2}"};
}
- throw "Unexpected biddingSignals: " + JSON.stringify(browserSignals);`,
+ throw "Unexpected browserSignals: " + JSON.stringify(browserSignals);`,
reportWin:
`if (browserSignals.renderURL === "${renderURL1}")
sendReportTo("${bidderReportURL1}");
diff --git a/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js b/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js
index 788558e5cf..a8cf93049f 100644
--- a/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js
@@ -356,8 +356,8 @@ subsetTest(promise_test, async test => {
throw "Wrong origin: " + interestGroup.owner;
if (!interestGroup.biddingLogicURL.startsWith("${bidderOrigin}"))
throw "Wrong origin: " + interestGroup.biddingLogicURL;
- if (interestGroup.ads[0].renderUrl != "${renderURL}")
- throw "Wrong renderURL: " + interestGroup.ads[0].renderUrl;
+ if (interestGroup.ads[0].renderURL != "${renderURL}")
+ throw "Wrong renderURL: " + interestGroup.ads[0].renderURL;
if (browserSignals.seller !== "${sellerOrigin}")
throw "Wrong origin: " + browserSignals.seller;`,
reportWin: `if (browserSignals.topWindowHostname !== "${document.location.hostname}")
diff --git a/testing/web-platform/tests/fledge/tentative/direct-from-seller-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/direct-from-seller-signals.https.window.js
index 339bc32ee5..d0c0e550c4 100644
--- a/testing/web-platform/tests/fledge/tentative/direct-from-seller-signals.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/direct-from-seller-signals.https.window.js
@@ -22,7 +22,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/ null,
/*expectedAuctionSignals=*/ null, /*expectedPerBuyerSignals=*/ null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -38,7 +38,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/1',
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -55,7 +55,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
'auctionSignals/2', /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -72,7 +72,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
/*expectedAuctionSignals=*/null, 'perBuyerSignals/3'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -89,7 +89,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/4',
'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -106,7 +106,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/1',
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -119,7 +119,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
'auctionSignals/2', /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -137,7 +137,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/4',
'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -216,7 +216,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/5',
'auctionSignals/5', /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -233,7 +233,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/5',
'auctionSignals/5', /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -250,7 +250,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -295,7 +295,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -312,7 +312,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -329,7 +329,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -347,7 +347,7 @@ subsetTest(promise_test, async test => {
await fetchDirectFromSellerSignals({ 'Buyer-Origin': window.location.origin });
await runReportTest(
test, uuid, codeToInsert,
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -366,7 +366,7 @@ subsetTest(promise_test, async test => {
await fetchDirectFromSellerSignals({ 'Buyer-Origin': window.location.origin }, OTHER_ORIGIN1);
await runReportTest(
test, uuid, codeToInsert,
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -386,7 +386,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/4',
'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid, '1', OTHER_ORIGIN1)],
// renderURLOverride
null,
@@ -406,7 +406,7 @@ subsetTest(promise_test, async test => {
test_instance, "${uuid}",
directFromSellerSignalsValidatorCode(
"${uuid}", 'sellerSignals/4', 'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL("${uuid}"), createBidderReportURL("${uuid}")],
// renderURLOverride
null,
@@ -425,7 +425,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
"${uuid}", 'sellerSignals/4',
'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL("${uuid}"), createBidderReportURL("${uuid}")],
// renderURLOverride
null,
@@ -447,7 +447,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
"${uuid}", 'sellerSignals/4',
'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL("${uuid}"), createBidderReportURL("${uuid}")],
// renderURLOverride
null,
@@ -467,7 +467,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals/4',
'auctionSignals/4', 'perBuyerSignals/4'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -484,7 +484,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sellerSignals',
'auctionSignals', /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -511,7 +511,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'altSellerSignals/1',
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -532,7 +532,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'altV2SellerSignals/1',
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -551,7 +551,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, /*expectedSellerSignals=*/null,
'auctionSignals/2', /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -569,7 +569,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'firstSellerSignals/1',
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -587,7 +587,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'nonDupSellerSignals/2',
/*expectedAuctionSignals=*/null, /*expectedPerBuyerSignals=*/null),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
@@ -606,7 +606,7 @@ subsetTest(promise_test, async test => {
directFromSellerSignalsValidatorCode(
uuid, 'sameSellerSignals',
'sameAuctionSignals', 'samePerBuyerSignals'),
- // expectedReportUrls
+ // expectedReportURLs
[createSellerReportURL(uuid), createBidderReportURL(uuid)],
// renderURLOverride
null,
diff --git a/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
new file mode 100644
index 0000000000..8687e3f296
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
@@ -0,0 +1,946 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.sub.js
+// META: script=/common/subset-tests.js
+// META: timeout=long
+// META: variant=?1-4
+// META: variant=?5-8
+// META: variant=?9-12
+// META: variant=?13-16
+// META: variant=?17-20
+// META: variant=?21-24
+// META: variant=?25-28
+// META: variant=?29-last
+
+"use strict;"
+
+// These tests focus on the browserSignals argument passed to generateBid().
+// Note that "topLevelSeller" is covered by component auction tests,
+// "dataVersion" by trusted signals tests, and cross-origin
+// "topWindowHostname" and "seller" are covered by cross origin tests.
+//
+// Some of these tests use the "uuid" for interest group name, to avoid
+// joins/bids from previous tests that failed to clean up after themselves
+// from affecting results.
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let expectedBrowserSignals = {
+ "topWindowHostname": window.location.hostname,
+ "seller": window.location.origin,
+ "adComponentsLimit": 40,
+ "joinCount": 1,
+ "bidCount": 0,
+ "prevWinsMs": []
+ }
+ let biddingLogicURL = createBiddingScriptURL(
+ { generateBid:
+ `let expectedBrowserSignals = ${JSON.stringify(expectedBrowserSignals)};
+
+ // Can't check this value exactly.
+ expectedBrowserSignals.recency = browserSignals.recency;
+
+ // This value may be affected by other recently run tests.
+ expectedBrowserSignals.forDebuggingOnlyInCooldownOrLockout =
+ browserSignals.forDebuggingOnlyInCooldownOrLockout;
+
+ // Remove deprecated field, if present.
+ delete browserSignals.prevWins;
+
+ if (!deepEquals(browserSignals, expectedBrowserSignals))
+ throw "Unexpected browserSignals: " + JSON.stringify(browserSignals);`
+ });
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {name: uuid,
+ biddingLogicURL: biddingLogicURL}});
+}, 'Only expected fields present.');
+
+// Creates a bidding script URL that expects the "joinCount" to be
+// "expectedJoinCount".
+function createJoinCountBiddingScriptURL(expectedJoinCount) {
+ return createBiddingScriptURL(
+ { generateBid:
+ `if (browserSignals.joinCount !== ${expectedJoinCount})
+ throw "Unexpected joinCount: " + browserSignals.joinCount;`
+ });
+}
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {name: uuid,
+ biddingLogicURL: createJoinCountBiddingScriptURL(1)}});
+
+ // Joining again, even with a different script URL, should increase the join count.
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {name: uuid,
+ biddingLogicURL: createJoinCountBiddingScriptURL(2)}});
+}, 'browserSignals.joinCount same joining page.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {name: uuid,
+ biddingLogicURL: createJoinCountBiddingScriptURL(1)}});
+
+ // Attempt to re-join the same interest group from a different top-level origin.
+ // The join count should still be persisted.
+ await joinCrossOriginInterestGroupInTopLevelWindow(
+ test, uuid, OTHER_ORIGIN1, window.location.origin,
+ { name: uuid,
+ biddingLogicURL: createJoinCountBiddingScriptURL(2)});
+
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.joinCount different top-level joining origin.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {name: uuid,
+ biddingLogicURL: createJoinCountBiddingScriptURL(1)}});
+
+ // Leaving interest group should clear join count.
+ await leaveInterestGroup({name: uuid});
+
+ // Check that join count was cleared.
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {name: uuid,
+ biddingLogicURL: createJoinCountBiddingScriptURL(1)}});
+}, 'browserSignals.joinCount leave and rejoin.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runReportTest(
+ test, uuid,
+ { generateBid:
+ `if (browserSignals.recency === undefined)
+ throw new Error("Missing recency in browserSignals.")
+
+ if (browserSignals.recency < 0)
+ throw new Error("Recency is a negative value.")
+
+ if (browserSignals.recency > 30000)
+ throw new Error("Recency is over 30 seconds threshold.")
+
+ if (browserSignals.recency % 100 !== 0)
+ throw new Error("Recency is not rounded to multiple of 100 milliseconds.")
+
+ return {'bid': 9,
+ 'render': interestGroup.ads[0].renderURL};`,
+ reportWin:
+ `sendReportTo('${createBidderReportURL(uuid)}');`
+ },
+ // expectedReportURLs
+ [createBidderReportURL(uuid)]
+ );
+}, 'Check recency in generateBid() is below a certain threshold and rounded ' +
+ 'to multiple of 100 milliseconds.');
+
+// Creates a bidding script URL that expects the "bidCount" to be
+// "expectedBidCount".
+function createBidCountBiddingScriptURL(expectedBidCount) {
+ return createBiddingScriptURL(
+ { generateBid:
+ `if (browserSignals.bidCount !== ${expectedBidCount})
+ throw "Unexpected bidCount: " + browserSignals.bidCount;`
+ });
+}
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Running an auction should not increment "bidCount".
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0)});
+
+ // These auctions would have no winner if the "bidCount" were incremented.
+ await runBasicFledgeAuction(test, uuid);
+ await runBasicFledgeAuction(test, uuid);
+}, 'browserSignals.bidCount not incremented when ad not used.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0) });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ // Wait for the navigation to trigger reports. "bidCount" should be updated before
+ // any reports are sent.
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1) });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ // Wait for the navigation to trigger reports. "bidCount" should be updated before
+ // any reports are sent.
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(2) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount incremented when ad used.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Join an interest group and run an auction and navigate to the winning ad,
+ // increasing the bid count to 1.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0)});
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ // Wait for the navigation to trigger reports. "bidCount" should be updated before
+ // any reports are sent.
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ await joinCrossOriginInterestGroupInTopLevelWindow(
+ test, uuid, OTHER_ORIGIN1, window.location.origin,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount persists across re-join from other top-level origin.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Join an interest group and run an auction and navigate to the winning ad,
+ // increasing the bid count to 1.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0) });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ // Leaving interest group should clear "bidCount".
+ await leaveInterestGroup({name: uuid});
+
+ // Check that bid count was cleared.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0)});
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount leave and rejoin.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0)} );
+
+ // Run two auctions at once, without any navigations.
+ // "bidCount" should be 0 for both auctions.
+ let fencedFrameConfigs =
+ await Promise.all([runBasicFledgeTestExpectingWinner(test, uuid),
+ runBasicFledgeTestExpectingWinner(test, uuid)]);
+
+ // Start navigating to both auction winners.
+ createAndNavigateFencedFrame(test, fencedFrameConfigs[0]);
+ createAndNavigateFencedFrame(test, fencedFrameConfigs[1]);
+
+ // Wait for navigations to have sent reports (and thus to have updated
+ // bid counts).
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ // Check that "bidCount" has increased by 2.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(2) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount two auctions at once.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Use a tracker URL for the ad. It won't be successfully loaded, due to missing
+ // the fenced frame header, but it should be fetched twice.
+ let trackedRenderURL =
+ createTrackerURL(window.location.origin, uuid, 'track_get', /*id=*/'ad');
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0),
+ ads: [{ renderURL: trackedRenderURL }]
+ });
+
+ let fencedFrameConfig = await runBasicFledgeTestExpectingWinner(test, uuid);
+
+ // Start navigating two frames to the winning ad.
+ createAndNavigateFencedFrame(test, fencedFrameConfig);
+ createAndNavigateFencedFrame(test, fencedFrameConfig);
+
+ // Wait for both navigations to have requested ads (and thus to have updated
+ // bid counts).
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ trackedRenderURL,
+ trackedRenderURL]);
+
+ // Check that "bidCount" has increased by only 1.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount incremented once when winning ad used twice.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let bidderReportURL = createBidderReportURL(uuid, /*id=*/'winner');
+
+ // Join an interest group named "uuid", which will bid 0.1, losing the first auction.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { bid: 0.1, reportWin: `sendReportTo('${createBidderReportURL(uuid, /*id=*/'loser')}')` })
+ });
+
+ // Join an interest group with the default name, which will bid 1 and win the first
+ // auction, sending a bidder report.
+ await joinInterestGroup(
+ test, uuid,
+ { biddingLogicURL: createBiddingScriptURL(
+ { bid: 1, reportWin: `sendReportTo('${bidderReportURL}')` })
+ });
+
+ // Run an auction that both bidders participate in. Despite the first interest group
+ // losing, its "bidCount" should be incremented.
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ // Make sure the right bidder won.
+ await waitForObservedRequests(uuid, [bidderReportURL, createSellerReportURL(uuid)]);
+
+ // Leave the second interest group (which has the default name).
+ await leaveInterestGroup();
+
+ // Re-join the first interest group, with a bidding script that checks its "bidCount".
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1)
+ });
+
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount incremented when another interest group wins.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Use default interest group, other than using a unique name. It will make a bid.
+ await joinInterestGroup(test, uuid, { name: uuid });
+ // Run auction with seller that rejects all bids.
+ await runBasicFledgeTestExpectingNoWinner(
+ test, uuid,
+ { decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: `return 0;`})});
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1)
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount incremented when seller rejects bid.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Use default interest group, other than using a unique name. It will make a bid.
+ await joinInterestGroup(test, uuid, { name: uuid });
+ // Run auction with seller that always throws.
+ await runBasicFledgeTestExpectingNoWinner(
+ test, uuid,
+ { decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: `throw "a fit";`})});
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1)
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount incremented when seller throws.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Interest group that does not bid.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { generateBid: 'return;' })
+ });
+ await runBasicFledgeTestExpectingNoWinner(test, uuid);
+
+ // Check that "bidCount" was not incremented.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0)
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount not incremented when no bid.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let bidderReportURL = createBidderReportURL(uuid, /*id=*/'winner');
+
+ // Interest group that does not bid.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { generateBid: 'return;' })
+ });
+
+ // Join an interest group with the default name, which will bid 1 and win the first
+ // auction, sending a bidder report.
+ await joinInterestGroup(
+ test, uuid,
+ { biddingLogicURL: createBiddingScriptURL(
+ { bid: 1, reportWin: `sendReportTo('${bidderReportURL}')` })
+ });
+
+ // Run an auction that both bidders participate in, and make sure the right bidder won.
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [bidderReportURL, createSellerReportURL(uuid)]);
+
+ // Leave the second interest group (which has the default name).
+ await leaveInterestGroup();
+
+ // Re-join the first interest group, with a bidding script that checks its "bidCount".
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(0)
+ });
+
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount not incremented when no bid and another interest group wins.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let bidderReportURL = createBidderReportURL(uuid, /*id=*/'winner');
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { bid: 42, reportWin: `sendReportTo('${createBidderReportURL(uuid, /*id=*/'loser')}')` })
+ });
+
+ // Join an interest group with the default name, which will bid 1 and win the first
+ // auction, sending a bidder report.
+ await joinInterestGroup(
+ test, uuid,
+ { biddingLogicURL: createBiddingScriptURL(
+ { bid: 1, reportWin: `sendReportTo('${bidderReportURL}')` })
+ });
+
+ // Run an auction that both bidders participate in. The scoreAd script rejects the
+ // first interest group's bid.
+ await runBasicFledgeAuctionAndNavigate(
+ test, uuid,
+ { decisionLogicURL: createDecisionScriptURL(
+ uuid,
+ { scoreAd: `if (bid === 42) return -1;`})});
+ // Make sure the second interest group won.
+ await waitForObservedRequests(uuid, [bidderReportURL]);
+
+ // Leave the second interest group (which has the default name).
+ await leaveInterestGroup();
+
+ // Re-join the first interest group, with a bidding script that checks its "bidCount".
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBidCountBiddingScriptURL(1)
+ });
+
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.bidCount incremented when makes largest bid, but seller rejects the bid.');
+
+// Creates a bidding script URL that expects "prevWinsMs" to be
+// "expectedPrevWinsMs". All times in "expectedPrevWinsMs" must be 0.
+//
+// "adIndex" is the index of the ad to use in the bid.
+function createPrevWinsMsBiddingScriptURL(expectedPrevWinsMs, adIndex = 0) {
+ return createBiddingScriptURL(
+ { generateBid:
+ `for (let i = 0; i < browserSignals.prevWinsMs.length; i++) {
+ // Check age is in a reasonable range.
+ if (browserSignals.prevWinsMs[i][0] < 0 ||
+ browserSignals.prevWinsMs[i][0] > 30000) {
+ throw "Unexpected prevWinsMs time: " + JSON.stringify(browserSignals.prevWinsMs);
+ }
+
+ // Set age to 0.
+ browserSignals.prevWinsMs[i][0] = 0;
+
+ // Remove obsolete field, if present.
+ delete browserSignals.prevWinsMs[i][1].render_url;
+ }
+ if (!deepEquals(browserSignals.prevWinsMs, ${JSON.stringify(expectedPrevWinsMs)}))
+ throw "Unexpected prevWinsMs: " + JSON.stringify(browserSignals.prevWinsMs);
+
+ return {
+ bid: 1,
+ render: interestGroup.ads[${adIndex}].renderURL
+ };`
+ });
+}
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Running an auction should not increment "prevWinsMs".
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])});
+
+ // These auctions would have no winner if the "prevWinsMs" were incremented.
+ await runBasicFledgeAuction(test, uuid);
+ await runBasicFledgeAuction(test, uuid);
+}, 'browserSignals.prevWinsMs not affected when ad not used.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([]) });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ // Wait for the navigation to trigger reports. "prevWinsMs" should be updated before
+ // any reports are sent.
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [[0, {renderURL: createRenderURL(uuid)}]]) });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ // Wait for the navigation to trigger reports. "prevWinsMs" should be updated before
+ // any reports are sent.
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [ [0, {renderURL: createRenderURL(uuid)}],
+ [0, {renderURL: createRenderURL(uuid)}]]) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs, no metadata.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const ads = [ {renderURL: createRenderURL(uuid, 0), metadata: null},
+ {renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]},
+ {renderURL: createRenderURL(uuid, 2)} ];
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([], /*adIndex=*/0),
+ ads: ads });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [[0, {renderURL: createRenderURL(uuid, 0), metadata: null}]],
+ /*adIndex=*/1),
+ ads: ads });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [ [0, {renderURL: createRenderURL(uuid, 0), metadata: null}],
+ [0, {renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]}] ],
+ /*adIndex=*/2),
+ ads: ads });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [ [0, {renderURL: createRenderURL(uuid, 0), metadata: null}],
+ [0, {renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]}],
+ [0, {renderURL: createRenderURL(uuid, 2)}] ]),
+ ads: ads });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs, with metadata.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const ads = [ {renderURL: createRenderURL(uuid, 0), metadata: null},
+ {renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]},
+ {renderURL: createRenderURL(uuid, 2)} ];
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([]),
+ ads: [{renderURL: createRenderURL(uuid, 0), metadata: null}] });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [[0, {renderURL: createRenderURL(uuid, 0), metadata: null}]]),
+ ads: [{renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]}] });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [ [0, {renderURL: createRenderURL(uuid, 0), metadata: null}],
+ [0, {renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]}] ]),
+ ads: [{renderURL: createRenderURL(uuid, 2)}] });
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [ [0, {renderURL: createRenderURL(uuid, 0), metadata: null}],
+ [0, {renderURL: createRenderURL(uuid, 1), metadata: ['1', 2, {3: 4}]}],
+ [0, {renderURL: createRenderURL(uuid, 2)}] ]) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs, different set of ads for each bid.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Join an interest group and run an auction and navigate to the winning ad,
+ // which should be logged in "prevWinsMs".
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])});
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ await joinCrossOriginInterestGroupInTopLevelWindow(
+ test, uuid, OTHER_ORIGIN1, window.location.origin,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [[0, {renderURL: createRenderURL(uuid)}]]) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs persists across re-join from other top-level origin.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Join an interest group and run an auction and navigate to the winning ad,
+ // which should be logged in "prevWinsMs".
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])});
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid)]);
+
+ // Leaving interest group should clear "prevWinsMs".
+ await leaveInterestGroup({name: uuid});
+
+ // Check that bid count was cleared.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])});
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs leave and rejoin.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])
+ });
+
+ // Run two auctions at once, without any navigations.
+ // "prevWinsMs" should be empty for both auctions.
+ let fencedFrameConfigs =
+ await Promise.all([runBasicFledgeTestExpectingWinner(test, uuid),
+ runBasicFledgeTestExpectingWinner(test, uuid)]);
+
+ // Start navigating to both auction winners.
+ createAndNavigateFencedFrame(test, fencedFrameConfigs[0]);
+ createAndNavigateFencedFrame(test, fencedFrameConfigs[1]);
+
+ // Wait for navigations to have sent reports (and thus to have updated
+ // "prevWinsMs").
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ createSellerReportURL(uuid)]);
+
+ // Check that "prevWinsMs" has two URLs.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [[0, {renderURL: createRenderURL(uuid)}],
+ [0, {renderURL: createRenderURL(uuid)}]])
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs two auctions at once.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Use a tracker URL for the ad. It won't be successfully loaded, due to missing
+ // the fenced frame header, but it should be fetched twice.
+ let trackedRenderURL =
+ createTrackerURL(window.location.origin, uuid, 'track_get', /*id=*/'ad');
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([]),
+ ads: [{ renderURL: trackedRenderURL }]
+ });
+
+ let fencedFrameConfig = await runBasicFledgeTestExpectingWinner(test, uuid);
+
+ // Start navigating two frames to the winning ad.
+ createAndNavigateFencedFrame(test, fencedFrameConfig);
+ createAndNavigateFencedFrame(test, fencedFrameConfig);
+
+ // Wait for both navigations to have requested ads (and thus to have updated
+ // "prevWinsMs").
+ await waitForObservedRequests(uuid, [createSellerReportURL(uuid),
+ trackedRenderURL,
+ trackedRenderURL]);
+
+ // Check that "prevWins" has only a single win.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL(
+ [[0, {renderURL: trackedRenderURL}]]) });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs has only one win when winning ad used twice.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let bidderReportURL = createBidderReportURL(uuid, /*id=*/'winner');
+
+ // Join an interest group named "uuid", which will bid 0.1, losing the first auction.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { bid: 0.1, reportWin: `sendReportTo('${createBidderReportURL(uuid, /*id=*/'loser')}')` })
+ });
+
+ // Join an interest group with the default name, which will bid 1 and win the first
+ // auction, sending a bidder report.
+ await joinInterestGroup(
+ test, uuid,
+ { biddingLogicURL: createBiddingScriptURL(
+ { bid: 1, reportWin: `sendReportTo('${bidderReportURL}')` })
+ });
+
+ // Run an auction that both bidders participate in, and make sure the right bidder won.
+ await runBasicFledgeAuctionAndNavigate(test, uuid);
+ await waitForObservedRequests(uuid, [bidderReportURL, createSellerReportURL(uuid)]);
+
+ // Leave the second interest group (which has the default name).
+ await leaveInterestGroup();
+
+ // Re-join the first interest group, with a bidding script that expects prevWinsMs to
+ // be empty.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])
+ });
+
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs not updated when another interest group wins.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Use default interest group, other than using a unique name. It will make a bid.
+ await joinInterestGroup(test, uuid, { name: uuid });
+ // Run auction with seller that rejects all bids.
+ await runBasicFledgeTestExpectingNoWinner(
+ test, uuid,
+ { decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: `return 0;`})});
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs not updated when seller rejects bid.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Use default interest group, other than using a unique name. It will make a bid.
+ await joinInterestGroup(test, uuid, { name: uuid });
+ // Run auction with seller that always throws.
+ await runBasicFledgeTestExpectingNoWinner(
+ test, uuid,
+ { decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: `throw "a fit";`})});
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs not updated when seller throws.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Interest group that does not bid.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { generateBid: 'return;' })
+ });
+ await runBasicFledgeTestExpectingNoWinner(test, uuid);
+
+ // Check that "prevWinsMs" was not modified.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])
+ });
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs not updated when no bid.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let bidderReportURL = createBidderReportURL(uuid, /*id=*/'winner');
+
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createBiddingScriptURL(
+ { bid: 42, reportWin: `sendReportTo('${createBidderReportURL(uuid, /*id=*/'loser')}')` })
+ });
+
+ // Join an interest group with the default name, which will bid 1 and win the first
+ // auction, sending a bidder report.
+ await joinInterestGroup(
+ test, uuid,
+ { biddingLogicURL: createBiddingScriptURL(
+ { bid: 1, reportWin: `sendReportTo('${bidderReportURL}')` })
+ });
+
+ // Run an auction that both bidders participate in. The scoreAd script returns a low
+ // score for the first interest group's bid.
+ await runBasicFledgeAuctionAndNavigate(
+ test, uuid,
+ { decisionLogicURL: createDecisionScriptURL(
+ uuid,
+ { scoreAd: `if (bid === 42) return 0.1;`})});
+ // Make sure the second interest group won.
+ await waitForObservedRequests(uuid, [bidderReportURL]);
+
+ // Leave the second interest group (which has the default name).
+ await leaveInterestGroup();
+
+ // Re-join the first interest group, with a bidding script that expects prevWinsMs to
+ // be empty.
+ await joinInterestGroup(
+ test, uuid,
+ { name: uuid,
+ biddingLogicURL: createPrevWinsMsBiddingScriptURL([])
+ });
+
+ await runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'browserSignals.prevWinsMs not updated when makes largest bid, but another interest group wins.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ // Join an interest group with a WASM helper that exposes a single "increment" method,
+ // and make sure that method can be invoked and behaves as expected.
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {
+ biddingWasmHelperURL: `${RESOURCE_PATH}wasm-helper.py`,
+ biddingLogicURL: createBiddingScriptURL(
+ { generateBid:
+ `if (!browserSignals.wasmHelper)
+ throw "No WASM helper";
+
+ let instance = new WebAssembly.Instance(browserSignals.wasmHelper);
+ if (!instance)
+ throw "Couldn't create WASM Instance";
+
+ if (!deepEquals(Object.keys(instance.exports), ["increment"]))
+ throw "Unexpected exports: " + JSON.stringify(instance.exports);
+
+ if (instance.exports.increment(1) != 2)
+ throw "Unexpected increment result: " + instance.exports.increment(1);` })
+ }
+ });
+}, 'browserSignals.wasmHelper.');
diff --git a/testing/web-platform/tests/fledge/tentative/generate-bid-recency.https.window.js b/testing/web-platform/tests/fledge/tentative/generate-bid-recency.https.window.js
deleted file mode 100644
index 07da463a2d..0000000000
--- a/testing/web-platform/tests/fledge/tentative/generate-bid-recency.https.window.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// META: script=/resources/testdriver.js
-// META: script=/common/utils.js
-// META: script=resources/fledge-util.sub.js
-// META: timeout=long
-
-"use strict;"
-
-promise_test(async test => {
- const uuid = generateUuid(test);
- await runReportTest(
- test, uuid,
- { generateBid:
- `if (browserSignals.recency === undefined)
- throw new Error("Missing recency in browserSignals.")
-
- if (browserSignals.recency < 0)
- throw new Error("Recency is a negative value.")
-
- if (browserSignals.recency > 30000)
- throw new Error("Recency is over 30 seconds threshold.")
-
- if (browserSignals.recency % 100 !== 0)
- throw new Error("Recency is not rounded to multiple of 100 milliseconds.")
-
- return {'bid': 9,
- 'render': interestGroup.ads[0].renderURL};`,
- reportWin:
- `sendReportTo('${createBidderReportURL(uuid)}');`
- },
- // expectedReportUrls
- [createBidderReportURL(uuid)]
- );
-}, 'Check recency in generateBid() is below a certain threshold and rounded ' +
- 'to multiple of 100 milliseconds.');
diff --git a/testing/web-platform/tests/fledge/tentative/join-leave-ad-interest-group.https.window.js b/testing/web-platform/tests/fledge/tentative/join-leave-ad-interest-group.https.window.js
index b5dfe025bf..02148ef36b 100644
--- a/testing/web-platform/tests/fledge/tentative/join-leave-ad-interest-group.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/join-leave-ad-interest-group.https.window.js
@@ -231,31 +231,31 @@ const SIMPLE_JOIN_LEAVE_TEST_CASES = [
biddingWasmHelperURL: 'relative/path' }
},
- // "dailyUpdateUrl" tests
+ // "updateURL" tests
{ expectJoinSucces: true,
expectLeaveSucces: true,
interestGroup: { ...BASE_INTEREST_GROUP,
- dailyUpdateUrl: null }
+ updateURL: null }
},
{ expectJoinSucces: false,
expectLeaveSucces: true,
interestGroup: { ...BASE_INTEREST_GROUP,
- dailyUpdateUrl: 'https://{{hosts[][www]}}/foo.js' }
+ updateURL: 'https://{{hosts[][www]}}/foo.js' }
},
{ expectJoinSucces: false,
expectLeaveSucces: true,
interestGroup: { ...BASE_INTEREST_GROUP,
- dailyUpdateUrl: 'data:application/wasm,Foo' }
+ updateURL: 'data:application/wasm,Foo' }
},
{ expectJoinSucces: true,
expectLeaveSucces: true,
interestGroup: { ...BASE_INTEREST_GROUP,
- dailyUpdateUrl: `${window.location.origin}/foo.js`}
+ updateURL: `${window.location.origin}/foo.js`}
},
{ expectJoinSucces: true,
expectLeaveSucces: true,
interestGroup: { ...BASE_INTEREST_GROUP,
- dailyUpdateUrl: 'relative/path' }
+ updateURL: 'relative/path' }
},
// "executionMode" tests
diff --git a/testing/web-platform/tests/fledge/tentative/kanon-status-below-threshold.https.window.js b/testing/web-platform/tests/fledge/tentative/kanon-status-below-threshold.https.window.js
index 4eac4a8e91..787283d687 100644
--- a/testing/web-platform/tests/fledge/tentative/kanon-status-below-threshold.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/kanon-status-below-threshold.https.window.js
@@ -13,7 +13,7 @@ subsetTest(promise_test, async test => {
`browserSignals.kAnonStatus === "belowThreshold"`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]);
},
'Check kAnonStatus is "belowThreshold" when FledgeConsiderKAnonymity' +
diff --git a/testing/web-platform/tests/fledge/tentative/kanon-status-not-calculated.https.window.js b/testing/web-platform/tests/fledge/tentative/kanon-status-not-calculated.https.window.js
index a3ac19bd85..4a750cd352 100644
--- a/testing/web-platform/tests/fledge/tentative/kanon-status-not-calculated.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/kanon-status-not-calculated.https.window.js
@@ -13,7 +13,7 @@ subsetTest(promise_test, async test => {
`browserSignals.kAnonStatus === "notCalculated"`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]);
},
'Check kAnonStatus is "notCalculated" when FledgeConsiderKAnonymity' +
diff --git a/testing/web-platform/tests/fledge/tentative/register-ad-beacon.https.window.js b/testing/web-platform/tests/fledge/tentative/register-ad-beacon.https.window.js
index 19fab2ac1b..3643e44662 100644
--- a/testing/web-platform/tests/fledge/tentative/register-ad-beacon.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/register-ad-beacon.https.window.js
@@ -17,9 +17,9 @@ subsetTest(promise_test, async test => {
`registerAdBeacon({beacon: '${createSellerBeaconURL(uuid)}'});`,
reportWin:
'' },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid)}, body: `],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -39,9 +39,9 @@ subsetTest(promise_test, async test => {
reportWin:
`registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});`
},
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createBidderBeaconURL(uuid)}, body: `],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -60,9 +60,9 @@ subsetTest(promise_test, async test => {
`registerAdBeacon({beacon: '${createSellerBeaconURL(uuid)}'});`,
reportWin:
'' },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid)}, body: body`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -81,9 +81,9 @@ subsetTest(promise_test, async test => {
'',
reportWin:
`registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createBidderBeaconURL(uuid)}, body: body`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -102,10 +102,10 @@ subsetTest(promise_test, async test => {
`registerAdBeacon({beacon: '${createSellerBeaconURL(uuid)}'});`,
reportWin:
'' },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid)}, body: body1`,
`${createSellerBeaconURL(uuid)}, body: body2`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -129,10 +129,10 @@ subsetTest(promise_test, async test => {
'',
reportWin:
`registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createBidderBeaconURL(uuid)}, body: body1`,
`${createBidderBeaconURL(uuid)}, body: body2`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -157,10 +157,10 @@ subsetTest(promise_test, async test => {
beacon2: '${createSellerBeaconURL(uuid, '2')}'});`,
reportWin:
'' },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid, '1')}, body: body1`,
`${createSellerBeaconURL(uuid, '2')}, body: body2`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -186,10 +186,10 @@ subsetTest(promise_test, async test => {
`registerAdBeacon({beacon1: '${createBidderBeaconURL(uuid, '1')}',
beacon2: '${createBidderBeaconURL(uuid, '2')}'});`
},
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createBidderBeaconURL(uuid, '1')}, body: body1`,
`${createBidderBeaconURL(uuid, '2')}, body: body2`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -213,10 +213,10 @@ subsetTest(promise_test, async test => {
`registerAdBeacon({beacon: '${createSellerBeaconURL(uuid)}'});`,
reportWin:
`registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid)}, body: body`,
`${createBidderBeaconURL(uuid)}, body: body`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -235,10 +235,10 @@ subsetTest(promise_test, async test => {
`registerAdBeacon({beacon: '${createSellerBeaconURL(uuid)}'});`,
reportWin:
`registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid)}, body: body1`,
`${createBidderBeaconURL(uuid)}, body: body2`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -268,9 +268,9 @@ subsetTest(promise_test, async test => {
'sellerSignals === null',
reportWin:
`registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});` },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createBidderBeaconURL(uuid)}, body: body`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
@@ -293,9 +293,9 @@ subsetTest(promise_test, async test => {
`sendReportTo('${createBidderReportURL(uuid)}');
registerAdBeacon({beacon: '${createBidderBeaconURL(uuid)}'});
registerAdBeacon({beacon1: '${createBidderBeaconURL(uuid)}'});` },
- // expectedReportUrls:
+ // expectedReportURLs:
[`${createSellerBeaconURL(uuid)}, body: body`],
- // renderUrlOverride:
+ // renderURLOverride:
createRenderURL(
uuid,
`window.fence.reportEvent({
diff --git a/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js b/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js
index f26a969328..c7c7120240 100644
--- a/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js
@@ -46,7 +46,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === 45',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid), createBidderReportURL(uuid)]
);
}, 'Seller passes number to bidder.');
@@ -62,7 +62,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === "foo"',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid), createBidderReportURL(uuid)]
);
}, 'Seller passes string to bidder.');
@@ -78,7 +78,7 @@ subsetTest(promise_test, async test => {
'JSON.stringify(sellerSignals) === "[3,1,2]"',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid), createBidderReportURL(uuid)]
);
}, 'Seller passes array to bidder.');
@@ -94,7 +94,7 @@ subsetTest(promise_test, async test => {
`JSON.stringify(sellerSignals) === '{"a":4,"b":["c",null,{}]}'`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid), createBidderReportURL(uuid)]
);
}, 'Seller passes object to bidder.');
diff --git a/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py b/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py
new file mode 100644
index 0000000000..060606b41d
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py
@@ -0,0 +1,59 @@
+"""Endpoint to return additional bids in the appropriate response header.
+
+Additional bids are returned using the "Ad-Auction-Additional-Bid" response
+header, as described at
+https://github.com/WICG/turtledove/blob/main/FLEDGE.md#63-http-response-headers.
+
+This script generates one of "Ad-Auction-Additional-Bid" response header for
+each additional bid provided in a url-encoded `additionalBids` query parameter.
+
+All requests to this endpoint requires a "Sec-Ad-Auction-Fetch" request header
+with a value of b"?1"; this entrypoint otherwise returns a 400 response.
+"""
+import json
+import base64
+
+import fledge.tentative.resources.fledge_http_server_util as fledge_http_server_util
+
+
+class BadRequestError(Exception):
+ pass
+
+
+def main(request, response):
+ try:
+ if fledge_http_server_util.handle_cors_headers_and_preflight(request, response):
+ return
+
+ # Verify that Sec-Ad-Auction-Fetch is present
+ if (request.headers.get("Sec-Ad-Auction-Fetch", default=b"").decode("utf-8") != "?1"):
+ raise BadRequestError("Sec-Ad-Auction-Fetch missing or unexpected value; expected '?1'")
+
+ # Return each signed additional bid in its own header
+ additional_bids = request.GET.get(b"additionalBids", default=b"").decode("utf-8")
+ if not additional_bids:
+ raise BadRequestError("Missing 'additionalBids' parameter")
+ for additional_bid in json.loads(additional_bids):
+ additional_bid_string = json.dumps(additional_bid)
+ auction_nonce = additional_bid.get("auctionNonce", None)
+ if not auction_nonce:
+ raise BadRequestError("Additional bid missing required 'auctionNonce' field")
+ signed_additional_bid = json.dumps({
+ "bid": additional_bid_string,
+ "signatures": []
+ })
+ additional_bid_header_value = (auction_nonce.encode("utf-8") + b":" +
+ base64.b64encode(signed_additional_bid.encode("utf-8")))
+ response.headers.append(b"Ad-Auction-Additional-Bid", additional_bid_header_value)
+
+ response.status = (200, b"OK")
+ response.headers.set(b"Content-Type", b"text/plain")
+
+ except BadRequestError as error:
+ response.status = (400, b"Bad Request")
+ response.headers.set(b"Content-Type", b"text/plain")
+ response.content = str(error)
+
+ except Exception as exception:
+ response.status = (500, b"Internal Server Error")
+ response.content = str(exception)
diff --git a/testing/web-platform/tests/fledge/tentative/resources/bidding-logic.sub.py b/testing/web-platform/tests/fledge/tentative/resources/bidding-logic.sub.py
index 707e37f36b..e17f2c2c75 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/bidding-logic.sub.py
+++ b/testing/web-platform/tests/fledge/tentative/resources/bidding-logic.sub.py
@@ -1,71 +1,83 @@
from pathlib import Path
+from fledge.tentative.resources import fledge_http_server_util
+
# General bidding logic script. Depending on query parameters, it can
-# simulate a variety of network errors, and its generateBid() and
-# reportWin() functions can have arbitrary Javascript code injected
-# in them. generateBid() will by default return a bid of 9 for the
+# simulate a variety of network errors, and its generateBid(), reportWin(),
+# and reportAdditionalBidWin() functions can have arbitrary Javascript code
+# injected in them. generateBid() will by default return a bid of 9 for the
# first ad.
def main(request, response):
- error = request.GET.first(b"error", None)
+ if fledge_http_server_util.handle_cors_headers_and_preflight(request, response):
+ return
+
+ error = request.GET.first(b"error", None)
- if error == b"close-connection":
- # Close connection without writing anything, to simulate a network
- # error. The write call is needed to avoid writing the default headers.
- response.writer.write("")
- response.close_connection = True
- return
+ if error == b"close-connection":
+ # Close connection without writing anything, to simulate a network
+ # error. The write call is needed to avoid writing the default headers.
+ response.writer.write("")
+ response.close_connection = True
+ return
- if error == b"http-error":
- response.status = (404, b"OK")
- else:
- response.status = (200, b"OK")
+ if error == b"http-error":
+ response.status = (404, b"OK")
+ else:
+ response.status = (200, b"OK")
- if error == b"wrong-content-type":
- response.headers.set(b"Content-Type", b"application/json")
- elif error != b"no-content-type":
- response.headers.set(b"Content-Type", b"application/javascript")
+ if error == b"wrong-content-type":
+ response.headers.set(b"Content-Type", b"application/json")
+ elif error != b"no-content-type":
+ response.headers.set(b"Content-Type", b"application/javascript")
- if error == b"bad-allow-fledge":
- response.headers.set(b"Ad-Auction-Allowed", b"sometimes")
- elif error == b"fledge-not-allowed":
- response.headers.set(b"Ad-Auction-Allowed", b"false")
- elif error != b"no-allow-fledge":
- response.headers.set(b"Ad-Auction-Allowed", b"true")
+ if error == b"bad-allow-fledge":
+ response.headers.set(b"Ad-Auction-Allowed", b"sometimes")
+ elif error == b"fledge-not-allowed":
+ response.headers.set(b"Ad-Auction-Allowed", b"false")
+ elif error != b"no-allow-fledge":
+ response.headers.set(b"Ad-Auction-Allowed", b"true")
- if error == b"no-body":
- return b''
+ if error == b"no-body":
+ return b''
- body = (Path(__file__).parent.resolve() / 'worklet-helpers.js').read_text().encode("ASCII")
- if error != b"no-generateBid":
- # Use bid query param if present. Otherwise, use a bid of 9.
- bid = (request.GET.first(b"bid", None) or b"9").decode("ASCII")
+ body = (Path(__file__).parent.resolve() / 'worklet-helpers.js').read_text().encode("ASCII")
+ if error != b"no-generateBid":
+ # Use bid query param if present. Otherwise, use a bid of 9.
+ bid = (request.GET.first(b"bid", None) or b"9").decode("ASCII")
- bidCurrency = ""
- bidCurrencyParam = request.GET.first(b"bidCurrency", None)
- if bidCurrencyParam != None:
- bidCurrency = "bidCurrency: '" + bidCurrencyParam.decode("ASCII") + "',"
+ bidCurrency = ""
+ bidCurrencyParam = request.GET.first(b"bidCurrency", None)
+ if bidCurrencyParam != None:
+ bidCurrency = "bidCurrency: '" + bidCurrencyParam.decode("ASCII") + "',"
- allowComponentAuction = ""
- allowComponentAuctionParam = request.GET.first(b"allowComponentAuction", None)
- if allowComponentAuctionParam != None:
- allowComponentAuction = f"allowComponentAuction: {allowComponentAuctionParam.decode('ASCII')},"
+ allowComponentAuction = ""
+ allowComponentAuctionParam = request.GET.first(b"allowComponentAuction", None)
+ if allowComponentAuctionParam != None:
+ allowComponentAuction = f"allowComponentAuction: {allowComponentAuctionParam.decode('ASCII')},"
- body += f"""
- function generateBid(interestGroup, auctionSignals, perBuyerSignals,
- trustedBiddingSignals, browserSignals,
- directFromSellerSignals) {{
- {{{{GET[generateBid]}}}};
- return {{
- bid: {bid},
- {bidCurrency}
- {allowComponentAuction}
- render: interestGroup.ads[0].renderURL
- }};
- }}""".encode()
- if error != b"no-reportWin":
- body += b"""
- function reportWin(auctionSignals, perBuyerSignals, sellerSignals,
- browserSignals, directFromSellerSignals) {
- {{GET[reportWin]}};
- }"""
- return body
+ body += f"""
+ function generateBid(interestGroup, auctionSignals, perBuyerSignals,
+ trustedBiddingSignals, browserSignals,
+ directFromSellerSignals) {{
+ {{{{GET[generateBid]}}}};
+ return {{
+ bid: {bid},
+ {bidCurrency}
+ {allowComponentAuction}
+ render: interestGroup.ads[0].renderURL
+ }};
+ }}""".encode()
+ if error != b"no-reportWin":
+ body += b"""
+ function reportWin(auctionSignals, perBuyerSignals, sellerSignals,
+ browserSignals, directFromSellerSignals) {
+ {{GET[reportWin]}};
+ }"""
+ if error != b"no-reportAdditionalBidWin":
+ body += b"""
+ function reportAdditionalBidWin(auctionSignals, perBuyerSignals,
+ sellerSignals, browserSignals,
+ directFromSellerSignals) {
+ {{GET[reportAdditionalBidWin]}};
+ }"""
+ return body
diff --git a/testing/web-platform/tests/fledge/tentative/resources/decision-logic.sub.py b/testing/web-platform/tests/fledge/tentative/resources/decision-logic.sub.py
index 78d459e3f9..3a23f98162 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/decision-logic.sub.py
+++ b/testing/web-platform/tests/fledge/tentative/resources/decision-logic.sub.py
@@ -43,8 +43,8 @@ def main(request, response):
// Don't bid on interest group with the wrong uuid. This is to prevent
// left over interest groups from other tests from affecting auction
// results.
- if (!browserSignals.renderUrl.endsWith('uuid={{GET[uuid]}}') &&
- !browserSignals.renderUrl.includes('uuid={{GET[uuid]}}&')) {
+ if (!browserSignals.renderURL.endsWith('uuid={{GET[uuid]}}') &&
+ !browserSignals.renderURL.includes('uuid={{GET[uuid]}}&')) {
return 0;
}
diff --git a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js
index 69573d4998..5819357e29 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js
+++ b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js
@@ -33,7 +33,7 @@ const OTHER_ORIGIN7 = 'https://{{hosts[alt][www]}}:{{ports[https][1]}}';
// on behavior of the script; it only serves to make the URL unique.
// `id` will always be the last query parameter.
function createTrackerURL(origin, uuid, dispatch, id = null) {
- let url = new URL(`${origin}${BASE_PATH}resources/request-tracker.py`);
+ let url = new URL(`${origin}${RESOURCE_PATH}request-tracker.py`);
let search = `uuid=${uuid}&dispatch=${dispatch}`;
if (id)
search += `&id=${id}`;
@@ -59,6 +59,10 @@ function createSellerReportURL(uuid, id = '1', origin = window.location.origin)
return createTrackerURL(origin, uuid, `track_get`, `seller_report_${id}`);
}
+function createHighestScoringOtherBidReportURL(uuid, highestScoringOtherBid) {
+ return createSellerReportURL(uuid) + '&highestScoringOtherBid=' + Math.round(highestScoringOtherBid);
+}
+
// Much like above ReportURL methods, except designed for beacons, which
// are expected to be POSTs.
function createBidderBeaconURL(uuid, id = '1', origin = window.location.origin) {
@@ -69,7 +73,7 @@ function createSellerBeaconURL(uuid, id = '1', origin = window.location.origin)
}
function createDirectFromSellerSignalsURL(origin = window.location.origin) {
- let url = new URL(`${origin}${BASE_PATH}resources/direct-from-seller-signals.py`);
+ let url = new URL(`${origin}${RESOURCE_PATH}direct-from-seller-signals.py`);
return url.toString();
}
@@ -80,7 +84,7 @@ function generateUuid(test) {
let uuid = token();
test.add_cleanup(async () => {
let response = await fetch(createCleanupURL(uuid),
- {credentials: 'omit', mode: 'cors'});
+ { credentials: 'omit', mode: 'cors' });
assert_equals(await response.text(), 'cleanup complete',
`Sever state cleanup failed`);
});
@@ -94,7 +98,7 @@ async function fetchTrackedData(uuid) {
let trackedRequestsURL = createTrackerURL(window.location.origin, uuid,
'tracked_data');
let response = await fetch(trackedRequestsURL,
- {credentials: 'omit', mode: 'cors'});
+ { credentials: 'omit', mode: 'cors' });
let trackedData = await response.json();
// Fail on fetch error.
@@ -118,23 +122,29 @@ async function fetchTrackedData(uuid) {
// Elements of `expectedRequests` should either be URLs, in the case of GET
// requests, or "<URL>, body: <body>" in the case of POST requests.
//
+// `filter` will be applied to the array of tracked requests.
+//
// If any other strings are received from the tracking script, or the tracker
// script reports an error, fails the test.
-async function waitForObservedRequests(uuid, expectedRequests) {
+async function waitForObservedRequests(uuid, expectedRequests, filter) {
// Sort array for easier comparison, as observed request order does not
// matter, and replace UUID to print consistent errors on failure.
- expectedRequests = expectedRequests.sort().map((url) => url.replace(uuid, '<uuid>'));
+ expectedRequests = expectedRequests.map((url) => url.replace(uuid, '<uuid>')).sort();
while (true) {
let trackedData = await fetchTrackedData(uuid);
// Clean up "trackedRequests" in same manner as "expectedRequests".
- let trackedRequests = trackedData.trackedRequests.sort().map(
- (url) => url.replace(uuid, '<uuid>'));
+ let trackedRequests = trackedData.trackedRequests.map(
+ (url) => url.replace(uuid, '<uuid>')).sort();
+
+ if (filter) {
+ trackedRequests = trackedRequests.filter(filter);
+ }
// If expected number of requests have been observed, compare with list of
// all expected requests and exit.
- if (trackedRequests.length == expectedRequests.length) {
+ if (trackedRequests.length >= expectedRequests.length) {
assert_array_equals(trackedRequests, expectedRequests);
break;
}
@@ -148,6 +158,16 @@ async function waitForObservedRequests(uuid, expectedRequests) {
}
}
+
+// Similar to waitForObservedRequests, but ignore forDebuggingOnly reports.
+async function waitForObservedRequestsIgnoreDebugOnlyReports(
+ uuid, expectedRequests) {
+ return waitForObservedRequests(
+ uuid,
+ expectedRequests,
+ request => !request.includes('forDebuggingOnly'));
+}
+
// Creates a bidding script with the provided code in the method bodies. The
// bidding script's generateBid() method will return a bid of 9 for the first
// ad, after the passed in code in the "generateBid" input argument has been
@@ -163,6 +183,8 @@ function createBiddingScriptURL(params = {}) {
url.searchParams.append('generateBid', params.generateBid);
if (params.reportWin != null)
url.searchParams.append('reportWin', params.reportWin);
+ if (params.reportAdditionalBidWin != null)
+ url.searchParams.append('reportAdditionalBidWin', params.reportAdditionalBidWin);
if (params.error != null)
url.searchParams.append('error', params.error);
if (params.bid != null)
@@ -254,7 +276,7 @@ async function joinInterestGroup(test, uuid, interestGroupOverrides = {},
await navigator.joinAdInterestGroup(interestGroup, durationSeconds);
test.add_cleanup(
- async () => {await navigator.leaveAdInterestGroup(interestGroup)});
+ async () => { await navigator.leaveAdInterestGroup(interestGroup) });
}
// Similar to joinInterestGroup, but leaves the interest group instead.
@@ -465,6 +487,24 @@ async function runReportTest(test, uuid, codeToInsert, expectedReportURLs,
await waitForObservedRequests(uuid, expectedReportURLs);
}
+async function runAdditionalBidTest(test, uuid, buyers, auctionNonce,
+ additionalBidsPromise,
+ highestScoringOtherBid,
+ winningAdditionalBidId) {
+ await runBasicFledgeAuctionAndNavigate(
+ test, uuid,
+ { interestGroupBuyers: buyers,
+ auctionNonce: auctionNonce,
+ additionalBids: additionalBidsPromise,
+ decisionLogicURL: createDecisionScriptURL(
+ uuid,
+ { reportResult: `sendReportTo("${createSellerReportURL(uuid)}&highestScoringOtherBid=" + Math.round(browserSignals.highestScoringOtherBid));` })});
+
+ await waitForObservedRequests(
+ uuid, [createHighestScoringOtherBidReportURL(uuid, highestScoringOtherBid),
+ createBidderReportURL(uuid, winningAdditionalBidId)]);
+}
+
// Runs "script" in "child_window" via an eval call. The "child_window" must
// have been created by calling "createFrame()" below. "param" is passed to the
// context "script" is run in, so can be used to pass objects that
@@ -504,7 +544,7 @@ async function runInFrame(test, child_window, script, param) {
// iframe or closes the window.
async function createFrame(test, origin, is_iframe = true, permissions = null) {
const frameUuid = generateUuid(test);
- const frameUrl =
+ const frameURL =
`${origin}${RESOURCE_PATH}subordinate-frame.sub.html?uuid=${frameUuid}`;
let promise = new Promise(function(resolve, reject) {
function WaitForMessage(event) {
@@ -523,7 +563,7 @@ async function createFrame(test, origin, is_iframe = true, permissions = null) {
let iframe = document.createElement('iframe');
if (permissions)
iframe.allow = permissions;
- iframe.src = frameUrl;
+ iframe.src = frameURL;
document.body.appendChild(iframe);
test.add_cleanup(async () => {
@@ -535,7 +575,7 @@ async function createFrame(test, origin, is_iframe = true, permissions = null) {
return iframe.contentWindow;
}
- let child_window = window.open(frameUrl);
+ let child_window = window.open(frameURL);
test.add_cleanup(async () => {
await runInFrame(test, child_window, "await test_instance.do_cleanup();");
child_window.close();
@@ -576,11 +616,23 @@ async function joinInterestGroupInTopLevelWindow(
let interestGroup = JSON.stringify(
createInterestGroupForOrigin(uuid, origin, interestGroupOverrides));
- let topLeveWindow = await createTopLevelWindow(test, origin);
- await runInFrame(test, topLeveWindow,
+ let topLevelWindow = await createTopLevelWindow(test, origin);
+ await runInFrame(test, topLevelWindow,
`await joinInterestGroup(test_instance, "${uuid}", ${interestGroup})`);
}
+// Opens a top-level window and calls joinCrossOriginInterestGroup() in it.
+async function joinCrossOriginInterestGroupInTopLevelWindow(
+ test, uuid, windowOrigin, interestGroupOrigin, interestGroupOverrides = {}) {
+ let interestGroup = JSON.stringify(
+ createInterestGroupForOrigin(uuid, interestGroupOrigin, interestGroupOverrides));
+
+ let topLevelWindow = await createTopLevelWindow(test, windowOrigin);
+ await runInFrame(test, topLevelWindow,
+ `await joinCrossOriginInterestGroup(
+ test_instance, "${uuid}", "${interestGroupOrigin}", ${interestGroup})`);
+}
+
// Fetch directFromSellerSignals from seller and check header
// 'Ad-Auction-Signals' is hidden from documents.
async function fetchDirectFromSellerSignals(headers_content, origin) {
@@ -643,3 +695,46 @@ function directFromSellerSignalsValidatorCode(uuid, expectedSellerSignals,
`sendReportTo("${createBidderReportURL(uuid)}");`,
};
}
+
+// Creates an additional bid with the given parameters. This additional bid
+// specifies a biddingLogicURL that provides an implementation of
+// reportAdditionalBidWin that triggers a sendReportTo() to the bidder report
+// URL of the winning additional bid. Additional bids are described in more
+// detail at
+// https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids.
+function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount,
+ additionalBidOverrides = {}) {
+ return {
+ interestGroup: {
+ name: interestGroupName,
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ origin: buyer,
+ reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");`
+ }),
+ owner: buyer
+ },
+ bid: {
+ ad: ['metadata'],
+ bid: bidAmount,
+ render: createRenderURL(uuid)
+ },
+ auctionNonce: auctionNonce,
+ seller: seller,
+ ...additionalBidOverrides
+ }
+}
+
+// Fetch some number of additional bid from a seller and verify that the
+// 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context.
+// The `additionalBids` parameter is a list of additional bids.
+async function fetchAdditionalBids(seller, additionalBids) {
+ const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`);
+ url.searchParams.append('additionalBids', JSON.stringify(additionalBids));
+ const response = await fetch(url.href, {adAuctionHeaders: true});
+
+ assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text());
+ assert_false(
+ response.headers.has('Ad-Auction-Additional-Bid'),
+ 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.');
+}
diff --git a/testing/web-platform/tests/fledge/tentative/resources/permissions.py b/testing/web-platform/tests/fledge/tentative/resources/permissions.py
new file mode 100644
index 0000000000..eed93c4275
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/resources/permissions.py
@@ -0,0 +1,54 @@
+"""Methods for the interest group cross-origin permissions endpoint."""
+import json
+import re
+
+from fledge.tentative.resources import fledge_http_server_util
+
+SUBDOMAIN_WWW = 'www'
+SUBDOMAIN_WWW1 = 'www1'
+SUBDOMAIN_WWW2 = 'www2'
+SUBDOMAIN_FRENCH = 'élève'.encode('idna').decode()
+SUBDOMAIN_JAPANESE = '天気の良い日'.encode('idna').decode()
+ALL_SUBDOMAINS = [SUBDOMAIN_WWW, SUBDOMAIN_WWW1, SUBDOMAIN_WWW2,
+ SUBDOMAIN_FRENCH, SUBDOMAIN_JAPANESE]
+
+def get_permissions(request, response):
+ """Returns JSON object containing interest group cross-origin permissions.
+
+ The structure returned is described in more detail at
+ https://github.com/WICG/turtledove/blob/main/FLEDGE.md#13-permission-delegation.
+ This correctly handles requests issued in CORS mode.
+
+ This .well-known is fetched at the origin of the interest group's owner, and
+ specifies as a URL parameter the origin of frame that's attempting to join or
+ leave that interest group.
+
+ This is implemented such that the origin of the frame is ignored altogether,
+ and the determination of which operations are allowed depends strictly on the
+ origin of the interest group owner, and specifically on the subdomain of the
+ origin of the interest group owner. wptserve serves each of its two domains
+ at both the raw domain and each of five subdomains.
+
+ - www: disallows both join and leave
+ - www1: allows join, but not leave
+ - www2: allows leave, but not join
+ - 天気の良い日 / élève: allow both join and leave
+ - anything else (including no subdomain): returns a 404
+ """
+ if fledge_http_server_util.handle_cors_headers_and_preflight(request, response):
+ return
+
+ first_domain_label = re.search(r"[^.]*", request.url_parts.netloc).group(0)
+ if first_domain_label not in ALL_SUBDOMAINS:
+ response.status = (404, b"Not Found")
+ response.content = "Not Found"
+ return
+
+ response.status = (200, b"OK")
+ response.headers.set(b"Content-Type", b"application/json")
+ response.content = json.dumps({
+ "joinAdInterestGroup": first_domain_label in [
+ SUBDOMAIN_WWW1, SUBDOMAIN_FRENCH, SUBDOMAIN_JAPANESE],
+ "leaveAdInterestGroup": first_domain_label in [
+ SUBDOMAIN_WWW2, SUBDOMAIN_FRENCH, SUBDOMAIN_JAPANESE],
+ })
diff --git a/testing/web-platform/tests/fledge/tentative/resources/request-tracker.py b/testing/web-platform/tests/fledge/tentative/resources/request-tracker.py
index c449d2ab02..3514741f63 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/request-tracker.py
+++ b/testing/web-platform/tests/fledge/tentative/resources/request-tracker.py
@@ -110,4 +110,6 @@ def simple_response(request, response, status_code, status_message, body,
content_type=b"text/plain"):
response.status = (status_code, status_message)
response.headers.set(b"Content-Type", content_type)
+ # Force refetch on reuse, so multiple requests to tracked URLs are all visible.
+ response.headers.set(b"Cache-control", b"no-store")
return body
diff --git a/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py b/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py
index 80488a5d6a..eccef5e762 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py
+++ b/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py
@@ -15,7 +15,7 @@ def main(request, response):
renderUrls = None
adComponentRenderURLs = None
# List of {type: <render URL type>, urls: <render URL list>} pairs, where <render URL type> is
- # one of the two render URL dictionary keys used in the response ("renderUrls" or
+ # one of the two render URL dictionary keys used in the response ("renderURLs" or
# "adComponentRenderURLs"). May be of length 1 or 2, depending on whether there
# are any component URLs.
urlLists = []
@@ -36,7 +36,7 @@ def main(request, response):
continue
if pair[0] == "renderUrls" and renderUrls == None:
renderUrls = list(map(unquote_plus, pair[1].split(",")))
- urlLists.append({"type":"renderUrls", "urls":renderUrls})
+ urlLists.append({"type":"renderURLs", "urls":renderUrls})
continue
if pair[0] == "adComponentRenderUrls" and adComponentRenderURLs == None:
adComponentRenderURLs = list(map(unquote_plus, pair[1].split(",")))
diff --git a/testing/web-platform/tests/fledge/tentative/round-a-value.https.window.js b/testing/web-platform/tests/fledge/tentative/round-a-value.https.window.js
index 5bccd4ab07..90523e2256 100644
--- a/testing/web-platform/tests/fledge/tentative/round-a-value.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/round-a-value.https.window.js
@@ -18,7 +18,7 @@ promise_test(async test => {
`browserSignals.adCost === 1.9921875 || browserSignals.adCost === 1.984375`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Check adCost is stochastically rounded with 8 bit mantissa and exponent.');
@@ -35,7 +35,7 @@ promise_test(async test => {
`browserSignals.bid === 1.9921875 || browserSignals.bid === 1.984375`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Check bid is stochastically rounded with 8 bit mantissa and exponent.');
@@ -52,7 +52,7 @@ promise_test(async test => {
`browserSignals.desirability === 1.9921875 || browserSignals.desirability === 1.984375`,
reportResult:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Check desirability is stochastically rounded with 8 bit mantissa and exponent.');
@@ -70,7 +70,7 @@ promise_test(async test => {
`browserSignals.highestScoringOtherBid === 1.9921875 || browserSignals.highestScoringOtherBid === 1.984375`,
reportResult:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Check highestScoringOtherBid is stochastically rounded with 8 bit mantissa and exponent.');
@@ -87,7 +87,7 @@ promise_test(async test => {
`browserSignals.adCost === 2`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Value is ignored as a non-valid floating-point number.');
@@ -104,7 +104,7 @@ promise_test(async test => {
`browserSignals.adCost === 0`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Value is rounded to 0 if value is greater than 0 and its exponent is less than -128.');
@@ -121,7 +121,7 @@ promise_test(async test => {
`browserSignals.adCost === -0`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Value is rounded to -0 if value is greater than 0 and its exponent is less than -128.');
@@ -138,7 +138,7 @@ promise_test(async test => {
`browserSignals.adCost === Infinity`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Value is rounded to Infinity if value is greater than 0 and its exponent is greater than 127.');
@@ -155,7 +155,7 @@ promise_test(async test => {
`browserSignals.adCost === -Infinity`,
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls
+ // expectedReportURLs
[createBidderReportURL(uuid)]
);
}, 'Value is rounded to -Infinity if value is less than 0 and its exponent is greater than 127.');
diff --git a/testing/web-platform/tests/fledge/tentative/send-report-to.https.window.js b/testing/web-platform/tests/fledge/tentative/send-report-to.https.window.js
index 65a2520420..e3cf0a95f1 100644
--- a/testing/web-platform/tests/fledge/tentative/send-report-to.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/send-report-to.https.window.js
@@ -18,7 +18,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === null',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid), createBidderReportURL(uuid)]
);
}, 'Both send reports, seller passes nothing to bidder.');
@@ -31,7 +31,7 @@ subsetTest(promise_test, async test => {
`sendReportTo('${createSellerReportURL(uuid)}');`,
reportWin:
'' },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid)]
);
}, 'Only seller sends a report');
@@ -44,7 +44,7 @@ subsetTest(promise_test, async test => {
`sendReportTo('${createSellerReportURL(uuid)}');`,
reportWin:
'throw new Error("Very serious exception")' },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid)]
);
}, 'Only seller sends a report, bidder throws an exception');
@@ -55,7 +55,7 @@ subsetTest(promise_test, async test => {
test, uuid,
{ reportResult:
`sendReportTo('${createSellerReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid)]
);
}, 'Only seller sends a report, bidder has no reportWin() method');
@@ -70,7 +70,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === null',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]
);
}, 'Only bidder sends a report');
@@ -85,7 +85,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === "foo"',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]
);
}, 'Only bidder sends a report, seller passes a message to bidder');
@@ -100,7 +100,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === null',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]
);
}, 'Only bidder sends a report, seller throws an exception');
@@ -113,7 +113,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === null',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]
);
}, 'Only bidder sends a report, seller has no reportResult() method');
@@ -130,7 +130,7 @@ subsetTest(promise_test, async test => {
'sellerSignals === null',
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createBidderReportURL(uuid)]
);
}, 'Seller calls sendReportTo() twice, which throws an exception.');
@@ -144,7 +144,7 @@ subsetTest(promise_test, async test => {
reportWin:
`sendReportTo('${createBidderReportURL(uuid)}');
sendReportTo('${createBidderReportURL(uuid)}');` },
- // expectedReportUrls:
+ // expectedReportURLs:
[createSellerReportURL(uuid)]
);
// Seller reports may be sent before bidder reports, since reportWin()
diff --git a/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js
index 67ae3577e4..4de5cfc0f3 100644
--- a/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js
@@ -82,7 +82,7 @@ async function runTrustedScoringSignalsDataVersionTest(
// Creates a render URL that, when sent to the trusted-scoring-signals.py,
// results in a trusted scoring signals response with the provided response
// body.
-function createScoringSignalsRenderUrlWithBody(uuid, responseBody) {
+function createScoringSignalsRenderURLWithBody(uuid, responseBody) {
return createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/`replace-body:${responseBody}`);
}
@@ -93,13 +93,13 @@ function createScoringSignalsRenderUrlWithBody(uuid, responseBody) {
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const decisionLogicScriptUrl = createDecisionScriptURL(
+ const decisionLogicScriptURL = createDecisionScriptURL(
uuid,
{ scoreAd: 'if (trustedScoringSignals !== null) throw "error";' });
await joinGroupAndRunBasicFledgeTestExpectingWinner(
test,
{ uuid: uuid,
- auctionConfigOverrides: { decisionLogicURL: decisionLogicScriptUrl }
+ auctionConfigOverrides: { decisionLogicURL: decisionLogicScriptURL }
});
}, 'No trustedScoringSignalsURL.');
@@ -149,35 +149,35 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const renderURL = createScoringSignalsRenderUrlWithBody(
+ const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response has no body.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const renderURL = createScoringSignalsRenderUrlWithBody(
+ const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'Not JSON');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is not JSON.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const renderURL = createScoringSignalsRenderUrlWithBody(
+ const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'[]');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is a JSON array.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const renderURL = createScoringSignalsRenderUrlWithBody(
+ const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{JSON_keys_need_quotes: 1}');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is invalid JSON object.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const renderURL = createScoringSignalsRenderUrlWithBody(
+ const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{}');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
@@ -444,7 +444,7 @@ subsetTest(promise_test, async test => {
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value');
// This should not be sent. If it is, it will take precedence over the "num-value" parameter
// from "renderURL", resulting in the "renderURL" having a null "trustedScoringSignals" value.
- const componentURL = createScoringSignalsRenderUrlWithBody(
+ const componentURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{}');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
@@ -456,7 +456,7 @@ subsetTest(promise_test, async test => {
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
- const renderURL = createScoringSignalsRenderUrlWithBody(
+ const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{}');
const componentURL = createRenderURL(uuid, /*script=*/null);
await runTrustedScoringSignalsTest(