From 30a60eceda8b33344112574df4b32d46655d97f8 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Fri, 8 Sep 2023 15:06:25 -0400 Subject: [PATCH 01/27] Spec for negative targeting. --- spec.bs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/spec.bs b/spec.bs index 4f236cb3f..75e5112b1 100644 --- a/spec.bs +++ b/spec.bs @@ -22,6 +22,8 @@ Assume Explicit For: yes
 urlPrefix: https://www.ietf.org/rfc/rfc4122.txt
   type: dfn; text: urn uuid
+  type: dfn; text: string representation
+  type: dfn; text: version 4 UUID
 spec: RFC6234; urlPrefix: https://www.ietf.org/rfc/rfc6234.txt
   type: dfn; text: SHA-256
 spec: html; urlPrefix: https://html.spec.whatwg.org/C
@@ -147,9 +149,12 @@ dictionary GenerateBidInterestGroup {
 dictionary AuctionAdInterestGroup : GenerateBidInterestGroup {
   double priority = 0.0;
   record prioritySignalsOverrides;
+  DOMString? additionalBidKey;
 };
 
 
+TODO: Maybe add a note about additionalBidKey cannot co-exist with ads.
+
 {{AuctionAdInterestGroup}} is used by {{Window/navigator}}.{{Navigator/joinAdInterestGroup()}}, and
 when an interest group is stored to [=interest group set=].
 `priority` and `prioritySignalsOverrides` are not passed to `generateBid()` because they can be
@@ -267,6 +272,11 @@ This is detectable because it can change the set of fields that are read from th
         1. If |ad|["{{AuctionAd/buyerAndSellerReportingId}}"] [=map/exists=],
           then set |igAd|'s [=interest group ad/buyer and seller reporting ID=] to it.
       1. [=list/Append=] |igAd| to |interestGroup|'s |interestGroupField|.
+  1. If |group|["{{AuctionAdInterestGroup/additionalBidKey}}"] [=map/exists=]:
+    1. If |group|["{{GenerateBidInterestGroup/ads}}"] [=map/exists=], then [=exception/throw=] a
+      {{TypeError}}.
+    1. If |group|["{{AuctionAdInterestGroup/additionalBidKey}}"]
+    1. Set |interestGroup|'s [=interest group/additional bid key=] to 
 1. If |interestGroup|'s [=interest group/estimated size=] is greater than 50 KB, then
   [=exception/throw=] a {{TypeError}}.
 1. Let |p| be [=a new promise=].
@@ -332,6 +342,8 @@ The estimated size of an [=interest group=] |ig|
   1. The [=string/length=] of the [=URL serializer|serialization=] of |ad|'s
     [=interest group ad/render url=].
   1. The [=string/length=] of |ad|'s [=interest group ad/metadata=] if the field is not null.
+1. If |ig|'s [=interest group/additional bid key=] is not null:
+  1. 32 bytes.
 
 
 
@@ -471,6 +483,35 @@ to conduct an auction to select an advertisement to display to the user, they ca
 tells the browser how to conduct the auction and which on-device recorded interests are allowed to
 bid in the auction for the chance to display their advertisement.
 
+

createAuctionNonce()

+ +{{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a one-time ID +uniquely associated with a single call to {{Window/navigator}}.{{Navigator/runAdAuction()}}. This +nonce will need to be passed back in via a subsequent call to +{{Window/navigator}}.{{Navigator/runAdAuction()}} via the {{AuctionAdConfig}}. This is currently +only needed for auctions that use additional bids, for which the auction nonce will be included in +each additional bid as a way of ensuring that those bids are only used in the auctions for which +they were intended. + + +[SecureContext] +partial interface Navigator { + Promise<DOMString> createAuctionNonce(); +}; + + +
+ +The createAuctionNonce() method steps are: + +1. Let |p| be [=a new promise=]. +1. Run the following steps [=in parallel=]: + 1. Let |nonce| be the [=string representation=] of [=version 4 UUID=]. + 1. [=Queue a task=] to [=resolve=] |p| with |nonce|. +1. Return |p|. + +
+

runAdAuction()

@@ -3045,11 +3086,15 @@ An interest group is a [=struct=] with the following [=struct/items=]: :: Null or a [=string=]. Additional metadata that the owner can use during on-device bidding. : <dfn>ads</dfn> :: Null or a [=list=] of [=interest group ad=]. Contains various ads that the interest group might - show. + show. Must be null if [=interest group/additional bid key=] is not null. : <dfn>ad components</dfn> :: Null or a [=list=] of [=interest group ad=]. Contains various ad components (or "products") that can be used to construct ads composed of multiple pieces — a top-level ad template "container" which includes some slots that can be filled in with specific "products". +: <dfn>additional bid key</dfn> +:: Null or a [=string=]. Must be null if [=interest group/ads=] is not null. The Ed25519 public key + (a 256-bit EdDSA public key) used to guarantee that this [=interest group=], if used by an + additional bid for a negative targeting, can only be used by its [=interest group/owner=]. : <dfn>joining origin</dfn> :: An [=origin=]. The top level page origin from where the interest group was joined. : <dfn>join counts</dfn> From cd8b7ff81cab997cd8e2a58578dc49e0cbda5bb8 Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Tue, 12 Sep 2023 15:07:08 -0400 Subject: [PATCH 02/27] Handling auction config for additional bids. --- spec.bs | 107 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/spec.bs b/spec.bs index e7e90a8dc..a14505321 100644 --- a/spec.bs +++ b/spec.bs @@ -160,7 +160,8 @@ dictionary AuctionAdInterestGroup : GenerateBidInterestGroup { }; -TODO: Maybe add a note about additionalBidKey cannot co-exist with ads. +Note: An {{AuctionAdInterestGroup}} cannot include both {{GenerateBidInterestGroup/ads}} and +{{AuctionAdInterestGroup/additionalBidKey}} at the same time. {{AuctionAdInterestGroup}} is used by {{Window/navigator}}.{{Navigator/joinAdInterestGroup()}}, and when an interest group is stored to [=interest group set=]. @@ -188,7 +189,8 @@ This is detectable because it can change the set of fields that are read from th 1. Let |frameOrigin| be [=this=]'s [=relevant settings object=]'s [=environment settings object/origin=]. 1. [=Assert=] that |frameOrigin| is not an [=opaque origin=] and its [=origin/scheme=] is "`https`". 1. Let |interestGroup| be a new [=interest group=]. -1. Validate the given |group| and set |interestGroup|'s fields accordingly. +1. Validate the given |group| and set |interestGroup|'s fields accordingly. If an [=exception=] is + [=exception/thrown=], return [=a new promise=]. 1. Set |interestGroup|'s [=interest group/expiry=] to the [=current wall time=] plus |group|["{{GenerateBidInterestGroup/lifetimeMs}}"] milliseconds. 1. Set |interestGroup|'s [=interest group/next update after=] to the [=current wall time=] plus 24 @@ -285,10 +287,13 @@ This is detectable because it can change the set of fields that are read from th then set |igAd|'s [=interest group ad/buyer and seller reporting ID=] to it. 1. [=list/Append=] |igAd| to |interestGroup|'s |interestGroupField|. 1. If |group|["{{AuctionAdInterestGroup/additionalBidKey}}"] [=map/exists=]: - 1. If |group|["{{GenerateBidInterestGroup/ads}}"] [=map/exists=], then [=exception/throw=] a - {{TypeError}}. - 1. If |group|["{{AuctionAdInterestGroup/additionalBidKey}}"] - 1. Set |interestGroup|'s [=interest group/additional bid key=] to + 1. Let |decodedKey| be the result of running [=forgiving-base64 decode=] with + |group|["{{AuctionAdInterestGroup/additionalBidKey}}"]. + 1. [=exception/Throw=] a {{TypeError}} if any of the following conditions hold: + * |decodedKey| is a failure; + * |decodedKey|'s [=byte sequence/length=] is not 32; + * |group|["{{GenerateBidInterestGroup/ads}}"] [=map/exists=]. + 1. Set |interestGroup|'s [=interest group/additional bid key=] to |decodedKey|. 1. If |interestGroup|'s [=interest group/estimated size=] is greater than 50 KB, then [=exception/throw=] a {{TypeError}}. 1. Let |p| be [=a new promise=]. @@ -513,15 +518,13 @@ partial interface Navigator {
- The createAuctionNonce() method steps are: -1. Let |p| be [=a new promise=]. -1. Run the following steps [=in parallel=]: - 1. Let |nonce| be the [=string representation=] of [=version 4 UUID=]. - 1. [=Queue a task=] to [=resolve=] |p| with |nonce|. -1. Return |p|. - + 1. Let |p| be [=a new promise=]. + 1. Run the following steps [=in parallel=]: + 1. Let |nonce| be the [=string representation=] of [=version 4 UUID=]. + 1. [=Queue a task=] to [=resolve=] |p| with |nonce|. + 1. Return |p|.

runAdAuction()

@@ -550,6 +553,8 @@ dictionary AuctionAdConfig { record> perBuyerPrioritySignals; Promise> perBuyerCurrencies; sequence componentAuctions = []; + Promise additionalBids; + USVString auctionNonce; AbortSignal? signal; Promise resolveToConfig; }; @@ -932,6 +937,26 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. Set |auctionConfig|'s [=auction config/seller signals=] to the result of [=serializing a JavaScript value to a JSON string=], given |result|. * To handle an error, set |auctionConfig|'s [=auction config/seller signals=] to failure. +1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=]: + 1. Let |nonce| be an empty [=string=]. + 1. [=list/For each=] |i| in [=the range=] from 1 to the [=string/length=] of + |config|["{{AuctionAdConfig/auctionNonce}}"]: + 1. Let |c| be the |i|th [=code unit=] of |config|["{{AuctionAdConfig/auctionNonce}}"] if it is + 0x0000 NULL or in the range 0x0020 SPACE to 0x007F DELETE, inclusive, otherwise "`?`". + 1. Append |c| to the end of |nonce|. + 1. Set |auctionConfig|'s [=auction config/auction nonce=] to the result of running + [=get uuid from string=] with |nonce|. +1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: + 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or + |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a + {{TypeError}}. + 1. [=Handle an input promise in configuration=] given |auctionConfig| and + |config|["{{AuctionAdConfig/additionalBids}}"]: + * To parse the value |result|: + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. + 1. TODO: handle additional bids. + * To handle an error: + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. 1. If |config|["{{AuctionAdConfig/directFromSellerSignals}}"] [=map/exists=]: 1. TODO: The receiving end of this isn't specified yet, so there is no place to put the computed value. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/directFromSellerSignals}}"]: @@ -1050,6 +1075,24 @@ To parse an origin given a [=string=] |input|: +
+ +To get uuid from string given a [=string=] |input|: +1. If |input|'s [=string/length=] is not 36, return an empty [=string=]. +1. Let |uuid| be an empty [=string=]. +1. [=list/For each=] |i| in [=the range=] from 1 to 36: + 1. Let |unit| be |input|'s |i|th [=code unit=]. + 1. If « 8, 13, 18, 23 » [=list/contains=] |i|: + 1. If |unit| is not 0x002d (-): + 1. Return an empty [=string=]. + 1. Otherwise: + 1. If |unit| is not an [=ASCII lower hex digit=]: + 1. Return an empty [=string=]. + 1. Append |unit| to the end of |uuid|. +1. Return |uuid|. + +
+
To update bid count given a [=list=] of [=interest group=]s |igs|: @@ -2309,8 +2352,8 @@ of the following global objects: |functionName|, |argumentsJS|, and 50 milliseconds. 1. If |result| is an [=ECMAScript/abrupt completion=], return « "null", null, null ». 1. Let |resultJSON| be "null". - 1. If |functionName| is "`reportResult`", then set |resultJSON| to the result of - [=serializing a JavaScript value to a JSON string=] given |result|. + 1. If |functionName| is "`reportResult`" or "`reportAdditionalBidResult`", then set |resultJSON| + to the result of [=serializing a JavaScript value to a JSON string=] given |result|. Note: Consider a return value that can't be converted to JSON a valid result, so if an exception was [=exception/thrown=] in the previous step, keep |resultJSON| as "null". @@ -2875,8 +2918,17 @@ The updateAdInterestGroups() method steps are: reporting ID=] to it. 1. [=list/Append=] |igAd| to |ig|'s |interestGroupField|. - +
"`additionalBidKey`" +
+ 1. Let |decodedKey| be the result of running [=forgiving-base64 decode=] with |value|. + 1. Jump to the step labeled Abort update if any of the + following conditions hold: + * |decodedKey| is a failure + * |decodedKey|'s [=byte sequence/length=] is not 32 + * |parsedUpdate|["`ads`"] [=map/exists=] + 1. Set |ig|'s [=interest group/additional bid key=] to |decodedKey|. + 1. Set |ig|'s [=interest group/next update after=] to the [=current wall time=] plus 24 hours. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and [=interest group/name=] in the browser’s [=interest group set=] with |ig|. @@ -3113,9 +3165,10 @@ An interest group is a [=struct=] with the following [=struct/items=]: can be used to construct ads composed of multiple pieces — a top-level ad template "container" which includes some slots that can be filled in with specific "products". : additional bid key -:: Null or a [=string=]. Must be null if [=interest group/ads=] is not null. The Ed25519 public key - (a 256-bit EdDSA public key) used to guarantee that this [=interest group=], if used by an - additional bid for a negative targeting, can only be used by its [=interest group/owner=]. +:: Null or a [=byte sequence=]. Must be null if [=interest group/ads=] is not null. The + Ed25519 public key (a 256-bit EdDSA public key) used to guarantee that this [=interest group=], + if used by an additional bid for a negative targeting, can only be used by its + [=interest group/owner=]. : joining origin :: An [=origin=]. The top level page origin from where the interest group was joined. : join counts @@ -3279,8 +3332,8 @@ An auction config is a [=struct=] with the following items: : pending promise count :: An integer, initially 0. The number of [=auction config/auction signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], - [=auction config/per buyer timeouts=], directFromSellerSignals, or [=auction config/seller signals=] - whose {{Promise}}s are not yet resolved. + [=auction config/per buyer timeouts=], directFromSellerSignals, [=auction config/seller signals=], + or {{AuctionAdConfig/additionalBids}} whose {{Promise}}s are not yet resolved. : config idl :: {{AuctionAdConfig}}. : resolve to config @@ -3289,17 +3342,23 @@ An auction config is a [=struct=] with the following items: : seller currency :: A [=currency tag=]. Specifies the currency bids returned by `scoreAd()` are expected to use, and which reporting for this auction will agree on. - : per buyer currencies :: A {{Promise}} or failure or an [=ordered map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=currency tags=]. Specifies the currency bids returned by `generateBid()` or `scoreAd()` in component auctions are expected to use. The initial value is an empty map. - : all buyers currency :: A [=currency tag=]. Specifies the currency bids returned by `generateBid()` or `scoreAd()` in component auctions are expected to use if [=auction config/per buyer currencies=] does not specify a particular value. - +: auction nonce +:: Null or a [=string=], initially null. + A unique identifier associated with this and only this invocation of + {{Window/navigator}}.{{Navigator/runAdAuction()}}. This must come from a prior call to + {{Window/navigator}}.{{Navigator/createAuctionNonce()}}. This is only required for auctions that + provide additional bids, and each of those additional bids must use the same auction nonce to + ensure that each of those additional bids was intended for this and only this auction. +: expects additional bids +:: A [=boolean=], initially false. Specifies that some bids will be provided as signed exchanges. From dfd9f6fcc9e4aa306b66b779f1dcecb8328f8b9c Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Tue, 12 Sep 2023 16:41:00 -0400 Subject: [PATCH 03/27] progress --- spec.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.bs b/spec.bs index 5d54f1691..7563189f8 100644 --- a/spec.bs +++ b/spec.bs @@ -974,10 +974,10 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a {{TypeError}}. + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: - 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. 1. TODO: handle additional bids. * To handle an error: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. From 1960076eaf6d797f480c57b9b562d9e437db40e3 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Wed, 13 Sep 2023 11:25:15 -0400 Subject: [PATCH 04/27] Rename pending promise count to scoring dependency count. --- spec.bs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/spec.bs b/spec.bs index 7563189f8..f5c20ae81 100644 --- a/spec.bs +++ b/spec.bs @@ -3386,11 +3386,12 @@ An auction config is a [=struct=] with the following items: :: Null or an {{unsigned short}}, initially null. Optional identifier for an experiment group to support coordinated experiments with buyers' trusted servers for buyers without a specified experiment group. -: pending promise count -:: An integer, initially 0. The number of [=auction config/auction signals=], +: scoring dependency count +:: An integer, initially 0. The number of things that are pending that are needed to score + everything. It includes waiting for {{Promise}}s [=auction config/auction signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], - [=auction config/per buyer timeouts=], directFromSellerSignals, [=auction config/seller signals=], - or {{AuctionAdConfig/additionalBids}} whose {{Promise}}s are not yet resolved. + [=auction config/per buyer timeouts=], directFromSellerSignals, [=auction config/seller signals=] + to resolve, waiting for additional bids to parse. : config idl :: {{AuctionAdConfig}}. : resolve to config @@ -3421,7 +3422,7 @@ An auction config is a [=struct=] with the following items:
To wait until configuration input promises resolve given an [=auction config=] |auctionConfig|: -1. Wait until |auctionConfig|'s [=auction config/pending promise count=] is 0. +1. Wait until |auctionConfig|'s [=auction config/scoring dependency count=] is 0. 1. [=Assert=] |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], and [=auction config/per buyer timeouts=] are not {{Promise}}s. @@ -3443,14 +3444,14 @@ To recursively wait until configuration input promises resolve given
To handle an input promise in configuration given an [=auction config=] |auctionConfig|, a {{Promise}} |p|, and two sequences of steps, covering the parsing of the value and error-handling: -1. Increment |auctionConfig|'s [=auction config/pending promise count=]. +1. Increment |auctionConfig|'s [=auction config/scoring dependency count=]. 1. Let |resolvedAndTypeChecked| be the promise representing performing the following steps [=upon fulfillment=] of |p| with |result|: 1. Execute the steps to be run for parsing of the value given |result|. 1. If no exception was [=exception/thrown=] in the previous step: - 1. Decrement |auctionConfig|'s [=auction config/pending promise count=]. + 1. Decrement |auctionConfig|'s [=auction config/scoring dependency count=]. 1. [=Upon rejection=] of |resolvedAndTypeChecked|: 1. Execute the steps for error-handling. - 1. Decrement |auctionConfig|'s [=auction config/pending promise count=]. + 1. Decrement |auctionConfig|'s [=auction config/scoring dependency count=].
From 4af1a81d6d06733a9af7808695e3a60fe464b0de Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Mon, 18 Sep 2023 17:18:19 -0400 Subject: [PATCH 05/27] Finish additional bids, including reporting. --- spec.bs | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 210 insertions(+), 19 deletions(-) diff --git a/spec.bs b/spec.bs index f5c20ae81..b4e60bdcc 100644 --- a/spec.bs +++ b/spec.bs @@ -970,16 +970,18 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. Append |c| to the end of |nonce|. 1. Set |auctionConfig|'s [=auction config/auction nonce=] to the result of running [=get uuid from string=] with |nonce|. +1. Let |additionalBids| be null. 1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a {{TypeError}}. 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. + 1. Set |additionalBids| to a new [=list=] of [=generated bids=]. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: - 1. TODO: handle additional bids. - * To handle an error: + 1. Nothing to do. + * To handle an error: (TODO: check if this is correct when the promise is rejected.) 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. 1. If |config|["{{AuctionAdConfig/directFromSellerSignals}}"] [=map/exists=]: 1. TODO: The receiving end of this isn't specified yet, so there is no place to put the computed value. @@ -1252,11 +1254,11 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Let |compWinner| be the result of running [=generate and score bids=] with |component|, |auctionConfig|, |global|, and |settings|. 1. If |compWinner| is failure, return failure. - 1. If [=recursively wait until configuration input promises resolve=] given |auctionConfig| returns failure, return failure. + 1. If [=recursively wait until scoring dependency done=] given |auctionConfig| returns failure, + return failure. 1. If |compWinner| is not null: 1. Run [=score and rank a bid=] with |auctionConfig|, |compWinner|, |leadingBidInfo|, |decisionLogicScript|, null, "top-level-auction", null, and |settings|'s [=environment/top-level origin=]. - 1. Decrement |pendingComponentAuctions| by 1. 1. Wait until |pendingComponentAuctions| is 0. 1. If |leadingBidInfo|'s [=leading bid info/leading bid=] is null, return null. @@ -1277,7 +1279,7 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Run [=report win=] with |leadingBidInfo|, |sellerSignals|, and |reportResultBrowserSignals|. 1. Return |leadingBidInfo|'s [=leading bid info/leading bid=]. -1. If [=waiting until configuration input promises resolve=] given |auctionConfig| returns failure, return failure. +1. If [=waiting until scoring dependency done=] given |auctionConfig| returns failure, return failure. 1. Let |allBuyersExperimentGroupId| be |auctionConfig|'s [=auction config/all buyer experiment group id=]. 1. Let |allBuyersGroupLimit| be |auctionConfig|'s @@ -1298,7 +1300,17 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Set |auctionLevel| to "component-auction". 1. Set |componentAuctionExpectedCurrency| to the result of [=looking up per-buyer currency=] with |topLevelAuctionConfig| and |auctionConfig|'s [=auction config/seller=]. -1. Let |pendingBuyers| be the [=map/size=] of |bidGenerators|. +1. Let |bidsBeingScored| be 0. +1. Let |additionalBids| be null. +1. If |auctionConfig|'s [=auction config/expects additional bids=] is true: + 1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with + |auctionConfig| and |topLevelAuctionConfig|. + 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: + 1. Increment |bidsBeingScored| by 1. + 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, + |decisionLogicScript|, null, |auctionLevel|, |componentAuctionExpectedCurrency|, and + |settings|'s [=environment/top-level origin=]. + 1. Decrement |bidsBeingScored| by 1. 1. [=map/For each=] |buyer| → |perBuyerGenerator| of |bidGenerators|, [=parallel queue/enqueue steps|enqueue the following steps=] to |queue|: 1. Let |buyerExperimentGroupId| be |allBuyersExperimentGroupId|. @@ -1386,12 +1398,12 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Set |ig|'s [=interest group/ad components=] to |originalAdComponents|. 1. If |generatedBid| is failure, [=iteration/continue=]. 1. [=list/Insert=] |generatedBid|'s [=generated bid/interest group=] in |bidIgs|. + 1. Increment |bidsBeingScored| by 1. 1. [=Score and rank a bid=] with |auctionConfig|, |generatedBid|, |leadingBidInfo|, |decisionLogicScript|, |dataVersion|, |auctionLevel|, |componentAuctionExpectedCurrency|, and |settings|'s [=environment/top-level origin=]. - - 1. Decrement |pendingBuyers| by 1. -1. Wait until |pendingBuyers| is 0. + 1. Decrement |bidsBeingScored| by 1. +1. Wait until |bidsBeingScored| is 0. 1. If |leadingBidInfo|'s [=leading bid info/leading bid=] is null, return null. 1. If |topLevelAuctionConfig| is null: 1. Let « |sellerSignals|, |reportResultBrowserSignals| » be the result of running @@ -1437,6 +1449,157 @@ To build an interest group passed to generateBid given an [=interest 1. Return |igGenerateBid|.
+
+To validate and convert additional bids given an [=auction config=] |auctionConfig|, and +an [=auction config=]-or-null |topLevelAuctionConfig|: + + 1. Let |auctionNonce| be |auctionConfig|'s [=auction config/auction nonce=]. + 1. Let |capturedAdditionalBidsHeaders| be [=this=]'s [=relevant global object=]'s + [=associated Document's=] [=node navigable's=] [=traversable navigable's=] + [=captured additional bids headers=]. + 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. + 1. [=list/For each] |encodedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: + 1. Let |additionalBid| be the result of running [=forgiving-base64 decode=] with + |encodedAdditionalBid|. + 1. If |additionalBid| is failure, [=iteration/continue=]. + 1. Increment |auctionConfig|’s [=auction config/scoring dependency count=]. + 1. Let |parsedAdditionalBid| be the result of [=parse a JSON string to an infra value=] with + |additionalBid|. + 1. If |parsedAdditionalBid| is not a [=map=], then decrement |auctionConfig|’s + [=auction config/scoring dependency count=], and [=iteration/continue=]. + 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] with + |parsedAdditionalBid|, |auctionConfig| and |topLevelAuctionConfig|. + 1. If |decodedAdditionalBid| is failure, then decrement |auctionConfig|’s + [=auction config/scoring dependency count=], and [=iteration/continue=]. + 1. If |decodedAdditionalBid|'s [=additional bid/bid=]'s [=generated bid/bid=]'s + [=bid with currency/currency=] is not the result of running [=look up per-buyer currency=] + with |auctionConfig|, then decrement |auctionConfig|’s + [=auction config/scoring dependency count=], and [=iteration/continue=]. + 1. TODO: Check whether drop due to negative targeting. + 1. [=list/Append=] |decodedAdditionalBid| to |additionalBids|. + 1. Return |additionalBids|. +
+ +
+To decode an additional bid given a [=map=] |parsedAdditionalBid|, an [=auction config=] +|auctionConfig|, and an [=auction config=]-or-null |topLevelAuctionConfig|: + + 1. Return faliure if any of the following conditions hold: + * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; + * |parsedAdditionalBid|["auctionNonce"] is not |auctionConfig|'s [=auction config/auction nonce=]; + * |parsedAdditionalBid|["seller"] does not [=map/exist=]; + * The result of running the [=parse an origin=] with |parsedAdditionalBid|["seller"] is + failure, or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. + 1. If |topLevelAuctionConfig| is null: + 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. + 1. Otherwise: + 1. Let |bidTopLevelSeller| be the result of running the [=parse an origin=] with + |parsedAdditionalBid|["topLevelSeller"]. + 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], or |bidTopLevelSeller| + is failure, or |bidTopLevelSeller| is not [=same origin=] with |topLevelAuctionConfig|'s + [=auction config/seller=], then return failure. + 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. + 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. + 1. Let |igName| be |igMap|["name"] if it [=map/exists=], otherwise return failure. + 1. Let |igBiddingUrlStr| be |igMap|["biddingLogicURL"] if it [=map/exists=], otherwise return + failure. + 1. Let |igOwnerStr| be |igMap|["owner"] if it [=map/exists=], otherwise return failure. + 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igBiddingUrlStr|. + 1. Let |igOwner| be the result of running [=parse an origin=] with |igOwnerStr|. + 1. Return failure if any of the following conditions hold: + * |igOwner| is failure; + * |igOwner|'s [=origin/scheme=] is not "`https`"; + * |igBiddingUrl| is failure; + * |igOwner| is not [=same origin=] with |igBiddingUrl|. + 1. Let |ig| be a new [=interest group=] with the following properties: + : [=interest group/owner=] + :: |igOwner| + : [=interest group/name=] + :: |igName| + : [=interest group/bidding url=] + :: |igBiddingUrl| + 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=map=], return failure. + 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. + 1. Let |renderUrlStr| be |bidMap|["render"] if it [=map/exists=], otherwise return failure. + 1. Let |renderUrl| be the result of running [=url parser=] on |renderUrlStr|. + 1. If |renderUrl| is failure, then return failure. + 1. Set |ig|'s [=interest group/ads=] to « |renderUrl| ». + 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. + 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, return failure. + 1. Let |adMetadata| be "null". + 1. If |bidMap|["ad"] [=map/exists=]: + 1. Set |adMetadata| to the result of running [=serialize an Infra value to a JSON string=] with + |bidMap|["ad"]. + 1. Let |bidCurrency| be null. + 1. If |bidMap|["bidCurrency"] [=map/exists=]: + 1. If |bidMap|["bidCurrency"] is not a [=string=], or the result of [=checking whether a string + is a valid currency tag=] is failure: + 1. Return failure. + 1. Set |bidCurrency| to |bidMap|["bidCurrency"]. + 1. Let |adCost| be null. + 1. If |bidMap|["adCost"] [=map/exists=]: + 1. If |bidMap|["adCost"] is not a {{double}}, then return failure. + 1. Set |adCost| to |bidMap|["adCost"]. + 1. Let |modelingSignals| be null. + 1. If |bidMap|["modelingSignals"] [=map/exists=]: + 1. If |bidMap|["modelingSignals"] is not a {{double}}, then return failure. + 1. If |bidMap|["modelingSignals"] is greater than or equal to 0, and less than 4096: + 1. Set |modelingSignals| to |bidMap|["modelingSignals"]. + 1. Let |adComponents| be a new [=list=] of [=ad descriptors=]. + 1. If |bidMap|["adComponents"] [=map/exists=]: + 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. + 1. [=list/For each=] |component| of |bidMap|["adComponents"]: + 1. If |component| is not a [=string=], then return failure. + 1. Let |componentUrl| be the result of running [=url parser=] on |component|. + 1. If |componentUrl| is failure, then return failure. + 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is + |componentUrl|. + 1. [=list/Append=] |componentDescriptor| to |adComponents|. + 1. Set |ig|'s [=interest group/ad components=] to |adComponents|. + 1. Let |result| be a new [=additional bid=]. + 1. If |parsedAdditionalBid|["negativeInterestGroup"] [=map/exists=]: + 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or + |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. + 1. [=list/Append=] |parsedAdditionalBid|["negativeInterestGroup"] to |result|'s + [=additional bid/negative target interest group names=]. + 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=]: + 1. Let |multipleNegativeIg| be |parsedAdditionalBid|["negativeInterestGroups"]. + 1. Return failure if any of the following conditions hold: + * |multipleNegativeIg| is not a [=map=]; + * |multipleNegativeIg|["joiningOrigin"] does not [=map/exist=], or it's not a [=string=]; + * |multipleNegativeIg|["interestGroupNames"] does not [=map/exist=], or it's not a [=list=]. + 1. Let |joiningOrigin| be the result of running [=parse an origin=] with + |multipleNegativeIg|["joiningOrigin"]. + 1. If |joiningOrigin| is failure, or its [=origin/scheme=] is not "`https`", then return failure. + 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. + 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: + 1. If |igName| is not a [=string=], then return failure. + 1. [=list/Append=] |igName| to |result|'s + [=additional bid/negative target interest group names=]. + 1. Set |result|'s [=additional bid/bid=] to a new [=generated bid=] with the following properties: + : [=generated bid/bid=] + :: A [=bid with currency=] whose [=bid with currency/value=] is |bidVal|, and + [=bid with currency/currency=] is |bidCurrency| + : [=generated bid/ad=] + :: |adMetadata| + : [=generated bid/ad descriptor=] + :: An [=ad descriptor=] whose [=ad descriptor/url=] is |renderUrl| + : [=generated bid/ad component descriptors=] + :: |adComponents| + : [=generated bid/ad cost=] + :: |adCost| + : [=generated bid/modeling signals=] + :: |modelingSignals| + : [=generated bid/interest group=] + :: |ig| + : [=generated bid/bid ad=] + :: A [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|, and + [=interest group ad/metadata=] is |adMetadata| + : [=generated bid/provided as additional bid=] + :: true + 1. Return |result|. +
+
To serialize a URL given a [=URL=]-or-null |url|: @@ -1901,8 +2064,9 @@ To round a value given a {{double}} |value|:
-To report result given a [=leading bid info=] |leadingBidInfo| and [=auction config=] or -null |winningComponentConfig|: +To report result given a [=leading bid info=] |leadingBidInfo| and [=auction config=]-or-null +|winningComponentConfig|: + 1. Let |config| be |leadingBidInfo|'s [=leading bid info/auction config=]. 1. Let |bidCurrency| be null. 1. If |winningComponentConfig| is not null: @@ -2039,6 +2203,9 @@ and a {{ReportingBrowserSignals}} |browserSignals|: 1. Otherwise, [=map/Set=] |reportWinBrowserSignals|["{{ReportWinBrowserSignals/interestGroupName}}"] to |winner|'s [=generated bid/interest group=] [=interest group/name=]. 1. Let |buyerReportingScript| be the result of [=fetching script=] with |winner|'s [=generated bid/interest group=]'s [=interest group/bidding url=]. + 1. Let |reportFunctionName| be "`reportWin`". + 1. If |winner|'s [=generated bid/provided as additional bid=] is true: + 1. Set |reportFunctionName| be "`reportContextualWin`". 1. Let « ignored, |resultUrl|, |reportingBeaconMap|, |reportingMacroMap| » be the result of [=evaluating a reporting script=] with |buyerReportingScript|, "`reportWin`", and « |leadingBidInfo|'s [=leading bid info/auction config=]'s [=auction config/config idl=]'s @@ -3420,8 +3587,25 @@ An auction config is a [=struct=] with the following items: +An additional bid is a [=struct=] with the following [=struct/items=]: + +
+: bid +:: A [=generated bid=]. Fields analogous to those returned by `generateBid()`. +: negative target interest group names +:: A [=list=] of [=strings=]. +: negative target joining origin +:: Null or an [=origin=]. Required if there is more than one entry in + [=additional bid/negative target interest group names=]. + +
+ +Each [=traversable navigable=] has a captured additional bids headers, which is a [=map=] +whose [=map/keys=] are [=strings=] for auction nonces, and whose values are [=list=] of [=strings=] +for encoded additional bids. +
-To wait until configuration input promises resolve given an [=auction config=] |auctionConfig|: +To wait until scoring dependency done given an [=auction config=] |auctionConfig|: 1. Wait until |auctionConfig|'s [=auction config/scoring dependency count=] is 0. 1. [=Assert=] |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], and @@ -3434,10 +3618,11 @@ To wait until configuration input promises resolve given an [=auction
-To recursively wait until configuration input promises resolve given an [=auction config=] |auctionConfig|: +To recursively wait until scoring dependency done given an [=auction config=] |auctionConfig|: 1. [=list/For each=] |componentAuctionConfig| in |auctionConfig|'s [=auction config/component auctions=]: - 1. If the result of [=waiting until configuration input promises resolve=] given |componentAuctionConfig| is failure, return failure. -1. Return the result of [=waiting until configuration input promises resolve=] given |auctionConfig|. + 1. If the result of [=waiting until scoring dependency done=] given |componentAuctionConfig| is + failure, return failure. +1. Return the result of [=waiting until scoring dependency done=] given |auctionConfig|.
@@ -3504,8 +3689,9 @@ Numeric value of a bid and the currency it is in.

Generated bid

-The output of running a Protected Audience `generateBid()` script, which needs to be scored by -the seller. +A bid that needs to be scored by the seller. The bid is either the output of running a Protected +Audience `generateBid()` script, or the additional bid provided by the "`Ad-Auction-Additional-Bid`" +response headers.
: bid @@ -3516,11 +3702,13 @@ the seller. original bid if the currency already matched, or a conversion provided by `scoreAd()`. : ad :: A [=string=]. JSON string to be passed to the scoring function. + TODO: It seems this and the [=generated bid/ad descriptor=] can be moved to + [=generated bid/bid ad=] to avoid duplication. : ad descriptor :: An [=ad descriptor=]. Render URL and size of the bid's ad. : ad component descriptors :: Null or a [=list=] of [=ad descriptors=]. Ad components associated with bid, if any. May have at - most 20 URLs. Must be null if the interest group making this bid has a null + most 20 elements. Must be null if the interest group making this bid has a null [=interest group/ad components=] field. : ad cost :: Null or a {{double}}. Advertiser click or conversion cost passed from `generateBid()` to @@ -3529,7 +3717,8 @@ the seller. : modeling signals :: Null or an {{unsigned short}}. A 0-4095 integer (12-bits) passed to `reportWin()`, with noising. : interest group -:: An [=interest group=], whose `generateBid()` invocation generated this bid. +:: An [=interest group=], whose `generateBid()` invocation generated this bid, or specified by the + additional bid. : bid ad :: The [=interest group ad=] within [=generated bid/interest group=] to display. : modified bid @@ -3537,6 +3726,8 @@ the seller. The bid value a component auction's `scoreAd()` script returns. : bid duration :: A [=duration=] in milliseconds. How long it took to run `generateBid()`. +: provided as additional bid +:: A [=boolean=]. Initially false.
From dd33c7042b8518efb76e70e360ad67c945fd6c72 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Tue, 19 Sep 2023 15:50:50 -0400 Subject: [PATCH 06/27] Finish negative targeting. --- spec.bs | 135 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 31 deletions(-) diff --git a/spec.bs b/spec.bs index 109d7230d..94e8a136e 100644 --- a/spec.bs +++ b/spec.bs @@ -26,6 +26,9 @@ urlPrefix: https://www.ietf.org/rfc/rfc4122.txt type: dfn; text: version 4 UUID spec: RFC6234; urlPrefix: https://www.ietf.org/rfc/rfc6234.txt type: dfn; text: SHA-256 +urlPrefix: https://datatracker.ietf.org/doc/html/rfc8032 + type: dfn + text: verify; url: section-5.1.7 spec: html; urlPrefix: https://html.spec.whatwg.org/C type: dfn text: create an agent; url: create-an-agent @@ -965,8 +968,8 @@ To validate and convert auction ad config given an {{AuctionAdConfig} * To handle an error, set |auctionConfig|'s [=auction config/seller signals=] to failure. 1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=]: 1. Let |nonce| be an empty [=string=]. - 1. [=list/For each=] |i| in [=the range=] from 1 to the [=string/length=] of - |config|["{{AuctionAdConfig/auctionNonce}}"]: + 1. [=list/For each=] |i| in [=the range=] from 0 to the [=string/length=] of + |config|["{{AuctionAdConfig/auctionNonce}}"], exclusive: 1. Let |c| be the |i|th [=code unit=] of |config|["{{AuctionAdConfig/auctionNonce}}"] if it is 0x0000 NULL or in the range 0x0020 SPACE to 0x007F DELETE, inclusive, otherwise "`?`". 1. Append |c| to the end of |nonce|. @@ -1109,7 +1112,7 @@ To parse an origin given a [=string=] |input|: To get uuid from string given a [=string=] |input|: 1. If |input|'s [=string/length=] is not 36, return an empty [=string=]. 1. Let |uuid| be an empty [=string=]. -1. [=list/For each=] |i| in [=the range=] from 1 to 36: +1. [=list/For each=] |i| in [=the range=] from 1 to 36, inclusive: 1. Let |unit| be |input|'s |i|th [=code unit=]. 1. If « 8, 13, 18, 23 » [=list/contains=] |i|: 1. If |unit| is not 0x002d (-): @@ -1162,9 +1165,20 @@ To get uuid from string given a [=string=] |input|: To build bid generators map given an [=auction config=] |auctionConfig|: 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=per buyer bid generators=]. +1. Let |negativeInfo| be a new [=map=] whose [=map/keys=] are [=tuples=] consisting of an [=origin=] + and a [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a + [=byte sequence=]. 1. [=list/For each=] |buyer| in |auctionConfig|'s [=auction config/interest group buyers=]: 1. [=list/For each=] |ig| of the user agent's [=interest group set=] whose [=interest group/owner=] is |buyer|: + 1. Let |igName| be |ig|'s [=interest group/name=]. + 1. If |ig|'s [=interest group/additional bid key=] is not null: + 1. [=map/Set=] |negativeInfo|[(|buyer|, |igName|)] to (|ig|'s + [=interest group/joining origin=], |ig|'s [=interest group/additional bid key=]) + 1. [=list/Append=] |ig| to |negativeInfo|. + 1. [=iteration/Continue=] if any of the following conditions hold: + * |ig|'s [=interest group/bidding url=] is null; + * |ig|'s [=interest group/ads=] is null, or [=list/is empty=]. 1. Let |signalsUrl| be |ig|'s [=interest group/trusted bidding signals url=]. 1. Let |joiningOrigin| be |ig|'s [=interest group/joining origin=]. 1. If |bidGenerators| does not [=map/contain=] |buyer|: @@ -1187,7 +1201,7 @@ To build bid generators map given an [=auction config=] |auctionConfi 1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to « |ig| ». 1. Otherwise: 1. [=list/Append=] |ig| to |perSignalsUrlGenerator|[|joiningOrigin|]. -1. Return |bidGenerators|. +1. Return « |bidGenerators|, |negativeInfo| ».
@@ -1245,7 +1259,8 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Let |decisionLogicScript| be the result of [=fetching script=] with |auctionConfig|'s [=auction config/decision logic url=]. 1. If |decisionLogicScript| is failure, return null. -1. Let |bidGenerators| be the result of running [=build bid generators map=] with |auctionConfig|. +1. Let « |bidGenerators|, |negativeInfo| » be the result of running + [=build bid generators map=] with |auctionConfig|. 1. Let |leadingBidInfo| be a new [=leading bid info=]. 1. Let |queue| be the result of [=starting a new parallel queue=]. 1. If |auctionConfig|'s [=auction config/component auctions=] are not [=list/is empty|empty=]: @@ -1307,7 +1322,7 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Let |additionalBids| be null. 1. If |auctionConfig|'s [=auction config/expects additional bids=] is true: 1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with - |auctionConfig| and |topLevelAuctionConfig|. + |auctionConfig|, |topLevelAuctionConfig| and |negativeInfo|. 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: 1. Increment |bidsBeingScored| by 1. 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, @@ -1453,8 +1468,10 @@ To build an interest group passed to generateBid given an [=interest
-To validate and convert additional bids given an [=auction config=] |auctionConfig|, and -an [=auction config=]-or-null |topLevelAuctionConfig|: +To validate and convert additional bids given an [=auction config=] |auctionConfig|, an +[=auction config=]-or-null |topLevelAuctionConfig|, and a new [=map=] |negativeInfo| whose +[=map/keys=] are [=tuples=] consisting of an [=origin=] and a [=string=], and whose [=map/values=] +are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: 1. Let |auctionNonce| be |auctionConfig|'s [=auction config/auction nonce=]. 1. Let |capturedAdditionalBidsHeaders| be [=this=]'s [=relevant global object=]'s @@ -1462,32 +1479,77 @@ an [=auction config=]-or-null |topLevelAuctionConfig|: [=captured additional bids headers=]. 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. 1. [=list/For each] |encodedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: - 1. Let |additionalBid| be the result of running [=forgiving-base64 decode=] with + 1. Let |decodedSignedAdditionalBid| be the result of running [=forgiving-base64 decode=] with |encodedAdditionalBid|. - 1. If |additionalBid| is failure, [=iteration/continue=]. + 1. If |decodedSignedAdditionalBid| is failure, [=iteration/continue=]. 1. Increment |auctionConfig|’s [=auction config/scoring dependency count=]. - 1. Let |parsedAdditionalBid| be the result of [=parse a JSON string to an infra value=] with - |additionalBid|. - 1. If |parsedAdditionalBid| is not a [=map=], then decrement |auctionConfig|’s + 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] + with |decodedSignedAdditionalBid|. + 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=], and + [=iteration/continue=] if any of the following conditions hold: + * |parsedSignedAdditionalBid| is not a [=map=]; + * |parsedSignedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=string=]; + * |parsedSignedAdditionalBid|["signatures"] does not [=map/exist=], or it's not a [=list=]. + 1. Let |signatures| be a new [=list=] of [=signed additional bid signatures=]. + 1. Let |decodeSignatureFailed| be false. + 1. [=list/For each=] |sig| of |parsedSignedAdditionalBid|["signatures"]: + 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following + conditions hold: + * |sig| is not a [=map=]; + * |sig|["key"] does not [=map/exist=], or it's not a [=string=]; + * |sig|["signature"] does not [=map/exist=], or it's not a [=string=]. + 1. Let |maybeKey| be the result of running [=forgiving-base64 decode=] with |sig|["key"]. + 1. Let |maybesignature| be the result of running [=forgiving-base64 decode=] with + |sig|["signature"]. + 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following + conditions hold: + * |maybeKey| is failure, or its [=byte sequence/length=] is not 32; + * |maybesignature| is failure, or its [=byte sequence/length=] is not 64; + 1. Let |signature| be a [=signed additional bid signatures=], whose + [=signed additional bid signature/key=] is |maybeKey|, and + [=signed additional bid signature/signature=] is |maybesignature|. + 1. [=list/Append=] |signature| to |signatures|. + 1. If |decodeSignatureFailed| is true, then decrement |auctionConfig|’s [=auction config/scoring dependency count=], and [=iteration/continue=]. 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] with - |parsedAdditionalBid|, |auctionConfig| and |topLevelAuctionConfig|. - 1. If |decodedAdditionalBid| is failure, then decrement |auctionConfig|’s - [=auction config/scoring dependency count=], and [=iteration/continue=]. - 1. If |decodedAdditionalBid|'s [=additional bid/bid=]'s [=generated bid/bid=]'s - [=bid with currency/currency=] is not the result of running [=look up per-buyer currency=] - with |auctionConfig|, then decrement |auctionConfig|’s - [=auction config/scoring dependency count=], and [=iteration/continue=]. - 1. TODO: Check whether drop due to negative targeting. - 1. [=list/Append=] |decodedAdditionalBid| to |additionalBids|. + |parsedSignedAdditionalBid|["bid"], |auctionConfig| and |topLevelAuctionConfig|. + 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=], and + [=iteration/continue=] if any of the following conditions hold: + * |decodedAdditionalBid| is failure; + * |decodedAdditionalBid|'s [=additional bid/bid=]'s [=generated bid/bid=]'s + [=bid with currency/currency=] is not the result of running [=look up per-buyer currency=] + with |auctionConfig|. + 1. Let |validSignatureIndices| be a new [=list=] of integers. + 1. [=list/For each=] |i| in [=list/get the indices|indices=] of |signatures|: + 1. If [=verify=] |signatures|[|i|] succeeded: + 1. [=list/Append=] |i| to |validSignatureIndices|. + 1. Let |negativeTargeted| be false. + 1. Let |additionalBidBuyer| be |decodedAdditionalBid|'s [=additional bid/bid=]'s + [=generated bid/interest group=]'s [=interest group/owner=]. + 1. [=list/For each=] |igName| of |decodedAdditionalBid|'s + [=additional bid/negative target interest group names=]: + 1. If |negativeInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: + 1. Let (|joiningOrigin|, |additionalBidKey|) be |negativeInfo|[(|additionalBidBuyer|, |igName|)]. + 1. Let |KeyMatchedValidSignature| be false. + 1. [=list/For each=] |i| of |validSignatureIndices|: + 1. If |signatures|[|i|]'s [=signed additional bid signature/key=] is |additionalBidKey|: + 1. Set |KeyMatchedValidSignature| to true, and [=iteration/break=]. + 1. If |KeyMatchedValidSignature| is false, then [=iteration/continue=]. + 1. If |joiningOrigin| is not null: + 1. If |joiningOrigin| is not [=same origin=] with |decodedAdditionalBid|'s + [=additional bid/negative target joining origin=], then [=iteration/continue=]. + 1. Set |negativeTargeted| to true. + 1. If |negativeTargeted| is false: + 1. [=list/Append=] |decodedAdditionalBid| to |additionalBids|. + 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=]. 1. Return |additionalBids|.
To decode an additional bid given a [=map=] |parsedAdditionalBid|, an [=auction config=] |auctionConfig|, and an [=auction config=]-or-null |topLevelAuctionConfig|: - - 1. Return faliure if any of the following conditions hold: + 1. Let |result| be a new [=additional bid=]. + 1. Return faliure if any of the following conditions hold: * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; * |parsedAdditionalBid|["auctionNonce"] is not |auctionConfig|'s [=auction config/auction nonce=]; * |parsedAdditionalBid|["seller"] does not [=map/exist=]; @@ -1512,6 +1574,7 @@ To decode an additional bid given a [=map=] |parsedAdditionalBid|, an 1. Return failure if any of the following conditions hold: * |igOwner| is failure; * |igOwner|'s [=origin/scheme=] is not "`https`"; + * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; * |igBiddingUrl| is failure; * |igOwner| is not [=same origin=] with |igBiddingUrl|. 1. Let |ig| be a new [=interest group=] with the following properties: @@ -1559,7 +1622,6 @@ To decode an additional bid given a [=map=] |parsedAdditionalBid|, an |componentUrl|. 1. [=list/Append=] |componentDescriptor| to |adComponents|. 1. Set |ig|'s [=interest group/ad components=] to |adComponents|. - 1. Let |result| be a new [=additional bid=]. 1. If |parsedAdditionalBid|["negativeInterestGroup"] [=map/exists=]: 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. @@ -1799,7 +1861,7 @@ The Ad-Auction-Allowed HTTP response header
To validate fetching response given a [=response=] |response|, null, failure, or a -[=byte sequence=]|responseBody|, and a [=string=] |mimeType|: +[=byte sequence=] |responseBody|, and a [=string=] |mimeType|: 1. If |responseBody| is null or failure, return false. 1. If [=header list/getting a structured field value|getting=] [:Ad-Auction-Allowed:] and @@ -2562,15 +2624,15 @@ of the following global objects: |functionName|, |argumentsJS|, and 50 milliseconds. 1. If |result| is an [=ECMAScript/abrupt completion=], return « "null", null, null ». 1. Let |resultJSON| be "null". - 1. If |functionName| is "`reportResult`" or "`reportAdditionalBidResult`", then set |resultJSON| - to the result of [=serializing a JavaScript value to a JSON string=] given |result|. + 1. If |functionName| is "`reportResult`", then set |resultJSON| to the result of + [=serializing a JavaScript value to a JSON string=] given |result|. Note: Consider a return value that can't be converted to JSON a valid result, so if an exception was [=exception/thrown=] in the previous step, keep |resultJSON| as "null". 1. Let |reportURL| be |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/report url=] 1. If |reportURL| is failure, set |reportURL| to null. 1. Let |macroMap| be |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/reporting macro map=] - if |functionName| is "`reportWin`", null otherwise. + if |functionName| is "`reportWin`" or "`reportContextualWin`", null otherwise. 1. Return « |resultJSON|, |reportURL|, |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/reporting beacon map=], |macroMap| ».
@@ -3397,7 +3459,7 @@ An interest group is a [=struct=] with the following [=struct/items=]: can be used to construct ads composed of multiple pieces — a top-level ad template "container" which includes some slots that can be filled in with specific "products". : additional bid key -:: Null or a [=byte sequence=]. Must be null if [=interest group/ads=] is not null. The +:: Null or a [=byte sequence=] of length 32. Must be null if [=interest group/ads=] is not null. The Ed25519 public key (a 256-bit EdDSA public key) used to guarantee that this [=interest group=], if used by an additional bid for a negative targeting, can only be used by its [=interest group/owner=]. @@ -3605,6 +3667,17 @@ An auction config is a [=struct=] with the following items: +A signed additional bid signature is a [=struct=] with the following [=struct/items=]: + +
+: key +:: A [=byte sequence=] of length 32. +: signature +:: A [=byte sequence=] of length 64. + +
+SignedAdditionalBidSignature + An additional bid is a [=struct=] with the following [=struct/items=]:
@@ -3727,7 +3800,7 @@ response headers. :: An [=ad descriptor=]. Render URL and size of the bid's ad. : ad component descriptors :: Null or a [=list=] of [=ad descriptors=]. Ad components associated with bid, if any. May have at - most 20 elements. Must be null if the interest group making this bid has a null + most 20 [=list/items=]. Must be null if the interest group making this bid has a null [=interest group/ad components=] field. : ad cost :: Null or a {{double}}. Advertiser click or conversion cost passed from `generateBid()` to From 9b00edc72eb0b8c4cb52244c80be3b1c2dd38f04 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 21 Sep 2023 15:44:38 -0400 Subject: [PATCH 07/27] Address comments. --- spec.bs | 440 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 226 insertions(+), 214 deletions(-) diff --git a/spec.bs b/spec.bs index 94e8a136e..9d9ab5852 100644 --- a/spec.bs +++ b/spec.bs @@ -975,13 +975,11 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. Append |c| to the end of |nonce|. 1. Set |auctionConfig|'s [=auction config/auction nonce=] to the result of running [=get uuid from string=] with |nonce|. -1. Let |additionalBids| be null. 1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a {{TypeError}}. 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. - 1. Set |additionalBids| to a new [=list=] of [=generated bids=]. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: @@ -1107,24 +1105,6 @@ To parse an origin given a [=string=] |input|:
-
- -To get uuid from string given a [=string=] |input|: -1. If |input|'s [=string/length=] is not 36, return an empty [=string=]. -1. Let |uuid| be an empty [=string=]. -1. [=list/For each=] |i| in [=the range=] from 1 to 36, inclusive: - 1. Let |unit| be |input|'s |i|th [=code unit=]. - 1. If « 8, 13, 18, 23 » [=list/contains=] |i|: - 1. If |unit| is not 0x002d (-): - 1. Return an empty [=string=]. - 1. Otherwise: - 1. If |unit| is not an [=ASCII lower hex digit=]: - 1. Return an empty [=string=]. - 1. Append |unit| to the end of |uuid|. -1. Return |uuid|. - -
-
To update bid count given a [=list=] of [=interest group=]s |igs|: @@ -1165,17 +1145,16 @@ To get uuid from string given a [=string=] |input|: To build bid generators map given an [=auction config=] |auctionConfig|: 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=per buyer bid generators=]. -1. Let |negativeInfo| be a new [=map=] whose [=map/keys=] are [=tuples=] consisting of an [=origin=] - and a [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a - [=byte sequence=]. +1. Let |negativeTargetInfo| be a new [=map=] whose [=map/keys=] are [=tuples=] consisting of an + [=origin=] and a [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] + and a [=byte sequence=]. 1. [=list/For each=] |buyer| in |auctionConfig|'s [=auction config/interest group buyers=]: 1. [=list/For each=] |ig| of the user agent's [=interest group set=] whose [=interest group/owner=] is |buyer|: 1. Let |igName| be |ig|'s [=interest group/name=]. 1. If |ig|'s [=interest group/additional bid key=] is not null: - 1. [=map/Set=] |negativeInfo|[(|buyer|, |igName|)] to (|ig|'s - [=interest group/joining origin=], |ig|'s [=interest group/additional bid key=]) - 1. [=list/Append=] |ig| to |negativeInfo|. + 1. [=map/Set=] |negativeTargetInfo|[(|buyer|, |igName|)] to (|ig|'s + [=interest group/joining origin=], |ig|'s [=interest group/additional bid key=]). 1. [=iteration/Continue=] if any of the following conditions hold: * |ig|'s [=interest group/bidding url=] is null; * |ig|'s [=interest group/ads=] is null, or [=list/is empty=]. @@ -1201,7 +1180,7 @@ To build bid generators map given an [=auction config=] |auctionConfi 1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to « |ig| ». 1. Otherwise: 1. [=list/Append=] |ig| to |perSignalsUrlGenerator|[|joiningOrigin|]. -1. Return « |bidGenerators|, |negativeInfo| ». +1. Return « |bidGenerators|, |negativeTargetInfo| ».
@@ -1259,7 +1238,7 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Let |decisionLogicScript| be the result of [=fetching script=] with |auctionConfig|'s [=auction config/decision logic url=]. 1. If |decisionLogicScript| is failure, return null. -1. Let « |bidGenerators|, |negativeInfo| » be the result of running +1. Let « |bidGenerators|, |negativeTargetInfo| » be the result of running [=build bid generators map=] with |auctionConfig|. 1. Let |leadingBidInfo| be a new [=leading bid info=]. 1. Let |queue| be the result of [=starting a new parallel queue=]. @@ -1318,17 +1297,18 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Set |auctionLevel| to "component-auction". 1. Set |componentAuctionExpectedCurrency| to the result of [=looking up per-buyer currency=] with |topLevelAuctionConfig| and |auctionConfig|'s [=auction config/seller=]. -1. Let |bidsBeingScored| be 0. +1. Let |pendingBuyers| be the [=map/size=] of |bidGenerators|. +1. Let |pendingAdditionalBids| be 0. 1. Let |additionalBids| be null. 1. If |auctionConfig|'s [=auction config/expects additional bids=] is true: 1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with - |auctionConfig|, |topLevelAuctionConfig| and |negativeInfo|. + |auctionConfig|, |topLevelAuctionConfig| and |negativeTargetInfo|. + 1. Set |pendingAdditionalBids| be the [=list/size=] of |additionalBids|. 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: - 1. Increment |bidsBeingScored| by 1. 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, |decisionLogicScript|, null, |auctionLevel|, |componentAuctionExpectedCurrency|, and |settings|'s [=environment/top-level origin=]. - 1. Decrement |bidsBeingScored| by 1. + 1. Decrement |pendingAdditionalBids| by 1. 1. [=map/For each=] |buyer| → |perBuyerGenerator| of |bidGenerators|, [=parallel queue/enqueue steps|enqueue the following steps=] to |queue|: 1. Let |buyerExperimentGroupId| be |allBuyersExperimentGroupId|. @@ -1416,12 +1396,11 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Set |ig|'s [=interest group/ad components=] to |originalAdComponents|. 1. If |generatedBid| is failure, [=iteration/continue=]. 1. [=list/Insert=] |generatedBid|'s [=generated bid/interest group=] in |bidIgs|. - 1. Increment |bidsBeingScored| by 1. 1. [=Score and rank a bid=] with |auctionConfig|, |generatedBid|, |leadingBidInfo|, |decisionLogicScript|, |dataVersion|, |auctionLevel|, |componentAuctionExpectedCurrency|, and |settings|'s [=environment/top-level origin=]. - 1. Decrement |bidsBeingScored| by 1. -1. Wait until |bidsBeingScored| is 0. + 1. Decrement |pendingBuyers| by 1. +1. Wait until both |pendingBuyers| and |pendingAdditionalBids| are 0. 1. If |leadingBidInfo|'s [=leading bid info/leading bid=] is null, return null. 1. If |topLevelAuctionConfig| is null: 1. Let « |sellerSignals|, |reportResultBrowserSignals| » be the result of running @@ -1469,7 +1448,7 @@ To build an interest group passed to generateBid given an [=interest
To validate and convert additional bids given an [=auction config=] |auctionConfig|, an -[=auction config=]-or-null |topLevelAuctionConfig|, and a new [=map=] |negativeInfo| whose +[=auction config=]-or-null |topLevelAuctionConfig|, and a [=map=] |negativeTargetInfo| whose [=map/keys=] are [=tuples=] consisting of an [=origin=] and a [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: @@ -1478,191 +1457,202 @@ are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: [=associated Document's=] [=node navigable's=] [=traversable navigable's=] [=captured additional bids headers=]. 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. - 1. [=list/For each] |encodedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: - 1. Let |decodedSignedAdditionalBid| be the result of running [=forgiving-base64 decode=] with - |encodedAdditionalBid|. - 1. If |decodedSignedAdditionalBid| is failure, [=iteration/continue=]. - 1. Increment |auctionConfig|’s [=auction config/scoring dependency count=]. - 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] - with |decodedSignedAdditionalBid|. - 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=], and - [=iteration/continue=] if any of the following conditions hold: - * |parsedSignedAdditionalBid| is not a [=map=]; - * |parsedSignedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=string=]; - * |parsedSignedAdditionalBid|["signatures"] does not [=map/exist=], or it's not a [=list=]. - 1. Let |signatures| be a new [=list=] of [=signed additional bid signatures=]. - 1. Let |decodeSignatureFailed| be false. - 1. [=list/For each=] |sig| of |parsedSignedAdditionalBid|["signatures"]: - 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following - conditions hold: - * |sig| is not a [=map=]; - * |sig|["key"] does not [=map/exist=], or it's not a [=string=]; - * |sig|["signature"] does not [=map/exist=], or it's not a [=string=]. - 1. Let |maybeKey| be the result of running [=forgiving-base64 decode=] with |sig|["key"]. - 1. Let |maybesignature| be the result of running [=forgiving-base64 decode=] with - |sig|["signature"]. - 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following - conditions hold: - * |maybeKey| is failure, or its [=byte sequence/length=] is not 32; - * |maybesignature| is failure, or its [=byte sequence/length=] is not 64; - 1. Let |signature| be a [=signed additional bid signatures=], whose - [=signed additional bid signature/key=] is |maybeKey|, and - [=signed additional bid signature/signature=] is |maybesignature|. - 1. [=list/Append=] |signature| to |signatures|. - 1. If |decodeSignatureFailed| is true, then decrement |auctionConfig|’s - [=auction config/scoring dependency count=], and [=iteration/continue=]. - 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] with - |parsedSignedAdditionalBid|["bid"], |auctionConfig| and |topLevelAuctionConfig|. - 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=], and - [=iteration/continue=] if any of the following conditions hold: - * |decodedAdditionalBid| is failure; - * |decodedAdditionalBid|'s [=additional bid/bid=]'s [=generated bid/bid=]'s - [=bid with currency/currency=] is not the result of running [=look up per-buyer currency=] - with |auctionConfig|. - 1. Let |validSignatureIndices| be a new [=list=] of integers. - 1. [=list/For each=] |i| in [=list/get the indices|indices=] of |signatures|: - 1. If [=verify=] |signatures|[|i|] succeeded: - 1. [=list/Append=] |i| to |validSignatureIndices|. - 1. Let |negativeTargeted| be false. - 1. Let |additionalBidBuyer| be |decodedAdditionalBid|'s [=additional bid/bid=]'s - [=generated bid/interest group=]'s [=interest group/owner=]. - 1. [=list/For each=] |igName| of |decodedAdditionalBid|'s - [=additional bid/negative target interest group names=]: - 1. If |negativeInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: - 1. Let (|joiningOrigin|, |additionalBidKey|) be |negativeInfo|[(|additionalBidBuyer|, |igName|)]. - 1. Let |KeyMatchedValidSignature| be false. - 1. [=list/For each=] |i| of |validSignatureIndices|: - 1. If |signatures|[|i|]'s [=signed additional bid signature/key=] is |additionalBidKey|: - 1. Set |KeyMatchedValidSignature| to true, and [=iteration/break=]. - 1. If |KeyMatchedValidSignature| is false, then [=iteration/continue=]. - 1. If |joiningOrigin| is not null: - 1. If |joiningOrigin| is not [=same origin=] with |decodedAdditionalBid|'s - [=additional bid/negative target joining origin=], then [=iteration/continue=]. - 1. Set |negativeTargeted| to true. - 1. If |negativeTargeted| is false: - 1. [=list/Append=] |decodedAdditionalBid| to |additionalBids|. + 1. [=list/For each] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: + 1. Let |signedAdditionalBid| be the result of running [=forgiving-base64 decode=] with + |encodedSignedAdditionalBid|. + 1. If |signedAdditionalBid| is failure, then [=iteration/continue=]. + 1. Let |additionalBid| be the result of running [=parse a signed additional bid=] given + |signedAdditionalBid|, |auctionConfig|, |topLevelAuctionConfig|, and |negativeTargetInfo|. + 1. If |additionalBid| is null: + 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=], and + [=iteration/continue=]. + 1. [=list/Append=] |additionalBid| to |additionalBids|. 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=]. 1. Return |additionalBids|.
+
+To parse a signed additional bid given a [=byte sequence=] |signedAdditionalBid|, an +[=auction config=] |auctionConfig|, an [=auction config=]-or-null |topLevelAuctionConfig|, and a +[=map=] |negativeTargetInfo| whose [=map/keys=] are [=tuples=] consisting of an [=origin=] and a +[=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: + + 1. Increment |auctionConfig|’s [=auction config/scoring dependency count=]. + 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] + with |signedAdditionalBid|. + 1. Return null if any of the following conditions hold: + * |parsedSignedAdditionalBid| is not a [=map=]; + * |parsedSignedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=string=]; + * |parsedSignedAdditionalBid|["signatures"] does not [=map/exist=], or it's not a [=list=]. + 1. Let |signatures| be a new [=list=] of [=signed additional bid signatures=]. + 1. Let |decodeSignatureFailed| be false. + 1. [=list/For each=] |sig| of |parsedSignedAdditionalBid|["signatures"]: + 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following + conditions hold: + * |sig| is not a [=map=]; + * |sig|["key"] does not [=map/exist=], or it's not a [=string=]; + * |sig|["signature"] does not [=map/exist=], or it's not a [=string=]. + 1. Let |maybeKey| be the result of running [=forgiving-base64 decode=] with |sig|["key"]. + 1. Let |maybeSignature| be the result of running [=forgiving-base64 decode=] with + |sig|["signature"]. + 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following + conditions hold: + * |maybeKey| is failure, or its [=byte sequence/length=] is not 32; + * |maybeSignature| is failure, or its [=byte sequence/length=] is not 64; + 1. Let |signature| be a [=signed additional bid signatures=], whose + [=signed additional bid signature/key=] is |maybeKey|, and + [=signed additional bid signature/signature=] is |maybeSignature|. + 1. [=list/Append=] |signature| to |signatures|. + 1. If |decodeSignatureFailed| is true, then return null. + 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] with + |parsedSignedAdditionalBid|["bid"], |auctionConfig| and |topLevelAuctionConfig|. + 1. Return null if any of the following conditions hold: + * |decodedAdditionalBid| is failure; + * The result of [=checking a currency tag=] with |decodedAdditionalBid|'s + [=additional bid/bid=]'s [=generated bid/bid=]'s [=bid with currency/currency=] and the + result of running [=look up per-buyer currency=] with |auctionConfig|. + 1. Check whether the additional bid is negative targeted: + 1. Let |verifiedSignatures| be a new [=ordered set=] of [=signed additional bid signatures=]. + 1. [=list/For each=] |signature| of |signatures|: + 1. If [=verify=] |signature| succeeded: + 1. [=set/Append=] |signature| to |verifiedSignatures|. + 1. Let |negativeTargeted| be false. + 1. Let |additionalBidBuyer| be |additionalBid|'s [=additional bid/bid=]'s + [=generated bid/interest group=]'s [=interest group/owner=]. + 1. [=list/For each=] |igName| of |additionalBid|'s + [=additional bid/negative target interest group names=]: + 1. If |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: + 1. Let (|joiningOrigin|, |additionalBidKey|) be + |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)]. + 1. If |verifiedSignatures| [=list/contains=] |additionalBidKey|: + 1. If |joiningOrigin| is not null: + 1. If |joiningOrigin| is not [=same origin=] with |additionalBid|'s + [=additional bid/negative target joining origin=], then [=iteration/continue=]. + 1. Set |negativeTargeted| to true, and [=iteration/break=]. + 1. If |negativeTargeted| is true: + 1. Return null. + 1. Return |decodedAdditionalBid|. +
+
To decode an additional bid given a [=map=] |parsedAdditionalBid|, an [=auction config=] |auctionConfig|, and an [=auction config=]-or-null |topLevelAuctionConfig|: - 1. Let |result| be a new [=additional bid=]. - 1. Return faliure if any of the following conditions hold: - * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; - * |parsedAdditionalBid|["auctionNonce"] is not |auctionConfig|'s [=auction config/auction nonce=]; - * |parsedAdditionalBid|["seller"] does not [=map/exist=]; - * The result of running the [=parse an origin=] with |parsedAdditionalBid|["seller"] is - failure, or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. - 1. If |topLevelAuctionConfig| is null: - 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. - 1. Otherwise: - 1. Let |bidTopLevelSeller| be the result of running the [=parse an origin=] with - |parsedAdditionalBid|["topLevelSeller"]. - 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], or |bidTopLevelSeller| - is failure, or |bidTopLevelSeller| is not [=same origin=] with |topLevelAuctionConfig|'s - [=auction config/seller=], then return failure. - 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. - 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. - 1. Let |igName| be |igMap|["name"] if it [=map/exists=], otherwise return failure. - 1. Let |igBiddingUrlStr| be |igMap|["biddingLogicURL"] if it [=map/exists=], otherwise return - failure. - 1. Let |igOwnerStr| be |igMap|["owner"] if it [=map/exists=], otherwise return failure. - 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igBiddingUrlStr|. - 1. Let |igOwner| be the result of running [=parse an origin=] with |igOwnerStr|. + + 1. Let |result| be a new [=additional bid=]. + 1. Return failure if any of the following conditions hold: + * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; + * |parsedAdditionalBid|["auctionNonce"] is not |auctionConfig|'s [=auction config/auction nonce=]; + * |parsedAdditionalBid|["seller"] does not [=map/exist=]; + * The result of running the [=parse an origin=] with |parsedAdditionalBid|["seller"] is failure, + or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. + 1. If |topLevelAuctionConfig| is null: + 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. + 1. Otherwise: + 1. Let |bidTopLevelSeller| be the result of running the [=parse an origin=] with + |parsedAdditionalBid|["topLevelSeller"]. + 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], or |bidTopLevelSeller| + is failure, or |bidTopLevelSeller| is not [=same origin=] with |topLevelAuctionConfig|'s + [=auction config/seller=], then return failure. + 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. + 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. + 1. Let |igName| be |igMap|["name"] if it [=map/exists=], otherwise return failure. + 1. Let |igBiddingUrlStr| be |igMap|["biddingLogicURL"] if it [=map/exists=], otherwise return + failure. + 1. Let |igOwnerStr| be |igMap|["owner"] if it [=map/exists=], otherwise return failure. + 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igBiddingUrlStr|. + 1. Let |igOwner| be the result of running [=parse an origin=] with |igOwnerStr|. + 1. Return failure if any of the following conditions hold: + * |igOwner| is failure; + * |igOwner|'s [=origin/scheme=] is not "`https`"; + * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; + * |igBiddingUrl| is failure; + * |igOwner| is not [=same origin=] with |igBiddingUrl|. + 1. Let |ig| be a new [=interest group=] with the following properties: + : [=interest group/owner=] + :: |igOwner| + : [=interest group/name=] + :: |igName| + : [=interest group/bidding url=] + :: |igBiddingUrl| + 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=map=], return failure. + 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. + 1. Let |renderUrlStr| be |bidMap|["render"] if it [=map/exists=], otherwise return failure. + 1. Let |renderUrl| be the result of running [=url parser=] on |renderUrlStr|. + 1. If |renderUrl| is failure, then return failure. + 1. Set |ig|'s [=interest group/ads=] to « |renderUrl| ». + 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. + 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, return failure. + 1. Let |adMetadata| be "null". + 1. If |bidMap|["ad"] [=map/exists=]: + 1. Set |adMetadata| to the result of running [=serialize an Infra value to a JSON string=] with + |bidMap|["ad"]. + 1. Let |bidCurrency| be null. + 1. If |bidMap|["bidCurrency"] [=map/exists=]: + 1. If |bidMap|["bidCurrency"] is not a [=string=], or the result of [=checking whether a string + is a valid currency tag=] is failure: + 1. Return failure. + 1. Set |bidCurrency| to |bidMap|["bidCurrency"]. + 1. Let |adCost| be null. + 1. If |bidMap|["adCost"] [=map/exists=]: + 1. If |bidMap|["adCost"] is not a {{double}}, then return failure. + 1. Set |adCost| to |bidMap|["adCost"]. + 1. Let |modelingSignals| be null. + 1. If |bidMap|["modelingSignals"] [=map/exists=]: + 1. If |bidMap|["modelingSignals"] is not a {{double}}, then return failure. + 1. If |bidMap|["modelingSignals"] is greater than or equal to 0, and less than 4096: + 1. Set |modelingSignals| to |bidMap|["modelingSignals"]. + 1. Let |adComponents| be a new [=list=] of [=ad descriptors=]. + 1. If |bidMap|["adComponents"] [=map/exists=]: + 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. + 1. [=list/For each=] |component| of |bidMap|["adComponents"]: + 1. If |component| is not a [=string=], then return failure. + 1. Let |componentUrl| be the result of running [=url parser=] on |component|. + 1. If |componentUrl| is failure, then return failure. + 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is + |componentUrl|. + 1. [=list/Append=] |componentDescriptor| to |adComponents|. + 1. Set |ig|'s [=interest group/ad components=] to |adComponents|. + 1. If |parsedAdditionalBid|["negativeInterestGroup"] [=map/exists=]: + 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or + |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. + 1. [=list/Append=] |parsedAdditionalBid|["negativeInterestGroup"] to |result|'s + [=additional bid/negative target interest group names=]. + 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=]: + 1. Let |multipleNegativeIg| be |parsedAdditionalBid|["negativeInterestGroups"]. 1. Return failure if any of the following conditions hold: - * |igOwner| is failure; - * |igOwner|'s [=origin/scheme=] is not "`https`"; - * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; - * |igBiddingUrl| is failure; - * |igOwner| is not [=same origin=] with |igBiddingUrl|. - 1. Let |ig| be a new [=interest group=] with the following properties: - : [=interest group/owner=] - :: |igOwner| - : [=interest group/name=] - :: |igName| - : [=interest group/bidding url=] - :: |igBiddingUrl| - 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=map=], return failure. - 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. - 1. Let |renderUrlStr| be |bidMap|["render"] if it [=map/exists=], otherwise return failure. - 1. Let |renderUrl| be the result of running [=url parser=] on |renderUrlStr|. - 1. If |renderUrl| is failure, then return failure. - 1. Set |ig|'s [=interest group/ads=] to « |renderUrl| ». - 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. - 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, return failure. - 1. Let |adMetadata| be "null". - 1. If |bidMap|["ad"] [=map/exists=]: - 1. Set |adMetadata| to the result of running [=serialize an Infra value to a JSON string=] with - |bidMap|["ad"]. - 1. Let |bidCurrency| be null. - 1. If |bidMap|["bidCurrency"] [=map/exists=]: - 1. If |bidMap|["bidCurrency"] is not a [=string=], or the result of [=checking whether a string - is a valid currency tag=] is failure: - 1. Return failure. - 1. Set |bidCurrency| to |bidMap|["bidCurrency"]. - 1. Let |adCost| be null. - 1. If |bidMap|["adCost"] [=map/exists=]: - 1. If |bidMap|["adCost"] is not a {{double}}, then return failure. - 1. Set |adCost| to |bidMap|["adCost"]. - 1. Let |modelingSignals| be null. - 1. If |bidMap|["modelingSignals"] [=map/exists=]: - 1. If |bidMap|["modelingSignals"] is not a {{double}}, then return failure. - 1. If |bidMap|["modelingSignals"] is greater than or equal to 0, and less than 4096: - 1. Set |modelingSignals| to |bidMap|["modelingSignals"]. - 1. Let |adComponents| be a new [=list=] of [=ad descriptors=]. - 1. If |bidMap|["adComponents"] [=map/exists=]: - 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. - 1. [=list/For each=] |component| of |bidMap|["adComponents"]: - 1. If |component| is not a [=string=], then return failure. - 1. Let |componentUrl| be the result of running [=url parser=] on |component|. - 1. If |componentUrl| is failure, then return failure. - 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is - |componentUrl|. - 1. [=list/Append=] |componentDescriptor| to |adComponents|. - 1. Set |ig|'s [=interest group/ad components=] to |adComponents|. - 1. If |parsedAdditionalBid|["negativeInterestGroup"] [=map/exists=]: - 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or - |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. - 1. [=list/Append=] |parsedAdditionalBid|["negativeInterestGroup"] to |result|'s + * |multipleNegativeIg| is not a [=map=]; + * |multipleNegativeIg|["joiningOrigin"] does not [=map/exist=], or it's not a [=string=]; + * |multipleNegativeIg|["interestGroupNames"] does not [=map/exist=], or it's not a [=list=]. + 1. Let |joiningOrigin| be the result of running [=parse an origin=] with + |multipleNegativeIg|["joiningOrigin"]. + 1. If |joiningOrigin| is failure, or its [=origin/scheme=] is not "`https`", then return failure. + 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. + 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: + 1. If |igName| is not a [=string=], then return failure. + 1. [=list/Append=] |igName| to |result|'s [=additional bid/negative target interest group names=]. - 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=]: - 1. Let |multipleNegativeIg| be |parsedAdditionalBid|["negativeInterestGroups"]. - 1. Return failure if any of the following conditions hold: - * |multipleNegativeIg| is not a [=map=]; - * |multipleNegativeIg|["joiningOrigin"] does not [=map/exist=], or it's not a [=string=]; - * |multipleNegativeIg|["interestGroupNames"] does not [=map/exist=], or it's not a [=list=]. - 1. Let |joiningOrigin| be the result of running [=parse an origin=] with - |multipleNegativeIg|["joiningOrigin"]. - 1. If |joiningOrigin| is failure, or its [=origin/scheme=] is not "`https`", then return failure. - 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. - 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: - 1. If |igName| is not a [=string=], then return failure. - 1. [=list/Append=] |igName| to |result|'s - [=additional bid/negative target interest group names=]. - 1. Set |result|'s [=additional bid/bid=] to a new [=generated bid=] with the following properties: - : [=generated bid/bid=] - :: A [=bid with currency=] whose [=bid with currency/value=] is |bidVal|, and - [=bid with currency/currency=] is |bidCurrency| - : [=generated bid/ad=] - :: |adMetadata| - : [=generated bid/ad descriptor=] - :: An [=ad descriptor=] whose [=ad descriptor/url=] is |renderUrl| - : [=generated bid/ad component descriptors=] - :: |adComponents| - : [=generated bid/ad cost=] - :: |adCost| - : [=generated bid/modeling signals=] - :: |modelingSignals| - : [=generated bid/interest group=] - :: |ig| - : [=generated bid/bid ad=] - :: A [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|, and - [=interest group ad/metadata=] is |adMetadata| - : [=generated bid/provided as additional bid=] - :: true - 1. Return |result|. + 1. Set |result|'s [=additional bid/bid=] to a new [=generated bid=] with the following properties: + : [=generated bid/bid=] + :: A [=bid with currency=] whose [=bid with currency/value=] is |bidVal|, and + [=bid with currency/currency=] is |bidCurrency| + : [=generated bid/ad=] + :: |adMetadata| + : [=generated bid/ad descriptor=] + :: An [=ad descriptor=] whose [=ad descriptor/url=] is |renderUrl| + : [=generated bid/ad component descriptors=] + :: |adComponents| + : [=generated bid/ad cost=] + :: |adCost| + : [=generated bid/modeling signals=] + :: |modelingSignals| + : [=generated bid/interest group=] + :: |ig| + : [=generated bid/bid ad=] + :: A [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|, and + [=interest group ad/metadata=] is |adMetadata| + : [=generated bid/provided as additional bid=] + :: true + 1. Return |result|.
@@ -2277,7 +2267,7 @@ and a {{ReportingBrowserSignals}} |browserSignals|: [=generated bid/interest group=]'s [=interest group/bidding url=]. 1. Let |reportFunctionName| be "`reportWin`". 1. If |winner|'s [=generated bid/provided as additional bid=] is true: - 1. Set |reportFunctionName| be "`reportContextualWin`". + 1. Set |reportFunctionName| be "`reportAdditionalBidWin`". 1. Let « ignored, |resultUrl|, |reportingBeaconMap|, |reportingMacroMap| » be the result of [=evaluating a reporting script=] with |buyerReportingScript|, "`reportWin`", and « |leadingBidInfo|'s [=leading bid info/auction config=]'s [=auction config/config idl=]'s @@ -2632,7 +2622,7 @@ of the following global objects: 1. Let |reportURL| be |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/report url=] 1. If |reportURL| is failure, set |reportURL| to null. 1. Let |macroMap| be |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/reporting macro map=] - if |functionName| is "`reportWin`" or "`reportContextualWin`", null otherwise. + if |functionName| is "`reportWin`" or "`reportAdditionalBidWin`", null otherwise. 1. Return « |resultJSON|, |reportURL|, |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/reporting beacon map=], |macroMap| ».
@@ -3233,6 +3223,28 @@ The updateAdInterestGroups() method steps are: +# Common Algorithms # {#common-algorithms} + +The following algorithms are some helper methods used in this document. + +
+ To get uuid from string given a [=string=] |input|: + + 1. If |input|'s [=string/length=] is not 36, return an empty [=string=]. + 1. Let |uuid| be an empty [=string=]. + 1. [=list/For each=] |i| in [=the range=] from 1 to 36, inclusive: + 1. Let |unit| be |input|'s |i|th [=code unit=]. + 1. If « 8, 13, 18, 23 » [=list/contains=] |i|: + 1. If |unit| is not 0x002d (-): + 1. Return an empty [=string=]. + 1. Otherwise: + 1. If |unit| is not an [=ASCII lower hex digit=]: + 1. Return an empty [=string=]. + 1. Append |unit| to the end of |uuid|. + 1. Return |uuid|. +
+ + # Permissions Policy integration # {#permissions-policy-integration} This specification defines two [=policy-controlled features=] identified by the strings From 1a1b706ad5c1768e1d78dce1d70302f1dd939947 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Mon, 25 Sep 2023 13:34:01 -0400 Subject: [PATCH 08/27] Fix promise handling issue. --- spec.bs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/spec.bs b/spec.bs index 9d9ab5852..40d7ace7a 100644 --- a/spec.bs +++ b/spec.bs @@ -979,13 +979,13 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a {{TypeError}}. - 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to null. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: - 1. Nothing to do. - * To handle an error: (TODO: check if this is correct when the promise is rejected.) - 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. + * To handle an error: + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to failure. 1. If |config|["{{AuctionAdConfig/directFromSellerSignals}}"] [=map/exists=]: 1. TODO: The receiving end of this isn't specified yet, so there is no place to put the computed value. 1. [=Handle an input promise in configuration=] given |auctionConfig| and @@ -1303,7 +1303,7 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. If |auctionConfig|'s [=auction config/expects additional bids=] is true: 1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with |auctionConfig|, |topLevelAuctionConfig| and |negativeTargetInfo|. - 1. Set |pendingAdditionalBids| be the [=list/size=] of |additionalBids|. + 1. Set |pendingAdditionalBids| to the [=list/size=] of |additionalBids|. 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, |decisionLogicScript|, null, |auctionLevel|, |componentAuctionExpectedCurrency|, and @@ -1457,7 +1457,7 @@ are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: [=associated Document's=] [=node navigable's=] [=traversable navigable's=] [=captured additional bids headers=]. 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. - 1. [=list/For each] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: + 1. [=list/For each=] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: 1. Let |signedAdditionalBid| be the result of running [=forgiving-base64 decode=] with |encodedSignedAdditionalBid|. 1. If |signedAdditionalBid| is failure, then [=iteration/continue=]. @@ -3650,7 +3650,7 @@ An auction config is a [=struct=] with the following items: everything. It includes waiting for {{Promise}}s [=auction config/auction signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], [=auction config/per buyer timeouts=], directFromSellerSignals, [=auction config/seller signals=] - to resolve, waiting for additional bids to parse. + to resolve, and waiting for additional bids to parse. : config idl :: {{AuctionAdConfig}}. : resolve to config @@ -3675,7 +3675,13 @@ An auction config is a [=struct=] with the following items: provide additional bids, and each of those additional bids must use the same auction nonce to ensure that each of those additional bids was intended for this and only this auction. : expects additional bids -:: A [=boolean=], initially false. Specifies that some bids will be provided as signed exchanges. +:: Null, a [=boolean=] or failure, initially false. + Specifies whether some bids will be provided as signed exchanges, and whether additional bid + headers are available now. + False if {{AuctionAdConfig/additionalBids}} does not [=map/exists=]. + Null if {{AuctionAdConfig/additionalBids}} [=map/exists=] but has not been [=resolved=] yet. + True if {{AuctionAdConfig/additionalBids}} is [=resolved=]. + Failure if {{AuctionAdConfig/additionalBids}} is [=rejected=]. @@ -3688,7 +3694,6 @@ A signed additional bid signature is a [=struct=] with the following :: A [=byte sequence=] of length 64. -SignedAdditionalBidSignature An additional bid is a [=struct=] with the following [=struct/items=]: @@ -3713,9 +3718,11 @@ To wait until scoring dependency done given an [=auction config=] |au 1. [=Assert=] |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], and [=auction config/per buyer timeouts=] are not {{Promise}}s. +1. [=Assert=] |auctionConfig|'s [=auction config/expects additional bids=] is not null. 1. If |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], - [=auction config/per buyer signals=], [=auction config/per buyer currencies=] or - [=auction config/per buyer timeouts=] is failure, return failure. + [=auction config/per buyer signals=], [=auction config/per buyer currencies=], + [=auction config/per buyer timeouts=], or [=auction config/expects additional bids=] is failure, + return failure. 1. TODO: the above two steps should also check directFromSellerSignals once something handles it. From 92c762039cd025fb07d882714b3a016f3c4f5ecf Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Mon, 25 Sep 2023 16:08:28 -0400 Subject: [PATCH 09/27] Modify how addition bids promise is handled. --- spec.bs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/spec.bs b/spec.bs index 9c64ea404..8ba562019 100644 --- a/spec.bs +++ b/spec.bs @@ -999,13 +999,16 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a {{TypeError}}. - 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to null. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. + 1. Set |auctionConfig|'s [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] + to {{undefined}}. * To handle an error: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to failure. + 1. Set |auctionConfig|'s [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] + to {{undefined}}. 1. If |config|["{{AuctionAdConfig/directFromSellerSignals}}"] [=map/exists=]: 1. TODO: The receiving end of this isn't specified yet, so there is no place to put the computed value. 1. [=Handle an input promise in configuration=] given |auctionConfig| and @@ -3695,13 +3698,9 @@ An auction config is a [=struct=] with the following items: provide additional bids, and each of those additional bids must use the same auction nonce to ensure that each of those additional bids was intended for this and only this auction. : expects additional bids -:: Null, a [=boolean=] or failure, initially false. - Specifies whether some bids will be provided as signed exchanges, and whether additional bid - headers are available now. - False if {{AuctionAdConfig/additionalBids}} does not [=map/exists=]. - Null if {{AuctionAdConfig/additionalBids}} [=map/exists=] but has not been [=resolved=] yet. - True if {{AuctionAdConfig/additionalBids}} is [=resolved=]. - Failure if {{AuctionAdConfig/additionalBids}} is [=rejected=]. +:: A [=boolean=] or failure, initially false. + Specifies whether some bids will be provided as signed exchanges. Sets to failure if the + {{AuctionAdConfig/additionalBids}} {{Promise}} is [=rejected=]. @@ -3736,9 +3735,9 @@ for encoded additional bids. To wait until scoring dependency done given an [=auction config=] |auctionConfig|: 1. Wait until |auctionConfig|'s [=auction config/scoring dependency count=] is 0. 1. [=Assert=] |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], - [=auction config/per buyer signals=], [=auction config/per buyer currencies=], and - [=auction config/per buyer timeouts=] are not {{Promise}}s. -1. [=Assert=] |auctionConfig|'s [=auction config/expects additional bids=] is not null. + [=auction config/per buyer signals=], [=auction config/per buyer currencies=], + [=auction config/per buyer timeouts=], and + [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] are not {{Promise}}s. 1. If |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], [=auction config/per buyer timeouts=], or [=auction config/expects additional bids=] is failure, From 1b986d5358aa527bba3e385690128ef0ee72e9f5 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Tue, 26 Sep 2023 00:52:54 -0400 Subject: [PATCH 10/27] Require interest group buyers. --- spec.bs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec.bs b/spec.bs index 8ba562019..23092a486 100644 --- a/spec.bs +++ b/spec.bs @@ -996,9 +996,10 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. Set |auctionConfig|'s [=auction config/auction nonce=] to the result of running [=get uuid from string=] with |nonce|. 1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: - 1. If |config|["{{AuctionAdConfig/componentAuctions}}"]'s [=list/size=] is greater than 0, or - |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=], [=exception/throw=] a - {{TypeError}}. + 1. [=exception/Throw=] a {{TypeError}} if any of the following conditions hold: + * |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=]; + * |config|["{{AuctionAdConfig/interestGroupBuyers}}"] does not [=map/exist=], or it's [=list/empty=]; + * |config|["{{AuctionAdConfig/componentAuctions}}"] is not [=list/empty=]. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: From 9e75f44a572f673a4f61ef82377f493127a369f4 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Tue, 26 Sep 2023 16:41:20 -0400 Subject: [PATCH 11/27] Address comments. --- spec.bs | 65 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/spec.bs b/spec.bs index 23092a486..15c2ae82a 100644 --- a/spec.bs +++ b/spec.bs @@ -215,8 +215,7 @@ This is detectable because it can change the set of fields that are read from th 1. Let |frameOrigin| be [=this=]'s [=relevant settings object=]'s [=environment settings object/origin=]. 1. [=Assert=] that |frameOrigin| is not an [=opaque origin=] and its [=origin/scheme=] is "`https`". 1. Let |interestGroup| be a new [=interest group=]. -1. Validate the given |group| and set |interestGroup|'s fields accordingly. If an [=exception=] is - [=exception/thrown=], return [=a new promise=]. +1. Validate the given |group| and set |interestGroup|'s fields accordingly. 1. Set |interestGroup|'s [=interest group/expiry=] to the [=current wall time=] plus |group|["{{GenerateBidInterestGroup/lifetimeMs}}"] milliseconds. 1. Set |interestGroup|'s [=interest group/next update after=] to the [=current wall time=] plus 24 @@ -542,8 +541,9 @@ bid in the auction for the chance to display their advertisement.

createAuctionNonce()

{{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a one-time ID -uniquely associated with a single call to {{Window/navigator}}.{{Navigator/runAdAuction()}}. This -nonce will need to be passed back in via a subsequent call to +uniquely associated with a single call to {{Window/navigator}}.{{Navigator/runAdAuction()}}. For +multi-seller auctions, this ID is uniquely associated with all {{AuctionAdConfig/componentAuctions}}. +This nonce will need to be passed back in via a subsequent call to {{Window/navigator}}.{{Navigator/runAdAuction()}} via the {{AuctionAdConfig}}. This is currently only needed for auctions that use additional bids, for which the auction nonce will be included in each additional bid as a way of ensuring that those bids are only used in the auctions for which @@ -1275,7 +1275,7 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Let |compWinner| be the result of running [=generate and score bids=] with |component|, |auctionConfig|, |global|, and |settings|. 1. If |compWinner| is failure, return failure. - 1. If [=recursively wait until scoring dependency done=] given |auctionConfig| returns failure, + 1. If [=recursively wait until configuration input promises resolve=] given |auctionConfig| returns failure, return failure. 1. If |compWinner| is not null: 1. Run [=score and rank a bid=] with |auctionConfig|, |compWinner|, |leadingBidInfo|, @@ -1300,7 +1300,7 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Run [=report win=] with |leadingBidInfo|, |sellerSignals|, and |reportResultBrowserSignals|. 1. Return |leadingBidInfo|'s [=leading bid info/leading bid=]. -1. If [=waiting until scoring dependency done=] given |auctionConfig| returns failure, return failure. +1. If [=waiting until configuration input promises resolve=] given |auctionConfig| returns failure, return failure. 1. Let |allBuyersExperimentGroupId| be |auctionConfig|'s [=auction config/all buyer experiment group id=]. 1. Let |allBuyersGroupLimit| be |auctionConfig|'s @@ -1487,11 +1487,8 @@ are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: 1. If |signedAdditionalBid| is failure, then [=iteration/continue=]. 1. Let |additionalBid| be the result of running [=parse a signed additional bid=] given |signedAdditionalBid|, |auctionConfig|, |topLevelAuctionConfig|, and |negativeTargetInfo|. - 1. If |additionalBid| is null: - 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=], and - [=iteration/continue=]. - 1. [=list/Append=] |additionalBid| to |additionalBids|. - 1. Decrement |auctionConfig|’s [=auction config/scoring dependency count=]. + 1. If |additionalBid| is not null: + 1. [=list/Append=] |additionalBid| to |additionalBids|. 1. Return |additionalBids|. @@ -1501,7 +1498,6 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd [=map=] |negativeTargetInfo| whose [=map/keys=] are [=tuples=] consisting of an [=origin=] and a [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: - 1. Increment |auctionConfig|’s [=auction config/scoring dependency count=]. 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] with |signedAdditionalBid|. 1. Return null if any of the following conditions hold: @@ -1553,6 +1549,12 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd 1. If |joiningOrigin| is not [=same origin=] with |additionalBid|'s [=additional bid/negative target joining origin=], then [=iteration/continue=]. 1. Set |negativeTargeted| to true, and [=iteration/break=]. + + Note: If the signature doesn't verify successfully, the additional bid proceeds as if the + negative interest group is not present. This ensures that only the owner of the negative + interest group, who created the additonalBidKey, is allowed to negatively target the + interest group, and that nobody else can learn whether the interest group is present on + the device. 1. If |negativeTargeted| is true: 1. Return null. 1. Return |decodedAdditionalBid|. @@ -3232,9 +3234,9 @@ The updateAdInterestGroups() method steps are: 1. Let |decodedKey| be the result of running [=forgiving-base64 decode=] with |value|. 1. Jump to the step labeled Abort update if any of the following conditions hold: - * |decodedKey| is a failure - * |decodedKey|'s [=byte sequence/length=] is not 32 - * |parsedUpdate|["`ads`"] [=map/exists=] + * |decodedKey| is a failure; + * |decodedKey|'s [=byte sequence/length=] is not 32; + * |parsedUpdate|["`ads`"] [=map/exists=]. 1. Set |ig|'s [=interest group/additional bid key=] to |decodedKey|. @@ -3669,12 +3671,12 @@ An auction config is a [=struct=] with the following items: :: Null or an {{unsigned short}}, initially null. Optional identifier for an experiment group to support coordinated experiments with buyers' trusted servers for buyers without a specified experiment group. -: scoring dependency count +: pending promise count :: An integer, initially 0. The number of things that are pending that are needed to score everything. It includes waiting for {{Promise}}s [=auction config/auction signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], - [=auction config/per buyer timeouts=], directFromSellerSignals, [=auction config/seller signals=] - to resolve, and waiting for additional bids to parse. + [=auction config/per buyer timeouts=], directFromSellerSignals, [=auction config/seller signals=], + and [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] to resolve. : config idl :: {{AuctionAdConfig}}. : resolve to config @@ -3694,10 +3696,12 @@ An auction config is a [=struct=] with the following items: : auction nonce :: Null or a [=string=], initially null. A unique identifier associated with this and only this invocation of - {{Window/navigator}}.{{Navigator/runAdAuction()}}. This must come from a prior call to - {{Window/navigator}}.{{Navigator/createAuctionNonce()}}. This is only required for auctions that - provide additional bids, and each of those additional bids must use the same auction nonce to - ensure that each of those additional bids was intended for this and only this auction. + {{Window/navigator}}.{{Navigator/runAdAuction()}}. For + multi-seller auctions, this ID is uniquely associated with all {{AuctionAdConfig/componentAuctions}}. + This must come from a prior call to {{Window/navigator}}.{{Navigator/createAuctionNonce()}}. This + is only required for auctions that provide additional bids, and each of those additional bids must + use the same auction nonce to ensure that each of those additional bids was intended for this and + only this auction. : expects additional bids :: A [=boolean=] or failure, initially false. Specifies whether some bids will be provided as signed exchanges. Sets to failure if the @@ -3733,8 +3737,8 @@ whose [=map/keys=] are [=strings=] for auction nonces, and whose values are [=li for encoded additional bids.
-To wait until scoring dependency done given an [=auction config=] |auctionConfig|: -1. Wait until |auctionConfig|'s [=auction config/scoring dependency count=] is 0. +To wait until configuration input promises resolve given an [=auction config=] |auctionConfig|: +1. Wait until |auctionConfig|'s [=auction config/pending promise count=] is 0. 1. [=Assert=] |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], [=auction config/per buyer timeouts=], and @@ -3748,26 +3752,27 @@ To wait until scoring dependency done given an [=auction config=] |au
-To recursively wait until scoring dependency done given an [=auction config=] |auctionConfig|: +To recursively wait until configuration input promises resolve given an [=auction config=] +|auctionConfig|: 1. [=list/For each=] |componentAuctionConfig| in |auctionConfig|'s [=auction config/component auctions=]: - 1. If the result of [=waiting until scoring dependency done=] given |componentAuctionConfig| is + 1. If the result of [=waiting until configuration input promises resolve=] given |componentAuctionConfig| is failure, return failure. -1. Return the result of [=waiting until scoring dependency done=] given |auctionConfig|. +1. Return the result of [=waiting until configuration input promises resolve=] given |auctionConfig|.
To handle an input promise in configuration given an [=auction config=] |auctionConfig|, a {{Promise}} |p|, and two sequences of steps, covering the parsing of the value and error-handling: -1. Increment |auctionConfig|'s [=auction config/scoring dependency count=]. +1. Increment |auctionConfig|'s [=auction config/pending promise count=]. 1. Let |resolvedAndTypeChecked| be the promise representing performing the following steps [=upon fulfillment=] of |p| with |result|: 1. Execute the steps to be run for parsing of the value given |result|. 1. If no exception was [=exception/thrown=] in the previous step: - 1. Decrement |auctionConfig|'s [=auction config/scoring dependency count=]. + 1. Decrement |auctionConfig|'s [=auction config/pending promise count=]. 1. [=Upon rejection=] of |resolvedAndTypeChecked|: 1. Execute the steps for error-handling. - 1. Decrement |auctionConfig|'s [=auction config/scoring dependency count=]. + 1. Decrement |auctionConfig|'s [=auction config/pending promise count=].
From fdd749be5d09536f703224ba60d029b14311e963 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Wed, 27 Sep 2023 16:12:49 -0400 Subject: [PATCH 12/27] Address Maks's comments. --- spec.bs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/spec.bs b/spec.bs index 15c2ae82a..77d1a2af6 100644 --- a/spec.bs +++ b/spec.bs @@ -1499,7 +1499,7 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] - with |signedAdditionalBid|. + given |signedAdditionalBid|. 1. Return null if any of the following conditions hold: * |parsedSignedAdditionalBid| is not a [=map=]; * |parsedSignedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=string=]; @@ -1524,7 +1524,7 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd [=signed additional bid signature/signature=] is |maybeSignature|. 1. [=list/Append=] |signature| to |signatures|. 1. If |decodeSignatureFailed| is true, then return null. - 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] with + 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] given |parsedSignedAdditionalBid|["bid"], |auctionConfig| and |topLevelAuctionConfig|. 1. Return null if any of the following conditions hold: * |decodedAdditionalBid| is failure; @@ -1532,10 +1532,14 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd [=additional bid/bid=]'s [=generated bid/bid=]'s [=bid with currency/currency=] and the result of running [=look up per-buyer currency=] with |auctionConfig|. 1. Check whether the additional bid is negative targeted: - 1. Let |verifiedSignatures| be a new [=ordered set=] of [=signed additional bid signatures=]. + 1. Let |verifiedSignatureKeys| be a new [=set=] of [=byte sequences=]. 1. [=list/For each=] |signature| of |signatures|: - 1. If [=verify=] |signature| succeeded: - 1. [=set/Append=] |signature| to |verifiedSignatures|. + 1. Let |isSignatureValid| be the result of running [=verify=] |signature|'s + [=signed additional bid signature/signature=] on message |parsedSignedAdditionalBid|["bid"] + using |signature|'s [=signed additional bid signature/key=], with 0 for Ed25519ctx. + 1. If |isSignatureValid| is true: + 1. [=set/Append=] |signature|'s [=signed additional bid signature/key=] to + |verifiedSignatureKeys|. 1. Let |negativeTargeted| be false. 1. Let |additionalBidBuyer| be |additionalBid|'s [=additional bid/bid=]'s [=generated bid/interest group=]'s [=interest group/owner=]. @@ -1544,7 +1548,7 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd 1. If |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: 1. Let (|joiningOrigin|, |additionalBidKey|) be |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)]. - 1. If |verifiedSignatures| [=list/contains=] |additionalBidKey|: + 1. If |verifiedSignatureKeys| [=list/contains=] |additionalBidKey|: 1. If |joiningOrigin| is not null: 1. If |joiningOrigin| is not [=same origin=] with |additionalBid|'s [=additional bid/negative target joining origin=], then [=iteration/continue=]. @@ -1561,9 +1565,12 @@ To parse a signed additional bid given a [=byte sequence=] |signedAdd
-To decode an additional bid given a [=map=] |parsedAdditionalBid|, an [=auction config=] +To decode an additional bid given a [=string=] |additionalBidJson|, an [=auction config=] |auctionConfig|, and an [=auction config=]-or-null |topLevelAuctionConfig|: + 1. Let |parsedAdditionalBid| be the result of [=parse a JSON string to an infra value=] given + |additionalBidJson|. + 1. If |parsedAdditionalBid| is not a [=map=], then return failure. 1. Let |result| be a new [=additional bid=]. 1. Return failure if any of the following conditions hold: * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; @@ -1574,13 +1581,14 @@ To decode an additional bid given a [=map=] |parsedAdditionalBid|, an 1. If |topLevelAuctionConfig| is null: 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. 1. Otherwise: + 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], then return failure. 1. Let |bidTopLevelSeller| be the result of running the [=parse an origin=] with |parsedAdditionalBid|["topLevelSeller"]. - 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], or |bidTopLevelSeller| - is failure, or |bidTopLevelSeller| is not [=same origin=] with |topLevelAuctionConfig|'s - [=auction config/seller=], then return failure. + 1. If |bidTopLevelSeller| is failure, or |bidTopLevelSeller| is not [=same origin=] with + |topLevelAuctionConfig|'s [=auction config/seller=], then return failure. 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. + 1. If |igMap| is not a [=map=], then return failure. 1. Let |igName| be |igMap|["name"] if it [=map/exists=], otherwise return failure. 1. Let |igBiddingUrlStr| be |igMap|["biddingLogicURL"] if it [=map/exists=], otherwise return failure. @@ -1605,7 +1613,8 @@ To decode an additional bid given a [=map=] |parsedAdditionalBid|, an 1. Let |renderUrlStr| be |bidMap|["render"] if it [=map/exists=], otherwise return failure. 1. Let |renderUrl| be the result of running [=url parser=] on |renderUrlStr|. 1. If |renderUrl| is failure, then return failure. - 1. Set |ig|'s [=interest group/ads=] to « |renderUrl| ». + 1. Let |ad| be a new [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|. + 1. Set |ig|'s [=interest group/ads=] to « |ad| ». 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, return failure. 1. Let |adMetadata| be "null". From 15f61df7e80b12615de700a6fa6da2ac8eaa2b3e Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 28 Sep 2023 14:19:35 -0400 Subject: [PATCH 13/27] Change to [=parse an https origin=] --- spec.bs | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/spec.bs b/spec.bs index d014cbb39..d048c2ceb 100644 --- a/spec.bs +++ b/spec.bs @@ -997,10 +997,11 @@ To validate and convert auction ad config given an {{AuctionAdConfig} * |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=]; * |config|["{{AuctionAdConfig/interestGroupBuyers}}"] does not [=map/exist=], or it's [=list/empty=]; * |config|["{{AuctionAdConfig/componentAuctions}}"] is not [=list/empty=]. + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: * To parse the value |result|: - 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. + 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. 1. Set |auctionConfig|'s [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] to {{undefined}}. * To handle an error: @@ -1333,17 +1334,14 @@ To generate and score bids given an [=auction config=] |auctionConfig 1. Set |componentAuctionExpectedCurrency| to the result of [=looking up per-buyer currency=] with |topLevelAuctionConfig| and |auctionConfig|'s [=auction config/seller=]. 1. Let |pendingBuyers| be the [=map/size=] of |bidGenerators|. -1. Let |pendingAdditionalBids| be 0. -1. Let |additionalBids| be null. -1. If |auctionConfig|'s [=auction config/expects additional bids=] is true: - 1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with - |auctionConfig|, |topLevelAuctionConfig| and |negativeTargetInfo|. - 1. Set |pendingAdditionalBids| to the [=list/size=] of |additionalBids|. - 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: - 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, - |decisionLogicScript|, null, |auctionLevel|, |componentAuctionExpectedCurrency|, and - |settings|'s [=environment/top-level origin=]. - 1. Decrement |pendingAdditionalBids| by 1. +1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with + |auctionConfig|, |topLevelAuctionConfig| and |negativeTargetInfo|. +1. Set |pendingAdditionalBids| to the [=list/size=] of |additionalBids|. +1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: + 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, + |decisionLogicScript|, null, |auctionLevel|, |componentAuctionExpectedCurrency|, and + |settings|'s [=environment/top-level origin=]. + 1. Decrement |pendingAdditionalBids| by 1. 1. [=map/For each=] |buyer| → |perBuyerGenerator| of |bidGenerators|, [=parallel queue/enqueue steps|enqueue the following steps=] to |queue|: 1. Let |buyerExperimentGroupId| be |allBuyersExperimentGroupId|. @@ -1507,7 +1505,8 @@ are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: To parse a signed additional bid given a [=byte sequence=] |signedAdditionalBid|, an [=auction config=] |auctionConfig|, an [=auction config=]-or-null |topLevelAuctionConfig|, and a [=map=] |negativeTargetInfo| whose [=map/keys=] are [=tuples=] consisting of an [=origin=] and a -[=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: +[=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a +[=byte sequence=]: 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] given |signedAdditionalBid|. @@ -1587,13 +1586,13 @@ To decode an additional bid given a [=string=] |additionalBidJson|, a * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; * |parsedAdditionalBid|["auctionNonce"] is not |auctionConfig|'s [=auction config/auction nonce=]; * |parsedAdditionalBid|["seller"] does not [=map/exist=]; - * The result of running the [=parse an origin=] with |parsedAdditionalBid|["seller"] is failure, - or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. + * The result of running the [=parse an https origin=] with |parsedAdditionalBid|["seller"] is + failure, or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. 1. If |topLevelAuctionConfig| is null: 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. 1. Otherwise: 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], then return failure. - 1. Let |bidTopLevelSeller| be the result of running the [=parse an origin=] with + 1. Let |bidTopLevelSeller| be the result of running the [=parse an https origin=] with |parsedAdditionalBid|["topLevelSeller"]. 1. If |bidTopLevelSeller| is failure, or |bidTopLevelSeller| is not [=same origin=] with |topLevelAuctionConfig|'s [=auction config/seller=], then return failure. @@ -1605,10 +1604,9 @@ To decode an additional bid given a [=string=] |additionalBidJson|, a failure. 1. Let |igOwnerStr| be |igMap|["owner"] if it [=map/exists=], otherwise return failure. 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igBiddingUrlStr|. - 1. Let |igOwner| be the result of running [=parse an origin=] with |igOwnerStr|. + 1. Let |igOwner| be the result of running [=parse an https origin=] with |igOwnerStr|. 1. Return failure if any of the following conditions hold: * |igOwner| is failure; - * |igOwner|'s [=origin/scheme=] is not "`https`"; * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; * |igBiddingUrl| is failure; * |igOwner| is not [=same origin=] with |igBiddingUrl|. @@ -1669,9 +1667,9 @@ To decode an additional bid given a [=string=] |additionalBidJson|, a * |multipleNegativeIg| is not a [=map=]; * |multipleNegativeIg|["joiningOrigin"] does not [=map/exist=], or it's not a [=string=]; * |multipleNegativeIg|["interestGroupNames"] does not [=map/exist=], or it's not a [=list=]. - 1. Let |joiningOrigin| be the result of running [=parse an origin=] with + 1. Let |joiningOrigin| be the result of running [=parse an https origin=] with |multipleNegativeIg|["joiningOrigin"]. - 1. If |joiningOrigin| is failure, or its [=origin/scheme=] is not "`https`", then return failure. + 1. If |joiningOrigin| is failure, then return failure. 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: 1. If |igName| is not a [=string=], then return failure. @@ -3760,9 +3758,9 @@ for encoded additional bids. To wait until configuration input promises resolve given an [=auction config=] |auctionConfig|: 1. Wait until |auctionConfig|'s [=auction config/pending promise count=] is 0. 1. [=Assert=] |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], - [=auction config/per buyer signals=], [=auction config/per buyer currencies=], - [=auction config/per buyer timeouts=], and - [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] are not {{Promise}}s. + [=auction config/per buyer signals=], [=auction config/per buyer currencies=], and + [=auction config/per buyer timeouts=] are not {{Promise}}s, and + [=auction config/expects additional bids=] is false. 1. If |auctionConfig|'s [=auction config/auction signals=], [=auction config/seller signals=], [=auction config/per buyer signals=], [=auction config/per buyer currencies=], [=auction config/per buyer timeouts=], or [=auction config/expects additional bids=] is failure, From 498393423af0bfa7ed7ccbd94db82dbbca684a4f Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 28 Sep 2023 14:30:30 -0400 Subject: [PATCH 14/27] Add a note: additional bid promise resolves after fetch finishes. --- spec.bs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec.bs b/spec.bs index d048c2ceb..cbb5fee40 100644 --- a/spec.bs +++ b/spec.bs @@ -1000,6 +1000,13 @@ To validate and convert auction ad config given an {{AuctionAdConfig} 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. 1. [=Handle an input promise in configuration=] given |auctionConfig| and |config|["{{AuctionAdConfig/additionalBids}}"]: + + Note: The JavaScript code calling {{Navigator/runAdAuction()}} is responsible for not resolving + |config|["{{AuctionAdConfig/additionalBids}}"] until a {{WindowOrWorkerGlobalScope/fetch()}} + call for fetching additional bids with the `{adAuctionHeaders: true}` option returns + a response, or alternatively, not calling {{Navigator/runAdAuction()}} until the + {{WindowOrWorkerGlobalScope/fetch()}} call returns a response. + * To parse the value |result|: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. 1. Set |auctionConfig|'s [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] From b8621831295f9be4b9998caa5d239c39146a2274 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 28 Sep 2023 16:19:53 -0400 Subject: [PATCH 15/27] Address comments. --- spec.bs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/spec.bs b/spec.bs index cbb5fee40..c1f8349a0 100644 --- a/spec.bs +++ b/spec.bs @@ -984,14 +984,8 @@ To validate and convert auction ad config given an {{AuctionAdConfig} [=serializing a JavaScript value to a JSON string=], given |result|. * To handle an error, set |auctionConfig|'s [=auction config/seller signals=] to failure. 1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=]: - 1. Let |nonce| be an empty [=string=]. - 1. [=list/For each=] |i| in [=the range=] from 0 to the [=string/length=] of - |config|["{{AuctionAdConfig/auctionNonce}}"], exclusive: - 1. Let |c| be the |i|th [=code unit=] of |config|["{{AuctionAdConfig/auctionNonce}}"] if it is - 0x0000 NULL or in the range 0x0020 SPACE to 0x007F DELETE, inclusive, otherwise "`?`". - 1. Append |c| to the end of |nonce|. 1. Set |auctionConfig|'s [=auction config/auction nonce=] to the result of running - [=get uuid from string=] with |nonce|. + [=get uuid from string=] with |config|["{{AuctionAdConfig/auctionNonce}}"]. 1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: 1. [=exception/Throw=] a {{TypeError}} if any of the following conditions hold: * |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=]; @@ -1013,8 +1007,6 @@ To validate and convert auction ad config given an {{AuctionAdConfig} to {{undefined}}. * To handle an error: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to failure. - 1. Set |auctionConfig|'s [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] - to {{undefined}}. 1. If |config|["{{AuctionAdConfig/directFromSellerSignals}}"] [=map/exists=]: 1. TODO: The receiving end of this isn't specified yet, so there is no place to put the computed value. 1. [=Handle an input promise in configuration=] given |auctionConfig| and @@ -1605,13 +1597,14 @@ To decode an additional bid given a [=string=] |additionalBidJson|, a |topLevelAuctionConfig|'s [=auction config/seller=], then return failure. 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. - 1. If |igMap| is not a [=map=], then return failure. - 1. Let |igName| be |igMap|["name"] if it [=map/exists=], otherwise return failure. - 1. Let |igBiddingUrlStr| be |igMap|["biddingLogicURL"] if it [=map/exists=], otherwise return - failure. - 1. Let |igOwnerStr| be |igMap|["owner"] if it [=map/exists=], otherwise return failure. - 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igBiddingUrlStr|. - 1. Let |igOwner| be the result of running [=parse an https origin=] with |igOwnerStr|. + 1. Return failure if any the following conditions hold: + * |igMap| is not a [=map=]; + * |igMap|["name"] does not [=map/exist=], or it's not a [=string=]; + * |igMap|["biddingLogicURL"] does not [=map/exist=], or it's not a [=string=]; + * |igMap|["owner"] does not [=map/exist=], or it's not a [=string=]; + 1. Let |igOwner| be the result of running [=parse an https origin=] given |igMap|["owner"]. + 1. Let |igName| be |igMap|["name"]. + 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igMap|["biddingLogicURL"]. 1. Return failure if any of the following conditions hold: * |igOwner| is failure; * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; @@ -1626,13 +1619,13 @@ To decode an additional bid given a [=string=] |additionalBidJson|, a :: |igBiddingUrl| 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=map=], return failure. 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. - 1. Let |renderUrlStr| be |bidMap|["render"] if it [=map/exists=], otherwise return failure. - 1. Let |renderUrl| be the result of running [=url parser=] on |renderUrlStr|. + 1. If |bidMap|["render"] does not [=map/exist=] or it's not a [=string=], then return failure. + 1. Let |renderUrl| be the result of running [=url parser=] on |bidMap|["render"]. 1. If |renderUrl| is failure, then return failure. 1. Let |ad| be a new [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|. 1. Set |ig|'s [=interest group/ads=] to « |ad| ». 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. - 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, return failure. + 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, then return failure. 1. Let |adMetadata| be "null". 1. If |bidMap|["ad"] [=map/exists=]: 1. Set |adMetadata| to the result of running [=serialize an Infra value to a JSON string=] with From 784a978d7c1a02d255abb4f552618cbf20a2b587 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Fri, 29 Sep 2023 11:23:16 -0400 Subject: [PATCH 16/27] Check ig's ads being null. --- spec.bs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec.bs b/spec.bs index c1f8349a0..12ec1e652 100644 --- a/spec.bs +++ b/spec.bs @@ -3224,6 +3224,7 @@ The updateAdInterestGroups() method steps are: 1. If |key| is not |groupMember|, [=iteration/continue=]. 1. If |value| is not a [=list=] of {{AuctionAd}}, jump to the step labeled Abort update. + 1. Let |igAds| be a new [=list=] of [=interest group ads=]. 1. [=list/For each=] |ad| of |value|: 1. Let |igAd| be a new [=interest group ad=]. 1. Let |renderURL| be the result of running the [=URL parser=] on @@ -3243,9 +3244,10 @@ The updateAdInterestGroups() method steps are: 1. If |ad|["{{AuctionAd/buyerReportingId}}"] [=map/exists=] then set |igAd|'s [=interest group ad/buyer reporting ID=] to it. 1. If |ad|["{{AuctionAd/buyerAndSellerReportingId}}"] [=map/exists=] - then set |igAd|'s [=interest group ad/buyer and seller - reporting ID=] to it. - 1. [=list/Append=] |igAd| to |ig|'s |interestGroupField|. + then set |igAd|'s [=interest group ad/buyer and seller reporting ID=] to it. + 1. [=list/Append=] |igAd| to |igAds|. + 1. If |igAds| is not [=list/is empty=]: + 1. Set |ig|'s |interestGroupField| to |igAds|.
"`additionalBidKey`"
@@ -3254,15 +3256,17 @@ The updateAdInterestGroups() method steps are: following conditions hold: * |decodedKey| is a failure; * |decodedKey|'s [=byte sequence/length=] is not 32; - * |parsedUpdate|["`ads`"] [=map/exists=]. + * |ig|'s [=interest group/ads=] is not null. 1. Set |ig|'s [=interest group/additional bid key=] to |decodedKey|. + 1. If |ig|'s [=interest group/estimated size=] is greater than 50 KB, then jump to the step + labeled Abort update. 1. Set |ig|'s [=interest group/next update after=] to the [=current wall time=] plus 24 hours. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and [=interest group/name=] in the browser’s [=interest group set=] with |ig|. 1. Abort update: We jump here if some part of the - [=interest group=] update failed. [=iteration/Continue=] to the next [=interest group=] update. + [=interest group=] update failed. [=iteration/Continue=] to the next [=interest group=] update.
From ff2bc2afadf3d993479a5370731a7335823ea2db Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Fri, 29 Sep 2023 15:20:28 -0400 Subject: [PATCH 17/27] Updated IG cannot have both ads and additional bid key. --- spec.bs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec.bs b/spec.bs index 12ec1e652..424fe3c72 100644 --- a/spec.bs +++ b/spec.bs @@ -3256,12 +3256,13 @@ The updateAdInterestGroups() method steps are: following conditions hold: * |decodedKey| is a failure; * |decodedKey|'s [=byte sequence/length=] is not 32; - * |ig|'s [=interest group/ads=] is not null. 1. Set |ig|'s [=interest group/additional bid key=] to |decodedKey|. - - 1. If |ig|'s [=interest group/estimated size=] is greater than 50 KB, then jump to the step - labeled Abort update. + 1. Jump to the step labeled Abort update if any of the + following conditions hold: + * |ig|'s [=interest group/ads=] is not null, and |ig|'s [=interest group/additional bid key=] + is not null; + * |ig|'s [=interest group/estimated size=] is greater than 50 KB. 1. Set |ig|'s [=interest group/next update after=] to the [=current wall time=] plus 24 hours. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and [=interest group/name=] in the browser’s [=interest group set=] with |ig|. From 77681333f41c0958af922ac6d21e4146915b0d94 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Fri, 29 Sep 2023 15:25:51 -0400 Subject: [PATCH 18/27] fix build error. --- spec.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/spec.bs b/spec.bs index 424fe3c72..475118d3b 100644 --- a/spec.bs +++ b/spec.bs @@ -3257,6 +3257,7 @@ The updateAdInterestGroups() method steps are: * |decodedKey| is a failure; * |decodedKey|'s [=byte sequence/length=] is not 32; 1. Set |ig|'s [=interest group/additional bid key=] to |decodedKey|. + 1. Jump to the step labeled Abort update if any of the following conditions hold: From 3b8353476347ccd3aa77867cc08f5e81fd4b010e Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Mon, 2 Oct 2023 00:09:49 -0400 Subject: [PATCH 19/27] Negative IG cannot be updated. --- spec.bs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec.bs b/spec.bs index 475118d3b..7ec0b45cb 100644 --- a/spec.bs +++ b/spec.bs @@ -186,8 +186,8 @@ dictionary AuctionAdInterestGroup : GenerateBidInterestGroup { }; -Note: An {{AuctionAdInterestGroup}} cannot include both {{GenerateBidInterestGroup/ads}} and -{{AuctionAdInterestGroup/additionalBidKey}} at the same time. +Note: An {{AuctionAdInterestGroup}} that includes {{AuctionAdInterestGroup/additionalBidKey}} cannot +include either of {{GenerateBidInterestGroup/ads}} or {{GenerateBidInterestGroup/updateURL}}. {{AuctionAdInterestGroup}} is used by {{Window/navigator}}.{{Navigator/joinAdInterestGroup()}}, and when an interest group is stored to [=interest group set=]. @@ -3497,7 +3497,8 @@ An interest group is a [=struct=] with the following [=struct/items=]:

