diff --git a/FLEDGE.md b/FLEDGE.md index e02d1a637..3f084aa27 100644 --- a/FLEDGE.md +++ b/FLEDGE.md @@ -44,7 +44,7 @@ See [the Protected Audience API specification](https://wicg.github.io/turtledove - [5.4 Reporting IDs](#54-reporting-ids) - [5.5 Losing Bidder Reporting](#55-losing-bidder-reporting) - [6. Additional Bids](#6-additional-bids) - - [6.1 Auction Nonce](#61-auction-nonce) + - [6.1 Auction and Bid Nonces](#61-auction-and-bid-nonces) - [6.2 Negative Targeting](#62-negative-targeting) - [6.2.1 Negative Interest Groups](#621-negative-interest-groups) - [6.2.2 How Additional Bids Specify their Negative Interest Groups](#622-how-additional-bids-specify-their-negative-interest-groups) @@ -1439,6 +1439,9 @@ const additionalBid = { "biddingLogicURL": "https://www.example-dsp.com/bid_logic.js" }, + + // Valid additional bids may have at most one of + // negativeInterestGroup or negativeInterestGroups. "negativeInterestGroup": "campaign123_negative_interest_group", "negativeInterestGroups": { joiningOrigin: "https://www.example-advertiser.com", @@ -1448,7 +1451,9 @@ const additionalBid = { ] }, - "auctionNonce": "12345678-90ab-cdef-fedc-ba0987654321", + // Valid additional bids must have exactly one of auctionNonce or bidNonce. + "auctionNonce": "5358e36b-741a-4619-8915-6cd5f6e4755f", + "bidNonce": "NcMr5MCj8gB1oE4iObgFXlNgkzywbiTssxE/KdGA5YQ=", "seller": "https://www.example-ssp.com", "topLevelSeller": "https://www.another-ssp.com" } @@ -1460,7 +1465,7 @@ The fields in `interestGroup` facilitate running `reportAdditionalBidWin()` for Each additional bid may provide a value for **at most** one of the `negativeInterestGroup` and `negativeInterestGroups` fields. These fields are described below in section [6.2.2 How Additional Bids Specify their Negative Interest Groups](#622-how-additional-bids-specify-their-negative-interest-groups). -The `auctionNonce`, `seller`, and `topLevelSeller` fields are used to prevent replay of this additional bid. The `auctionNonce` is described below in section [6.1 Auction Nonce](#61-auction-nonce). The `seller` and `topLevelSeller` fields echo those present in the `browserSignals` argument to `generateBid()` as described in section [3.2 On-Device Bidding](#32-on-device-bidding). In `generateBid()`, these are meant to ensure that the buyer acknowledges and accepts that their bid can participate in an auction with those parties. Additional bids don't have a corresponding call to `generateBid()`, and so the `seller` and `topLevelSeller` fields in an additional bid are intended to allow for the same acknowledgement as those in `browserSignals`. +The `auctionNonce`, `bidNonce`, `seller`, and `topLevelSeller` fields are used to prevent replay of this additional bid. Each additional bid must provide a value for exactly one of the `auctionNonce` or `bidNonce` fields. The `auctionNonce` and `bidNonce` fields are described below in section [6.1 Auction and Bid Nonces](#61-auction-and-bid-nonces). The `seller` and `topLevelSeller` fields echo those present in the `browserSignals` argument to `generateBid()` as described in section [3.2 On-Device Bidding](#32-on-device-bidding). In `generateBid()`, these are meant to ensure that the buyer acknowledges and accepts that their bid can participate in an auction with those parties. Additional bids don't have a corresponding call to `generateBid()`, and so the `seller` and `topLevelSeller` fields in an additional bid are intended to allow for the same acknowledgement as those in `browserSignals`. Additional bids are not provided through the auction config passed to `runAdAuction()`, but rather through the response headers of a Fetch request or `iframe` navigation, as described below in section [6.3 HTTP Response Headers](#63-http-response-headers). However, the auction config still has an `additionalBids` field, which is a Promise with no value, used only to signal to the auction that the additional bids have arrived and are ready to be accepted in the auction. For each additional bid, its owner must be included in interestGroupBuyers for that additional bid to participate in the auction. @@ -1472,7 +1477,7 @@ navigator.runAdAuction({ }); ``` -#### 6.1 Auction Nonce +#### 6.1 Auction and Bid Nonces To prevent unintended replaying of additional bids, any auction config, whether top-level or component auction config, must include an auction nonce value if it includes additional bids. The auction nonce is created and provided like so: @@ -1485,7 +1490,13 @@ navigator.runAdAuction({ }); ``` -The same nonce value will need to appear in the `auctionNonce` field of each [additional bid](#6-additional-bids) associated with that auction config. Auctions that don't use additional bids don't need to create or provide an auction nonce. +The auction nonce returned by the browser will be a string representation of a [UUIDv4](https://datatracker.ietf.org/doc/html/rfc4122). A seller may optionally create a second UUIDv4, referred to as the seller nonce. The seller can then combine the auction and seller nonces to produce a bid nonce, which it will need to send to each buyer that may provide additional bids. That bid nonce would then need to appear in the `bidNonce` field of each [additional bid](#6-additional-bids) returned from the buyer to seller, and then from the seller to the browser's `runAdAuction()` invocation. + +The seller must combine the auction and seller nonces to construct a bid nonce by concatenating the [standard UUIDv4 string representation](https://datatracker.ietf.org/doc/html/rfc4122) - 32 lower-case hexadecimal characters with blocks of 8, 4, 4, 4, and 12 separated by dashes - of the auction and seller nonces, and computing the SHA-256 hash of the resulting string. The bid nonce would be the result of this hash function expressed as a base64 string. + +For backwards compatibility, the seller may instead send the auction nonce as-is to buyers, and buyers may include the auction nonce in their additional bid using the `auctionNonce` field. + +Auctions that don't use additional bids don't need to create or provide auction, seller, or bid nonces. #### 6.2 Negative Targeting @@ -1594,11 +1605,22 @@ If script that invokes `runAdAuction()` is part of the response to that iframe n The browser will make the request for either the Fetch or the `iframe` navigation that it otherwise would, with the exception that the request will also include a request header, `Sec-Ad-Auction-Fetch: ?1`. This header indicates to the server that each `Ad-Auction-Additional-Bid` response header from the server will be decoded as an additional bid and loaded into the auction. Each instance of the `Ad-Auction-Additional-Bid` response header will correspond 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. The structure of each instance of the `Ad-Auction-Additional-Bid` header must be as follows: +``` +Ad-Auction-Additional-Bid: + :: +``` + +The browser uses the auction nonce from each response header to associate each additional bid to its corresponding auction. For single-seller auctions, this maps to a particular call to `runAdAuction()`, whereas for multi-seller auctions, this maps to a particular component auction. The browser uses the seller nonce from each response header to compute the bid nonce used to prevent replay of this additional bid. For information on constructing a bid nonce, see [6.1 Auction and Bid Nonces](#61-auction-and-bid-nonces). + +For backwards compatibility, a seller that is passing auction nonce as-is to buyers may return an `Ad-Auction-Additional-Bid` header that includes only the auction nonce and not a seller nonce: + ``` Ad-Auction-Additional-Bid: : ``` +Additional bids that are returned with a seller nonce in the response header must include the `bidNonce` field but not the `auctionNonce` field. Additional bids that are returned without a seller nonce in the response header must include the `auctionNonce` field but not the `bidNonce` field. + The browser uses the auction nonce prefix from each response header to associate each additional bid to its corresponding auction. For single-seller auctions, this maps to a particular call to `runAdAuction()`, whereas for multi-seller auctions, this maps to a particular component auction. All `Ad-Auction-Additional-Bid` response headers are intercepted by the browser and diverted to participate in the auction without passing through the JavaScript context. When all of the additional bids for an auction have been received this way, the seller should resolve the `additionalBids` Promise passed as described above. The browser will use this as the signal that it has all of the additional bids intended for this auction. diff --git a/PA_Feature_Detecting.md b/PA_Feature_Detecting.md index dfbe5aee0..8e29679ca 100644 --- a/PA_Feature_Detecting.md +++ b/PA_Feature_Detecting.md @@ -147,6 +147,14 @@ navigator.protectedAudience && navigator.protectedAudience.queryFeatureSupport( "selectableReportingIds") ``` +## Seller Nonce + +From context of a web page: +``` +navigator.protectedAudience && navigator.protectedAudience.queryFeatureSupport( + "sellerNonce") +``` + ## Trusted Signals KVv2 Support Intent to Ship(TBD) diff --git a/spec.bs b/spec.bs index c133b130c..34797d43b 100644 --- a/spec.bs +++ b/spec.bs @@ -1752,7 +1752,8 @@ a {{DirectFromSellerSignalsForBuyer}} |directFromSellerSignalsForBuyer|, a [=dur [=moment=] |auctionStartTime|, and an [=environment settings object=] |settings|, perform the following steps. They return a failure if failing to fetch the script or wasm, otherwise a [=tuple=] of ([=list=] of [=generated bids=], [=bid debug reporting info=], -[=list=] of [=real time reporting contributions=]). +[=list=] of [=real time reporting contributions=], [=Private Aggregation contributions=], +[=execution metrics=]). 1. Let |igGenerateBid| be the result of [=building an interest group passed to generateBid=] with |ig|. 1. Set |browserSignals|["{{BiddingBrowserSignals/joinCount}}"] to the sum of |ig|'s @@ -1779,14 +1780,22 @@ following steps. They return a failure if failing to fetch the script or wasm, o 1. Let |prevWinElement| be the [=sequence=]<{{PreviousWinElement}}> «|timeDelta|, |prevWinAdIDL|». 1. [=list/Append=] |prevWinElement| to |prevWins|. 1. [=map/Set=] |browserSignals|["{{BiddingBrowserSignals/prevWinsMs}}"] to |prevWins|. + 1. Let |metrics| be a new [=execution metrics=]. 1. Let |biddingScriptFetcher| be the result of [=creating a new script fetcher=] with |ig|'s [=interest group/bidding url=], and |settings|. 1. Let |biddingScript| be the result of [=waiting for script body from a fetcher=] given |biddingScriptFetcher|. + 1. [=Add a sample to an averager=] given |metrics|'s [=execution metrics/code fetch time + averager=] and |biddingScriptFetcher|'s [=script fetcher/fetch duration=]. 1. If |biddingScript| is failure, return failure. 1. If |ig|'s [=interest group/bidding wasm helper url=] is not null: + 1. Let |wasmFetchStart| be |settings|'s [=environment settings object/current monotonic time=]. 1. Let |wasmModuleObject| be the result of [=fetching WebAssembly=] with |ig|'s [=interest group/bidding wasm helper url=] and |settings|. + 1. Let |wasmFetchDuration| be the [=duration from=] |wasmFetchStart| to + |settings|'s [=environment settings object/current monotonic time=], in milliseconds. + 1. [=Add a sample to an averager=] given |metrics|'s [=execution metrics/code fetch time + averager=] and |wasmFetchDuration|. 1. If |wasmModuleObject| is not failure, then [=map/set=] |browserSignals|["{{BiddingBrowserSignals/wasmHelper}}"] to |wasmModuleObject|. 1. Otherwise, return failure. @@ -1810,7 +1819,7 @@ following steps. They return a failure if failing to fetch the script or wasm, o |crossOriginTrustedBiddingSignalsOrigin|. 1. [=map/Set=] |crossOriginTrustedBiddingSignalsOrigin|[|originKey|] to |trustedBiddingSignals|. 1. Return the result of [=evaluating a bidding script=] with |biddingScript|, |multiBidLimit|, - |ig|, |reportingContext|, |expectedCurrency|, |igGenerateBid|, |auctionSignals|, + |ig|, |reportingContext|, |metrics|, |expectedCurrency|, |igGenerateBid|, |auctionSignals|, |perBuyerSignals|, |sameOriginTrustedBiddingSignals|, |crossOriginTrustedBiddingSignals|, |browserSignals|, |directFromSellerSignalsForBuyer| and |perBuyerTimeout|. @@ -1964,6 +1973,8 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: 1. If |perBuyerExperimentGroupIds| is not null and |perBuyerExperimentGroupIds|[|buyer|] [=map/exists=], then set |buyerExperimentGroupId| to |perBuyerExperimentGroupIds|[|buyer|]. 1. Apply interest groups limits to prioritized list: + 1. Let |metrics| be the result of [=accessing per-participant metrics=] for |reportingContext|, + |buyer|, [=worklet function/generate-bid=]. 1. Let |buyerGroupLimit| be |allBuyersGroupLimit|. 1. Let |perBuyerGroupLimits| be |auctionConfig|'s [=auction config/per buyer group limits=]. @@ -1974,6 +1985,10 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: 1. [=map/For each=] slotSizeQueryParam → |perSignalsUrlGenerator| of |perSlotSizeQueryParam|: 1. [=map/For each=] joiningOrigin → |groups| of |perSignalsUrlGenerator|: 1. [=list/Extend=] |igs| with |groups|. + 1. [=Update storage metrics=] given |metrics| and |igs|. + + Note: This is done before any filtering, since it represents storage resource usage. + 1. [=list/Sort in descending order=] |igs|, with |a| being less than |b| if |a|'s [=interest group/priority=] is less than |b|'s [=interest group/priority=]. 1. [=list/Remove=] the first |buyerGroupLimit| items from |igs|. @@ -1981,6 +1996,8 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: 1. [=map/For each=] slotSizeQueryParam → |perSignalsUrlGenerator| of |perSlotSizeQueryParam|: 1. [=map/For each=] joiningOrigin → |groups| of |perSignalsUrlGenerator|: 1. [=list/Remove=] from |groups| any [=interest group=] [=list/contained=] in |igs|. + 1. Increment |metrics|'s [=per participant metrics/participating interest group count=] + by [=list/size=] of |groups|. 1. Let |perBuyerSignals| be null. 1. If |auctionConfig|'s [=auction config/per buyer signals=] is not null and [=auction config/per buyer signals=][|buyer|] [=map/exists=], then set |perBuyerSignals| to @@ -2057,8 +2074,8 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: 1. If |optedInForRealTimeReporting| is true, then [=add a platform contribution=] with [=bidding script failure bucket=], |realTimeContributionsMap| and |buyer|. 1. [=iteration/Continue=]. - 1. Let (|bidsBatch|, |bidDebugReportInfo|, |realTimeContributions|, |paContributions|) be - |generateBidResult|. + 1. Let (|bidsBatch|, |bidDebugReportInfo|, |realTimeContributions|, |paContributions|, + |executionMetrics|) be |generateBidResult|. 1. Let |generateBidDuration| be the [=duration from=] |generateBidStartTime| to |settings|'s [=environment settings object/current monotonic time=], in milliseconds. 1. If |perBuyerCumulativeTimeout| is not null, decrement |perBuyerCumulativeTimeout| by @@ -2083,8 +2100,8 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: 1. Let |generateBidStartTime| be |settings|'s [=environment settings object/current monotonic time=]. 1. Set (|generatedBids|, |bidDebugReportInfo|, |realTimeContributions|, - |paContributions|) to the result of running [=generate potentially multiple bids=] - with |allTrustedBiddingSignals|, + |paContributions|, |executionMetrics|) to the result of running [=generate potentially + multiple bids=] with |allTrustedBiddingSignals|, |crossOriginTrustedBiddingSignalsOrigin|, |auctionSignals|, a [=map/clone=] of |browserSignals|, |perBuyerSignals|, |directFromSellerSignalsForBuyer|, |perBuyerTimeout|, |expectedCurrency|, 1 (for multiBidLimit), |kAnonRestrictedIG|, |reportingContext|, |auctionStartTime|, @@ -2102,7 +2119,7 @@ and a [=real time reporting contributions map=] |realTimeContributionsMap|: 1. [=Apply any component ads target to a bid=] given |generatedBid|. 1. [=list/Append=] |generatedBid| to |bidsToScore|. 1. [=Register bids for reporting=] given |bidsToScore|, |ig|, |bidDebugReportInfo|, - |paContributions|, and |reportingContext|. + |paContributions|, |executionMetrics|, and |reportingContext|. 1. If |auctionConfig|'s [=auction config/per buyer real time reporting config=][|buyer|] is "`default-local-reporting`", then [=insert entries to map=] given |realTimeContributionsMap|, |buyer|, and |realTimeContributions|. @@ -2354,11 +2371,19 @@ or "component-auction", a [=currency tag=] |componentAuctionExpectedCurrency|, a "`default-local-reporting`", then [=add a platform contribution=] with [=scoring script failure bucket=], |realTimeContributionsMap| and |seller|. 1. Return. +1. Let |metrics| be the result of [=access per-participant metrics=] given |reportingContext|, + |auctionConfig|'s [=auction config/seller=], [=worklet function/score-ad=]. +1. [=Add a sample to an averager=] given |metrics|'s [=per participant metrics/code fetch time + averager=] and |decisionLogicFetcher|'s [=script fetcher/fetch duration=]. 1. Let « |scoreAdResult|, |debugWinReportUrl|, |debugLossReportUrl|, |realTimeContributions|, - |paContributions| » be the result of [=evaluating a scoring script=] with |decisionLogicScript|, - |adMetadata|, |bidValue|'s [=bid with currency/value=], |auctionConfig|, |reportingContext|, - |sameOriginTrustedScoringSignals|, |crossOriginTrustedScoringSignals|, |browserSignals|, - |directFromSellerSignalsForSeller|, and |auctionConfig|'s [=auction config/seller timeout=]. + |paContributions|, |executionMetrics| » be the result of [=evaluating a scoring script=] with + |decisionLogicScript|, |adMetadata|, |bidValue|'s [=bid with currency/value=], |auctionConfig|, + |reportingContext|, |sameOriginTrustedScoringSignals|, |crossOriginTrustedScoringSignals|, + |browserSignals|, |directFromSellerSignalsForSeller|, and |auctionConfig|'s [=auction config/ + seller timeout=]. +1. Increment |metrics|'s [=per participant metrics/script executions attempted=] by 1. +1. If |executionMetrics|'s [=execution metrics/script timed out=] is true, increment |metrics|'s + [=per participant metrics/script timeouts occurred=] by 1. 1. If |generatedBid|'s [=generated bid/for k-anon auction=] is true: Note: Non-k-anonymous bids do not participate in reporting (except for platform real-time @@ -2570,18 +2595,17 @@ To fetch WebAssembly given a [=URL=] |url| and an [=environment setti : [=request/mode=] :: "`no-cors`" : [=request/referrer=] - :: "`no-referrer`" + :: "`no-referrer`" : [=request/credentials mode=] :: "`omit`" : [=request/redirect mode=] - :: "`error`" + :: "`error`" + : [=request/service-workers mode=] + :: "`none`" : [=request/policy container=] :: A new [=policy container=] whose [=policy container/IP address space=] is |settings|'s [=environment settings object/policy container=]'s [=policy container/IP address space=] - Issue: One of the side-effects of a `null` client for this subresource request is it neuters all - service worker interceptions, despite not having to set the service workers mode. - Issue: Stop using "`no-cors`" mode where possible (WICG/turtledove#667). 1. Let |moduleObject| be null. @@ -2617,18 +2641,17 @@ To fetch trusted signals given a [=URL=] |url|, an [=origin=] |script : [=request/mode=] :: "`cors`" : [=request/referrer=] - :: "`no-referrer`" + :: "`no-referrer`" : [=request/credentials mode=] :: "`omit`" : [=request/redirect mode=] - :: "`error`" + :: "`error`" + : [=request/service-workers mode=] + :: "`none`" : [=request/policy container=] :: A new [=policy container=] whose [=policy container/IP address space=] is |policyContainer|'s [=policy container/IP address space=] - Issue: One of the side-effects of a `null` client for this subresource request is it neuters all - service worker interceptions, despite not having to set the service workers mode. - 1. Let |signals| be null. 1. Let |dataVersion| be null. 1. Let |formatVersion| be null. @@ -2742,18 +2765,17 @@ To send report given a [=URL=] |url|, and an [=environment settings o : [=request/mode=] :: "`no-cors`" : [=request/referrer=] - :: "`no-referrer`" + :: "`no-referrer`" : [=request/credentials mode=] :: "`omit`" : [=request/redirect mode=] - :: "`error`" + :: "`error`" + : [=request/service-workers mode=] + :: "`none`" : [=request/policy container=] :: A new [=policy container=] whose [=policy container/IP address space=] is |settings|'s [=environment settings object/policy container=]'s [=policy container/IP address space=] - Issue: One of the side-effects of a `null` client for this subresource request is it neuters - all service worker interceptions, despite not having to set the service workers mode. - Issue: Stop using "`no-cors`" mode where possible (WICG/turtledove#667). 1. [=Fetch=] |request| with [=fetch/useParallelQueue=] set to true. @@ -2893,17 +2915,25 @@ and a [=global object=] |global|: [=generated bid/interest group=], |igAd|, and null is true: 1. If |igAd|'s [=interest group ad/buyer and seller reporting ID=] is not null, [=map/set=] |browserSignals|["{{ReportingBrowserSignals/buyerAndSellerReportingId}}"] to it. + 1. Let |metrics| be the result of [=access per-participant metrics=] given |reportingContext|, + |config|'s [=auction config/seller=], [=worklet function/report-result=]. 1. Let |sellerReportingScriptFetcher| be the result of [=creating a new script fetcher=] with |config|'s [=auction config/decision logic url=] and |global|'s [=relevant settings object=]. 1. Let |sellerReportingScript| be the result of [=waiting for script body from a fetcher=] given |sellerReportingScriptFetcher|. - 1. Let « |sellerSignals|, |reportUrl|, |reportingBeaconMap|, ignored, |paContributions| » be the - result of [=evaluating a reporting script=] with |sellerReportingScript|, "`reportResult`", - |reportingContext|, |config|'s [=auction config/seller=], |config|'s + 1. [=Add a sample to an averager=] given |metrics|'s [=per participant metrics/code fetch time + averager=] and |sellerReportingScriptFetcher|'s [=script fetcher/fetch duration=]. + 1. Let « |sellerSignals|, |reportUrl|, |reportingBeaconMap|, ignored, |paContributions|, + |executionMetrics| » be the result of [=evaluating a reporting script=] with + |sellerReportingScript|, "`reportResult`", |reportingContext|, + |config|'s [=auction config/seller=], |config|'s [=auction config/seller Private Aggregation coordinator=], |config|'s [=auction config/ config idl=]'s {{AuctionAdConfig/reportingTimeout}}, and « |config|'s [=auction config/config idl=], |browserSignals|, |directFromSellerSignals| ». - 1. Let |reportingResult| be a [=reporting result=] with the following [=struct/items=]: + 1. Set |metrics|'s [=per participant metrics/script executions attempted=] to 1. + 1. If |executionMetrics|'s [=execution metrics/script timed out=] is true, set |metrics|'s + [=per participant metrics/script timeouts occurred=] to 1. + 1. Let |reportingResult| be a [=reporting result=] with the following [=struct/items=]: : [=reporting result/report url=] :: |reportUrl| : [=reporting result/reporting beacon map=] @@ -2958,6 +2988,8 @@ a {{ReportingBrowserSignals}} |browserSignals|, a [=direct from seller signals=] (TODO: noise and bucket this signal) 1. Let |ig| be |winner|'s [=generated bid/interest group=]. + 1. Let |metrics| be the result of [=access per-participant metrics=] given |reportingContext|, + |ig|'s [=interest group/owner=], [=worklet function/report-win=]. 1. Let |igAd| be the [=interest group ad=] from ig's [=interest group/ads=] whose [=interest group ad/render url=] is |winner|'s [=generated bid/ad descriptor=]'s [=ad descriptor/url=]. @@ -2993,17 +3025,23 @@ a {{ReportingBrowserSignals}} |browserSignals|, a [=direct from seller signals=] |winner|'s [=generated bid/interest group=]'s [=interest group/bidding url=] and |settings|. 1. Let |buyerReportingScript| be the result of [=waiting for script body from a fetcher=] given |buyerReportingScriptFetcher|. + 1. [=Add a sample to an averager=] given |metrics|'s [=per participant metrics/code fetch time + averager=] and |buyerReportingScriptFetcher|'s [=script fetcher/fetch duration=]. 1. Let |reportFunctionName| be "`reportWin`". 1. If |winner|'s [=generated bid/provided as additional bid=] is true: 1. Set |reportFunctionName| be "`reportAdditionalBidWin`". - 1. Let « ignored, |resultUrl|, |reportingBeaconMap|, |reportingMacroMap|, |paContributions| » be - the result of [=evaluating a reporting script=] with |buyerReportingScript|, - |reportFunctionName|, |reportingContext|, |ig|'s [=interest group/owner=], |ig|'s [=interest - group/Private Aggregation coordinator=], |leadingBidInfo|'s [=leading bid info/auction config=]'s + 1. Let « ignored, |resultUrl|, |reportingBeaconMap|, |reportingMacroMap|, |paContributions|, + |executionMetrics| » be the result of [=evaluating a reporting script=] with + |buyerReportingScript|, |reportFunctionName|, |reportingContext|, |ig|'s + [=interest group/owner=], |ig|'s [=interest group/ + Private Aggregation coordinator=], |leadingBidInfo|'s [=leading bid info/auction config=]'s [=auction config/config idl=]'s {{AuctionAdConfig/reportingTimeout}}, and « |leadingBidInfo|'s [=leading bid info/auction config=]'s [=auction config/config idl=]'s {{AuctionAdConfig/auctionSignals}}, |perBuyerSignalsForBuyer|, |sellerSignals|, |reportWinBrowserSignals|, |directFromSellerSignals| ». + 1. Set |metrics|'s [=per participant metrics/script executions attempted=] to 1. + 1. If |executionMetrics|'s [=execution metrics/script timed out=] is true, set |metrics|'s + [=per participant metrics/script timeouts occurred=] to 1. 1. [=Commit private aggregation contributions=] given |paContributions|, |winner|'s [=generated bid/reporting id=] and |reportingContext|. 1. Set |leadingBidInfo|'s [=leading bid info/buyer reporting result=] to a [=reporting result=] @@ -3070,17 +3108,40 @@ a [=list=] of [=interest groups=] |bidIgs|, and a [=reporting context map=] equals |componentAd|. If there is no matching element, return failure. 1. [=list/Append=] a new [=ad descriptor=] whose [=ad descriptor/url=] is |componentAd| to |winningAdComponents|. +1. Let |bidDebugReportingInfo| be a new [=bid debug reporting info=]. +1. [=list/For each=] |key| → |maybeDebugReportUrl| in |response|'s + [=server auction response/component win debugging only reports=]: + 1. If |key|'s [=server auction debug report key/from seller=] is true: + 1. Set |bidDebugReportingInfo|'s [=bid debug reporting info/component seller=] to |seller|. + 1. If |key|'s [=server auction debug report key/is debug win=] is true, then set + |bidDebugReportingInfo|'s [=bid debug reporting info/seller debug win report url=] to + |maybeDebugReportUrl|. + 1. Otherwise, set |bidDebugReportingInfo|'s + [=bid debug reporting info/seller debug loss report url=] to |maybeDebugReportUrl|. + 1. Otherwise, + 1. Set |bidDebugReportingInfo|'s [=bid debug reporting info/interest group owner=] to + |response|'s [=server auction response/interest group owner=]. + 1. If |key|'s [=server auction debug report key/is debug win=] is true, then set + |bidDebugReportingInfo|'s [=bid debug reporting info/bidder debug win report url=] to + |maybeDebugReportUrl|. + 1. Otherwise, set |bidDebugReportingInfo|'s + [=bid debug reporting info/bidder debug loss report url=] to |maybeDebugReportUrl|. +1. Set |bidDebugReportingInfo|'s [=bid debug reporting info/server filtered debugging only reports=] + to [=server auction response/server filtered debugging only reports=]. +1. Set |reportingContextMap|[|auctionConfig|]'s [=reporting context/debug reporting info=] to + |bidDebugReportingInfo|. +1. Let |reportingId| be a [=reporting bid key=] with the following [=struct/items=]: + : [=reporting bid key/context=] + :: |reportingContextMap|[|auctionConfig|] + : [=reporting bid key/source=] + :: [=reporting bid source/bidding-and-auction-services=] + : [=reporting bid key/bidder origin=] + :: |response|'s [=server auction response/interest group owner=] + : [=reporting bid key/bid identifier=] + :: |response|'s [=server auction response/interest group name=] 1. Let |winningBid| be a new [=generated bid=] with the following [=struct/items=]: : [=generated bid/reporting id=] - :: A [=reporting bid key=] with the following [=struct/items=]: - : [=reporting bid key/context=] - :: |reportingContextMap|[|auctionConfig|] - : [=reporting bid key/source=] - :: [=reporting bid source/bidding-and-auction-services=] - : [=reporting bid key/bidder origin=] - :: |response|'s [=server auction response/interest group owner=] - : [=reporting bid key/bid identifier=] - :: |response|'s [=server auction response/interest group name=] + :: |reportingId| : [=generated bid/bid=] :: |response|'s [=server auction response/bid=] : [=generated bid/bid in seller currency=] @@ -3172,7 +3233,7 @@ a [=list=] of [=interest groups=] |bidIgs|, and a [=reporting context map=] 1. If |updateIfOlderThan| is less than 10 mintues, set it to 10 minutes. 1. If [=current coarsened wall time=] − |ig|'s [=interest group/last updated=] ≥ |updateIfOlderThan|, set |ig|'s [=interest group/next update after=] to the - [=current coarsened wall time=] + |updateIfOlderThan|. + [=current coarsened wall time=] + |updateIfOlderThan|. 1. Return |winningBidInfo|. @@ -3292,6 +3353,8 @@ dictionary AdAuctionDataBuyerConfig { specified. +A server auction is an auction executed on a trusted auction server. + A server auction interest group is a [=struct=] with the following [=struct/items=]:
: name @@ -3349,8 +3412,8 @@ A server auction request context is a [=struct=] with the following [ [Section 2.2.4 of Bidding and Auction Services](https://privacysandbox.github.io/draft-ietf-bidding-and-auction-services/draft-ietf-bidding-and-auction-services.html#name-generating-a-request)
-A server auction response is a [=struct=] that contains auction result -from an auction executed on the trusted auction server. It has the following [=struct/items=]: +A server auction response is a [=struct=] that contains auction result from a +[=server auction=]. It has the following [=struct/items=]:
: ad render url @@ -3403,6 +3466,11 @@ from an auction executed on the trusted auction server. It has the following [=s :: Null or [=server auction reporting info=]. : component seller reporting :: Null or [=server auction reporting info=]. + : component win debugging only reports + :: A [=map=] whose [=map/keys=] are [=server auction debug report keys=], and whose [=map/values=] + are [=lists=] of [=urls=]. + : server filtered debugging only reports + :: A [=map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=lists=] of [=urls=].
A server auction reporting info is a [=struct=] with the following [=struct/items=]: @@ -3414,6 +3482,14 @@ A server auction reporting info is a [=struct=] with the following [= are [=URLs=] whose [=url/schemes=] are "`https`". +a server auction debug report key is a [=struct=] with the following [=struct/items=]: +
+ : from seller + :: A [=boolean=]. + : is debug win + :: A [=boolean=]. +
+
The getInterestGroupAdAuctionData(|configIDL|) method steps are: @@ -3639,11 +3715,21 @@ A reporting context is a [=struct=] with the following [=struct/items some of these may be based on bids that started as additional bids or from component auctions run by Bidding and Auction services, and not interest groups, so this list may be quite different from [=reporting context/bidder participants=]. + : participant metrics + :: A [=map=] from a pair ([=origin=], [=worklet function=]) to [=per participant metrics=]. +
+To access per-participant metrics given a [=reporting context=] |reportingContext|, +an [=origin=] |origin| and [=worklet function=] |workletFunction|: +1. Let |key| be (|origin|, |workletFunction|). +1. If |reportingContext|'s [=reporting context/participant metrics=][|key|] does not [=map/exist=], + set it to a new [=per participant metrics=]. +1. Return |reportingContext|'s [=reporting context/participant metrics=][|key|]. + +
+ A reporting context map is a [=map=] from [=auction config=] to [=reporting context=]. -Here the keys are configurations for auctions that actually produce bids (e.g. not the top-level -auction in a multi-party auction).
To create a reporting context map given the [=auction config=] |auctionConfig| and @@ -3662,8 +3748,8 @@ auction in a multi-party auction).
To register bids for reporting given a [=list=] of [=generated bids=] |generatedBids|, [=interest group=] |ig|, [=bid debug reporting info=] - |bidDebugReportInfo|, a [=Private Aggregation contributions=] |paContributions| and a [=reporting - context=] |reportingContext|: + |bidDebugReportInfo|, a [=Private Aggregation contributions=] |paContributions|, + [=execution metrics=] |executionMetrics| and a [=reporting context=] |reportingContext|: 1. Let |id| be a new [=reporting bid key=] with the following [=struct/items=]: : [=reporting bid key/context=] :: |reportingContext| @@ -3678,6 +3764,14 @@ auction in a multi-party auction). :: |ig|'s [=interest group/name=] 1. [=map/Set=] |reportingContext|'s [=reporting context/debug reporting info=][|id|] to |bidDebugReportInfo|. + 1. Let |metrics| be the result of [=access per-participant metrics=] given |reportingContext|, + |ig|'s [=interest group/owner=], [=worklet function/generate-bid=]. + 1. [=Merge samples to an averager=] given |metrics|'s [=per participant metrics/code fetch time + averager=] and |executionMetrics|'s [=execution metrics/code fetch time averager=]. + 1. Set |metrics|'s [=per participant metrics/script executions attempted=] to + |metrics|'s [=per participant metrics/participating interest group count=]. + 1. If |executionMetrics|'s [=execution metrics/script timed out=] is true, increment |metrics|'s + [=per participant metrics/script timeouts occurred=] by 1. 1. [=set/Insert=] |id| into |reportingContext|'s [=reporting context/bidder participants=]. 1. [=Commit private aggregation contributions=] given |paContributions|, |id| and |reportingContext|. @@ -3697,7 +3791,7 @@ methods for event-level forDebuggingOnly reports for winning and losi
To collect a single forDebuggingOnly report given a [=URL=] |reportUrl|, an [=origin=] - |invokingOrigin|, and a [=list=] |debugReportUrls|: + |invokingOrigin|, a [=boolean=] |fromServer|, and a [=list=] |debugReportUrls|: Note: While the browser is experimenting with third party cookie deprecation (before they have been fully removed), the {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope/forDebuggingOnly}} @@ -3706,8 +3800,8 @@ methods for event-level forDebuggingOnly reports for winning and losi 1. If |reportUrl| is null, or the result of running [=is debugging only in cooldown or lockout=] with |invokingOrigin| is true, then return. - 1. If the result of running [=sample a debug report=] with |invokingOrigin| is true, then - [=list/append=] |reportUrl| to |debugReportUrls|. + 1. If the result of running [=sample a debug report=] with |invokingOrigin| and |fromServer| is + true, then [=list/append=] |reportUrl| to |debugReportUrls|.
@@ -3720,42 +3814,64 @@ methods for event-level forDebuggingOnly reports for winning and losi 1. [=map/For each=] _ → |reportingContext| of |reportingContextMap|: 1. [=map/For each=] |reportingId| -> |bidDebugReportInfo| of |reportingContext|'s [=reporting context/debug reporting info=]: + 1. Let |fromServer| be true if |reportingId|'s [=reporting bid key/source=] is + [=reporting bid source/bidding-and-auction-services=], false otherwise. 1. If |winningBid| is not null and |reportingId| is equal to |winningBid|'s [=generated bid/reporting id=]: 1. [=Assert=] that |winningBid|'s [=generated bid/reporting id=] is not null. 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s [=bid debug reporting info/bidder debug win report url=], |bidDebugReportInfo|'s - [=bid debug reporting info/interest group owner=], and |auctionReportInfo|'s + [=bid debug reporting info/interest group owner=], |fromServer|, and |auctionReportInfo|'s [=auction report info/debug win report urls=]. 1. If |bidDebugReportInfo|'s [=bid debug reporting info/component seller=] is null: 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s - [=bid debug reporting info/seller debug win report url=], |seller|, and + [=bid debug reporting info/seller debug win report url=], |seller|, |fromServer|, and |auctionReportInfo|'s [=auction report info/debug win report urls=]. 1. Otherwise: 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s [=bid debug reporting info/seller debug win report url=], |bidDebugReportInfo|'s - [=bid debug reporting info/component seller=], |auctionReportInfo|'s + [=bid debug reporting info/component seller=], |fromServer|, and |auctionReportInfo|'s [=auction report info/debug win report urls=]. 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s - [=bid debug reporting info/top level seller debug win report url=], |seller|, and - |auctionReportInfo|'s [=auction report info/debug win report urls=]. + [=bid debug reporting info/top level seller debug win report url=], |seller|, |fromServer|, + and |auctionReportInfo|'s [=auction report info/debug win report urls=]. 1. Otherwise: 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s [=bid debug reporting info/bidder debug loss report url=], |bidDebugReportInfo|'s - [=bid debug reporting info/interest group owner=], and |auctionReportInfo|'s + [=bid debug reporting info/interest group owner=], |fromServer|, and |auctionReportInfo|'s [=auction report info/debug loss report urls=]. 1. If |bidDebugReportInfo|'s [=bid debug reporting info/component seller=] is null: 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s - [=bid debug reporting info/seller debug loss report url=], |seller|, and + [=bid debug reporting info/seller debug loss report url=], |seller|, |fromServer|, and |auctionReportInfo|'s [=auction report info/debug loss report urls=]. 1. Otherwise: 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s [=bid debug reporting info/seller debug loss report url=], |bidDebugReportInfo|'s - [=bid debug reporting info/component seller=], |auctionReportInfo|'s + [=bid debug reporting info/component seller=], |fromServer|, |auctionReportInfo|'s [=auction report info/debug loss report urls=]. 1. [=Collect a single forDebuggingOnly report=] with |bidDebugReportInfo|'s - [=bid debug reporting info/top level seller debug loss report url=], |seller|, and - |auctionReportInfo|'s [=auction report info/debug loss report urls=]. + [=bid debug reporting info/top level seller debug loss report url=], |seller|, + |fromServer|, and |auctionReportInfo|'s [=auction report info/debug loss report urls=]. + 1. [=map/For each=] |invokingOrigin| → |debugURLs| of |bidDebugReportInfo|'s + [=bid debug reporting info/server filtered debugging only reports=]: + 1. If |debugURLs| [=list/is empty=]: + 1. If the result of running [=is debugging only in cooldown or lockout=] with + |invokingOrigin| is false, then [=update debug report cooldown=] with |invokingOrigin|. + 1. [=iteration/Continue=]. + + Note: An entry for |invokingOrigin| is still needed when |debugURLs| is empty. It could + happen when forDebuggingOnly API is called but the bidding and auction server + filtered the debug report URL out with the downsampling algorithm. In this case the + client still needs to know that forDebuggingOnly API was called and update its cooldown + information. This is different from forDebuggingOnly API not being called by one origin, + in which case the list won't have an entry for that origin. + 1. [=list/For each=] |url| of |debugURLs|: + 1. [=Collect a single forDebuggingOnly report=] with |url|, |invokingOrigin|, + |fromServer| (true), and |auctionReportInfo|'s [=auction report info/debug loss report urls=]. + + Note: For server filtered ones, post auction signals were filled on the server side, so + it's fine to collect them all to [=auction report info/debug loss report urls=] without + the need of getting post auction signals for debug win urls. 1. Return |auctionReportInfo|.
@@ -3807,23 +3923,30 @@ and [=map/values=] are [=moments=] at which the cool down for the origin key exp
- To sample a debug report given an [=origin=] |origin|: + To sample a debug report given an [=origin=] |origin| and a [=boolean=] |fromServer|: + Note: forDebuggingOnly reports from [=server auction response=] were downsampled on trusted + auction servers, so do not downsample them again on client. 1. Let |canSendAfterSampled| be false. 1. Let |sampleRand| be a random {{long}}, 0 ≤ |sampleRand| < 1000, so each possible long would be chosen with a probability equal to [=sampling rate=]. - 1. If |sampleRand| is 0: + 1. If |fromServer| is true or |sampleRand| is 0: 1. Set |canSendAfterSampled| to true. 1. Set [=user agent=]'s [=debug report lockout until=] to [=current coarsened wall time=] plus [=lockout period=]. - 1. Let |cooldownRand| be a random {{long}} ≥ 0 and < 10, which corresponds to + 1. [=Update debug report cooldown=] with |origin|. + 1. Return |canSendAfterSampled|. +
+
+ To update debug report cooldown given an [=origin=] |origin|: + + 1. Let |cooldownRand| be a random {{long}} ≥ 0 and < 10, which corresponds to [=long cooldown rate=]. 1. Let |cooldownPeriod| be [=long cooldown period=] if |cooldownRand| is 0, [=short cooldown period=] otherwise. 1. Set [=user agent=]'s [=debug report cooldown=][|origin|] to [=current coarsened wall time=] plus |cooldownPeriod|. - 1. Return |canSendAfterSampled|.
## {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope/realTimeReporting}} ## {#real-time-reporting-header} @@ -3946,18 +4069,17 @@ Initial implementation of this specification defines : [=request/mode=] :: "`no-cors`" : [=request/referrer=] - :: "`no-referrer`" + :: "`no-referrer`" : [=request/credentials mode=] :: "`omit`" : [=request/redirect mode=] - :: "`error`" + :: "`error`" + : [=request/service-workers mode=] + :: "`none`" : [=request/policy container=] :: A new [=policy container=] whose [=policy container/IP address space=] is |settings|'s [=environment settings object/policy container=]'s [=policy container/IP address space=] - Issue: One of the side-effects of a `null` client for this subresource request is it neuters - all service worker interceptions, despite not having to set the service workers mode. - Issue: Stop using "`no-cors`" mode where possible (WICG/turtledove#667). 1. [=Fetch=] |request| with [=fetch/useParallelQueue=] set to true. @@ -4075,6 +4197,33 @@ A signal base value is one of the following: Note: this mapping to an integer is defined in [=determine a signal's numeric value=]. +: "average-code-fetch-time" +:: The numeric value is the average time it took to fetch code resources (JavaScript or WebAssembly) + for this particular worklet function, for this participant. +: "participating-ig-count" +:: The numeric value is the number of interest groups for the buyer actually participating in the + auction, after considering prioritization and capabilities. Interest groups included in this might + not actually get to bid if the cumulative timeout expires, or the script fails to load, etc; or + might decide not to bid, but they would have gotten a chance if nothing went wrong. +: "percent-scripts-timeout" +:: The numeric value is percentage of executions of this script that hit their individual timeout, + out of all executions that were expected to happen. +: "regular-igs-count" +:: Number of [=regular interest groups=] for the given buyer. (0 if this is used by a seller). +: "percent-regular-ig-count-quota-used" +:: Ratio of number of [=regular interest groups=] for the given buyer and [=Max regular interest + groups per owner=] expressed as a percentage, capped to 110. +: "negative-igs-count" +:: Number of [=negative interest groups=] for the given buyer. (0 if this is used by a seller). +: "percent-negative-ig-count-quota-used" +:: Ratio of number of [=negative interest groups=] for the given buyer and [=Max negative interest + groups per owner=] expressed as a percentage, capped to 110. +: "ig-storage-used" +:: Bytes of storage used by the given buyer. (0 if this is used by a seller). +: "percent-ig-storage-quota-used" +:: Percentage of [=max interest groups total size per owner=] used by the given buyer, + capped to 110. +

On event contribution entry

@@ -4090,6 +4239,8 @@ An on event contribution entry is a [=struct=] with the following items: :: A [=debug details=] or null (default null) : worklet function :: A [=worklet function=]. +: origin +:: The [=origin=] of the script that contributed the entry. @@ -4107,6 +4258,83 @@ A worklet function is one of the following: +
+To find corresponding bid and score phase function given a [=worklet function=] |fn|: + + 1.Switch on |fn|: +
+ : [=worklet function/generate-bid=] + :: Return [=worklet function/generate-bid=]. + : [=worklet function/score-ad=] + :: Return [=worklet function/score-ad=]. + : [=worklet function/report-result=] + :: Return [=worklet function/score-ad=]. + : [=worklet function/report-win=] + :: Return [=worklet function/generate-bid=]. + +
+
+ +### Averager ### {#private-aggregation-averager} +An averager is a a [=struct=] with the following [=struct/items=]: +
+ : count + :: A {{long}}, initially 0. + : sum + :: A {{double}}, initially 0. +
+ +
+To add a sample to an averager given an [=averager=] |averager|, a {{double}} |sample|: +1. Increment |averager|'s [=averager/count=] by 1. +1. Increment |averager|'s [=averager/sum=] by |sample|. + +
+ +
+To merge samples to an averager given [=averagers=] |dest| and |source|: +1. Set |dest|'s [=averager/count=] to |dest|'s [=averager/count=] + |source|'s [=averager/count=]. +1. Set |dest|'s [=averager/sum=] to |dest|'s [=averager/sum=] + |source|'s [=averager/sum=]. + +
+ +
+To get the value to report from an averager given an [=averager=] |averager|: +1. If |averager|'s [=averager/count=] is 0, return 0. +1. Return |averager|'s [=averager/sum=] / [=averager/count=]. + +
+ +### Metrics structures ### {#private-aggregation-metrics-structures} +An execution metrics is a [=struct=] with the following [=struct/items=], representing +metrics collected from a single execution of a worklet function: +
+ : code fetch time averager + :: An [=averager=]. + : script timed out + :: A [=boolean=], initially false. This refers to the script hitting its own time out, and does + not include the cumulative timeout affecting buyers. +
+ +A per participant metrics is a [=struct=] with the following [=struct/items=], +representing metrics aggregated over a particular participant (e.g. bidder or seller). It has: +
+ : participating interest group count + :: A {{long}}, initially 0. + : code fetch time averager + :: An [=averager=]. + : script timeouts occurred + :: A {{long}}, initially 0. + : script executions attempted + :: A {{long}}, initially 0. + : regular interest group count + :: A {{long}}, initially 0. + : negative interest group count + :: A {{long}}, initially 0. + : storage quota used + :: A {{long}}, initially 0. +
+

Private Aggregation contributions

Private Aggregation contributions is a [=map=] from [=string=] to a [=list=] of [=on event contribution entries=]. @@ -4119,6 +4347,7 @@ To prepare for private aggregation given a a [=reporting context=] |reportingContext|, and [=origins=] |origin| and |aggregationCoordinator|: 1. Let |debugScope| be a new [=debug scope=]. 1. Set |global|'s [=InterestGroupScriptRunnerGlobalScope/worklet function=] to |workletFunction|. +1. Set |global|'s [=InterestGroupScriptRunnerGlobalScope/origin=] to |origin|. 1. Set |global|'s [=InterestGroupScriptRunnerGlobalScope/private aggregation=] to a new [=PrivateAggregation=] with the following [=struct/items=]: : allowed to use @@ -4227,7 +4456,7 @@ an [=auction config=] |auctionConfig| and a [=reporting context=] |reportingCont 1. Otherwise: 1. If |bidId| ≠ |sellerOnceRep|, [=iteration/continue=]. 1. Let |filledInContribution| be the result of [=filling in the contribution=] given - |onEventEntry|'s [=on event contribution entry/contribution=] and |leadingBidInfo|. + |reportingContext|, |onEventEntry| and |leadingBidInfo|. Issue: Once WICG/turtledove#627 is resolved, align 'filling in' logic with `forDebuggingOnly`. @@ -4339,15 +4568,18 @@ an [=auction config=] |auctionConfig| and a [=reporting context=] |reportingCont
-To fill in the contribution given a {{PAExtendedHistogramContribution}} |contribution| -and a [=leading bid info=] |leadingBidInfo|, perform the following steps. They return a -{{PAHistogramContribution}}: +To fill in the contribution given a [=reporting context=] |reportingContext|, +an [=on event contribution entry=] |onEventEntry| and a [=leading bid info=] |leadingBidInfo|, +perform the following steps. They return a {{PAHistogramContribution}}: +1. Let |contribution| be |onEventEntry|'s [=on event contribution entry/contribution=]. 1. Let |bucket| be |contribution|["{{PAExtendedHistogramContribution/bucket}}"]. 1. If |bucket| is a {{PASignalValue}}, set |bucket| to the result of [=filling - in the signal value=] given |bucket|, 2128−1 and |leadingBidInfo|. + in the signal value=] given |reportingContext|, |onEventEntry|, |bucket|, 2128−1 + and |leadingBidInfo|. 1. Let |value| be |contribution|["{{PAExtendedHistogramContribution/value}}"]. 1. If |value| is a {{PASignalValue}}, set |value| to the result of [=filling in - the signal value=] given |value|, 231−1 and |leadingBidInfo|. + the signal value=] given |reportingContext|, |onEventEntry|, |value|, 231−1 and + |leadingBidInfo|. 1. Let |filledInContribution| be a new {{PAHistogramContribution}} with the items: : {{PAHistogramContribution/bucket}} @@ -4361,13 +4593,15 @@ and a [=leading bid info=] |leadingBidInfo|, perform the following steps. They r
-To fill in the signal value given a {{PASignalValue}} |value|, an +To fill in the signal value given a [=reporting context=] |reportingContext|, +an [=on event contribution entry=] |onEventEntry|, a {{PASignalValue}} |value|, an integer |maxAllowed| and a [=leading bid info=] |leadingBidInfo|, perform the following steps. They return an integer. 1. [=Assert=]: |value|["{{PASignalValue/baseValue}}"] is a valid [=signal base value=]. 1. Let |returnValue| be the result of [=determining a signal's numeric value=] - given |value|["{{PASignalValue/baseValue}}"] and |leadingBidInfo|. + given |reportingContext|, |onEventEntry|, |value|["{{PASignalValue/baseValue}}"] and + |leadingBidInfo|. 1. If |value|["{{PASignalValue/scale}}"] [=map/exists=], set |returnValue| to the result of multiplying |value|["{{PASignalValue/scale}}"] with |returnValue|. @@ -4382,22 +4616,31 @@ They return an integer.
-To determine a signal's numeric value given a [=signal base value=] +To determine a signal's numeric value given a [=reporting context=] |reportingContext|, +an [=on event contribution entry=] |onEventEntry|, a [=signal base value=] |signalBaseValue| and a [=leading bid info=] |leadingBidInfo|, perform the following steps. They return a {{double}}. -1. If |signalBaseValue| is "[=signal base value/winning-bid=]": - 1. If |leadingBidInfo|'s [=leading bid info/leading bid=] is null, return 0. - 1. Otherwise, return |leadingBidInfo|'s [=leading bid info/leading bid=]'s [=generated bid/bid=]. -1. If |signalBaseValue| is - "[=signal base value/highest-scoring-other-bid=]": - 1. If |leadingBidInfo|'s [=leading bid info/highest scoring other bid=] is null, return 0. - 1. Otherwise, return |leadingBidInfo|'s [=leading bid info/highest scoring other bid=]'s - [=generated bid/bid=]. -1. If |signalBaseValue| is "[=signal base value/script-run-time=]": - 1. Return the number of milliseconds of CPU time that the calling function +1. Let |metrics| be the result of [=access per-participant metrics=] given |reportingContext|, + |onEventEntry|'s [=on event contribution entry/origin=], [=on event contribution entry/ + worklet function=]. +1. Let |bidAndScoreMetrics| be the result of [=access per-participant metrics=] given + |reportingContext|, |onEventEntry|'s [=on event contribution entry/origin=], and the result of + [=find corresponding bid and score phase function=] given [=on event contribution entry/worklet + function=]. +1. If |signalBaseValue| is: +
+ : "[=signal base value/winning-bid=]" + :: 1. If |leadingBidInfo|'s [=leading bid info/leading bid=] is null, return 0. + 1. Otherwise, return |leadingBidInfo|'s [=leading bid info/leading bid=]'s [=generated bid/bid=]. + : "[=signal base value/highest-scoring-other-bid=]": + :: 1. If |leadingBidInfo|'s [=leading bid info/highest scoring other bid=] is null, return 0. + 1. Otherwise, return |leadingBidInfo|'s [=leading bid info/highest scoring other bid=]'s + [=generated bid/bid=]. + : "[=signal base value/script-run-time=]" + :: Return the number of milliseconds of CPU time that the calling function (e.g. `generateBid()`) took to run. -1. If |signalBaseValue| is "[=signal base value/signals-fetch-time=]": - Switch on the associated [=worklet function=]: + : "[=signal base value/signals-fetch-time=]" + :: Switch on the associated [=worklet function=]:
: [=worklet function/generate-bid=] :: Return the number of milliseconds it took for the trusted bidding signals @@ -4410,43 +4653,108 @@ They return a {{double}}. :: Return 0.
-1. If |signalBaseValue| is "[=signal base value/bid-reject-reason=]": - 1. If the bid did not succeed purely because it didn't meet the required - k-anonymity threshold, return 8. - 1. Let |bidRejectReason| be "`not-available`". - 1. If the seller provided a reject reason, set |bidRejectReason| to that - value. - 1. If |bidRejectReason| is: -
- : "`not-available`" - :: Return 0. - : "`invalid-bid`" - :: Return 1. - : "`bid-below-auction-floor`" - :: Return 2. - : "`pending-approval-by-exchange`" - :: Return 3. - : "`disapproved-by-exchange`" - :: Return 4. - : "`blocked-by-publisher`" - :: Return 5. - : "`language-exclusions`" - :: Return 6. - : "`category-exclusions`" - :: Return 7. - : None of the above values - :: [=Assert=]: false - - Note: this enum value is validated in `scoreAd()`. - - Issue: Verify this once - WICG/turtledove#627 is resolved. - - Issue: There are some automatically generated values that are not described here. - - Issue: Verify handling when the bid was not rejected. - -
+ : "[=signal base value/bid-reject-reason=]" + :: 1. If the bid did not succeed purely because it didn't meet the required + k-anonymity threshold, return 8. + 1. Let |bidRejectReason| be "`not-available`". + 1. If the seller provided a reject reason, set |bidRejectReason| to that + value. + 1. If |bidRejectReason| is: +
+ : "`not-available`" + :: Return 0. + : "`invalid-bid`" + :: Return 1. + : "`bid-below-auction-floor`" + :: Return 2. + : "`pending-approval-by-exchange`" + :: Return 3. + : "`disapproved-by-exchange`" + :: Return 4. + : "`blocked-by-publisher`" + :: Return 5. + : "`language-exclusions`" + :: Return 6. + : "`category-exclusions`" + :: Return 7. + : None of the above values + :: [=Assert=]: false + + Note: this enum value is validated in `scoreAd()`. + + Issue: Verify this once + WICG/turtledove#627 is resolved. + + Issue: There are some automatically generated values that are not described here. + + Issue: Verify handling when the bid was not rejected. + +
+ : "[=signal base value/average-code-fetch-time=]" + :: Return the result of [=getting the value to report from an averager=] given |metrics|'s [=per + participant metrics/code fetch time averager=]. + + : "[=signal base value/participating-ig-count=]" + :: Return |bidAndScoreMetrics|'s [=per participant metrics/participating interest group count=]. + + : "[=signal base value/percent-scripts-timeout=]" + :: Return the result of [=computing a percentage metric=] given |metrics|'s [=per participant + metrics/script timeouts occurred=] and |metrics|'s [=per participant metrics/script executions + attempted=]. + + : "[=signal base value/regular-igs-count=]" + :: Return |bidAndScoreMetrics|'s [=per participant metrics/regular interest group count=]. + + : "[=signal base value/percent-regular-ig-count-quota-used=]" + :: Return the result of [=computing a percentage metric=] given |bidAndScoreMetrics|'s [=per + participant metrics/regular interest group count=] and [=max regular interest groups per + owner=]. + + : "[=signal base value/negative-igs-count=]" + :: Return |bidAndScoreMetrics|'s [=per participant metrics/negative interest group count=]. + + : "[=signal base value/percent-negative-ig-count-quota-used=]": + :: Return the result of [=computing a percentage metric=] given |bidAndScoreMetrics|'s [=per + participant metrics/negative interest group count=] and [=max negative interest groups per + owner=]. + + : "[=signal base value/ig-storage-used=]": + :: Return |bidAndScoreMetrics|'s [=per participant metrics/storage quota used=]. + + : "[=signal base value/percent-ig-storage-quota-used=]": + :: Return the result of [=computing a percentage metric=] given |bidAndScoreMetrics|'s [=per + participant metrics/storage quota used=] and [=max interest groups total size per owner=]. + +
+ +
+ +
+To compute a percentage metric given {{long}}s |numerator| and |denominator|: +1. If |denominator| is 0, return 0. +1. Let |result| be 100.0 * |numerator| / |denominator|, performing the computation with + {{double}}s. +1. If |result| > 110.0, set |result| to 110.0 + + Note: Since [=perform storage maintenance|Interest Group Storage Maintenance=] happens + periodically, metrics measuring storage utilization may report + values that exceed 100%. The 110% cap exists to make it easier to allocate histogram bucket space + for those metrics, by providing a predictable upper bound. + +1. Return |result|. + +
+ +
+To update storage metrics given [=per participant metrics=] |metrics| and a [=list=] of +[=interest groups=] |igs|: +1. [=list/For each=] |ig| of |igs|: + 1. If [=interest group/additional bid key=] is null, increment |metrics|'s [=per participant + metrics/regular interest group count=] by 1. + 1. Otherwise, increment |metrics|'s [=per participant metrics/negative interest group count=] + by 1. + 1. Increment |metrics|'s [=per participant metrics/storage quota used=] by |ig|'s [=interest + group/estimated size=]
@@ -5047,7 +5355,7 @@ from querying the server during an auction. 1. Let |reportingHashCode| be the result of [=query reporting ID k-anonymity count=] given |ig|, |igAd|, and |selectableReportingId|. 1. If [=query k-anonymity cache=] for |reportingHashCode| returns true, then - [=list/Append=] |selectableReportingId| to |kAnonRestrictedSelectableReportingIds|. + [=list/append=] |selectableReportingId| to |kAnonRestrictedSelectableReportingIds|. 1. Set |igAd|'s [=interest group ad/selectable buyer and seller reporting IDs=] to |kAnonRestrictedSelectableReportingIds|. 1. [=list/Append=] |igAd| to |kAnonRestrictedIG|'s [=interest group/ads=]. @@ -5352,6 +5660,7 @@ of the following global objects:
To evaluate a bidding script given a [=string=] |script|, an {{unsigned short}} |multiBidLimit|, an [=interest group=] |ig|, a [=reporting context=] |reportingContext|, + an [=execution metrics=] |executionMetrics|, a [=currency tag=] |expectedCurrency|, a {{GenerateBidInterestGroup}} |igGenerateBid|, a [=string=]-or-null |auctionSignals|, a [=string=]-or-null |perBuyerSignals|, an [=ordered map=]-or-null |sameOriginTrustedBiddingSignals|, an [=ordered map=]-or-null @@ -5359,7 +5668,7 @@ of the following global objects: a {{DirectFromSellerSignalsForBuyer}} |directFromSellerSignalsForBuyer|, and an integer millisecond [=duration=] |timeout|, perform the following steps. They return a [=tuple=] ([=list=] of [=generated bids=], [=bid debug reporting info=], [=list=] of [=real time reporting - contributions=]). + contributions=], [=Private Aggregation contributions=], [=execution metrics=]). 1. Let |realm| be the result of [=creating a new script runner realm=] given {{InterestGroupBiddingScriptRunnerGlobalScope}}. @@ -5395,7 +5704,8 @@ of the following global objects: 1. Let |crossOriginTrustedBiddingSignalsJS| be |crossOriginTrustedBiddingSignals| [=converted to ECMAScript values=]. 1. Let |startTime| be |settings|'s [=environment settings object/current monotonic time=]. - 1. Let |result| be the result of [=evaluating a script=] with |realm|, |script|, "`generateBid`", + 1. Let (|result|, |executionMetrics|'s [=execution metrics/script timed out=]) be the + result of [=evaluating a script=] with |realm|, |script|, "`generateBid`", « |igJS|, |auctionSignalsJS|, |perBuyerSignalsJS|, |sameOriginTrustedBiddingSignalsJS|, |browserSignalsJS|, |directFromSellerSignalsJS|, |crossOriginTrustedBiddingSignalsJS| », and |timeout|. @@ -5450,7 +5760,7 @@ of the following global objects: 1. Let |paContributions| be the result of [=extracting private aggregation contributions=] given |global|. 1. Return a [=tuple=] (|generatedBids|, |bidDebugReportInfo|, |realTimeContributions|, - |paContributions|). + |paContributions|, |executionMetrics|).
@@ -5478,7 +5788,9 @@ of the following global objects: 1. Let |directFromSellerSignalsJs| be |directFromSellerSignalsForSeller| [=converted to ECMAScript values=]. 1. Let |startTime| be |settings|'s [=environment settings object/current monotonic time=]. - 1. Let |scoreAdResult| be the result of [=evaluating a script=] with |realm|, |script|, "`scoreAd`", + 1. Let |executionMetrics| be a new [=execution metrics=]. + 1. Let (|scoreAdResult|, |executionMetrics|'s [=execution metrics/script timed out=]) be + the result of [=evaluating a script=] with |realm|, |script|, "`scoreAd`", «|adMetadata|, |bidValue|, |auctionConfigJS|, |sameOriginTrustedScoringSignalsJS|, |browserSignalsJS|, |directFromSellerSignalsJs|, |crossOriginTrustedScoringSignalsJS|», and |timeout|. @@ -5500,7 +5812,7 @@ of the following global objects: 1. Let |paContributions| be the result of [=extracting private aggregation contributions=] given |global|. 1. Return « |scoreAdResult|, |debugWinReportUrl|, |debugLossReportUrl|, |realTimeContributions|, - |paContributions| ». + |paContributions|, |executionMetrics| ».
@@ -5518,9 +5830,12 @@ of the following global objects: and |privateAggregationCoordinator|. 1. Let |argumentsJS| be the result of [=converting a Web IDL arguments list to an ECMAScript arguments list|converting=] |arguments| to an ECMAScript arguments list. If this - [=exception/throws=] an exception, return « "null", null, null, null ». - 1. Let |result| be the result of [=evaluating a script=] with |realm|, |script|, - |functionName|, |argumentsJS|, and |timeout|. + [=exception/throws=] an exception, return « "null", null, null, null, a new [=Private + Aggregation Contributions=], |executionMetrics| ». + 1. Let |executionMetrics| be a new [=execution metrics=]. + 1. Let (|result|, |executionMetrics|'s [=execution metrics/script timed out=]) be the + result of [=evaluating a script=] with |realm|, |script|, |functionName|, |argumentsJS|, + and |timeout|. 1. If |result| is an [=ECMAScript/abrupt completion=], return « "null", null, null, null ». 1. Let |resultJSON| be "null". 1. If |functionName| is "`reportResult`", then set |resultJSON| to the result of @@ -5537,20 +5852,23 @@ of the following global objects: |global|. 1. Return « |resultJSON|, |reportURL|, |global|'s [=InterestGroupReportingScriptRunnerGlobalScope/reporting beacon map=], |macroMap|, - |paContributions| ». + |paContributions|, |executionMetrics| ».
To evaluate a script with a [=ECMAScript/realm=] |realm|, [=string=] |script|, [=string=] |functionName|, a [=list=] |arguments|, and an integer millisecond [=duration=] |timeout|, run these steps. - They return a [=ECMAScript/Completion Record=], which is either an [=ECMAScript/abrupt completion=] (in - the case of a parse failure or execution error), or a [=ECMAScript/normal completion=] populated with the - [=ECMAScript/ECMAScript language value=] result of invoking |functionName|. + They return a tuple of a [=ECMAScript/Completion Record=], which is either an [=ECMAScript/abrupt + completion=] (in the case of a parse failure or execution error), or a [=ECMAScript/normal + completion=] populated with the [=ECMAScript/ECMAScript language value=] result of invoking + |functionName|, and a [=boolean=] stating whether the script was interrupted due to reaching + |timeout|. 1. [=Assert=] that these steps are running [=in parallel=]. - 1. If |timeout| ≤ 0, [=immediately=] interrupt the execution and set |finalCompletion| to a - new [=ECMAScript/throw completion=] given null. + 1. If |timeout| ≤ 0, return (new [=ECMAScript/throw completion=] given null, true). + + 1. Let |timedOut| be false. 1. Let |global| be |realm|'s [=realm/global object=], and run these steps in |realm|'s [=realm/agent=]: @@ -5560,7 +5878,7 @@ of the following global objects: unlike traditional [=scripts=] on the web platform. 1. If |result| is a list of errors, return - Completion { \[[Type]]: `throw`, \[[Value]]: |result|, \[[Target]]: `empty` }. + (Completion { \[[Type]]: `throw`, \[[Value]]: |result|, \[[Target]]: `empty` }, false). 1. [=Assert=]: |result| is a [=ECMAScript/Script Record=]. @@ -5570,6 +5888,11 @@ of the following global objects: 1. Let |evaluationStatus| be the result of [$ScriptEvaluation$](result). + In |timeout| milliseconds, if the invocation of [$ScriptEvaluation$] has not completed, + [=immediately=] interrupt the execution, set |finalCompletion| to a new + [=ECMAScript/throw completion=] given null, set |timedOut| to true, and jump to the step + labeled return. + 1. If |evaluationStatus| is an [=ECMAScript/abrupt completion=], jump to the step labeled return. @@ -5580,9 +5903,9 @@ of the following global objects: 1. Set |finalCompletion| be [=ECMAScript/Completion Record|Completion=]([$Call$](F, `undefined`, |arguments|)). - In |timeout| milliseconds, if the invocation of [$Call$] has not completed, - [=immediately=] interrupt the execution and set |finalCompletion| to a new - [=ECMAScript/throw completion=] given null. + In |timeout| milliseconds minus the execution time of [$ScriptEvaluation$], if the invocation + of [$Call$] has not completed, [=immediately=] interrupt the execution, set |finalCompletion| + to a new [=ECMAScript/throw completion=] given null, and set |timedOut| to true. 1. Return: at this point |finalCompletion| will be set to a [=ECMAScript/Completion Record=]. @@ -5591,7 +5914,7 @@ of the following global objects: execution context|running JavaScript execution context=], and remove it from the [=ECMAScript/execution context stack|JavaScript execution context stack=]. - 1. Return |finalCompletion|. + 1. Return (|finalCompletion|, timedOut).
## Global scopes ## {#global-scopes} @@ -5637,6 +5960,8 @@ Each {{InterestGroupScriptRunnerGlobalScope}} has a :: A [=worklet function=]. Affects some [Private Aggregation API](https://github.com/patcg-individual-drafts/private-aggregation-api) functionality. + : origin + :: The [=origin=] of the script being executed.
@@ -5652,6 +5977,7 @@ The contributeToHistogramOnEvent(DOMString event, PAExtendedHistogramContribution contribution) method steps are: 1. Let |global| be [=this=]'s [=relevant global object=]. 1. Let |function| be |global|'s [=InterestGroupScriptRunnerGlobalScope/worklet function=]. +1. Let |origin| be |global|'s [=InterestGroupScriptRunnerGlobalScope/origin=]. 1. If [=this=]'s allowed to use is false, [=exception/throw=] a {{TypeError}}. 1. Let |scopingDetails| be [=this=]'s @@ -5711,7 +6037,8 @@ event, PAExtendedHistogramContribution contribution) method steps are: for="scoping details">get debug scope steps. : [=on event contribution entry/worklet function=] :: |function| - + : [=on event contribution entry/origin=] + :: |origin| 1. Let |onEventContributionMap| be |global|'s [=InterestGroupScriptRunnerGlobalScope/on event contribution map=]. 1. If |onEventContributionMap|[|event|] does not [=map/exist=], set @@ -6263,18 +6590,17 @@ navigating to another page. Some implementations, such as Chromium, have chosen : [=request/mode=] :: "`no-cors`" : [=request/referrer=] - :: "`no-referrer`" + :: "`no-referrer`" : [=request/credentials mode=] :: "`omit`" : [=request/redirect mode=] - :: "`error`" + :: "`error`" + : [=request/service-workers mode=] + :: "`none`" : [=request/policy container=] :: A new [=policy container=] whose [=policy container/IP address space=] is |policyContainer|'s [=policy container/IP address space=] - Issue: One of the side-effects of a `null` client for this subresource request is it neuters - all service worker interceptions, despite not having to set the service workers mode. - Issue: Stop using "`no-cors`" mode where possible (WICG/turtledove#667). 1. Let |update| be null. @@ -6815,11 +7141,11 @@ To check interest group permissions given an [=origin=] |ownerOrigin| : [=request/mode=] :: "`cors`" : [=request/referrer=] - :: "`no-referrer`" + :: "`no-referrer`" : [=request/credentials mode=] :: "`omit`" : [=request/redirect mode=] - :: "`error`" + :: "`error`" : [=request/service-workers mode=] :: `none` : [=request/policy container=] @@ -7904,6 +8230,8 @@ headers. It's a [=struct=] with the following [=struct/items=]: : origins authorized for cross origin trusted signals :: A [=list=] of [=origins=] or null. Initially null. Parsed value of [:Ad-Auction-Allow-Trusted-Scoring-Signals-From:]. + : fetch duration + :: A [=duration=] in milliseconds, denoting how long it took the fetch to complete.
@@ -7972,15 +8300,16 @@ a [=script fetcher=] |fetcher|: :: "`omit`" : [=request/redirect mode=] :: "`error`" + : [=request/service-workers mode=] + :: "`none`" : [=request/policy container=] :: A new [=policy container=] whose [=policy container/IP address space=] is |settings|'s [=environment settings object/policy container=]'s [=policy container/IP address space=] - Issue: One of the side-effects of a `null` client for this subresource request is it neuters all - service worker interceptions, despite not having to set the service workers mode. - Issue: Stop using "`no-cors`" mode where possible (WICG/turtledove#667). + + 1. Let |fetchStart| be |settings|'s [=environment settings object/current monotonic time=]. 1. Let |fetchController| be the result of [=fetching=] |request| with [=fetch/useParallelQueue=] set to true, and [=fetch/processResponse=] set to the following steps given a [=response=] |response|: @@ -7988,7 +8317,10 @@ a [=script fetcher=] |fetcher|: 1. [=fetch controller/Abort=] |fetchController|. 1. Set |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=] to an empty [=list=] of [=origins=]. + 1. Set |fetcher|'s [=script fetcher/fetch duration=] to the [=duration from=] |fetchStart| to + |settings|'s [=environment settings object/current monotonic time=], in milliseconds. 1. Set |fetcher|'s [=script fetcher/script body=] to failure. + 1. Return. 1. Set |fetcher|'s [=script fetcher/origins authorized for cross origin trusted signals=] to the result of [=parsing allowed trusted scoring signals origins=] given |response|'s [=response/ header list=]. @@ -7996,6 +8328,8 @@ a [=script fetcher=] |fetcher|: 1. Let |bodyReader| be result of [=ReadableStream/getting a reader=] from |bodyStream|. 1. Let |successSteps| be a set of steps that take a [=byte sequence=] |responseBody|, and perform the following: + 1. Set |fetcher|'s [=script fetcher/fetch duration=] to the [=duration from=] |fetchStart| to + |settings|'s [=environment settings object/current monotonic time=], in milliseconds. 1. If [=validate fetching response mime and body=] with |response|, |responseBody| and "`text/javascript`" returns false, set |fetcher|'s [=script fetcher/script body=] to failure. @@ -8594,6 +8928,9 @@ A bid debug reporting info is a [=struct=] with the following [=struc won it, and was then scored by the top-level seller. Set by top-level seller's `scoreAd()`'s {{InterestGroupBiddingAndScoringScriptRunnerGlobalScope/forDebuggingOnly}}'s {{ForDebuggingOnly/reportAdAuctionLoss(url)}}. + : server filtered debugging only reports + :: A [=map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=lists=] of [=urls=]. + forDebuggingOnly reports that have been filtered (also downsampled) by the trusted auction server. A bid with currency is a [=struct=] with the following [=struct/items=]: