1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
// This file is adapted from /fledge/tentative/resources/fledge-util.js,
// removing unnecessary logic and modifying to allow it to be run in the
// private-aggregation directory.
"use strict;"
const FULL_URL = window.location.href;
let BASE_URL = FULL_URL.substring(0, FULL_URL.lastIndexOf('/') + 1)
const BASE_PATH = (new URL(BASE_URL)).pathname;
const DEFAULT_INTEREST_GROUP_NAME = 'default name';
// Use python script files under fledge directory
const FLEDGE_DIR = '/fledge/tentative/';
const FLEDGE_BASE_URL = BASE_URL.replace(BASE_PATH, FLEDGE_DIR);
// Sleep method that waits for prescribed number of milliseconds.
const sleep = ms => new Promise(resolve => step_timeout(resolve, ms));
// Generates a UUID by token.
function generateUuid() {
let uuid = token();
return uuid;
}
// Creates a URL that will be sent to the handler.
// `uuid` is used to identify the stash shard to use.
// `operate` is used to set action as write or read.
// `report` is used to carry the message for write requests.
function createReportingURL(uuid, operation, report = 'default-report') {
let url = new URL(`${window.location.origin}${BASE_PATH}resources/protected_audience_event_level_report_handler.py`);
url.searchParams.append('uuid', uuid);
url.searchParams.append('operation', operation);
if (report)
url.searchParams.append('report', report);
return url.toString();
}
function createWritingURL(uuid, report) {
return createReportingURL(uuid, 'write');
}
function createReadingURL(uuid) {
return createReportingURL(uuid, 'read');
}
async function waitForObservedReports(uuid, expectedNumReports, timeout = 5000 /*ms*/) {
expectedReports = Array(expectedNumReports).fill('default-report');
const reportURL = createReadingURL(uuid);
let startTime = performance.now();
while (performance.now() - startTime < timeout) {
let response = await fetch(reportURL, { credentials: 'omit', mode: 'cors' });
let actualReports = await response.json();
// If expected number of reports have been observed, compare with list of
// all expected reports and exit.
if (actualReports.length == expectedReports.length) {
assert_array_equals(actualReports.sort(), expectedReports);
return;
}
await sleep(/*ms=*/ 100);
}
assert_unreached("Report fetching timed out: " + uuid);
}
// 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
// run, unless it returns something or throws.
//
// The default reportWin() method is empty.
function createBiddingScriptURL(params = {}) {
let url = new URL(`${FLEDGE_BASE_URL}resources/bidding-logic.sub.py`);
if (params.generateBid)
url.searchParams.append('generateBid', params.generateBid);
if (params.reportWin)
url.searchParams.append('reportWin', params.reportWin);
if (params.error)
url.searchParams.append('error', params.error);
if (params.bid)
url.searchParams.append('bid', params.bid);
return url.toString();
}
// Creates a decision script with the provided code in the method bodies. The
// decision script's scoreAd() method will reject ads with renderURLs that
// don't ends with "uuid", and will return a score equal to the bid, after the
// passed in code in the "scoreAd" input argument has been run, unless it
// returns something or throws.
//
// The default reportResult() method is empty.
function createDecisionScriptURL(uuid, params = {}) {
let url = new URL(`${FLEDGE_BASE_URL}resources/decision-logic.sub.py`);
url.searchParams.append('uuid', uuid);
if (params.scoreAd)
url.searchParams.append('scoreAd', params.scoreAd);
if (params.reportResult)
url.searchParams.append('reportResult', params.reportResult);
if (params.error)
url.searchParams.append('error', params.error);
return url.toString();
}
// Creates a renderURL for an ad that runs the passed in "script". "uuid" has
// no effect, beyond making the URL distinct between tests, and being verified
// by the decision logic script before accepting a bid. "uuid" is expected to
// be last.
function createRenderURL(uuid, script) {
let url = new URL(`${FLEDGE_BASE_URL}resources/fenced-frame.sub.py`);
if (script)
url.searchParams.append('script', script);
url.searchParams.append('uuid', uuid);
return url.toString();
}
// Joins an interest group that, by default, is owned by the current frame's
// origin, is named DEFAULT_INTEREST_GROUP_NAME, has a bidding script that
// issues a bid of 9 with a renderURL of "https://not.checked.test/${uuid}".
// `interestGroupOverrides` is required to override fields in the joined
// interest group.
async function joinInterestGroup(test, uuid, interestGroupOverrides) {
const INTEREST_GROUP_LIFETIME_SECS = 60;
let interestGroup = {
owner: window.location.origin,
name: DEFAULT_INTEREST_GROUP_NAME,
ads: [{renderURL: createRenderURL(uuid)}],
...interestGroupOverrides
};
await navigator.joinAdInterestGroup(interestGroup,
INTEREST_GROUP_LIFETIME_SECS);
test.add_cleanup(
async () => {await navigator.leaveAdInterestGroup(interestGroup)});
}
// Runs a FLEDGE auction and returns the result. `auctionConfigOverrides` is
// required to override fields in the auction configuration.
async function runBasicFledgeAuction(test, uuid, auctionConfigOverrides) {
let auctionConfig = {
seller: window.location.origin,
interestGroupBuyers: [window.location.origin],
resolveToConfig: true,
...auctionConfigOverrides
};
return await navigator.runAdAuction(auctionConfig);
}
// Calls runBasicFledgeAuction(), expecting the auction to have a winner.
// Creates a fenced frame that will be destroyed on completion of "test", and
// navigates it to the URN URL returned by the auction. Does not wait for the
// fenced frame to finish loading, since there's no API that can do that.
async function runBasicFledgeAuctionAndNavigate(test, uuid,
auctionConfigOverrides) {
let config = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides);
assert_true(config instanceof FencedFrameConfig,
`Wrong value type returned from auction: ${config.constructor.type}`);
let fencedFrame = document.createElement('fencedframe');
fencedFrame.mode = 'opaque-ads';
fencedFrame.config = config;
document.body.appendChild(fencedFrame);
test.add_cleanup(() => { document.body.removeChild(fencedFrame); });
}
// Joins an interest group and runs an auction, expecting no winner to be
// returned. "testConfig" can optionally modify the interest group or
// auctionConfig.
async function runBasicFledgeTestExpectingNoWinner(test, testConfig) {
const uuid = generateUuid(test);
await joinInterestGroup(test, uuid, testConfig.interestGroupOverrides);
let result = await runBasicFledgeAuction(
test, uuid, testConfig.auctionConfigOverrides);
assert_true(result === null, 'Auction unexpectedly had a winner');
}
// Test helper for report phase of auctions that lets the caller specify the
// body of scoreAd(), reportResult(), generateBid() and reportWin(), as well as
// additional arguments to be passed to joinAdInterestGroup() and runAdAuction()
async function runReportTest(test, uuid, codeToInsert,
expectedNumReports = 0, overrides = {}) {
let generateBid = codeToInsert.generateBid;
let scoreAd = codeToInsert.scoreAd;
let reportWin = codeToInsert.reportWin;
let reportResult = codeToInsert.reportResult;
let extraInterestGroupOverrides = overrides.joinAdInterestGroup || {}
let extraAuctionConfigOverrides = overrides.runAdAuction || {}
let interestGroupOverrides = {
biddingLogicURL: createBiddingScriptURL({ generateBid, reportWin }),
...extraInterestGroupOverrides
};
let auctionConfigOverrides = {
decisionLogicURL: createDecisionScriptURL(
uuid, { scoreAd, reportResult }),
...extraAuctionConfigOverrides
}
await joinInterestGroup(test, uuid, interestGroupOverrides);
await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
if (expectedNumReports) {
await waitForObservedReports(uuid, expectedNumReports);
}
}
|