Skip to content

Commit

Permalink
FLEDGE: Send creative scanning metadata when enabled
Browse files Browse the repository at this point in the history
(As part of V1 trusted scoring signals fetch).

See WICG/turtledove#792 (comment) for more context.

Bug: 383513677

Change-Id: I27e2a3357c3897edd1d1c612186a39a0afa91cc4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6149249
Commit-Queue: Maks Orlovich <[email protected]>
Reviewed-by: mmenke <[email protected]>
Reviewed-by: Giovanni Ortuno Urquidi <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1410923}
  • Loading branch information
Maks Orlovich authored and chromium-wpt-export-bot committed Jan 24, 2025
1 parent c2a02fc commit 4bf37c1
Show file tree
Hide file tree
Showing 2 changed files with 321 additions and 1 deletion.
9 changes: 9 additions & 0 deletions fledge/tentative/resources/fledge_http_server_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ def decode_trusted_scoring_signals_params(request):
adComponentRenderURLs = list(map(unquote_plus, pair[1].split(",")))
urlLists.append({"type":"adComponentRenderURLs", "urls":adComponentRenderURLs})
continue
# Ignore the various creative scanning params; they're expected, but we
# don't parse them here.
if (pair[0] == 'adCreativeScanningMetadata' or
pair[0] == 'adComponentCreativeScanningMetadata' or
pair[0] == 'adSizes' or
pair[0] == 'adComponentSizes' or
pair[0] == 'adBuyer' or
pair[0] == 'adComponentBuyer'):
continue
raise ValueError("Unexpected query parameter: " + param)

# "hostname" and "renderUrls" are mandatory.
Expand Down
313 changes: 312 additions & 1 deletion fledge/tentative/trusted-scoring-signals.https.window.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
// META: variant=?36-40
// META: variant=?41-45
// META: variant=?46-50
// META: variant=?51-last
// META: variant=?51-55
// META: variant=?56-last

"use strict";

Expand Down Expand Up @@ -786,3 +787,313 @@ subsetTest(promise_test, async test => {

await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
}, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of small value.');

// A little helper to extract out trusted signals query params echoed back
// by trusted-scoring-signals.py; sticks them in a map named `parsed`.
function makeParseHelper(renderURL) {
return `
let payload = trustedScoringSignals.renderURL['${renderURL}'];
payload = payload.substring(payload.indexOf('?') + 1).split('&');
let parsed = new Map();
for (let entry of payload) {
let kv = entry.split('=');
parsed.set(kv[0], decodeURIComponent(kv[1]));
}
`;
}

subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL =
createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url');

const bidder_origin = OTHER_ORIGIN1;

await joinCrossOriginInterestGroup(
test, uuid, bidder_origin,
{ads: [{renderURL: renderURL, creativeScanningMetadata: 'hello'}]});

const scoreAdBody = `
${makeParseHelper(renderURL)}
if (parsed.get('renderUrls') !== '${renderURL}')
throw 'Wrong URL';
if (parsed.get('adCreativeScanningMetadata') !== 'hello')
throw 'Wrong creative scanning metadata';
if (parsed.get('adSizes') !== ',')
throw 'Wrong adSizes';
if (parsed.get('adBuyer') !== '${bidder_origin}')
throw 'Wrong adBuyer';
if (parsed.has('adComponentRenderUrls'))
throw 'Unexpected adComponentRenderUrls';
if (parsed.has('adComponentCreativeScanningMetadata'))
throw 'Unexpected adComponentCreativeScanningMetadata';
if (parsed.has('adComponentSizes'))
throw 'Unexpected adComponentSizes';
if (parsed.has('adComponentBuyer'))
throw 'Unexpected adComponentBuyer';
`;

const auctionConfigOverrides = {
interestGroupBuyers : [bidder_origin],
trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
sendCreativeScanningMetadata: true,
decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: scoreAdBody})
};

await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
}, 'Creative scanning metadata - basic data flow');

subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL =
createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url');

const bidder_origin = OTHER_ORIGIN1;

await joinCrossOriginInterestGroup(
test, uuid, bidder_origin,
{ads: [{renderURL: renderURL}]});

const scoreAdBody = `
${makeParseHelper(renderURL)}
if (parsed.get('renderUrls') !== '${renderURL}')
throw 'Wrong URL';
if (parsed.get('adCreativeScanningMetadata') !== '')
throw 'Wrong creative scanning metadata';
if (parsed.get('adSizes') !== ',')
throw 'Wrong adSizes';
if (parsed.get('adBuyer') !== '${bidder_origin}')
throw 'Wrong adBuyer';
if (parsed.has('adComponentRenderUrls'))
throw 'Unexpected adComponentRenderUrls';
if (parsed.has('adComponentCreativeScanningMetadata'))
throw 'Unexpected adComponentCreativeScanningMetadata';
if (parsed.has('adComponentSizes'))
throw 'Unexpected adComponentSizes';
if (parsed.has('adComponentBuyer'))
throw 'Unexpected adComponentBuyer';
`;

const auctionConfigOverrides = {
interestGroupBuyers : [bidder_origin],
trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
sendCreativeScanningMetadata: true,
decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: scoreAdBody})
};

await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
}, 'Creative scanning metadata - sending enabled but no metadata specified');

subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL =
createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url');

await joinInterestGroup(
test, uuid,
{ads: [{renderURL: renderURL, creativeScanningMetadata: 'hello'}]});

const scoreAdBody = `
${makeParseHelper(renderURL)}
if (parsed.get('renderUrls') !== '${renderURL}')
throw 'Wrong URL';
if (parsed.has('adCreativeScanningMetadata'))
throw 'Unexpected creative scanning metadata';
if (parsed.has('adSizes'))
throw 'Unexpected adSizes';
if (parsed.has('adBuyer'))
throw 'Unexpected adBuyer';
if (parsed.has('adComponentRenderUrls'))
throw 'Unexpected adComponentRenderUrls';
if (parsed.has('adComponentCreativeScanningMetadata'))
throw 'Unexpected adComponentCreativeScanningMetadata';
if (parsed.has('adComponentSizes'))
throw 'Unexpected adComponentSizes';
if (parsed.has('adComponentBuyer'))
throw 'Unexpected adComponentBuyer';
`;

const auctionConfigOverrides = {
trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
sendCreativeScanningMetadata: false,
decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: scoreAdBody})
};

await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
}, 'Creative scanning metadata - disabled');

subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL =
createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url');

const generateBidBody = `
return {
bid: 1,
render: {
url: interestGroup.ads[0].renderURL,
width: '100px',
height: '10sh'
}
};
`;

await joinInterestGroup(test, uuid, {
ads: [{
renderURL: renderURL,
creativeScanningMetadata: 'hello',
sizeGroup: 'flexible'
}],
sizeGroups: {'flexible': ['small', 'big']},
adSizes: {
'small': {width: '100px', height: '10sh'},
'big': {width: '50sw', height: '200px'},
},
biddingLogicURL: createBiddingScriptURL({generateBid: generateBidBody})
});

const scoreAdBody = `
${makeParseHelper(renderURL)}
if (parsed.get('renderUrls') !== '${renderURL}')
throw 'Wrong URL';
if (parsed.get('adCreativeScanningMetadata') !== 'hello')
throw 'Wrong creative scanning metadata';
if (parsed.get('adSizes') !== '100px,10sh')
throw 'Wrong adSizes';
if (parsed.get('adBuyer') !== '${window.location.origin}')
throw 'Wrong adBuyer';
if (parsed.has('adComponentRenderUrls'))
throw 'Unexpected adComponentRenderUrls';
if (parsed.has('adComponentCreativeScanningMetadata'))
throw 'Unexpected adComponentCreativeScanningMetadata';
if (parsed.has('adComponentSizes'))
throw 'Unexpected adComponentSizes';
if (parsed.has('adComponentBuyer'))
throw 'Unexpected adComponentBuyer';
`;

const auctionConfigOverrides = {
trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
sendCreativeScanningMetadata: true,
decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: scoreAdBody})
};

