From e72310cd019fe7338c8bd710a0b377d098d70fec Mon Sep 17 00:00:00 2001 From: Tianyang Xu <40476544+xtlsheep@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:18:30 -0500 Subject: [PATCH] Add Trusted Signals KVv2 support to W3C Spec (#1340) * Add Trusted Signals KVv2 support to W3C * Add Trusted Signals KVv2 support to W3C * Apply suggestions from code review Co-authored-by: qingxinwu <6334674+qingxinwu@users.noreply.github.com> * Apply suggestions from code review * Apply suggestions from code review * Add struct for compressiong group and partition * Add struct for compressiong group and partition * Update spec based on comments and suggestions * Add Trusted Signals KVv2 support to W3C Spec * Add Trusted Signals KVv2 support to W3C Spec * Use map set in proper positions * Update spec based on comments and suggestions * Update spec based on comments and suggestions * Update spec based on comments and suggestions * Update spec based on comments and suggestions * Update spec based on comments and suggestions * Update spec.bs * Apply suggestions from code review Co-authored-by: Paul Jensen --------- Co-authored-by: Tianyang Xu Co-authored-by: qingxinwu <6334674+qingxinwu@users.noreply.github.com> Co-authored-by: Paul Jensen --- spec.bs | 584 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 502 insertions(+), 82 deletions(-) diff --git a/spec.bs b/spec.bs index b2e3d67c8..4b9923c00 100644 --- a/spec.bs +++ b/spec.bs @@ -239,6 +239,7 @@ dictionary GenerateBidInterestGroup { sequence trustedBiddingSignalsKeys; DOMString trustedBiddingSignalsSlotSizeMode = "none"; long maxTrustedBiddingSignalsURLLength; + USVString trustedBiddingSignalsCoordinator; any userBiddingSignals; sequence ads; sequence adComponents; @@ -379,6 +380,12 @@ This is detectable because it can change the set of fields that are read from th |interestGroup|'s [=interest group/max trusted bidding signals url length=] to |group|["{{GenerateBidInterestGroup/maxTrustedBiddingSignalsURLLength}}"], otherwise [=exception/throw=] a {{TypeError}}. + 1. If |group|["{{GenerateBidInterestGroup/trustedBiddingSignalsCoordinator}}"] [=map/exists=]: + 1. Let |parsedCoordinator| be the result of [=parsing an https origin=] on + |group|["{{GenerateBidInterestGroup/trustedBiddingSignalsCoordinator}}"]. + 1. If |parsedCoordinator| is failure, then [=exception/throw=] a {{TypeError}}. + 1. Set |interestGroup|'s [=interest group/trusted bidding signals coordinator=] to + |parsedCoordinator|. 1. If |group|["{{GenerateBidInterestGroup/userBiddingSignals}}"] [=map/exists=]: 1. Set |interestGroup|'s [=interest group/user bidding signals=] to the result of [=serializing a JavaScript value to a JSON string=], given @@ -742,6 +749,7 @@ dictionary AuctionAdConfig { USVString trustedScoringSignalsURL; long maxTrustedScoringSignalsURLLength; + USVString trustedScoringSignalsCoordinator; sequence interestGroupBuyers; Promise auctionSignals; Promise sellerSignals; @@ -1255,6 +1263,12 @@ To validate and convert auction ad config given an {{AuctionAdConfig} [=auction config/max trusted scoring signals url length=] to |config|["{{AuctionAdConfig/maxTrustedScoringSignalsURLLength}}"], otherwise [=exception/throw=] a {{TypeError}}. +1. If |config|["{{AuctionAdConfig/trustedScoringSignalsCoordinator}}"] [=map/exists=]: + 1. Let |parsedCoordinator| be the result of [=parsing an https origin=] on + |config|["{{AuctionAdConfig/trustedScoringSignalsCoordinator}}"]. + 1. If |parsedCoordinator| is failure, then [=exception/throw=] a {{TypeError}}. + 1. Set |auctionConfig|'s [=auction config/trusted scoring signals coordinator=] to + |parsedCoordinator|. 1. If |config|["{{AuctionAdConfig/interestGroupBuyers}}"] [=map/exists=]: 1. Let |buyers| be a new empty [=list=]. 1. [=list/For each=] |buyerString| in |config|["{{AuctionAdConfig/interestGroupBuyers}}"]: @@ -2024,6 +2038,8 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: and |settings|'s [=environment settings object/policy container=]. 1. [=Fetch the current outstanding trusted signals batch=] given |trustedBiddingSignalsBatcher|, |signalsUrl|, |buyer|, |buyerExperimentGroupId|, |topLevelOrigin|, and |slotSizeQueryParam|. + 1. [=Fetch the trusted key value signals batch=] given |trustedBiddingSignalsBatcher|, + |signalsUrl|, |buyer|, |buyerExperimentGroupId|, |topLevelOrigin|, and |slotSizeQueryParam|. 1. [=Process updateIfOlderThanMs=] with |buyer|, and |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/all per interest group data=]. 1. Let |fetchSignalDuration| be the [=duration from=] |fetchSignalStartTime| to |settings|'s @@ -2201,6 +2217,8 @@ To build an interest group passed to generateBid given an [=interest
|ig|'s [=interest group/trusted bidding signals slot size mode=]
{{GenerateBidInterestGroup/maxTrustedBiddingSignalsURLLength}}
|ig|'s [=interest group/max trusted bidding signals url length=] +
{{GenerateBidInterestGroup/trustedBiddingSignalsCoordinator}} +
The [=serialization of an origin|serialization=] of |ig|'s [=interest group/trusted bidding signals coordinator=]
{{GenerateBidInterestGroup/userBiddingSignals}}
[=Parse a JSON string to a JavaScript value=] given |ig|'s [=interest group/user bidding signals=] @@ -2283,6 +2301,15 @@ contributions map=] |realTimeContributionsMap|, and a [=policy container=] |poli : [=trusted scoring signals request/policy container=] :: |policyContainer| + : [=trusted scoring signals request/signal coordinator=] + :: |auctionConfig|'s [=auction config/trusted scoring signals coordinator=] + + : [=trusted scoring signals request/owner origin=] + :: |generatedBid|'s [=generated bid=/interest group=]'s [=interest group/owner=] + + : [=trusted scoring signals request/joining origin=] + :: |generatedBid|'s [=generated bid=/interest group=]'s [=interest group/joining origin=] + 1. If |auctionConfig|'s [=auction config/trusted scoring signals url=]'s [=url/origin=] is not [=same origin=] with |auctionConfig|'s [=auction config/seller=], then set |isCrossOrigin| to true. @@ -2559,6 +2586,9 @@ failure, or a [=byte sequence=] |responseBody|, and a [=string=] |mimeType|: * |mimeType| is "`application/wasm`" and the result of [=header list/getting=] "`Content-Type`" from |response|'s [=response/header list=] is null or not [=byte-case-insensitive=] equal to "`application/wasm`". + * |mimeType| is "`message/ad-auction-trusted-signals-response`" and the result of + [=header list/getting=] "`Content-Type`" from |response|'s [=response/header list=] is null + or not [=byte-case-insensitive=] equal to "`message/ad-auction-trusted-signals-response`". Note: This was intended to match the behavior of [=compiling a potential WebAssembly response=], but diverges by failing to remove leading and trailing [=HTTP tab or space @@ -2661,7 +2691,8 @@ To fetch trusted signals given a [=URL=] |url|, an [=origin=] |script 1. Let |signals| be null. 1. Let |dataVersion| be null. 1. Let |formatVersion| be null. - 1. Let |perInterestGroupData| be an [=ordered map=]. + 1. Let |perInterestGroupData| be an [=ordered map=] whose [=map/keys=] are [=interest group/name=] + [=strings=] and whose [=map/values=] are [=bidding signals per interest group data=]. 1. [=Fetch=] |request| with [=fetch/useParallelQueue=] set to true, and [=fetch/processResponseConsumeBody=] set to the following steps given a [=response=] |response| and null, failure, or a [=byte sequence=] |responseBody|: @@ -2693,6 +2724,83 @@ To fetch trusted signals given a [=URL=] |url|, an [=origin=] |script 1. Return « |signals|, |perInterestGroupData|, |dataVersion| ». +
+To fetch trusted key value signals given a [=URL=] |url|, a [=byte sequence=] |body|, a +[=string=]-or-null |context|, an [=origin=] |scriptOrigin|, a [=policy container=] |policyContainer|, +an [=map=]-or-null |indexMap| whose keys are [=strings=] and values are [=tuples=] consisting of an +interger and an integer , and a [=boolean=] |isBiddingSignal|: + +1. Let |request| be a new [=request=] with the following properties: + : [=request/URL=] + :: |url| + : [=request/method=] + :: `POST` + : [=request/body=] + :: |body| + : [=request/origin=] + :: |scriptOrigin| + : [=request/header list=] + :: «`Content-Type`: `message/ad-auction-trusted-signals-request`, + `Accept`: `message/ad-auction-trusted-signals-response`» + : [=request/client=] + :: `null` + : [=request/mode=] + :: "`cors`" + : [=request/referrer=] + :: "`no-referrer`" + : [=request/credentials mode=] + :: "`omit`" + : [=request/redirect mode=] + :: "`error`" + : [=request/service-workers mode=] + :: "`none`" + : [=request/policy container=] + :: A new [=policy container=] whose [=policy container/IP address space=] is |policyContainer|'s + [=policy container/IP address space=] + +1. Let |signals| be null. +1. Let |signalsMap| be an empty [=map=], whose keys are [=strings=] and values are [=maps=]. +1. Let |dataVersion| be an empty [=ordered map=], whose [=map/keys=] are [=strings=] and [=map/values=] are integers. +1. Let |perInterestGroupData| be an [=ordered map=] whose [=map/keys=] are [=interest group/name=] [=strings=] + and whose [=map/values=] are [=bidding signals per interest group data=]. +1. [=Fetch=] |request| with [=fetch/useParallelQueue=] set to true, and + [=fetch/processResponseConsumeBody=] set to the following steps given a [=response=] |response| + and null, failure, or a [=byte sequence=] |responseBody|: + 1. If [=validate fetching response=] with |response|, |responseBody| and + "`message/ad-auction-trusted-signals-response`" returns false, return « failure, null, null ». + 1. Let |resultList| be the result of deserializing |responseBody| using |context|. The + deserialization method may follow that described in + [Section 2.3.6 of the Protected Audience Key Value Services](https://privacysandbox.github.io/draft-ietf-protected-audience-key-value-service/draft-ietf-protected-audience-key-value-services.html#name-parsing-a-response). + 1. [=list/For each=] |result| in |resultList|: + 1. If |result| [=map/contains=] "`dataVersion`": + 1. If |result|["dataVersion"] is not an integer, or is less than 0 or more than + 232−1, return « failure, null, null ». + 1. If |isBiddingSignal| is true: + 1. [=map/For each=] |name| → |value| in |result|["interestGroupNames"]: + 1. If |indexMap|[|name|] does not equal to |result|["index"], return « failure, null, null ». + 1. [=map/Set=] |perInterestGroupData|[|name|] to |value|. + 1. If |result| [=map/contains=] "`dataVersion`", [=map/set=] |dataVersion|[|name|] to |result|["dataVersion"]. + 1. [=map/For each=] |key| → |value| in |result|["keys"]: + 1. [=map/Set=] |signalsMap|[|key|] to |value|. + 1. Otherwise: + 1. [=map/For each=] |url| → |value| in |result|["renderUrls"]: + 1. [=map/Set=] |signalsMap|[|url|] to |value|. + 1. If |result| [=map/contains=] `"dataVersion"`, [=map/set=] |dataVersion|[|url|] to |result|["dataVersion"]. + 1. [=map/For each=] |url| → |value| in |result|["adComponentRenderUrls"]: + 1. [=map/Set=] |signalsMap|[|url|] to |value|. +1. If |signalsMap| is not empty, set |signals| to |signalsMap|. +1. Return « |signals|, |perInterestGroupData|, |dataVersion| ». + +
+ +
+ +Add "message/ad-auction-trusted-signals-request" to mimeType’s +essence check list under "content-type" in +[CORS-safelisted request-header](https://fetch.spec.whatwg.org/#cors-safelisted-request-header). + +
+
To encode trusted signals keys given an [=ordered set=] of [=strings=] |keys|: @@ -2982,7 +3090,7 @@ a {{ReportingBrowserSignals}} |browserSignals|, a [=direct from seller signals=]
|leadingBidInfo|'s [=leading bid info/bidding data version=] if it is not null, {{undefined}} otherwise.
{{ReportWinBrowserSignals/adCost}} -
[=Round a value|Rounded=] |winner|’s [=generated bid/ad cost=] +
[=Round a value|Rounded=] |winner|'s [=generated bid/ad cost=]
{{ReportWinBrowserSignals/seller}}
[=serialization of an origin|Serialized=] |config|'s [=auction config/seller=]
{{ReportWinBrowserSignals/madeHighestScoringOtherBid}} @@ -3744,11 +3852,11 @@ The getInterestGroupAdAuctionData(|configIDL|) m
- To look up the server encryption key given an [=origin=] |seller| + To look up the server encryption key given an [=origin=] |origin| and an [=origin=] |coordinator|: 1. Let |keys| be a [=list=] of ([=byte sequence=], [=byte=]) [=tuples=] returned from looking up the [[RFC9180|HPKE]] public key encryption keys and their - corresponding key IDs for |seller| specified by |coordinator|. The actual + corresponding key IDs for |origin| specified by |coordinator|. The actual implementation of this lookup is [=implementation-defined=], and may consist of fetching the keys from a known [=URL=]. 1. If |keys| is failure or |keys| [=list/is empty=], return failure. @@ -5364,7 +5472,7 @@ A decoded additional bid is a [=struct=] with the following [=struct/ *This first introductory paragraph is non-normative.* -In online ad auctions for ad space, it’s sometimes useful to prevent showing an ad to certain +In online ad auctions for ad space, it's sometimes useful to prevent showing an ad to certain audiences, a concept known as negative targeting. To facilitate [=negative targeting=] in Protected Audience [=auctions=], each [=additional bid=] is allowed to identify one or more [=negative interest groups=]. If the user has been joined to any of the identified @@ -6911,6 +7019,16 @@ navigating to another page. Some implementations, such as Chromium, have chosen [=interest group/max trusted bidding signals url length=] to |convertedValue|. 1. Otherwise, jump to the step labeled Abort update. +
"`trustedBiddingSignalsCoordinator`" +
+ 1. If |value| is null: + 1. Set |ig|'s [=interest group/trusted bidding signals coordinator=] to null. + 1. If |value| is a [=string=]: + 1. Let |parsedCoordinator| be the result of [=parsing an https origin=] on |value|. + 1. If |parsedCoordinator| is a failure, jump to the step labeled + Abort update. + 1. Set |ig|'s [=interest group/trusted bidding signals coordinator=] to |parsedCoordinator|. +
"`userBiddingSignals`"
1. Set |ig|'s [=interest group/user bidding signals=] to the result of [=serialize an Infra @@ -7029,10 +7147,10 @@ To process updateIfOlderThanMs given an [=origin=] |buyer|, and an [= 1. [=map/For each=] |igName| → |perIgData| of |perInterestGroupData|, the following steps should be done: - 1. If |perIgData|'s [=bidding signals per interest group data/updateIfOlderThanMs=] is null, + 1. If |perIgData|'s [=bidding signals per interest group data/update if older than ms=] is null, [=iteration/continue=]. 1. Let |updateIfOlderThan| be a [=duration=] of |perIgData|'s - [=bidding signals per interest group data/updateIfOlderThanMs=] milliseconds. + [=bidding signals per interest group data/update if older than ms=] milliseconds. 1. Let |ig| be the [=interest group=] of the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |buyer| and whose [=interest group/name=] is |igName|, or null if [=interest group set=] does not have such an interest group. @@ -7066,7 +7184,7 @@ interface ProtectedAudience {
The queryFeatureSupport(feature) method steps are: -1. Let |featuresTable| be an [=ordered map=] whose keys are {{DOMString}}s and whose values are +1. Let |featuresTable| be an [=ordered map=] whose [=map/keys=] are {{DOMString}}s and whose [=map/values=] are {{boolean}}s or {{long}}s, with the following entries: : "adComponentsLimit" :: 40 @@ -7078,6 +7196,8 @@ The queryFeatureSupport(feature) method :: true : "reportingTimeout" :: true + : "trustedSignalsKVv2" + :: true 1. If |feature| is "*", then return |featuresTable|. 1. If |featuresTable|[|feature|] [=map/exists=], then return |featuresTable|[|feature|]. 1. Return `undefined`. @@ -7130,6 +7250,8 @@ The estimated size of an [=interest group=] |ig| 1. The [=string/length=] of |key|. 1. If |ig|'s [=interest group/max trusted bidding signals url length=] is not null: 1. 4, which is the size of |ig|'s [=interest group/max trusted bidding signals url length=]. +1. If |ig|'s [=interest group/trusted bidding signals coordinator=] is not null: + 1. The [=string/length=] of the [=serialization of an origin|serialization=] of |ig|'s [=interest group/trusted bidding signals coordinator=]. 1. The [=string/length=] of |ig|'s [=interest group/user bidding signals=]. 1. If |ig|'s [=interest group/ads=] is not null, [=list/for each=] |ad| of it: 1. The [=string/length=] of the [=URL serializer|serialization=] of |ad|'s @@ -7507,7 +7629,7 @@ response by either repeating the header or by specifying multiple hashes separat
The following steps will be added to the [=HTTP fetch=] algorithm, immediately under the step "If -internalResponse’s [=status=] is a [=redirect status=]:" +internalResponse's [=status=] is a [=redirect status=]:" 1. [=header list/Delete=] "[:Ad-Auction-Signals:]" from |response|'s [=response/header list=]. @@ -7938,6 +8060,12 @@ An interest group is a [=struct=] with the following [=struct/ : max trusted bidding signals url length :: A {{long}} integer, initially 0. Indicates the maximum trusted bidding signals fetch url length for the interest group. 0 means no limit. + : trusted bidding signals coordinator + :: Null or an [=origin=], initially null. This is used to specify where to obtain the public key + used for encryption and decryption in communication with a trusted bidding signal server running + in a Trust Execution Environment (TEE). When this field is specified, the request will be sent to + a trusted bidding signals server running in a TEE, and the value of + [=interest group/max trusted bidding signals url length=] is ignored. : user bidding signals :: Null or a [=string=]. Additional metadata that the owner can use during on-device bidding. : ads @@ -8097,6 +8225,12 @@ An auction config is a [=struct=] with the following [=struct/ : max trusted scoring signals url length :: A {{long}} integer, initially 0. Indicates the maximum trusted scoring signals fetch url length for the auction config. 0 means no limit. + : trusted scoring signals coordinator + :: Null or an [=origin=], initially null. This is used to specify where to obtain the public key + used for encryption and decryption in communication with a trusted scoring signal server running + in a Trust Execution Environment (TEE). When this field is specified, the request will be sent to + a trusted scoring signals server running in a TEE, and the value of + [=auction config/max trusted scoring signals url length=] is ignored. : interest group buyers :: Null or a [=list=] of [=origins=]. Owners of interest groups allowed to participate in the auction. Each [=origin's=] @@ -8482,7 +8616,7 @@ a [=script fetcher=] |fetcher|: 1. Set |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=] to the result of [=parsing allowed trusted scoring signals origins=] given |response|'s [=response/ header list=]. - 1. Let |bodyStream| be |response|’s [=response/body=]’s [=body/stream=]. + 1. Let |bodyStream| be |response|'s [=response/body=]'s [=body/stream=]. 1. Let |bodyReader| be result of [=ReadableStream/getting a reader=] from |bodyStream|. 1. Let |successSteps| be a set of steps that take a [=byte sequence=] |responseBody|, and perform the following: @@ -8529,13 +8663,17 @@ into smaller number of fetches. It's a [=struct=] with the following [=struct/it : length limit :: A {{long}}, initially 2147483647 (the maximum value that it can hold). Describes the URL length limit the current batch is limited to, the smallest of the limits of the interest groups included. + : key value interest groups + :: An [=ordered map=] whose [=map/keys=] are [=tuples=] of (an [=origin=] for + [=interest group/joining origin=], an [=origin=] for [=interest group/owner=]) and whose + [=map/values=] are [=sets=] of [=interest groups=]. A bidding signals per interest group data is a [=struct=] with the following [=struct/items=]:
- : updateIfOlderThanMs + : update if older than ms :: Null or a {{double}}. If non-null, it is the [=duration=] in milliseconds after which the [=interest group=] is eligible for an [update](#interest-group-update), if it hasn't been joined or updated within that duration. When eligible, the update is performed either following a call @@ -8543,6 +8681,32 @@ A bidding signals per interest group data is a [=struct=] with the fo or explicitly by the {{Navigator/updateAdInterestGroups()}} method.
+A bidding compression group is a collection of [=bidding partitions=] that can be compressed +together in the signals response. It's a [=struct=] with the following [=struct/items=]: + +
+ : compression group id + :: An integer indicates the index of this compression group. + : partitions + :: A [=list=] of [=bidding partition=]. Contains all the [=bidding partitions=] belong to this compression + group. +
+ +A bidding partition is a collection of [=trusted bidding signals batcher/keys=] and +[=trusted bidding signals batcher/ig names=] that can be processed together by the service without +any potential privacy leakage. It's a [=struct=] with the following [=struct/items=]: + +
+ : id + :: An integer indicates the index of this partition. + : namespace + :: A [=map=], whose [=map/keys=] are [=strings=] and [=map/values=] are [=list=] of + [=strings=]. A namespace contains all [=interest group/names=] and + [=interest group/trusted bidding signals keys=] in the partition. + : metadata + :: A [=map=], whose [=map/keys=] and [=map/values=] are [=strings=]. +
+
To append to a bidding signals per-interest group data map given an [=ordered map=] @@ -8558,9 +8722,9 @@ To append to a bidding signals per-interest group data map given an [ 1. If |updateIfOlderThanMs| < 600,000, set |updateIfOlderThanMs| to 600,000. Note: 600,000 milliseconds is 10 minutes. - 1. Set |perGroupData|'s [=bidding signals per interest group data/updateIfOlderThanMs=] to + 1. Set |perGroupData|'s [=bidding signals per interest group data/update if older than ms=] to |updateIfOlderThanMs|. - 1. If |perGroupData|'s [=bidding signals per interest group data/updateIfOlderThanMs=] is not + 1. If |perGroupData|'s [=bidding signals per interest group data/update if older than ms=] is not null, then [=map/set=] |destinationMap|[|sourceKey|] to |perGroupData|.
@@ -8651,43 +8815,151 @@ To batch or fetch trusted bidding signals given a [=trusted bidding s Note: An interest group with no trusted signals keys requests would still fetch and process per-interest group data like priorityVector and - [=bidding signals per interest group data/updateIfOlderThanMs=], but it will get null passed in + [=bidding signals per interest group data/update if older than ms=], but it will get null passed in to its bidding function. - 1. Let |putativeKeys| be a [=set/clone=] of |trustedBiddingSignalsBatcher|'s - [=trusted bidding signals batcher/keys=]. - 1. Let |putativeIgNames| be a [=set/clone=] of |trustedBiddingSignalsBatcher|'s - [=trusted bidding signals batcher/ig names=]. - 1. Let |putativeLengthLimit| be |ig|'s [=interest group/max trusted bidding signals url length=]. - 1. If |ig|'s [=interest group/max trusted bidding signals url length=] is 0 or > - |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=], - then set |putativeLengthLimit| to |trustedBiddingSignalsBatcher|'s - [=trusted bidding signals batcher/length limit=]. - 1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, [=list/extend=] - |putativeKeys| with |ig|'s [=interest group/trusted bidding signals keys=]. - 1. [=list/Append=] |igName| to |putativeIgNames|. - 1. Let |biddingSignalsUrl| be the result of [=building trusted bidding signals url=] with - |signalsUrl|, |putativeKeys|, |putativeIgNames|, |experimentGroupId|, |topLevelOrigin|, and - |slotSizeQueryParam|. - 1. If [=URL serializer|serialized=] |biddingSignalsUrl|'s [=string/length=] ≤ |putativeLengthLimit|: - 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to |putativeKeys|. + 1. If |ig|'s [=interest group/trusted bidding signals coordinator=] is null: + 1. Let |putativeKeys| be a [=set/clone=] of |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/keys=]. + 1. Let |putativeIgNames| be a [=set/clone=] of |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/ig names=]. + 1. Let |putativeLengthLimit| be |ig|'s [=interest group/max trusted bidding signals url length=]. + 1. If |ig|'s [=interest group/max trusted bidding signals url length=] is 0 or > + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=], + then set |putativeLengthLimit| to |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/length limit=]. + 1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, [=list/extend=] + |putativeKeys| with |ig|'s [=interest group/trusted bidding signals keys=]. + 1. [=list/Append=] |igName| to |putativeIgNames|. + 1. Let |biddingSignalsUrl| be the result of [=building trusted bidding signals url=] with + |signalsUrl|, |putativeKeys|, |putativeIgNames|, |experimentGroupId|, |topLevelOrigin|, and + |slotSizeQueryParam|. + 1. If [=URL serializer|serialized=] |biddingSignalsUrl|'s [=string/length=] ≤ |putativeLengthLimit|: + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to |putativeKeys|. + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/ig names=] to + |putativeIgNames|. + 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=] to + |putativeLengthLimit|. + 1. Otherwise: + 1. [=Fetch the current outstanding trusted signals batch=] given |trustedBiddingSignalsBatcher|, + |signalsUrl|, |scriptOrigin|, |experimentGroupId|, |topLevelOrigin|, |slotSizeQueryParam|, and + |policyContainer|. + 1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, set + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to a + [=list/clone=] of |ig|'s [=interest group/trusted bidding signals keys=]. + 1. Otherwise, set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to a + new [=list=]. 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/ig names=] to - |putativeIgNames|. + « |igName| ». 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=] to - |putativeLengthLimit|. + |ig|'s [=interest group/max trusted bidding signals url length=]. 1. Otherwise: - 1. [=Fetch the current outstanding trusted signals batch=] given |trustedBiddingSignalsBatcher|, - |signalsUrl|, |scriptOrigin|, |experimentGroupId|, |topLevelOrigin|, |slotSizeQueryParam|, and - |policyContainer|. - 1. If |ig|'s [=interest group/trusted bidding signals keys=] is not null, set - |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to a - [=list/clone=] of |ig|'s [=interest group/trusted bidding signals keys=]. - 1. Otherwise, set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/keys=] to a - new [=list=]. - 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/ig names=] to - « |igName| ». - 1. Set |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/length limit=] to - |ig|'s [=interest group/max trusted bidding signals url length=]. + 1. Let |keyValueInterestGroups| be |trustedBiddingSignalsBatcher|'s [=trusted bidding signals + batcher/key value interest groups=]. + 1. Let |key| be [=tuple=] of (|ig|'s [=interest group/joining origin=], |ig|'s [=interest group/owner=]). + 1. If |keyValueInterestGroups| [=map/contains=] |key|, [=set/append=] |ig| to + |keyValueInterestGroups|[|key|]. + 1. Otherwise, [=map/set=] |keyValueInterestGroups|[|key|] to a [=set=] « |ig| ». + +
+ +
+ +To build trusted key value bidding signals request body given a [=set=] |interestGroups|, +an {{unsigned short}}-or-null |experimentGroupId|, an [=origin=] |topLevelOrigin|, a [=string=] +|slotSizeQueryParam|, an [=origin=] |coordinator|, and an [=origin=] |owner|: + +1. Let |compressionGroups| be an empty [=list=], whose [=list/items=] are [=bidding compression groups=]. +1. Let |compressionGroupMap| be an empty [=map=], where the [=map/keys=] are integers as + [=bidding compression group/compression group id=], and the [=map/values=] are [=maps=] with integers as [=map/keys=] + for [=bidding partition/id=], and [=bidding partitions=] as their [=map/values=]. +1. Let |compressionIdMap| be an empty [=map=], whose [=map/keys=] are [=origins=] and [=map/values=] are integers. +1. Let |interestGroupIdMap| be an empty [=map=], whose [=map/keys=] are [=strings=] and [=map/values=] are [=tuples=] + of (interger, integer). +1. Let |slotSizeParams| be the result of [=strictly splitting=] |slotSizeQueryParam| on U+003D (=). +1. Let |nextCompressionGroupId| be 0. +1. [=list/For each=] |group| of |interestGroups|: + 1. Let |joiningOrigin| be |group|'s [=interest group/joining origin=]. + 1. If |compressionIdMap| does not [=map/contain=] |joiningOrigin|: + 1. [=map/Set=] |compressionIdMap|[|joiningOrigin|] to |nextCompressionGroupId|. + 1. Increment |nextCompressionGroupId| by 1. + 1. Let |compressionGroupId| be |compressionIdMap|[|joiningOrigin|]. + 1. If |compressionGroupMap| does not [=map/contain=] |compressionGroupId|: + 1. [=map/Set=] |compressionGroupMap|[|compressionGroupId|] to an empty [=map=], whose [=map/keys=] are integers + and [=map/values=] are [=maps=]. + 1. Let |partitionId| be 0. + 1. If |group|'s [=interest group/execution mode=] is not "`group-by-origin`": + 1. If |compressionGroupMap|[|compressionGroupId|] [=map/contains=] 0, set |partitionId| to + [=map/size=] of |compressionGroupMap|[|compressionGroupId|]. + 1. Otherwise, set |partitionId| to [=list/size=] of |compressionGroupMap|[|joiningOrigin|] plus 1. + 1. [=map/Set=] |interestGroupIdMap|[|group|'s [=interest group/name=]] to [=tuple=] of + (|compressionGroupId|, |partitionId|). + 1. If |compressionGroupMap|[|compressionGroupId|] does not [=map/contain=] |partitionId|, set + |compressionGroupMap|[|compressionGroupId|][|partitionId|] to a [=bidding partition=] with the following [=struct/items=]: + : [=bidding partition/id=] + :: |partitionId| + + : [=bidding partition/namespace=] + :: The [=ordered map=] «[ "interestGroupNames" → « |group|'s [=interest group/name=] », + "biddingKeys" → |group|'s [=interest group/trusted bidding signals keys=] ]» + + : [=bidding partition/metadata=] + :: The [=ordered map=] «[ "experimentGroupId" → |experimentGroupId|, |slotSizeParams|[0] → |slotSizeParams|[1] ]» + 1. Otherwise: + 1. [=list/Append=] |group|'s [=interest group/name=] into |compressionGroupMap|[|compressionGroupId|] + [|partitionId|]["interest_group_names"]. + 1. [=list/Append=] |group|'s [=interest group/trusted bidding signals keys=] into + |compressionGroupMap|[|compressionGroupId|][|partitionId|]["bidding_keys"]. +1. [=map/For each=] |id| → |group| in |compressionGroupMap|: + 1. Let |compressionGroup| be a [=bidding compression group=], whose + [=bidding compression group/compression group id=] is |id|, and + [=bidding compression group/partition=] is an empty [=list=]. + 1. [=list/For each=] |partition| in |group|'s [=map/values=]: + 1. [=list/Append=] |partition| to |compressionGroup|'s [=bidding compression group/partition=]. + 1. [=list/Append=] |compressionGroup| to |compressionGroups|. +1. Let |metadata| be an empty [=map=], whose [=map/keys=] and [=map/values=] are [=strings=]. +1. Let |hostname| be the result of [=string/UTF-8 percent-encoding=] the + [=serialization of an origin|serialized=] |topLevelOrigin| using [=component percent-encode set=]. +1. [=map/Set=] |metadata|["hostname"] to |hostname|. +1. Let |keyInfo| be the result of [=looking up the server encryption key=] with |owner| and + |coordinator|. +1. If |keyInfo| is failure, then return « null, null, null ». +1. Let (|requestBlob|, |context|) be the result of generating request with |keyInfo|, |metadata| and + |compressionGroups|. The generation method may follow that described in + [Section 2.2.4 of the Protected Audience Key Value Services](https://privacysandbox.github.io/draft-ietf-protected-audience-key-value-service/draft-ietf-protected-audience-key-value-services.html#name-generating-a-request). +1. Return « |requestBlob|, |interestGroupIdMap|, |context| ». + +
+ +
+To fetch the trusted key value signals batch given a +[=trusted bidding signals batcher=] |trustedBiddingSignalsBatcher|, a [=URL=] |signalsUrl|, +an [=origin=] |scriptOrigin|, an {{unsigned short}}-or-null |experimentGroupId|, an [=origin=] +|topLevelOrigin|, a [=string=] |slotSizeQueryParam|, and a [=policy container=] |policyContainer|: + + 1. If |signalsUrl| is null, return. + 1. [=map/For each=] (|joiningOrigin|, |owner|) → |interestGroups| of [=trusted bidding signals + batcher/key value interest groups=]: + 1. Let « |requestBody|, |interestGroupIdMap|, |context| » be the result of [=building trusted + key value bidding signals request body=] with |interestGroups|, |experimentGroupId|, + |topLevelOrigin|, |slotSizeQueryParam|, |joiningOrigin| and |owner|. + 1. If |requestBody| is not null: + 1. Let « |partialTrustedBiddingSignals|, |partialPerInterestGroupData|, |dataVersion| » be the + result of [=fetching trusted key value signals=] with |signalsUrl|, |requestBody|, |context|, + |scriptOrigin|, |policyContainer|, |interestGroupIdMap| and true. + 1. If |partialTrustedBiddingSignals| is not null: + 1. [=map/For each=] |key| → |value| in |partialTrustedBiddingSignals|, [=map/set=] + |trustedBiddingSignalsBatcher|'s [=trusted bidding signals batcher/all trusted bidding + signals=][|key|] to |value|. + 1. Let |igNames| be |interestGroupIdMap|'s [=map/keys=]. + 1. [=set/For each=] |igName| of |igNames|, [=map/set=] |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/data versions=][|igName|] to |dataVersion|[|igName|]. + 1. [=Append to a bidding signals per-interest group data map=] with + |partialPerInterestGroupData|, |igNames|, and |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/all per interest group data=]. + 1. Otherwise, [=set/for each=] |igName| of |igNames|: + 1. [=map/Set=] |trustedBiddingSignalsBatcher|'s + [=trusted bidding signals batcher/no signals flags=][|igName|] to "fetch-failed".
@@ -8702,9 +8974,9 @@ requests into smaller number of fetches. It's a [=struct=] with the following [= batching. : request map :: A [=map=] from a tuple of [=script fetcher=], a [=URL=] representating the trusted signals - base [=URL=], {{unsigned short}} or null for experiment ID, and - [=origin=], representing the top frame's [=origin=], to a [=list=] of [=trusted scoring signals - requests=]. This organizes fetches that can possibly be merged together. + base [=URL=], {{unsigned short}} or null for experiment ID, [=origin=] or null for coordinator, + and [=origin=], representing the top frame's [=origin], to a [=list=] of [=trusted scoring + signals requests=]. This organizes fetches that can possibly be merged together. : url length limit :: A {{long}} denoting a user-configured limit which should not be exceeded due to combining of fetches. 0 denotes no limit, and is the initial value. @@ -8733,6 +9005,12 @@ invocation: fetch. : policy container :: A [=policy container=] to use for the network fetch. + : signal coordinator + :: An [=origin=] to use for the public key fetch. + : owner origin + :: An [=origin=] of the owner on behalf of the initial interest group. + : joining origin + :: An [=origin=] of the owner's joining origin on behalf of the initial interest group. : reply :: A [=trusted scoring signals reply=], null, or failure. Initially null. Set to a non-null value when the fetch has been completed. @@ -8746,6 +9024,32 @@ A trusted scoring signals reply is a [=struct=] with the following [= :: An {{unsigned long}} or null. +A scoring compression group is a collection of [=scoring partitions=] that can be compressed +together in the signals response. It's a [=struct=] with the following [=struct/items=]: + +
+ : compression group id + :: An integer indicates the index of this compression group. + : partitions + :: A [=list=] of [=scoring partition=]. Contains all the [=scoring partitions=] belong to this compression + group. +
+ +A scoring partition is a collection of [=trusted scoring signals request/render URLs=] and +[=trusted scoring signals request/ad component URLs=] that can be processed together by the service +without any potential privacy leakage. It's a [=struct=] with the following [=struct/items=]: + +
+ : id + :: An integer indicates the index of this partition. + : namespace + :: A [=map=], whose [=map/keys=] are [=strings=] and [=map/values=] are [=list=] of + [=strings=]. A namespace contains all [=trusted scoring signals request/render URLs=] and + [=trusted scoring signals request/ad component URLs=] in the partition. + : metadata + :: A [=map=], whose [=map/keys=] and [=map/values=] are [=strings=]. +
+
To create a trusted scoring signals batcher given a {{long}} |urlLengthLimit|: @@ -8841,6 +9145,90 @@ scoring signals requests=] |entriesToBatch|:
+To build trusted key value scoring signals request body given a non-empty [=list=] of +[=trusted scoring signals requests=] |entriesToBatch|: + +1. Let |firstRequest| be |entriesToBatch|[0]. +1. Let |compressionGroups| be an empty [=list=], whose [=list/items=] are [=scoring compression groups=]. +1. Let |compressionGroupMap| be an empty [=map=], where the [=map/keys=] are integers as + [=scoring compression group/compression group id=], and the [=map/values=] are [=maps=] with integers as [=map/keys=] + for [=scoring partition/id=], and [=scoring partitions=] as their [=map/values=]. +1. Let |compressionIdMap| be an empty [=map=], whose [=map/keys=] are [=tuples=] of ([=origin=], + [=origin=]) and [=map/values=] are integers. +1. Let |renderUrlIdMap| be an empty [=map=], whose [=map/keys=] are [=URLs=] and [=map/values=] are [=tuples=] + of (interger, integer). +1. Let |nextCompressionGroupId| be 0. +1. [=map/For each=] |request| of |entriesToBatch|: + 1. Let |mapKey| be a [=tuple=] of (|request|'s [=trusted scoring signals request/owner origin=], + |request|'s [=trusted scoring signals request/joining origin=]). + 1. If |compressionIdMap| does not [=map/contain=] |mapKey|: + 1. [=map/Set=] |compressionIdMap|[|mapKey|] to |nextCompressionGroupId|. + 1. Increment |nextCompressionGroupId| by 1. + 1. Let |compressionGroupId| be |compressionIdMap|[|mapKey|]. + 1. If |compressionGroupMap| does not [=map/contain=] |compressionGroupId|, then set + |compressionGroupMap|[|compressionGroupId|] to an empty [=map=], whose [=map/keys=] are integers. + 1. Let |partitionId| be [=map/size=] of |compressionGroupMap|[|compressionGroupId|]. + 1. [=map/Set=] |renderUrlIdMap|[|request|'s [=trusted scoring signals request/render URL=]] to [=tuple=] + of (|compressionGroupId|, |partitionId|). + 1. [=map/Set=] |compressionGroupMap|[|compressionGroupId|][|partitionId|] to a [=scoring partition=] with the + following [=struct/items=]: + : [=scoring partition/id=] + :: |partitionId| + + : [=scoring partition/namespace=] + :: The [=ordered map=] «[ "renderUrls" → « |request|'s [=trusted scoring signals request/render URL=] », + "adComponentRenderUrls" → |request|'s [=trusted scoring signals request/ad component URLs=] ]» + + : [=scoring partition/metadata=] + :: The [=ordered map=] «[ "experimentGroupId" → |firstRequest|'s [=trusted scoring signals request/seller experiment group id=] ]» +1. [=map/For each=] |id| → |group| in |compressionGroupMap|: + 1. Let |compressionGroup| be an [=scoring compression group=] whose [=scoring compression group/compression group id=] + is |id| and [=scoring compression group/partition=] is an empty [=list=]. + 1. [=list/For each=] |partition| in |group|'s [=map/values=]: + 1. [=list/Append=] |partition| to |compressionGroup|'s [=scoring compression group/partitions=]. + 1. [=list/Append=] |compressionGroup| to |compressionGroups|. +1. Let |metadata| be an empty [=map=], whose [=map/keys=] and [=map/values=] are [=strings=]. +1. Let |hostname| be the result of [=string/UTF-8 percent-encoding=] the + [=serialization of an origin|serialized=] |firstRequest|'s [=trusted scoring signals request/top + level origin=] using [=component percent-encode set=]. +1. [=map/Set=] |metadata|["hostname"] to |hostname|. +1. Let |keyInfo| be the result of [=looking up the server encryption key=] with |firstRequest|'s + [=trusted scoring signals request/seller=] and |firstRequest|'s [=trusted scoring signals + request/signal coordinator=]. +1. If |keyInfo| is failure, then return « null, null, null ». +1. Let (|requestBlob|, |context|) be the result of generating request with |keyInfo|, |metadata| and + |compressionGroups|. The generation method may follow that described in + [Section 2.2.4 of the Protected Audience Key Value Services](https://privacysandbox.github.io/draft-ietf-protected-audience-key-value-service/draft-ietf-protected-audience-key-value-services.html#name-generating-a-request). +1. Return « |requestBlob|, |renderUrlIdMap| and |context| ». + +
+ +
+ +To build batched trusted key value scoring signals request body given a non-empty [=list=] of +[=trusted scoring signals requests=] |entriesToBatch|: + +1. Let |firstEntry| be |entriesToBatch|[0]. +1. [=list/For each=] |entry| of |entriesToBatch|: + 1. [=Assert=] that |entry|'s [=trusted scoring signals request/base URL=] is equal to + |firstEntry|'s [=trusted scoring signals request/base URL=]. + 1. [=Assert=] that |entry|'s [=trusted scoring signals request/seller=] is equal to + |firstEntry|'s [=trusted scoring signals request/seller=]. + 1. [=Assert=] that |entry|'s [=trusted scoring signals request/seller experiment group id=] is + equal to |firstEntry|'s [=trusted scoring signals request/seller experiment group id=]. + 1. [=Assert=] that |entry|'s [=trusted scoring signals request/top level origin=] is equal to + |firstEntry|'s [=trusted scoring signals request/top level origin=]. + 1. [=Assert=] that |entry|'s [=trusted scoring signals request/seller script fetcher=] is equal to + |firstEntry|'s [=trusted scoring signals request/seller script fetcher=]. + 1. [=Assert=] that |entry|'s [=trusted scoring signals request/policy container=] is equal to + |firstEntry|'s [=trusted scoring signals request/policy container=]. +1. Return the result of [=building trusted key value scoring signals request body=] with + |entriesToBatch|. + +
+ +
+ To batch and fetch trusted scoring signals given a [=trusted scoring signals batcher=] |batcher|: @@ -8864,50 +9252,82 @@ To batch and fetch trusted scoring signals given a [=trusted scoring 1. Let |key| be (|request|'s [=trusted scoring signals request/seller script fetcher=], |request|'s [=trusted scoring signals request/base url=], |request|'s [=trusted scoring signals request/seller experiment group id=], |request|'s [=trusted scoring signals request/ - top level origin=]). + top level origin=], |request|'s [=trusted scoring signals request/signal coordinator=]). 1. If |batcher|'s [=trusted scoring signals batcher/request map=][|key|] does not [=map/exist=], then [=map/set=] |batcher|'s [=trusted scoring signals batcher/request map=][|key|] to an [=list/is empty|empty=] [=list=]. 1. [=list/Append=] |request| to |batcher|'s [=trusted scoring signals batcher/request map=][|key|]. - 1. Some number of times, heuristically, select a |key| and a non-[=list/is empty|empty=] [=set/ - subset=] of |batcher|'s [=trusted scoring signals batcher/request map=][|key|], called - |entriesToBatch|, such that [=checking if trusted scoring signals batch honors URL length - limit=] on |batcher| and |entriesToBatch| returns true: - - Note: implementations are free to wait to collect more requests to merge (by leaving things - in the |batcher|'s [=trusted scoring signals batcher/request map=]), or send them - individually, but need to take into account the configured URL length limit if they do combine - requests. All entries need to be handled eventually. - - 1. [=list/Remove=] |entriesToBatch| from |batcher|'s [=trusted scoring signals batcher/ - request map=][|key|]. - 1. If |batcher|'s [=trusted scoring signals batcher/request map=][|key|] [=list/is empty=], - then [=map/remove=] |key| from |batcher|'s [=trusted scoring signals batcher/request map=]. - 1. Let |fullSignalsUrl| be the result of [=building batched trusted scoring signals url=] for - |entriesToBatch|. + 1. Some number of times, heuristically, select a |key| from |batcher|'s [=trusted scoring signals + batcher/request map=]: + 1. If |key|'s [=trusted scoring signals request/signal coordinator=] is null: + 1. Select a non-[=list/is empty|empty=] [=set/subset=] of |batcher|'s [=trusted scoring + signals batcher/request map=][|key|], called |entriesToBatch|, such that [=checking if + trusted scoring signals batch honors URL length limit=] on |batcher| and |entriesToBatch| + returns true: + + Note: implementations are free to wait to collect more requests to merge (by leaving things + in the |batcher|'s [=trusted scoring signals batcher/request map=]), or send them + individually, but need to take into account the configured URL length limit if they do + combine requests. All entries need to be handled eventually. + + 1. [=list/Remove=] |entriesToBatch| from |batcher|'s [=trusted scoring signals batcher/ + request map=][|key|]. + 1. If |batcher|'s [=trusted scoring signals batcher/request map=][|key|] [=list/is empty=], + then [=map/remove=] |key| from |batcher|'s [=trusted scoring signals batcher/request map=]. + 1. Let |fullSignalsUrl| be the result of [=building batched trusted scoring signals url=] for + |entriesToBatch|. + 1. Let |seller| be |entriesToBatch|[0]'s [=trusted scoring signals request/seller=]. + 1. Let |scriptFetcher| be |entriesToBatch|[0]'s [=trusted scoring signals request/seller + script fetcher=]. + 1. If |fullSignalsUrl|'s [=url/origin=] is not [=same origin=] with |seller| then: + 1. Let |allowCrossOriginTrustedScoringSignalsFrom| be the result of [=wait for cross origin + trusted scoring signals authorization from a fetcher=] given |scriptFetcher|. + 1. If |allowCrossOriginTrustedScoringSignalsFrom| does not [=list/contain=] + |fullSignalsUrl|'s [=url/origin=], set |fullSignalsUrl| to null. + 1. Let |result| be failure. + 1. If |fullSignalsUrl| is not null: + 1. Let «|allTrustedScoringSignals|, ignored, |scoringDataVersion|» be + the result of [=fetching trusted signals=] with |fullSignalsUrl|, |seller|, + |entriesToBatch|[0]'s [=trusted scoring signals request/policy container=], and false. + 1. If |allTrustedScoringSignals| is an [=ordered map=]: + 1. Set |result| to a new [=trusted scoring signals reply=] + 1. Set |result|'s [=trusted scoring signals reply/all trusted scoring signals=] to + |allTrustedScoringSignals|. + 1. Set |result|'s [=trusted scoring signals reply/data version=] to |scoringDataVersion|. + 1. [=list/For each=] |entry| in |entriesToBatch|: + 1. Set |entry|'s [=trusted scoring signals request/reply=] to |result|. + 1. Otherwise: + 1. Select a non-[=list/is empty|empty=] [=set/subset=] of |batcher|'s [=trusted scoring + signals batcher/request map=][|key|], called |entriesToBatch|. + 1. [=map/Remove=] |key| from |batcher|'s [=trusted scoring signals batcher/request map=]. + 1. Let « |requestBody|, |renderUrlIdMap|, |context| » be the result of [=building batched + trusted key value scoring signals request body=] for |entriesToBatch|. + 1. Let |baseUrl| be |entriesToBatch|[0]'s [=trusted scoring signals request/base url=]. 1. Let |seller| be |entriesToBatch|[0]'s [=trusted scoring signals request/seller=]. 1. Let |scriptFetcher| be |entriesToBatch|[0]'s [=trusted scoring signals request/seller script fetcher=]. - 1. Let |policyContainer| be |entriesToBatch|[0]'s [=trusted scoring signals request/policy - container=]. - 1. If |fullSignalsUrl|'s [=url/origin=] is not [=same origin=] with |seller| then: + 1. If |baseUrl|'s [=url/origin=] is not [=same origin=] with |seller| then: 1. Let |allowCrossOriginTrustedScoringSignalsFrom| be the result of [=wait for cross origin trusted scoring signals authorization from a fetcher=] given |scriptFetcher|. 1. If |allowCrossOriginTrustedScoringSignalsFrom| does not [=list/contain=] - |fullSignalsUrl|'s [=url/origin=], set |fullSignalsUrl| to null. - 1. Let |result| be failure. - 1. If |fullSignalsUrl| is not null: - 1. Let «|allTrustedScoringSignals|, ignored, |scoringDataVersion|» be - the result of [=fetching trusted signals=] with |fullSignalsUrl|, |seller|, - |policyContainer|, and false. - 1. If |allTrustedScoringSignals| is an [=ordered map=]: - 1. Set |result| to a new [=trusted scoring signals reply=] + |baseUrl|'s [=url/origin=], set |baseUrl| to null. + 1. Let |allTrustedScoringSignals| be failure. + 1. If |baseUrl| and |requestBody| are not null, let «|allTrustedScoringSignals|, ignored, + |scoringDataVersionMap|» be the result of [=fetching trusted key value signals=] with |baseUrl|, |requestBody|, + |context|, |seller|, |entriesToBatch|[0]'s [=trusted scoring signals request/policy container=], |renderUrlIdMap| + and false. + 1. If |allTrustedScoringSignals| is an [=ordered map=]: + 1. [=list/For each=] |entry| in |entriesToBatch|: + 1. Let |result| be a new [=trusted scoring signals reply=]. 1. Set |result|'s [=trusted scoring signals reply/all trusted scoring signals=] to |allTrustedScoringSignals|. - 1. Set |result|'s [=trusted scoring signals reply/data version=] to |scoringDataVersion|. - 1. [=list/For each=] |entry| in |entriesToBatch|: - 1. Set |entry|'s [=trusted scoring signals request/reply=] to |result|. + 1. If |scoringDataVersionMap| is not null: + 1. Set |result|'s [=trusted scoring signals reply/data version=] to |scoringDataVersionMap| + [|entry|'s [=URL serializer|serialized=] [=trusted scoring signals request/render URL=]]. + 1. Set |entry|'s [=trusted scoring signals request/reply=] to |result|. + 1. Otherwise, [=list/for each=] |entry| in |entriesToBatch|: + 1. Set |entry|'s [=trusted scoring signals request/reply=] to failure.
@@ -9472,8 +9892,8 @@ The is expired of a [=cumulative tim # Privacy Considerations # {#privacy-considerations} Protected Audience aims to advance the privacy of remarketing and custom audience -advertising on the web, so naturally privacy considerations are paramount to Protected Audience’s -design. Partitioning data by site is the central mechanism to prevent joining a user’s identity +advertising on the web, so naturally privacy considerations are paramount to Protected Audience's +design. Partitioning data by site is the central mechanism to prevent joining a user's identity across sites: - Interest group definitions come from just one site, the site that called {{Navigator/joinAdInterestGroup()}}. @@ -9501,7 +9921,7 @@ Protected Audience involves the browser running untrusted JavaScript downloaded parties, so security concerns are top of mind. Fortunately Protected Audience is a highly constrained API not attempting to be a general purpose execution environment. Execution of this JavaScript is controlled and limited as follows: -- Protected Audience requires the origin of the scripts’ URLs to match that of the origin of the +- Protected Audience requires the origin of the scripts' URLs to match that of the origin of the interest group owner, which is in turn required to match the origin of the context calling the {{Navigator/joinAdInterestGroup()}}. - URL schemes are required to be HTTPS.