: update url :: Null or a [=URL=]. Provides a mechanism for the group's owner to periodically update the - attributes of the interest group. See [interest group updates](#interest-group-updates). + attributes of the interest group. See [interest group updates](#interest-group-updates). Must be + null if [=interest group/additional bid key=] is not null.

When non-null, the [=interest group/update url=]'s [=origin=] will always be [=same origin=] with [=interest group/owner=]. From f33935aa7559486724102371db0528ee9d469272 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Mon, 9 Oct 2023 18:54:31 -0400 Subject: [PATCH 20/27] Address Dom's comments. --- spec.bs | 713 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 413 insertions(+), 300 deletions(-) diff --git a/spec.bs b/spec.bs index 7ec0b45cb..45146e801 100644 --- a/spec.bs +++ b/spec.bs @@ -182,13 +182,10 @@ dictionary GenerateBidInterestGroup { dictionary AuctionAdInterestGroup : GenerateBidInterestGroup { double priority = 0.0; record prioritySignalsOverrides; - DOMString? additionalBidKey; + DOMString additionalBidKey; }; -Note: An {{AuctionAdInterestGroup}} that includes {{AuctionAdInterestGroup/additionalBidKey}} cannot -include either of {{GenerateBidInterestGroup/ads}} or {{GenerateBidInterestGroup/updateURL}}. - {{AuctionAdInterestGroup}} is used by {{Window/navigator}}.{{Navigator/joinAdInterestGroup()}}, and when an interest group is stored to [=interest group set=]. `priority` and `prioritySignalsOverrides` are not passed to `generateBid()` because they can be @@ -326,6 +323,7 @@ This is detectable because it can change the set of fields that are read from th * |decodedKey| is a failure; * |decodedKey|'s [=byte sequence/length=] is not 32; * |group|["{{GenerateBidInterestGroup/ads}}"] [=map/exists=]. + * |group|["{{GenerateBidInterestGroup/updateURL}}"] [=map/exists=]. 1. Set |interestGroup|'s [=interest group/additional bid key=] to |decodedKey|. 1. If |interestGroup|'s [=interest group/estimated size=] is greater than 50 KB, then [=exception/throw=] a {{TypeError}}. @@ -359,17 +357,17 @@ The estimated size of an [=interest group=] |ig| 1. The [=string/length=] of the [=serialization of an origin|serialization=] of |ig|'s [=interest group/owner=]. 1. The [=string/length=] of |ig|'s [=interest group/name=]. -1. 8 bytes, which is the size of |ig|'s [=interest group/priority=]. +1. 8, which is the size of |ig|'s [=interest group/priority=]. 1. The [=string/length=] of |ig|'s [=interest group/execution mode=]. -1. 2 bytes, which is the size of |ig|'s [=interest group/enable bidding signals prioritization=]. +1. 2, which is the size of |ig|'s [=interest group/enable bidding signals prioritization=]. 1. If |ig|'s [=interest group/priority vector=] is not null, [=map/for each=] |key| → |value| of [=interest group/priority vector=]: 1. The [=string/length=] of |key|. - 1. 8 bytes, which is the size of |value|. + 1. 8, which is the size of |value|. 1. If |ig|'s [=interest group/priority signals overrides=] is not null, [=map/for each=] |key| → |value| of [=interest group/priority signals overrides=]: 1. The [=string/length=] of |key|. - 1. 8 bytes, which is the size of |value|. + 1. 8, which is the size of |value|. 1. The [=string/length=] of the [=URL serializer|serialization=] of |ig|'s [=interest group/bidding url=], if the field is not null. 1. The [=string/length=] of the [=URL serializer|serialization=] of |ig|'s @@ -396,7 +394,7 @@ The estimated size of an [=interest group=] |ig| [=interest group ad/render url=]. 1. The [=string/length=] of |ad|'s [=interest group ad/metadata=] if the field is not null. 1. If |ig|'s [=interest group/additional bid key=] is not null: - 1. 32 bytes. + 1. 32, which is its size (number of bytes). @@ -536,34 +534,6 @@ to conduct an auction to select an advertisement to display to the user, they ca tells the browser how to conduct the auction and which on-device recorded interests are allowed to bid in the auction for the chance to display their advertisement. -

createAuctionNonce()

- -{{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a one-time ID -uniquely associated with a single call to {{Window/navigator}}.{{Navigator/runAdAuction()}}. For -multi-seller auctions, this ID is uniquely associated with all {{AuctionAdConfig/componentAuctions}}. -This nonce will need to be passed back in via a subsequent call to -{{Window/navigator}}.{{Navigator/runAdAuction()}} via the {{AuctionAdConfig}}. This is currently -only needed for auctions that use additional bids, for which the auction nonce will be included in -each additional bid as a way of ensuring that those bids are only used in the auctions for which -they were intended. - - -[SecureContext] -partial interface Navigator { - Promise<DOMString> createAuctionNonce(); -}; - - -
-The createAuctionNonce() method steps are: - - 1. Let |p| be [=a new promise=]. - 1. Run the following steps [=in parallel=]: - 1. Let |nonce| be the [=string representation=] of [=version 4 UUID=]. - 1. [=Queue a task=] to [=resolve=] |p| with |nonce|. - 1. Return |p|. -
-

runAdAuction()

@@ -983,13 +953,13 @@ To <dfn>validate and convert auction ad config</dfn> given an {{AuctionAdConfig} 1. Set |auctionConfig|'s [=auction config/seller signals=] to the result of [=serializing a JavaScript value to a JSON string=], given |result|. * To handle an error, set |auctionConfig|'s [=auction config/seller signals=] to failure. -1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=]: - 1. Set |auctionConfig|'s [=auction config/auction nonce=] to the result of running - [=get uuid from string=] with |config|["{{AuctionAdConfig/auctionNonce}}"]. +1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=], then set |auctionConfig|'s + [=auction config/auction nonce=] to the result of running [=get uuid from string=] with + |config|["{{AuctionAdConfig/auctionNonce}}"]. 1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: 1. [=exception/Throw=] a {{TypeError}} if any of the following conditions hold: * |config|["{{AuctionAdConfig/auctionNonce}}"] does not [=map/exist=]; - * |config|["{{AuctionAdConfig/interestGroupBuyers}}"] does not [=map/exist=], or it's [=list/empty=]; + * |config|["{{AuctionAdConfig/interestGroupBuyers}}"] does not [=map/exist=], or is [=list/empty=]; * |config|["{{AuctionAdConfig/componentAuctions}}"] is not [=list/empty=]. 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to true. 1. [=Handle an input promise in configuration=] given |auctionConfig| and @@ -1180,9 +1150,7 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|: To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfig|: 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=per buyer bid generators=]. -1. Let |negativeTargetInfo| be a new [=map=] whose [=map/keys=] are [=tuples=] consisting of an - [=origin=] and a [=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] - and a [=byte sequence=]. +1. Let |negativeTargetInfo| be a new [=negative target info=]. 1. [=list/For each=] |buyer| in |auctionConfig|'s [=auction config/interest group buyers=]: 1. [=list/For each=] |ig| of the user agent's [=interest group set=] whose [=interest group/owner=] is |buyer|: @@ -1333,9 +1301,9 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig 1. Set |componentAuctionExpectedCurrency| to the result of [=looking up per-buyer currency=] with |topLevelAuctionConfig| and |auctionConfig|'s [=auction config/seller=]. 1. Let |pendingBuyers| be the [=map/size=] of |bidGenerators|. -1. Set |additionalBids| to the result of running [=validate and convert additional bids=] with +1. Let |additionalBids| be the result of running [=validate and convert additional bids=] with |auctionConfig|, |topLevelAuctionConfig| and |negativeTargetInfo|. -1. Set |pendingAdditionalBids| to the [=list/size=] of |additionalBids|. +1. Let |pendingAdditionalBids| be the [=list/size=] of |additionalBids|. 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, |decisionLogicScript|, null, |auctionLevel|, |componentAuctionExpectedCurrency|, and @@ -1478,227 +1446,6 @@ To <dfn>build an interest group passed to generateBid</dfn> given an [=interest 1. Return |igGenerateBid|. </div> -<div algorithm> -To <dfn>validate and convert additional bids</dfn> given an [=auction config=] |auctionConfig|, an -[=auction config=]-or-null |topLevelAuctionConfig|, and a [=map=] |negativeTargetInfo| whose -[=map/keys=] are [=tuples=] consisting of an [=origin=] and a [=string=], and whose [=map/values=] -are [=tuples=] consisting of an [=origin=] and a [=byte sequence=]: - - 1. Let |auctionNonce| be |auctionConfig|'s [=auction config/auction nonce=]. - 1. Let |capturedAdditionalBidsHeaders| be [=this=]'s [=relevant global object=]'s - [=associated Document's=] [=node navigable's=] [=traversable navigable's=] - [=captured additional bids headers=]. - 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. - 1. [=list/For each=] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: - 1. Let |signedAdditionalBid| be the result of running [=forgiving-base64 decode=] with - |encodedSignedAdditionalBid|. - 1. If |signedAdditionalBid| is failure, then [=iteration/continue=]. - 1. Let |additionalBid| be the result of running [=parse a signed additional bid=] given - |signedAdditionalBid|, |auctionConfig|, |topLevelAuctionConfig|, and |negativeTargetInfo|. - 1. If |additionalBid| is not null: - 1. [=list/Append=] |additionalBid| to |additionalBids|. - 1. Return |additionalBids|. -</div> - -<div algorithm> -To <dfn>parse a signed additional bid</dfn> given a [=byte sequence=] |signedAdditionalBid|, an -[=auction config=] |auctionConfig|, an [=auction config=]-or-null |topLevelAuctionConfig|, and a -[=map=] |negativeTargetInfo| whose [=map/keys=] are [=tuples=] consisting of an [=origin=] and a -[=string=], and whose [=map/values=] are [=tuples=] consisting of an [=origin=] and a -[=byte sequence=]: - - 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] - given |signedAdditionalBid|. - 1. Return null if any of the following conditions hold: - * |parsedSignedAdditionalBid| is not a [=map=]; - * |parsedSignedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=string=]; - * |parsedSignedAdditionalBid|["signatures"] does not [=map/exist=], or it's not a [=list=]. - 1. Let |signatures| be a new [=list=] of [=signed additional bid signatures=]. - 1. Let |decodeSignatureFailed| be false. - 1. [=list/For each=] |sig| of |parsedSignedAdditionalBid|["signatures"]: - 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following - conditions hold: - * |sig| is not a [=map=]; - * |sig|["key"] does not [=map/exist=], or it's not a [=string=]; - * |sig|["signature"] does not [=map/exist=], or it's not a [=string=]. - 1. Let |maybeKey| be the result of running [=forgiving-base64 decode=] with |sig|["key"]. - 1. Let |maybeSignature| be the result of running [=forgiving-base64 decode=] with - |sig|["signature"]. - 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following - conditions hold: - * |maybeKey| is failure, or its [=byte sequence/length=] is not 32; - * |maybeSignature| is failure, or its [=byte sequence/length=] is not 64; - 1. Let |signature| be a [=signed additional bid signatures=], whose - [=signed additional bid signature/key=] is |maybeKey|, and - [=signed additional bid signature/signature=] is |maybeSignature|. - 1. [=list/Append=] |signature| to |signatures|. - 1. If |decodeSignatureFailed| is true, then return null. - 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid=] given - |parsedSignedAdditionalBid|["bid"], |auctionConfig| and |topLevelAuctionConfig|. - 1. Return null if any of the following conditions hold: - * |decodedAdditionalBid| is failure; - * The result of [=checking a currency tag=] with |decodedAdditionalBid|'s - [=additional bid/bid=]'s [=generated bid/bid=]'s [=bid with currency/currency=] and the - result of running [=look up per-buyer currency=] with |auctionConfig|. - 1. Check whether the additional bid is negative targeted: - 1. Let |verifiedSignatureKeys| be a new [=set=] of [=byte sequences=]. - 1. [=list/For each=] |signature| of |signatures|: - 1. Let |isSignatureValid| be the result of running [=verify=] |signature|'s - [=signed additional bid signature/signature=] on message |parsedSignedAdditionalBid|["bid"] - using |signature|'s [=signed additional bid signature/key=], with 0 for Ed25519ctx. - 1. If |isSignatureValid| is true: - 1. [=set/Append=] |signature|'s [=signed additional bid signature/key=] to - |verifiedSignatureKeys|. - 1. Let |negativeTargeted| be false. - 1. Let |additionalBidBuyer| be |additionalBid|'s [=additional bid/bid=]'s - [=generated bid/interest group=]'s [=interest group/owner=]. - 1. [=list/For each=] |igName| of |additionalBid|'s - [=additional bid/negative target interest group names=]: - 1. If |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: - 1. Let (|joiningOrigin|, |additionalBidKey|) be - |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)]. - 1. If |verifiedSignatureKeys| [=list/contains=] |additionalBidKey|: - 1. If |joiningOrigin| is not null: - 1. If |joiningOrigin| is not [=same origin=] with |additionalBid|'s - [=additional bid/negative target joining origin=], then [=iteration/continue=]. - 1. Set |negativeTargeted| to true, and [=iteration/break=]. - - Note: If the signature doesn't verify successfully, the additional bid proceeds as if the - negative interest group is not present. This ensures that only the owner of the negative - interest group, who created the additonalBidKey, is allowed to negatively target the - interest group, and that nobody else can learn whether the interest group is present on - the device. - 1. If |negativeTargeted| is true: - 1. Return null. - 1. Return |decodedAdditionalBid|. -</div> - -<div algorithm> -To <dfn>decode an additional bid</dfn> given a [=string=] |additionalBidJson|, an [=auction config=] -|auctionConfig|, and an [=auction config=]-or-null |topLevelAuctionConfig|: - - 1. Let |parsedAdditionalBid| be the result of [=parse a JSON string to an infra value=] given - |additionalBidJson|. - 1. If |parsedAdditionalBid| is not a [=map=], then return failure. - 1. Let |result| be a new [=additional bid=]. - 1. Return failure if any of the following conditions hold: - * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; - * |parsedAdditionalBid|["auctionNonce"] is not |auctionConfig|'s [=auction config/auction nonce=]; - * |parsedAdditionalBid|["seller"] does not [=map/exist=]; - * The result of running the [=parse an https origin=] with |parsedAdditionalBid|["seller"] is - failure, or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. - 1. If |topLevelAuctionConfig| is null: - 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. - 1. Otherwise: - 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], then return failure. - 1. Let |bidTopLevelSeller| be the result of running the [=parse an https origin=] with - |parsedAdditionalBid|["topLevelSeller"]. - 1. If |bidTopLevelSeller| is failure, or |bidTopLevelSeller| is not [=same origin=] with - |topLevelAuctionConfig|'s [=auction config/seller=], then return failure. - 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. - 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. - 1. Return failure if any the following conditions hold: - * |igMap| is not a [=map=]; - * |igMap|["name"] does not [=map/exist=], or it's not a [=string=]; - * |igMap|["biddingLogicURL"] does not [=map/exist=], or it's not a [=string=]; - * |igMap|["owner"] does not [=map/exist=], or it's not a [=string=]; - 1. Let |igOwner| be the result of running [=parse an https origin=] given |igMap|["owner"]. - 1. Let |igName| be |igMap|["name"]. - 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igMap|["biddingLogicURL"]. - 1. Return failure if any of the following conditions hold: - * |igOwner| is failure; - * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; - * |igBiddingUrl| is failure; - * |igOwner| is not [=same origin=] with |igBiddingUrl|. - 1. Let |ig| be a new [=interest group=] with the following properties: - : [=interest group/owner=] - :: |igOwner| - : [=interest group/name=] - :: |igName| - : [=interest group/bidding url=] - :: |igBiddingUrl| - 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or it's not a [=map=], return failure. - 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. - 1. If |bidMap|["render"] does not [=map/exist=] or it's not a [=string=], then return failure. - 1. Let |renderUrl| be the result of running [=url parser=] on |bidMap|["render"]. - 1. If |renderUrl| is failure, then return failure. - 1. Let |ad| be a new [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|. - 1. Set |ig|'s [=interest group/ads=] to « |ad| ». - 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. - 1. If |bidVal| is not a {{double}}, or it's less than or equal to 0, then return failure. - 1. Let |adMetadata| be "null". - 1. If |bidMap|["ad"] [=map/exists=]: - 1. Set |adMetadata| to the result of running [=serialize an Infra value to a JSON string=] with - |bidMap|["ad"]. - 1. Let |bidCurrency| be null. - 1. If |bidMap|["bidCurrency"] [=map/exists=]: - 1. If |bidMap|["bidCurrency"] is not a [=string=], or the result of [=checking whether a string - is a valid currency tag=] is failure: - 1. Return failure. - 1. Set |bidCurrency| to |bidMap|["bidCurrency"]. - 1. Let |adCost| be null. - 1. If |bidMap|["adCost"] [=map/exists=]: - 1. If |bidMap|["adCost"] is not a {{double}}, then return failure. - 1. Set |adCost| to |bidMap|["adCost"]. - 1. Let |modelingSignals| be null. - 1. If |bidMap|["modelingSignals"] [=map/exists=]: - 1. If |bidMap|["modelingSignals"] is not a {{double}}, then return failure. - 1. If |bidMap|["modelingSignals"] is greater than or equal to 0, and less than 4096: - 1. Set |modelingSignals| to |bidMap|["modelingSignals"]. - 1. Let |adComponents| be a new [=list=] of [=ad descriptors=]. - 1. If |bidMap|["adComponents"] [=map/exists=]: - 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. - 1. [=list/For each=] |component| of |bidMap|["adComponents"]: - 1. If |component| is not a [=string=], then return failure. - 1. Let |componentUrl| be the result of running [=url parser=] on |component|. - 1. If |componentUrl| is failure, then return failure. - 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is - |componentUrl|. - 1. [=list/Append=] |componentDescriptor| to |adComponents|. - 1. Set |ig|'s [=interest group/ad components=] to |adComponents|. - 1. If |parsedAdditionalBid|["negativeInterestGroup"] [=map/exists=]: - 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or - |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. - 1. [=list/Append=] |parsedAdditionalBid|["negativeInterestGroup"] to |result|'s - [=additional bid/negative target interest group names=]. - 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=]: - 1. Let |multipleNegativeIg| be |parsedAdditionalBid|["negativeInterestGroups"]. - 1. Return failure if any of the following conditions hold: - * |multipleNegativeIg| is not a [=map=]; - * |multipleNegativeIg|["joiningOrigin"] does not [=map/exist=], or it's not a [=string=]; - * |multipleNegativeIg|["interestGroupNames"] does not [=map/exist=], or it's not a [=list=]. - 1. Let |joiningOrigin| be the result of running [=parse an https origin=] with - |multipleNegativeIg|["joiningOrigin"]. - 1. If |joiningOrigin| is failure, then return failure. - 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. - 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: - 1. If |igName| is not a [=string=], then return failure. - 1. [=list/Append=] |igName| to |result|'s - [=additional bid/negative target interest group names=]. - 1. Set |result|'s [=additional bid/bid=] to a new [=generated bid=] with the following properties: - : [=generated bid/bid=] - :: A [=bid with currency=] whose [=bid with currency/value=] is |bidVal|, and - [=bid with currency/currency=] is |bidCurrency| - : [=generated bid/ad=] - :: |adMetadata| - : [=generated bid/ad descriptor=] - :: An [=ad descriptor=] whose [=ad descriptor/url=] is |renderUrl| - : [=generated bid/ad component descriptors=] - :: |adComponents| - : [=generated bid/ad cost=] - :: |adCost| - : [=generated bid/modeling signals=] - :: |modelingSignals| - : [=generated bid/interest group=] - :: |ig| - : [=generated bid/bid ad=] - :: A [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|, and - [=interest group ad/metadata=] is |adMetadata| - : [=generated bid/provided as additional bid=] - :: true - 1. Return |result|. -</div> - <div algorithm> To <dfn>serialize a URL</dfn> given a [=URL=]-or-null |url|: @@ -2329,6 +2076,398 @@ and a {{ReportingBrowserSignals}} |browserSignals|: :: |reportingMacroMap| </div> +# Additional Bids and Negative Targeting # {#additional-bids-and-negative-targeting} + +## createAuctionNonce() ## {#create-auction-nonce} + +{{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a one-time +[=version 4 UUID=] uniquely associated with a single call to +{{Window/navigator}}.{{Navigator/runAdAuction()}}. For multi-seller auctions, this ID is uniquely +associated with all {{AuctionAdConfig/componentAuctions}}. +This nonce will need to be passed back in via a subsequent call to +{{Window/navigator}}.{{Navigator/runAdAuction()}} via the {{AuctionAdConfig}}. This is currently +only needed for auctions that use additional bids, for which the auction nonce will be included in +each additional bid as a way of ensuring that those bids are only used in the auctions for which +they were intended. + +<xmp class="idl"> +[SecureContext] +partial interface Navigator { + Promise<DOMString> createAuctionNonce(); +}; + + +
+The createAuctionNonce() method steps are: + + 1. Let |p| be [=a new promise=]. + 1. Run the following steps [=in parallel=]: + 1. Let |nonce| be the [=string representation=] of a [=version 4 UUID=]. + +
+ Because we're going in parallel: + * There is no guarantee that the promise will be resolved before other tasks get queued on the + main thread; + * ...which gives browsers the freedom to generate this UUID in another process, and + asynchronously send it back to the main thread at an arbitrary future time. +
+ 1. [=Queue a task=] to [=resolve=] |p| with |nonce|. + 1. Return |p|. +
+ +## Additional Bids ## {#additional-bids} + +In addition to [=generate a bid|bids generated by interest groups=], sellers can enable buyers to +introduce bids generated outside of the auction, which are called additional bids. Additional bids +are commonly triggered using contextual signals. Buyers compute the additional bids, likely as part +of a contextual auction. Buyers need to package up each additional bid using a new data structure +that encapsulates all of the information needed for the additional bid to compete against other bids +in a [[#running-ad-auctions|Protected Audience auction]]. + +
+

Each additional bid is expressed using the following JSON data structure:

+
+  const additionalBid = {
+    "bid": {
+      "ad": 'ad-metadata',
+      "adCost": 2.99,
+      "bid": 1.99,
+      "bidCurrency": "USD",
+      "render": "https://www.example-dsp.com/ad/123.jpg",
+      "adComponents": [adComponent1, adComponent2],
+      "allowComponentAuction": true,
+      "modelingSignals": 123,
+    },
+    "interestGroup": {
+      "owner": "https://www.example-dsp.com"
+      "name": "campaign123",
+      "biddingLogicURL": "https://www.example-dsp.com/bid_logic.js"
+    },
+    "negativeInterestGroups": {
+      joiningOrigin: "https://www.example-advertiser.com",
+      interestGroupNames: [
+        "example_advertiser_negative_interest_group_a",
+        "example_advertiser_negative_interest_group_b",
+      ]
+    },
+    "auctionNonce": "12345678-90ab-cdef-fedcba09876543210",
+    "seller": "https://www.example-ssp.com",
+    "topLevelSeller": "https://www.another-ssp.com"
+  }
+  
+
+ +
+To validate and convert additional bids given an [=auction config=] |auctionConfig|, an +[=auction config=]-or-null |topLevelAuctionConfig|, and a [=negative target info=] +|negativeTargetInfo|: + + 1. [=Assert=] that these steps are running [=in parallel=]. + 1. Let |auctionNonce| be the [=string representation=] of |auctionConfig|'s + [=auction config/auction nonce=]. + 1. Let |capturedAdditionalBidsHeaders| be [=this=]'s [=relevant global object=]'s + [=associated Document's=] [=node navigable's=] [=traversable navigable's=] + [=captured additional bids headers=]. + 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. + 1. [=list/For each=] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: + 1. Let |signedAdditionalBid| be the result of running [=forgiving-base64 decode=] with + |encodedSignedAdditionalBid|. + 1. If |signedAdditionalBid| is failure, then [=iteration/continue=]. + 1. Let |additionalBid| be the result of running [=parse a signed additional bid=] given + |signedAdditionalBid|, |auctionConfig|, |topLevelAuctionConfig|, and |negativeTargetInfo|. + 1. If |additionalBid| is not null: + 1. [=list/Append=] |additionalBid| to |additionalBids|. + 1. Return |additionalBids|. +
+ +
+To parse a signed additional bid given a [=byte sequence=] |signedAdditionalBid|, an +[=auction config=] |auctionConfig|, an [=auction config=]-or-null |topLevelAuctionConfig|, and a +[=negative target info=] |negativeTargetInfo|: + + 1. [=Assert=] that these steps are running [=in parallel=]. + 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] + given |signedAdditionalBid|. + 1. Return null if any of the following conditions hold: + * |parsedSignedAdditionalBid| is not a [=map=]; + * |parsedSignedAdditionalBid|["bid"] does not [=map/exist=], or is not a [=string=]; + * |parsedSignedAdditionalBid|["signatures"] does not [=map/exist=], or is not a [=list=]. + 1. Let |signatures| be a new [=list=] of [=signed additional bid signatures=]. + 1. Let |decodeSignatureFailed| be false. + 1. [=list/For each=] |sig| of |parsedSignedAdditionalBid|["signatures"]: + 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following + conditions hold: + * |sig| is not a [=map=]; + * |sig|["key"] does not [=map/exist=], or is not a [=string=]; + * |sig|["signature"] does not [=map/exist=], or is not a [=string=]. + 1. Let |maybeKey| be the result of running [=forgiving-base64 decode=] with |sig|["key"]. + 1. Let |maybeSignature| be the result of running [=forgiving-base64 decode=] with + |sig|["signature"]. + 1. Set |decodeSignatureFailed| to true and [=iteration/break=] if any of the following + conditions hold: + * |maybeKey| is failure, or its [=byte sequence/length=] is not 32; + * |maybeSignature| is failure, or its [=byte sequence/length=] is not 64; + 1. Let |signature| be a [=signed additional bid signatures=], whose + [=signed additional bid signature/key=] is |maybeKey|, and + [=signed additional bid signature/signature=] is |maybeSignature|. + 1. [=list/Append=] |signature| to |signatures|. + 1. If |decodeSignatureFailed| is true, then return null. + 1. Let |decodedAdditionalBid| be the result of [=decode an additional bid json=] given + |parsedSignedAdditionalBid|["bid"], |auctionConfig| and |topLevelAuctionConfig|. + 1. Return null if any of the following conditions hold: + * |decodedAdditionalBid| is failure; + * The result of [=checking a currency tag=] with |decodedAdditionalBid|'s + [=additional bid/bid=]'s [=generated bid/bid=]'s [=bid with currency/currency=] and the + result of running [=look up per-buyer currency=] with |auctionConfig|. + 1. Let |verifiedSignatureKeys| be a new [=set=] of [=byte sequences=]. + 1. [=list/For each=] |signature| of |signatures|: + 1. Let |isSignatureValid| be the result of running [=verify=] |signature|'s + [=signed additional bid signature/signature=] on message |parsedSignedAdditionalBid|["bid"] + using |signature|'s [=signed additional bid signature/key=], with 0 for Ed25519ctx. + 1. If |isSignatureValid| is true, then [=set/append=] |signature|'s + [=signed additional bid signature/key=] to |verifiedSignatureKeys|. + 1. If the result of [=checking whether negative targeted=] given |decodedAdditionalBid|, + |verifiedSignatureKeys| and |negativeTargetInfo| is true, then return null. + 1. Return |decodedAdditionalBid|. +
+ +
+To decode an additional bid json given a [=string=] |additionalBidJson|, an +[=auction config=] |auctionConfig|, and an [=auction config=]-or-null |topLevelAuctionConfig|: + + 1. [=Assert=] that these steps are running [=in parallel=]. + 1. Let |parsedAdditionalBid| be the result of [=parse a JSON string to an infra value=] given + |additionalBidJson|. + 1. If |parsedAdditionalBid| is not a [=map=], then return failure. + 1. Let |result| be a new [=additional bid=]. + 1. Return failure if any of the following conditions hold: + * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; + * |parsedAdditionalBid|["auctionNonce"] is not the [=string representation=] of + |auctionConfig|'s [=auction config/auction nonce=]; + * |parsedAdditionalBid|["seller"] does not [=map/exist=]; + * The result of running the [=parse an https origin=] with |parsedAdditionalBid|["seller"] is + failure, or not [=same origin=] with |auctionConfig|'s [=auction config/seller=]. + 1. If |topLevelAuctionConfig| is null: + 1. If |parsedAdditionalBid|["topLevelSeller"] [=map/exists=], then return failure. + 1. Otherwise: + 1. If |parsedAdditionalBid|["topLevelSeller"] does not [=map/exist=], then return failure. + 1. Let |bidTopLevelSeller| be the result of running the [=parse an https origin=] with + |parsedAdditionalBid|["topLevelSeller"]. + 1. If |bidTopLevelSeller| is failure, or |bidTopLevelSeller| is not [=same origin=] with + |topLevelAuctionConfig|'s [=auction config/seller=], then return failure. + 1. If |parsedAdditionalBid|["interestGroup"] does not [=map/exist=], then return failure. + 1. Let |igMap| be |parsedAdditionalBid|["interestGroup"]. + 1. Return failure if any the following conditions hold: + * |igMap| is not a [=map=]; + * |igMap|["name"] does not [=map/exist=], or is not a [=string=]; + * |igMap|["biddingLogicURL"] does not [=map/exist=], or is not a [=string=]; + * |igMap|["owner"] does not [=map/exist=], or is not a [=string=]; + 1. Let |igOwner| be the result of running [=parse an https origin=] given |igMap|["owner"]. + 1. Let |igName| be |igMap|["name"]. + 1. Let |igBiddingUrl| be the result of running [=url parser=] on |igMap|["biddingLogicURL"]. + 1. Return failure if any of the following conditions hold: + * |igOwner| is failure; + * |auctionConfig|'s [=auction config/interest group buyers=] does not [=list/contain=] |igOwner|; + * |igBiddingUrl| is failure; + * |igOwner| is not [=same origin=] with |igBiddingUrl|. + 1. Let |ig| be a new [=interest group=] with the following properties: + : [=interest group/owner=] + :: |igOwner| + : [=interest group/name=] + :: |igName| + : [=interest group/bidding url=] + :: |igBiddingUrl| + 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or is not a [=map=], return failure. + 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. + 1. If |bidMap|["render"] does not [=map/exist=] or is not a [=string=], then return failure. + 1. Let |renderUrl| be the result of running [=url parser=] on |bidMap|["render"]. + 1. If |renderUrl| is failure, then return failure. + 1. Let |ad| be a new [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|. + 1. Set |ig|'s [=interest group/ads=] to « |ad| ». + 1. Let |bidVal| be |bidMap|["bid"] if it [=map/exists=], otherwise return failure. + 1. If |bidVal| is not a {{double}}, or is less than or equal to 0, then return failure. + 1. Let |adMetadata| be "null". + 1. If |bidMap|["ad"] [=map/exists=]: + 1. Set |adMetadata| to the result of running [=serialize an Infra value to a JSON string=] with + |bidMap|["ad"]. + 1. Let |bidCurrency| be null. + 1. If |bidMap|["bidCurrency"] [=map/exists=]: + 1. If |bidMap|["bidCurrency"] is not a [=string=], or the result of [=checking whether a string + is a valid currency tag=] is failure: + 1. Return failure. + 1. Set |bidCurrency| to |bidMap|["bidCurrency"]. + 1. Let |adCost| be null. + 1. If |bidMap|["adCost"] [=map/exists=]: + 1. If |bidMap|["adCost"] is not a {{double}}, then return failure. + 1. Set |adCost| to |bidMap|["adCost"]. + 1. Let |modelingSignals| be null. + 1. If |bidMap|["modelingSignals"] [=map/exists=]: + 1. If |bidMap|["modelingSignals"] is not a {{double}}, then return failure. + 1. If |bidMap|["modelingSignals"] is greater than or equal to 0, and less than 4096: + 1. Set |modelingSignals| to |bidMap|["modelingSignals"]. + 1. Let |adComponents| be a new [=list=] of [=ad descriptors=]. + 1. If |bidMap|["adComponents"] [=map/exists=]: + 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. + 1. [=list/For each=] |component| of |bidMap|["adComponents"]: + 1. If |component| is not a [=string=], then return failure. + 1. Let |componentUrl| be the result of running [=url parser=] on |component|. + 1. If |componentUrl| is failure, then return failure. + 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is + |componentUrl|. + 1. [=list/Append=] |componentDescriptor| to |adComponents|. + 1. Set |ig|'s [=interest group/ad components=] to |adComponents|. + 1. If |parsedAdditionalBid|["negativeInterestGroup"] [=map/exists=]: + 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or + |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. + 1. [=list/Append=] |parsedAdditionalBid|["negativeInterestGroup"] to |result|'s + [=additional bid/negative target interest group names=]. + 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=]: + 1. Let |multipleNegativeIg| be |parsedAdditionalBid|["negativeInterestGroups"]. + 1. Return failure if any of the following conditions hold: + * |multipleNegativeIg| is not a [=map=]; + * |multipleNegativeIg|["joiningOrigin"] does not [=map/exist=], or is not a [=string=]; + * |multipleNegativeIg|["interestGroupNames"] does not [=map/exist=], or is not a [=list=]. + 1. Let |joiningOrigin| be the result of running [=parse an https origin=] with + |multipleNegativeIg|["joiningOrigin"]. + 1. If |joiningOrigin| is failure, then return failure. + 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. + 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: + 1. If |igName| is not a [=string=], then return failure. + 1. [=list/Append=] |igName| to |result|'s + [=additional bid/negative target interest group names=]. + 1. Set |result|'s [=additional bid/bid=] to a new [=generated bid=] with the following properties: + : [=generated bid/bid=] + :: A [=bid with currency=] whose [=bid with currency/value=] is |bidVal|, and + [=bid with currency/currency=] is |bidCurrency| + : [=generated bid/ad=] + :: |adMetadata| + : [=generated bid/ad descriptor=] + :: An [=ad descriptor=] whose [=ad descriptor/url=] is |renderUrl| + : [=generated bid/ad component descriptors=] + :: |adComponents| + : [=generated bid/ad cost=] + :: |adCost| + : [=generated bid/modeling signals=] + :: |modelingSignals| + : [=generated bid/interest group=] + :: |ig| + : [=generated bid/bid ad=] + :: A [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|, and + [=interest group ad/metadata=] is |adMetadata| + : [=generated bid/provided as additional bid=] + :: true + 1. Return |result|. +
+ +A signed additional bid signature is a [=struct=] with the following [=struct/items=]: +
+ : key + :: A [=byte sequence=] of length 32. + : signature + :: A [=byte sequence=] of length 64. +
+ +An additional bid is a [=struct=] with the following [=struct/items=]: +
+ : bid + :: A [=generated bid=]. Fields analogous to those returned by `generateBid()`. + : negative target interest group names + :: A [=list=] of [=strings=]. + : negative target joining origin + :: Null or an [=origin=]. Required if there is more than one entry in + [=additional bid/negative target interest group names=]. +
+ +Each [=traversable navigable=] has a captured additional bids headers, which is a [=map=] +whose [=map/keys=] are [=strings=] for auction nonces, and whose values are [=list=] of [=strings=] +for encoded additional bids. + +## Negative Targeting ## {#negative-targeting} + +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 negative interest groups, the additional bid is +dropped; otherwise it participates in the auction, competing alongside bids created by calls to +`generateBid()`. An additional bid that specifies no negative interest groups is always accepted +into the auction. + +
+To check whether negative targeted given an [=additional bid=] |additionalBid|, a [=set=] +of [=byte sequences=] |verifiedSignatureKeys|, and a [=negative target info=] |negativeTargetInfo|: + + 1. [=Assert=] that these steps are running [=in parallel=]. + 1. Let |negativeTargeted| be false. + 1. Let |additionalBidBuyer| be |additionalBid|'s [=additional bid/bid=]'s + [=generated bid/interest group=]'s [=interest group/owner=]. + 1. [=list/For each=] |igName| of |additionalBid|'s + [=additional bid/negative target interest group names=]: + 1. If |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: + 1. Let (|joiningOrigin|, |additionalBidKey|) be + |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)]. + 1. If |verifiedSignatureKeys| [=list/contains=] |additionalBidKey|: + 1. If |joiningOrigin| is not null: + 1. If |joiningOrigin| is not [=same origin=] with |additionalBid|'s + [=additional bid/negative target joining origin=], then [=iteration/continue=]. + 1. Set |negativeTargeted| to true, and [=iteration/break=]. + + Note: If the signature doesn't verify successfully, the additional bid proceeds as if the + [[#negative interest group]] is not present. This ensures that only the owner of the negative + interest group, who created the {{AuctionAdInterestGroup/additionalBidKey}}, is allowed to + negatively target the interest group, and that nobody else can learn whether the + [=interest group set=] [=list/contains=] the interest group. + 1. Return |negativeTargeted|. +
+ +A negative target info is a [=map=]. Its [=map/keys=] are [=tuples=] consisting of an +[=origin=] for [=interest group/owner=] and a [=string=] for [=interest group/name=]. +Its [=map/values=] are [=tuples=] consisting of an [=origin=] for [=interest group/joining origin=] +and a [=byte sequence=] for [=interest group/additional bid key=]. + +### Negative Interest Groups ### {#negative-interest-groups} + +Though negative interest groups are joined using the same {{Navigator/joinAdInterestGroup()}} API as +regular interest groups, they remain distinct from one another. Only negative interest groups can +provide an {{AuctionAdInterestGroup/additionalBidKey}}, while only regular interest groups can +provide {{GenerateBidInterestGroup/ads}}. No interest group may provide both. Because the subset of +fields used by a negative interest group cannot be meaningfully updated, a negative interest group +can not provide an {{GenerateBidInterestGroup/updateURL}}. + +[[#additional-bids]] specify the negative interest groups they're negatively targeting against +using at most one of the following two fields in their JSON data structure: + * negativeInterestGroup, for a single negative interest group; + * negativeInterestGroups, for more than one negative interest group. + +If an additional bid needs to specify more than one negative interest groups, all of those negative +interest groups must be joined from the same origin, and that origin must be identified ahead of +time in the additional bid's `joiningOrigin` field. Any negative interest group that wasn't joined +from that identified origin is ignored for negative targeting. + +
+

Use `negativeInterestGroup` in additional bid's JSON:

+
+  const additionalBid = {
+    ...
+    "negativeInterestGroup": "example_advertiser_negative_interest_group",
+    ...
+  }
+  
+

Use `negativeInterestGroups` in additional bid's JSON:

+
+  const additionalBid = {
+    ...
+    "negativeInterestGroups": {
+      joiningOrigin: "https://example-advertiser.com",
+      interestGroupNames: [
+        "example_advertiser_negative_interest_group_a",
+        "example_advertiser_negative_interest_group_b",
+      ]
+    },
+    ...
+  }
+  
+
+ # K-anonymity # {#k-anonymity} Two goals of this specification rely on applying [=k-anonymity=] thresholds: @@ -3281,7 +3420,7 @@ The following algorithms are some helper methods used in this document. To get uuid from string given a [=string=] |input|: 1. If |input|'s [=string/length=] is not 36, return an empty [=string=]. - 1. Let |uuid| be an empty [=string=]. + 1. Let |uuidStr| be an empty [=string=]. 1. [=list/For each=] |i| in [=the range=] from 1 to 36, inclusive: 1. Let |unit| be |input|'s |i|th [=code unit=]. 1. If « 8, 13, 18, 23 » [=list/contains=] |i|: @@ -3290,8 +3429,8 @@ The following algorithms are some helper methods used in this document. 1. Otherwise: 1. If |unit| is not an [=ASCII lower hex digit=]: 1. Return an empty [=string=]. - 1. Append |unit| to the end of |uuid|. - 1. Return |uuid|. + 1. Append |unit| to the end of |uuidStr|. + 1. Return [=ASCII encoded=] |uuidStr|. @@ -3719,7 +3858,7 @@ An auction config is a [=struct=] with the following items: component auctions are expected to use if [=auction config/per buyer currencies=] does not specify a particular value. : auction nonce -:: Null or a [=string=], initially null. +:: Null or a [=version 4 UUID=], initially null. A unique identifier associated with this and only this invocation of {{Window/navigator}}.{{Navigator/runAdAuction()}}. For multi-seller auctions, this ID is uniquely associated with all {{AuctionAdConfig/componentAuctions}}. @@ -3734,33 +3873,6 @@ An auction config is a [=struct=] with the following items: -A signed additional bid signature is a [=struct=] with the following [=struct/items=]: - -
-: key -:: A [=byte sequence=] of length 32. -: signature -:: A [=byte sequence=] of length 64. - -
- -An additional bid is a [=struct=] with the following [=struct/items=]: - -
-: bid -:: A [=generated bid=]. Fields analogous to those returned by `generateBid()`. -: negative target interest group names -:: A [=list=] of [=strings=]. -: negative target joining origin -:: Null or an [=origin=]. Required if there is more than one entry in - [=additional bid/negative target interest group names=]. - -
- -Each [=traversable navigable=] has a captured additional bids headers, which is a [=map=] -whose [=map/keys=] are [=strings=] for auction nonces, and whose values are [=list=] of [=strings=] -for encoded additional bids. -
To wait until configuration input promises resolve given an [=auction config=] |auctionConfig|: 1. Wait until |auctionConfig|'s [=auction config/pending promise count=] is 0. @@ -3852,7 +3964,7 @@ Numeric value of a bid and the currency it is in. A bid that needs to be scored by the seller. The bid is either the output of running a Protected Audience `generateBid()` script, or the additional bid provided by the "`Ad-Auction-Additional-Bid`" -response headers. +response headers. TODO: define the header and link to it correctly.
: bid @@ -3863,7 +3975,8 @@ response headers. original bid if the currency already matched, or a conversion provided by `scoreAd()`. : ad :: A [=string=]. JSON string to be passed to the scoring function. - TODO: It seems this and the [=generated bid/ad descriptor=] can be moved to + + Issue: TODO: It seems this and the [=generated bid/ad descriptor=] can be moved to [=generated bid/bid ad=] to avoid duplication. : ad descriptor :: An [=ad descriptor=]. Render URL and size of the bid's ad. From e65dff86602e09d82a5e452895a32e36794064a7 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Tue, 17 Oct 2023 00:29:38 -0400 Subject: [PATCH 21/27] Add non-normative, and link to adAuctionHeaders. --- spec.bs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/spec.bs b/spec.bs index e06eb1a52..718622ec5 100644 --- a/spec.bs +++ b/spec.bs @@ -125,7 +125,7 @@ spec: Shared Storage API; urlPrefix: https://wicg.github.io/shared-storage # Introduction # {#intro} -This section is non-normative +*This section is non-normative.* The Protected Audience API facilitates selecting an advertisement to display to a user based on a previous interaction with the advertiser or advertising network. @@ -141,6 +141,8 @@ advertisement.

Joining Interest Groups

+*This first introductory paragraph is non-normative.* + When a user's interactions with a website indicate that the user may have a particular interest, an advertiser or someone working on behalf of the advertiser (e.g. a demand side platform, DSP) can ask the user's browser to record this interest on-device by calling @@ -471,6 +473,8 @@ To build an interest group permissions url given a [=origin=] |ownerO

Leaving Interest Groups

+*This first introductory paragraph is non-normative.* + {{Window/navigator}}.{{Navigator/leaveAdInterestGroup()}} removes a user from a particular interest group. @@ -533,6 +537,8 @@ are:

Running Ad Auctions

+*This first introductory paragraph is non-normative.* + When a website or someone working on behalf of the website (e.g. a supply side platform, SSP) wants to conduct an auction to select an advertisement to display to the user, they can call the {{Window/navigator}}.{{Navigator/runAdAuction()}} function, providing an auction configuration that @@ -566,7 +572,7 @@ dictionary AuctionAdConfig { Promise> perBuyerCurrencies; sequence componentAuctions = []; Promise additionalBids; - USVString auctionNonce; + DOMString auctionNonce; AbortSignal? signal; Promise resolveToConfig; }; @@ -962,10 +968,10 @@ To validate and convert auction ad config given an {{AuctionAdConfig} |config|["{{AuctionAdConfig/additionalBids}}"]: Note: The JavaScript code calling {{Navigator/runAdAuction()}} is responsible for not resolving - |config|["{{AuctionAdConfig/additionalBids}}"] until a {{WindowOrWorkerGlobalScope/fetch()}} - call for fetching additional bids with the `{adAuctionHeaders: true}` option returns - a response, or alternatively, not calling {{Navigator/runAdAuction()}} until the - {{WindowOrWorkerGlobalScope/fetch()}} call returns a response. + |config|["{{AuctionAdConfig/additionalBids}}"] until an associated [=request=] whose + [=request/initiator type=] is `"fetch"` and the {{RequestInit/adAuctionHeaders}} option set to + `true` returns a response, or alternatively, not calling {{Navigator/runAdAuction()}} until the + [=request=] returns a response. * To parse the value |result|: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. @@ -2157,6 +2163,8 @@ a {{ReportingBrowserSignals}} |browserSignals|, and a [=direct from seller signa ## createAuctionNonce() ## {#create-auction-nonce} +*This first introductory paragraph is non-normative.* + {{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a one-time [=version 4 UUID=] uniquely associated with a single call to {{Window/navigator}}.{{Navigator/runAdAuction()}}. For multi-seller auctions, this ID is uniquely @@ -3276,6 +3284,8 @@ Each {{InterestGroupReportingScriptRunnerGlobalScope}} has a # Interest Group Updates # {#interest-group-updates} +*This first introductory paragraph is non-normative.* + [=Interest groups=] have an [=interest group/update url=] field that allows updating the interest group definition stored on disk with information periodically retrieved from the [=interest group/update url=]. The [=interest group update=] steps are triggered during From baa13e3a02cca47986706546aa40aa3932b74bed Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Tue, 17 Oct 2023 00:56:22 -0400 Subject: [PATCH 22/27] Define Ad-Auction-Additional-Bid header. --- spec.bs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/spec.bs b/spec.bs index 718622ec5..7e7875031 100644 --- a/spec.bs +++ b/spec.bs @@ -2202,6 +2202,8 @@ The createAuctionNonce() method steps are: ## Additional Bids ## {#additional-bids} +*This first introductory paragraph is non-normative.* + In addition to [=generate a bid|bids generated by interest groups=], sellers can enable buyers to introduce bids generated outside of the auction, which are called additional bids. Additional bids are commonly triggered using contextual signals. Buyers compute the additional bids, likely as part @@ -3646,6 +3648,14 @@ response header The \`Ad-Auction-Signals\` response header provides value of a JSON array of dictionaries, each with an `adSlot` key. Protected Audience non-component, component, and top-level auctions may specify which signals to load by the `adSlot` key. + +

The \`Ad-Auction-Additional-Bid\` +HTTP response header.

+ +The \`Ad-Auction-Additional-Bid\` response header provides value +of a string in the format of `:`, which +corresponds to a single additional bid. The response may include more than one additional bid by +specifying multiple instances of the [:Ad-Auction-Additional-Bid:] response header.
@@ -4264,8 +4274,8 @@ Numeric value of a bid and the currency it is in.

Generated bid

A bid that needs to be scored by the seller. The bid is either the output of running a Protected -Audience `generateBid()` script, or the additional bid provided by the "`Ad-Auction-Additional-Bid`" -response headers. TODO: define the header and link to it correctly. +Audience `generateBid()` script, or an additional bid provided by the [:Ad-Auction-Additional-Bid:] +response headers.
: bid From b5efcafdec8c25b09ba6cc53b2c4fda816b685a5 Mon Sep 17 00:00:00 2001 From: qingxinwu <6334674+qingxinwu@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:55:13 -0400 Subject: [PATCH 23/27] Apply suggestions from code review Co-authored-by: Dominic Farolino --- spec.bs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec.bs b/spec.bs index c7dbc9b98..e1db4cd0c 100644 --- a/spec.bs +++ b/spec.bs @@ -329,7 +329,8 @@ This is detectable because it can change the set of fields that are read from th 1. [=exception/Throw=] a {{TypeError}} if any of the following conditions hold: * |decodedKey| is a failure; * |decodedKey|'s [=byte sequence/length=] is not 32; - * |group|["{{GenerateBidInterestGroup/ads}}"] [=map/exists=]. + * |group|["{{GenerateBidInterestGroup/ads}}"] [=map/exists=]; + * |group|["{{GenerateBidInterestGroup/updateURL}}"] [=map/exists=]. 1. Set |interestGroup|'s [=interest group/additional bid key=] to |decodedKey|. 1. If |interestGroup|'s [=interest group/estimated size=] is greater than 50 KB, then @@ -955,7 +956,8 @@ To validate and convert auction ad config given an {{AuctionAdConfig} * To parse the value |result|, set |auctionConfig|'s [=auction config/seller signals=] to the result of [=serializing a JavaScript value to a JSON string=], given |result|. * To handle an error, set |auctionConfig|'s [=auction config/seller signals=] to failure. -1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=], then set |auctionConfig|'s +1. If |config|["{{AuctionAdConfig/auctionNonce}}"] [=map/exists=], then [=map/set=] |auctionConfig|'s + [=auction config/auction nonce=] to the result of running [=get uuid from string=] with |config|["{{AuctionAdConfig/auctionNonce}}"]. 1. If |config|["{{AuctionAdConfig/additionalBids}}"] [=map/exists=]: From cec0122c2b9a11a53c6bc3b6fc198cd7d9d671f8 Mon Sep 17 00:00:00 2001 From: qingxinwu <6334674+qingxinwu@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:01:23 -0400 Subject: [PATCH 24/27] Apply suggestions from code review Co-authored-by: Dominic Farolino --- spec.bs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/spec.bs b/spec.bs index e1db4cd0c..98543b531 100644 --- a/spec.bs +++ b/spec.bs @@ -2370,7 +2370,8 @@ To decode an additional bid json given a [=string=] |additionalBidJso 1. If |parsedAdditionalBid|["bid"] does not [=map/exist=], or is not a [=map=], return failure. 1. Let |bidMap| be |parsedAdditionalBid|["bid"]. 1. If |bidMap|["render"] does not [=map/exist=] or is not a [=string=], then return failure. - 1. Let |renderUrl| be the result of running [=url parser=] on |bidMap|["render"]. + 1. Let |renderUrl| be the result of running [=URL parser=] on |bidMap|["render"]. + 1. If |renderUrl| is failure, then return failure. 1. Let |ad| be a new [=interest group ad=] whose [=interest group ad/render url=] is |renderUrl|. 1. Set |ig|'s [=interest group/ads=] to « |ad| ». @@ -2400,7 +2401,8 @@ To decode an additional bid json given a [=string=] |additionalBidJso 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. 1. [=list/For each=] |component| of |bidMap|["adComponents"]: 1. If |component| is not a [=string=], then return failure. - 1. Let |componentUrl| be the result of running [=url parser=] on |component|. + 1. Let |componentUrl| be the result of running [=URL parser=] on |component|. + 1. If |componentUrl| is failure, then return failure. 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is |componentUrl|. @@ -3518,7 +3520,8 @@ The following algorithms are some helper methods used in this document. 1. [=list/For each=] |i| in [=the range=] from 1 to 36, inclusive: 1. Let |unit| be |input|'s |i|th [=code unit=]. 1. If « 8, 13, 18, 23 » [=list/contains=] |i|: - 1. If |unit| is not 0x002d (-): + 1. If |unit| is not 0x002D (-): + 1. Return an empty [=string=]. 1. Otherwise: 1. If |unit| is not an [=ASCII lower hex digit=]: @@ -4315,7 +4318,8 @@ response headers. : bid duration :: A [=duration=] in milliseconds. How long it took to run `generateBid()`. : provided as additional bid -:: A [=boolean=]. Initially false. +:: A [=boolean=], initially false. +
From fc02ac9136e63ff97554a4633689d3aa03e3c3c7 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 19 Oct 2023 15:10:37 -0400 Subject: [PATCH 25/27] Address comments. --- spec.bs | 88 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/spec.bs b/spec.bs index 98543b531..38f833da6 100644 --- a/spec.bs +++ b/spec.bs @@ -977,8 +977,6 @@ To validate and convert auction ad config given an {{AuctionAdConfig} * To parse the value |result|: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to false. - 1. Set |auctionConfig|'s [=auction config/config idl=]["{{AuctionAdConfig/additionalBids}}"] - to {{undefined}}. * To handle an error: 1. Set |auctionConfig|'s [=auction config/expects additional bids=] to failure. 1. If |config|["{{AuctionAdConfig/directFromSellerSignalsHeaderAdSlot}}"] [=map/exists=]: @@ -1337,7 +1335,7 @@ To generate and score bids given an [=auction config=] |auctionConfig |topLevelAuctionConfig| and |auctionConfig|'s [=auction config/seller=]. 1. Let |pendingBuyers| be the [=map/size=] of |bidGenerators|. 1. Let |additionalBids| be the result of running [=validate and convert additional bids=] with - |auctionConfig|, |topLevelAuctionConfig| and |negativeTargetInfo|. + |auctionConfig|, |topLevelAuctionConfig|, |negativeTargetInfo| and |global|. 1. Let |pendingAdditionalBids| be the [=list/size=] of |additionalBids|. 1. [=list/For each=] |additionalBid| of |additionalBids|, run the following steps [=in parallel=]: 1. [=Score and rank a bid=] with |auctionConfig|, |additionalBid|, |leadingBidInfo|, @@ -2199,7 +2197,9 @@ The createAuctionNonce() method steps are: * ...which gives browsers the freedom to generate this UUID in another process, and asynchronously send it back to the main thread at an arbitrary future time.
- 1. [=Queue a task=] to [=resolve=] |p| with |nonce|. + 1. Let |global| be [=this=]'s [=relevant global object=]. + 1. [=Queue a global task=] on [=DOM manipulation task source=], given |global|, to [=resolve=] + |p| with |nonce|. 1. Return |p|. @@ -2249,15 +2249,15 @@ in a [[#running-ad-auctions|Protected Audience auction]].
To validate and convert additional bids given an [=auction config=] |auctionConfig|, an -[=auction config=]-or-null |topLevelAuctionConfig|, and a [=negative target info=] -|negativeTargetInfo|: +[=auction config=]-or-null |topLevelAuctionConfig|, a [=negative target info=] |negativeTargetInfo|, +and a [=global object=] |global|: 1. [=Assert=] that these steps are running [=in parallel=]. + 1. [=Assert=] that |auctionConfig|'s [=auction config/auction nonce=] is not null. 1. Let |auctionNonce| be the [=string representation=] of |auctionConfig|'s [=auction config/auction nonce=]. - 1. Let |capturedAdditionalBidsHeaders| be [=this=]'s [=relevant global object=]'s - [=associated Document's=] [=node navigable's=] [=traversable navigable's=] - [=captured additional bids headers=]. + 1. Let |capturedAdditionalBidsHeaders| be |global|'s [=associated Document's=] + [=node navigable's=] [=traversable navigable's=] [=captured additional bids headers=]. 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. 1. [=list/For each=] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: 1. Let |signedAdditionalBid| be the result of running [=forgiving-base64 decode=] with @@ -2265,8 +2265,7 @@ To validate and convert additional bids given an [=auction config=] | 1. If |signedAdditionalBid| is failure, then [=iteration/continue=]. 1. Let |additionalBid| be the result of running [=parse a signed additional bid=] given |signedAdditionalBid|, |auctionConfig|, |topLevelAuctionConfig|, and |negativeTargetInfo|. - 1. If |additionalBid| is not null: - 1. [=list/Append=] |additionalBid| to |additionalBids|. + 1. If |additionalBid| is not null, then [=list/append=] |additionalBid| to |additionalBids|. 1. Return |additionalBids|.
@@ -2384,8 +2383,7 @@ To decode an additional bid json given a [=string=] |additionalBidJso 1. Let |bidCurrency| be null. 1. If |bidMap|["bidCurrency"] [=map/exists=]: 1. If |bidMap|["bidCurrency"] is not a [=string=], or the result of [=checking whether a string - is a valid currency tag=] is failure: - 1. Return failure. + is a valid currency tag=] is failure, then return failure. 1. Set |bidCurrency| to |bidMap|["bidCurrency"]. 1. Let |adCost| be null. 1. If |bidMap|["adCost"] [=map/exists=]: @@ -2394,15 +2392,14 @@ To decode an additional bid json given a [=string=] |additionalBidJso 1. Let |modelingSignals| be null. 1. If |bidMap|["modelingSignals"] [=map/exists=]: 1. If |bidMap|["modelingSignals"] is not a {{double}}, then return failure. - 1. If |bidMap|["modelingSignals"] is greater than or equal to 0, and less than 4096: - 1. Set |modelingSignals| to |bidMap|["modelingSignals"]. + 1. If |bidMap|["modelingSignals"] ≥ 0, and < 4096, then set |modelingSignals| to + |bidMap|["modelingSignals"]. 1. Let |adComponents| be a new [=list=] of [=ad descriptors=]. 1. If |bidMap|["adComponents"] [=map/exists=]: 1. If |bidMap|["adComponents"] is not a [=list=], then return failure. 1. [=list/For each=] |component| of |bidMap|["adComponents"]: 1. If |component| is not a [=string=], then return failure. 1. Let |componentUrl| be the result of running [=URL parser=] on |component|. - 1. If |componentUrl| is failure, then return failure. 1. Let |componentDescriptor| be a new [=ad descriptor=] whose [=ad descriptor/url=] is |componentUrl|. @@ -2474,15 +2471,17 @@ Each [=traversable navigable=] has a captured additional bids headers whose [=map/keys=] are [=strings=] for auction nonces, and whose values are [=list=] of [=strings=] for encoded additional bids. -## Negative Targeting ## {#negative-targeting} +## Negative Targeting ## {#negative-targeting-section} + +*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 -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 negative interest groups, the additional bid is -dropped; otherwise it participates in the auction, competing alongside bids created by calls to -`generateBid()`. An additional bid that specifies no negative interest groups is always accepted -into the auction. +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 [=negative interest groups=], +the additional bid is dropped; otherwise it participates in the auction, competing alongside bids +created by calls to `generateBid()`. An additional bid that specifies no [=negative interest groups=] +is always accepted into the auction.
To check whether negative targeted given an [=additional bid=] |additionalBid|, a [=set=] @@ -2518,22 +2517,27 @@ and a [=byte sequence=] for [=interest group/additional bid key=]. ### Negative Interest Groups ### {#negative-interest-groups} -Though negative interest groups are joined using the same {{Navigator/joinAdInterestGroup()}} API as -regular interest groups, they remain distinct from one another. Only negative interest groups can -provide an {{AuctionAdInterestGroup/additionalBidKey}}, while only regular interest groups can -provide {{GenerateBidInterestGroup/ads}}. No interest group may provide both. Because the subset of -fields used by a negative interest group cannot be meaningfully updated, a negative interest group -can not provide an {{GenerateBidInterestGroup/updateURL}}. +*This section is non-normative.* + +Though [=negative interest groups=] are joined using the same {{Navigator/joinAdInterestGroup()}} +API as [=regular interest groups=], they remain distinct from one another. Only +[=negative interest groups=] can provide an {{AuctionAdInterestGroup/additionalBidKey}}, while only +[=regular interest groups=] can provide {{GenerateBidInterestGroup/ads}}. No interest group may +provide both. Because the subset of fields used by a [=negative interest group=] cannot be +meaningfully updated, a [=negative interest group=] can not provide an +{{GenerateBidInterestGroup/updateURL}}, otherwise a {{TypeError}} will be [=exception/thrown=] by +{{Navigator/joinAdInterestGroup()}} API. -[[#additional-bids]] specify the negative interest groups they're negatively targeting against +[[#additional-bids]] specify the [=negative interest groups=] they're negatively targeting against using at most one of the following two fields in their JSON data structure: * negativeInterestGroup, for a single negative interest group; - * negativeInterestGroups, for more than one negative interest group. + * negativeInterestGroups, for more than one negative interest groups. -If an additional bid needs to specify more than one negative interest groups, all of those negative -interest groups must be joined from the same origin, and that origin must be identified ahead of -time in the additional bid's `joiningOrigin` field. Any negative interest group that wasn't joined -from that identified origin is ignored for negative targeting. +If an additional bid needs to specify more than one [=negative interest groups=], all of those +[=negative interest groups=] must be joined from the [=same origin=], and that [=origin=] must be +identified ahead of time in the additional bid's `joiningOrigin` field. Any +[=negative interest group=] that wasn't joined from that identified [=origin=] is ignored for +[=negative targeting=].

Use `negativeInterestGroup` in additional bid's JSON:

@@ -3975,10 +3979,10 @@ An interest group is a [=struct=] with the following [=struct/items=]: can be used to construct ads composed of multiple pieces — a top-level ad template "container" which includes some slots that can be filled in with specific "products". : additional bid key -:: Null or a [=byte sequence=] of length 32. Must be null if [=interest group/ads=] is not null. The - Ed25519 public key (a 256-bit EdDSA public key) used to guarantee that this [=interest group=], - if used by an additional bid for a negative targeting, can only be used by its - [=interest group/owner=]. +:: Null or a [=byte sequence=] of length 32. Must be null if [=interest group/ads=] or + [=interest group/update url=] is not null. The Ed25519 public key (a 256-bit EdDSA public key) + used to guarantee that this [=interest group=], if used by an additional bid for a negative + targeting, can only be used by its [=interest group/owner=]. : joining origin :: An [=origin=]. The top level page origin from where the interest group was joined. : join counts @@ -4001,6 +4005,12 @@ An interest group is a [=struct=] with the following [=struct/items=]: +A regular interest group is an [=interest group=] whose +[=interest group/additional bid key=] is null. + +A negative interest group is an [=interest group=] whose +[=interest group/additional bid key=] is not null. +

Interest group ad

An interest group ad is a [=struct=] with the following [=struct/items=]: From 5b689104cd126d56d5884d25714186d081cce6a0 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 19 Oct 2023 17:30:19 -0400 Subject: [PATCH 26/27] Address remaining comments. --- spec.bs | 115 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/spec.bs b/spec.bs index 38f833da6..ba5d0d956 100644 --- a/spec.bs +++ b/spec.bs @@ -134,9 +134,9 @@ When a user's interactions with an advertiser indicate an interest in something, ask the browser to record this interest on-device by calling {{Window/navigator}}.{{Navigator/joinAdInterestGroup()}}. Later, when a website wants to select an advertisement to show to the user, the website can call -{{Window/navigator}}.{{Navigator/runAdAuction()}} to ask the browser to conduct an auction where -each of these on-device recorded interests are given the chance to calculate a bid to display their -advertisement. +{{Window/navigator}}.{{Navigator/runAdAuction()}} to ask the browser to conduct an +auction where each of these on-device recorded interests are given the chance to +calculate a bid to display their advertisement.

Joining Interest Groups

@@ -2166,15 +2166,14 @@ a {{ReportingBrowserSignals}} |browserSignals|, and a [=direct from seller signa *This first introductory paragraph is non-normative.* -{{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a one-time -[=version 4 UUID=] uniquely associated with a single call to +{{Window/navigator}}.{{Navigator/createAuctionNonce()}} creates an auction nonce, a +one-time [=version 4 UUID=] uniquely associated with a single call to {{Window/navigator}}.{{Navigator/runAdAuction()}}. For multi-seller auctions, this ID is uniquely -associated with all {{AuctionAdConfig/componentAuctions}}. -This nonce will need to be passed back in via a subsequent call to -{{Window/navigator}}.{{Navigator/runAdAuction()}} via the {{AuctionAdConfig}}. This is currently -only needed for auctions that use additional bids, for which the auction nonce will be included in -each additional bid as a way of ensuring that those bids are only used in the auctions for which -they were intended. +associated with all {{AuctionAdConfig/componentAuctions}}. This nonce will need to be passed back in +via a subsequent call to {{Window/navigator}}.{{Navigator/runAdAuction()}} via the +{{AuctionAdConfig}}. This is currently only needed for [=auctions=] that use [=additional bids=], +for which the [=auction nonce=] will be included in each [=additional bid=] as a way of ensuring +that those bids are only used in the [=auctions=] for which they were intended. [SecureContext] @@ -2203,16 +2202,16 @@ The <dfn for=Navigator method>createAuctionNonce()</dfn> method steps are: 1. Return |p|. </div> -## Additional Bids ## {#additional-bids} +## Additional Bids ## {#additional-bids-section} *This first introductory paragraph is non-normative.* In addition to [=generate a bid|bids generated by interest groups=], sellers can enable buyers to -introduce bids generated outside of the auction, which are called additional bids. Additional bids -are commonly triggered using contextual signals. Buyers compute the additional bids, likely as part -of a contextual auction. Buyers need to package up each additional bid using a new data structure -that encapsulates all of the information needed for the additional bid to compete against other bids -in a [[#running-ad-auctions|Protected Audience auction]]. +introduce bids generated outside of the auction, which are called <dfn>additional bids</dfn>. +[=Additional bids=] are commonly triggered using contextual signals. Buyers compute the +[=additional bids=], likely as part of a contextual auction. Buyers need to package up each +[=additional bid=] using a new data structure that encapsulates all of the information needed for +the [=additional bid=] to compete against other bids in a Protected Audience [=auction=]. <div id="additional-bid-example" class=example> <p> Each additional bid is expressed using the following JSON data structure:</p> @@ -2258,7 +2257,7 @@ and a [=global object=] |global|: [=auction config/auction nonce=]. 1. Let |capturedAdditionalBidsHeaders| be |global|'s [=associated Document's=] [=node navigable's=] [=traversable navigable's=] [=captured additional bids headers=]. - 1. Let |additionalBids| be a new [=list=] of [=additional bids=]. + 1. Let |additionalBids| be a new [=list=] of [=decoded additional bids=]. 1. [=list/For each=] |encodedSignedAdditionalBid| of |capturedAdditionalBidsHeaders|[|auctionNonce|]: 1. Let |signedAdditionalBid| be the result of running [=forgiving-base64 decode=] with |encodedSignedAdditionalBid|. @@ -2275,7 +2274,7 @@ To <dfn>parse a signed additional bid</dfn> given a [=byte sequence=] |signedAdd [=negative target info=] |negativeTargetInfo|: 1. [=Assert=] that these steps are running [=in parallel=]. - 1. Let |parsedSignedAdditionalBid| be the result of [=parse a JSON string to an infra value=] + 1. Let |parsedSignedAdditionalBid| be the result of running [=parse a JSON string to an infra value=] given |signedAdditionalBid|. 1. Return null if any of the following conditions hold: * |parsedSignedAdditionalBid| is not a [=map=]; @@ -2306,8 +2305,8 @@ To <dfn>parse a signed additional bid</dfn> given a [=byte sequence=] |signedAdd 1. Return null if any of the following conditions hold: * |decodedAdditionalBid| is failure; * The result of [=checking a currency tag=] with |decodedAdditionalBid|'s - [=additional bid/bid=]'s [=generated bid/bid=]'s [=bid with currency/currency=] and the - result of running [=look up per-buyer currency=] with |auctionConfig|. + [=decoded additional bid/bid=]'s [=generated bid/bid=]'s [=bid with currency/currency=] and + the result of running [=look up per-buyer currency=] with |auctionConfig|. 1. Let |verifiedSignatureKeys| be a new [=set=] of [=byte sequences=]. 1. [=list/For each=] |signature| of |signatures|: 1. Let |isSignatureValid| be the result of running [=verify=] |signature|'s @@ -2328,7 +2327,7 @@ To <dfn>decode an additional bid json</dfn> given a [=string=] |additionalBidJso 1. Let |parsedAdditionalBid| be the result of [=parse a JSON string to an infra value=] given |additionalBidJson|. 1. If |parsedAdditionalBid| is not a [=map=], then return failure. - 1. Let |result| be a new [=additional bid=]. + 1. Let |result| be a new [=decoded additional bid=]. 1. Return failure if any of the following conditions hold: * |parsedAdditionalBid|["auctionNonce"] does not [=map/exist=]; * |parsedAdditionalBid|["auctionNonce"] is not the [=string representation=] of @@ -2409,7 +2408,7 @@ To <dfn>decode an additional bid json</dfn> given a [=string=] |additionalBidJso 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=], or |parsedAdditionalBid|["negativeInterestGroup"] is not a [=string=], then return failure. 1. [=list/Append=] |parsedAdditionalBid|["negativeInterestGroup"] to |result|'s - [=additional bid/negative target interest group names=]. + [=decoded additional bid/negative target interest group names=]. 1. If |parsedAdditionalBid|["negativeInterestGroups"] [=map/exists=]: 1. Let |multipleNegativeIg| be |parsedAdditionalBid|["negativeInterestGroups"]. 1. Return failure if any of the following conditions hold: @@ -2419,12 +2418,13 @@ To <dfn>decode an additional bid json</dfn> given a [=string=] |additionalBidJso 1. Let |joiningOrigin| be the result of running [=parse an https origin=] with |multipleNegativeIg|["joiningOrigin"]. 1. If |joiningOrigin| is failure, then return failure. - 1. Set |result|'s [=additional bid/negative target joining origin=] to |joiningOrigin|. + 1. Set |result|'s [=decoded additional bid/negative target joining origin=] to |joiningOrigin|. 1. [=list/For each=] |igName| of |multipleNegativeIg|["interestGroupNames"]: 1. If |igName| is not a [=string=], then return failure. 1. [=list/Append=] |igName| to |result|'s - [=additional bid/negative target interest group names=]. - 1. Set |result|'s [=additional bid/bid=] to a new [=generated bid=] with the following properties: + [=decoded additional bid/negative target interest group names=]. + 1. Set |result|'s [=decoded additional bid/bid=] to a new [=generated bid=] with the following + properties: : [=generated bid/bid=] :: A [=bid with currency=] whose [=bid with currency/value=] is |bidVal|, and [=bid with currency/currency=] is |bidCurrency| @@ -2456,20 +2456,20 @@ A <dfn>signed additional bid signature</dfn> is a [=struct=] with the following :: A [=byte sequence=] of length 64. </dl> -An <dfn>additional bid</dfn> is a [=struct=] with the following [=struct/items=]: -<dl dfn-for="additional bid"> +A <dfn>decoded additional bid</dfn> is a [=struct=] with the following [=struct/items=]: +<dl dfn-for="decoded additional bid"> : <dfn>bid</dfn> :: A [=generated bid=]. Fields analogous to those returned by `generateBid()`. : <dfn>negative target interest group names</dfn> :: A [=list=] of [=strings=]. : <dfn>negative target joining origin</dfn> :: Null or an [=origin=]. Required if there is more than one entry in - [=additional bid/negative target interest group names=]. + [=decoded additional bid/negative target interest group names=]. </dl> Each [=traversable navigable=] has a <dfn>captured additional bids headers</dfn>, which is a [=map=] -whose [=map/keys=] are [=strings=] for auction nonces, and whose values are [=list=] of [=strings=] -for encoded additional bids. +whose [=map/keys=] are [=strings=] for [=auction nonces=], and whose values are [=list=] of +[=strings=] for encoded additional bids. ## Negative Targeting ## {#negative-targeting-section} @@ -2477,36 +2477,38 @@ for encoded additional bids. In online ad auctions for ad space, it’s sometimes useful to prevent showing an ad to certain audiences, a concept known as <dfn>negative targeting</dfn>. 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 [=negative interest groups=], -the additional bid is dropped; otherwise it participates in the auction, competing alongside bids -created by calls to `generateBid()`. An additional bid that specifies no [=negative interest groups=] -is always accepted into the auction. +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 +[=negative interest groups=], the [=additional bid=] is dropped; otherwise it participates in the +[=auction=], competing alongside bids created by calls to `generateBid()`. An [=additional bid=] +that specifies no [=negative interest groups=] is always accepted into the [=auction=]. <div algorithm> -To <dfn>check whether negative targeted</dfn> given an [=additional bid=] |additionalBid|, a [=set=] -of [=byte sequences=] |verifiedSignatureKeys|, and a [=negative target info=] |negativeTargetInfo|: +To <dfn>check whether negative targeted</dfn> given an [=decoded additional bid=] |additionalBid|, a +[=set=] of [=byte sequences=] |verifiedSignatureKeys|, and a [=negative target info=] +|negativeTargetInfo|: 1. [=Assert=] that these steps are running [=in parallel=]. 1. Let |negativeTargeted| be false. - 1. Let |additionalBidBuyer| be |additionalBid|'s [=additional bid/bid=]'s + 1. Let |additionalBidBuyer| be |additionalBid|'s [=decoded additional bid/bid=]'s [=generated bid/interest group=]'s [=interest group/owner=]. 1. [=list/For each=] |igName| of |additionalBid|'s - [=additional bid/negative target interest group names=]: + [=decoded additional bid/negative target interest group names=]: 1. If |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)] [=map/exists=]: 1. Let (|joiningOrigin|, |additionalBidKey|) be |negativeTargetInfo|[(|additionalBidBuyer|, |igName|)]. 1. If |verifiedSignatureKeys| [=list/contains=] |additionalBidKey|: 1. If |joiningOrigin| is not null: 1. If |joiningOrigin| is not [=same origin=] with |additionalBid|'s - [=additional bid/negative target joining origin=], then [=iteration/continue=]. + [=decoded additional bid/negative target joining origin=], then [=iteration/continue=]. 1. Set |negativeTargeted| to true, and [=iteration/break=]. - Note: If the signature doesn't verify successfully, the additional bid proceeds as if the - [[#negative interest group]] is not present. This ensures that only the owner of the negative - interest group, who created the {{AuctionAdInterestGroup/additionalBidKey}}, is allowed to - negatively target the interest group, and that nobody else can learn whether the - [=interest group set=] [=list/contains=] the interest group. + Note: If the signature doesn't verify successfully, the [=additional bid=] proceeds as if the + [=negative interest group=] is not present. This ensures that only the + [=interest group/owner=] of the [=negative interest group=], who created the + {{AuctionAdInterestGroup/additionalBidKey}}, is allowed to + [=negative targeting|negatively target=] the interest group, and that nobody else can learn + whether the [=interest group set=] [=list/contains=] the interest group. 1. Return |negativeTargeted|. </div> @@ -2521,21 +2523,20 @@ and a [=byte sequence=] for [=interest group/additional bid key=]. Though [=negative interest groups=] are joined using the same {{Navigator/joinAdInterestGroup()}} API as [=regular interest groups=], they remain distinct from one another. Only -[=negative interest groups=] can provide an {{AuctionAdInterestGroup/additionalBidKey}}, while only -[=regular interest groups=] can provide {{GenerateBidInterestGroup/ads}}. No interest group may -provide both. Because the subset of fields used by a [=negative interest group=] cannot be -meaningfully updated, a [=negative interest group=] can not provide an -{{GenerateBidInterestGroup/updateURL}}, otherwise a {{TypeError}} will be [=exception/thrown=] by +[=negative interest groups=]'s [=interest group/additional bid key=] can be non-null, while only +[=regular interest groups=]'s [=interest group/ads=] can be non-null. Because the subset of fields +used by a [=negative interest group=] cannot be meaningfully updated, a [=negative interest group=]'s +[=interest group/update url=] must be null, otherwise a {{TypeError}} will be [=exception/thrown=] by {{Navigator/joinAdInterestGroup()}} API. -[[#additional-bids]] specify the [=negative interest groups=] they're negatively targeting against -using at most one of the following two fields in their JSON data structure: +[=Additional bids=] specify the [=negative interest groups=] they're [=negatively targeting=] +against using at most one of the following two fields in their JSON data structure: * negativeInterestGroup, for a single negative interest group; * negativeInterestGroups, for more than one negative interest groups. -If an additional bid needs to specify more than one [=negative interest groups=], all of those +If an [=additional bid=] needs to specify more than one [=negative interest groups=], all of those [=negative interest groups=] must be joined from the [=same origin=], and that [=origin=] must be -identified ahead of time in the additional bid's `joiningOrigin` field. Any +identified ahead of time in the [=additional bid=]'s `joiningOrigin` field. Any [=negative interest group=] that wasn't joined from that identified [=origin=] is ignored for [=negative targeting=]. @@ -3954,8 +3955,8 @@ An interest group is a [=struct=] with the following [=struct/items=]: </p> : <dfn>update url</dfn> :: Null or a [=URL=]. Provides a mechanism for the group's owner to periodically update the - attributes of the interest group. See [interest group updates](#interest-group-updates). Must be - null if [=interest group/additional bid key=] is not null. + attributes of the interest group. See [[#interest-group-updates]]. Must be null if + [=interest group/additional bid key=] is not null. <p class="note"> When non-null, the [=interest group/update url=]'s [=origin=] will always be [=same origin=] with [=interest group/owner=]. From bee4ccc92f0b15a81bb01b444b62ade3afe02d43 Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Thu, 19 Oct 2023 17:49:06 -0400 Subject: [PATCH 27/27] Add link to a github issue for TODO. --- spec.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec.bs b/spec.bs index ba5d0d956..ab6271c2d 100644 --- a/spec.bs +++ b/spec.bs @@ -4304,8 +4304,9 @@ response headers. : <dfn>ad</dfn> :: A [=string=]. JSON string to be passed to the scoring function. - Issue: TODO: It seems this and the [=generated bid/ad descriptor=] can be moved to - [=generated bid/bid ad=] to avoid duplication. + Issue: TODO: Check whether [=generated bid/ad descriptor=] can be moved to + [=generated bid/bid ad=] to avoid duplication + (<a href="https://github.com/WICG/turtledove/issues/667">WICG/turtledove#868</a>). : <dfn>ad descriptor</dfn> :: An [=ad descriptor=]. Render URL and size of the bid's ad. : <dfn>ad component descriptors</dfn>