await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
}, 'Creative scanning metadata - ad size');

subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL =
createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url');
const componentURL1 = createRenderURL(
uuid, /*script=*/null, /*signalsParam=*/'url,component1');
const componentURL2 = createRenderURL(
uuid, /*script=*/null, /*signalsParam=*/'url,component2');

const generateBidBody = `
return {
bid: 1,
render: {
url: interestGroup.ads[0].renderURL,
},
'adComponents': [
{url: '${componentURL1}'},
{url: '${componentURL2}', width: '50sw', height: '200px'},
]
};
`;

await joinInterestGroup(test, uuid, {
ads: [{
renderURL: renderURL,
creativeScanningMetadata: 'hello',
sizeGroup: 'flexible'
}],
adComponents: [
{
renderUrl: componentURL1,
sizeGroup: 'flexible',
creativeScanningMetadata: 'c1'
},
{
renderUrl: componentURL2,
sizeGroup: 'flexible',
creativeScanningMetadata: 'c2'
}
],
sizeGroups: {'flexible': ['small', 'big']},
adSizes: {
'small': {width: '100px', height: '10sh'},
'big': {width: '50sw', height: '200px'},
},
biddingLogicURL: createBiddingScriptURL({generateBid: generateBidBody})
});

const scoreAdBody = `
${makeParseHelper(renderURL)}
if (parsed.get('renderUrls') !== '${renderURL}')
throw 'Wrong URL';
if (parsed.get('adCreativeScanningMetadata') !== 'hello')
throw 'Wrong creative scanning metadata';
if (parsed.get('adSizes') !== ',')
throw 'Wrong adSizes';
if (parsed.get('adBuyer') !== '${window.location.origin}')
throw 'Wrong adBuyer';
// We have to be careful here since we don't order which order the
// components are going to be reported in; so we normalize them and sort
// them.
let adComponentRenderUrls = parsed.get('adComponentRenderUrls').split(',');
let adComponentCreativeScanningMetadata =
parsed.get('adComponentCreativeScanningMetadata').split(',');
let adComponentSizes = parsed.get('adComponentSizes').split(',');
let adComponentBuyer = parsed.get('adComponentBuyer').split(',');
if (adComponentCreativeScanningMetadata.length !=
adComponentRenderUrls.length) {
throw 'Wrong adComponentCreativeScanningMetadata.length';
}
if (adComponentSizes.length !== 2 * adComponentRenderUrls.length) {
throw 'Wrong adComponentSizes.length';
}
if (adComponentBuyer.length !== adComponentRenderUrls.length) {
throw 'Wrong adComponentBuyer.length';
}
let composed = [];
for (let i = 0; i < adComponentRenderUrls.length; ++i) {
let entry = 'url:' + adComponentRenderUrls[i] +
'; creativeScanningMetadata:' +
adComponentCreativeScanningMetadata[i] +
'; size:' + adComponentSizes[2*i] + 'x' + adComponentSizes[2*i+1] +
'; buyer:' + adComponentBuyer[i];
entry = entry.replaceAll('${componentURL1}', 'componentURL1');
entry = entry.replaceAll('${componentURL2}', 'componentURL2');
entry = entry.replaceAll('${window.location.origin}', 'buyer');
composed.push(entry);
}
composed.sort();
if (composed.length !== 2) {
throw 'Wrong # of component entries overall';
}
if (composed[0] !==
'url:componentURL1; creativeScanningMetadata:c1; size:x; buyer:buyer') {
throw 'Wrong component 0';
}
if (composed[1] !==
'url:componentURL2; creativeScanningMetadata:c2; size:50swx200px; ' +
'buyer:buyer') {
throw 'Wrong component 1';
}
`;

const auctionConfigOverrides = {
trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
sendCreativeScanningMetadata: true,
decisionLogicURL: createDecisionScriptURL(uuid, {scoreAd: scoreAdBody})
};

await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
}, 'Creative scanning metadata - ad components');

0 comments on commit 4bf37c1

Please sign in to comment.