Protected Audience (formerly FLEDGE)

Draft Community Group Report,

This version:
https://wicg.github.io/turtledove/
Editor:
(Google)
Participate:
GitHub WICG/turtledove (new issue, open issues)
Commits:
GitHub spec.bs commits

Abstract

Provides a privacy advancing API to facilitate interest group based advertising.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

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.

When a user’s interactions with an advertiser indicate an interest in something, the advertiser can ask the browser to record this interest on-device by calling navigator.joinAdInterestGroup(). Later, when a website wants to select an advertisement to show to the user, the website can call 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.

2. 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 navigator.joinAdInterestGroup(). This indicates an intent to display an advertisement relevant to this interest to this user in the future. The user agent has an interest group set, a list of interest groups in which owner / name pairs are unique.

2.1. joinAdInterestGroup()

TODO: Currently, several of the IDL fields in AuctionAdInterestGroup are specified to use USVString rather than DOMString, only because the initial implementation currently does. This may contradict the recommended use of DOMString. (WICG/turtledove#1250)

[SecureContext]
partial interface Navigator {
  Promise<undefined> joinAdInterestGroup(AuctionAdInterestGroup group);
};

dictionary AuctionAd {
  required USVString renderURL;
  USVString sizeGroup;
  any metadata;

  USVString buyerReportingId;
  USVString buyerAndSellerReportingId;
  sequence<USVString> allowedReportingOrigins;
  DOMString adRenderId;
};

dictionary AuctionAdInterestGroupSize {
  required USVString width;
  required USVString height;
};

dictionary GenerateBidInterestGroup {
  required USVString owner;
  required USVString name;

  boolean enableBiddingSignalsPrioritization = false;
  record<DOMString, double> priorityVector;

  record<USVString, sequence<DOMString>> sellerCapabilities;
  DOMString executionMode = "compatibility";
  USVString biddingLogicURL;
  USVString biddingWasmHelperURL;
  USVString updateURL;
  USVString trustedBiddingSignalsURL;
  sequence<USVString> trustedBiddingSignalsKeys;
  DOMString trustedBiddingSignalsSlotSizeMode = "none";
  long maxTrustedBiddingSignalsURLLength;
  any userBiddingSignals;
  sequence<AuctionAd> ads;
  sequence<AuctionAd> adComponents;
  record<DOMString, AuctionAdInterestGroupSize> adSizes;
  record<DOMString, sequence<DOMString>> sizeGroups;
};

dictionary AuctionAdInterestGroup : GenerateBidInterestGroup {
  double priority = 0.0;
  record<DOMString, double> prioritySignalsOverrides;
  required double lifetimeMs;
  DOMString additionalBidKey;
};

AuctionAdInterestGroup is used by 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 modified by generatedBid() calls, so could theoretically be used to create a cross-site profile of a user accessible to generateBid() methods, otherwise. lifetimeMs is not passed to generateBid() because it’s ambiguous what should be passed: the lifetime when the group was joined, or the remaining lifetime. Providing the remaining lifetime would also potentially give access to more granular timing information than the API would otherwise allow, when state is shared across interest groups.

The joinAdInterestGroup(group) method steps are:

Temporarily, Chromium does not include the required keyword for lifetimeMs, and instead starts this algorithm with the step

  1. If group["lifetimeMs"] does not exist, throw a TypeError.

This is detectable because it can change the set of fields that are read from the argument when a TypeError is eventually thrown, but it will never change whether the call succeeds or fails.

  1. Let global be this's relevant global object.

  2. If global’s associated Document is not allowed to use the "join-ad-interest-group" policy-controlled feature, then throw a "NotAllowedError" DOMException.

  3. Let settings be this's relevant settings object.

  4. Assert that settings’s origin is not an opaque origin and its scheme is "https".

  5. Let interestGroup be a new interest group.

  6. Validate the given group and set interestGroup’s fields accordingly.

    1. Set interestGroup’s expiry to the current wall time plus group["lifetimeMs"] milliseconds.

    2. Set interestGroup’s next update after to the current wall time plus 24 hours.

    3. Set interestGroup’s last updated to the current wall time.

    4. Set interestGroup’s owner to the result of parsing an https origin on group["owner"].

    5. If interestGroup’s owner is failure, then throw a TypeError.

    6. Optionally, throw a "NotAllowedError" DOMException.

      Note: This implementation-defined condition is intended to allow user agents to decline for a number of reasons, for example the owner's site not being enrolled.

    7. Set interestGroup’s name to group["name"].

    8. Set interestGroup’s priority to group["priority"].

    9. Set interestGroup’s enable bidding signals prioritization to group["enableBiddingSignalsPrioritization"].

    10. If group["priorityVector"] exists, then set interestGroup’s priority vector to group["priorityVector"].

    11. If group["prioritySignalsOverrides"] exists, then set interestGroup’s priority signals overrides to group["prioritySignalsOverrides"].

    12. If group["sellerCapabilities"] exists, for each sellerStringcapabilities of group["sellerCapabilities"]:

      1. Let sellerCapabilities be a new set of seller capabilities.

      2. For each capabilityString of capabilities:

        1. If capabilityString is "interest-group-counts" or "latency-stats", then append capabilityString to sellerCapabilities.

          Note: For forward compatibility with new values, don’t throw.

      3. If sellerString is "*", then set interestGroup’s all sellers capabilities to sellerCapabilities.

      4. Otherwise:

        1. If interestGroup’s seller capabilities is null, then set interestGroup’s seller capabilities to a new ordered map whose keys are origins and whose values are sets of seller capabilities.

        2. Let sellerUrl be the result of running the URL parser on sellerString.

        3. If sellerUrl is not failure:

          1. Let seller be sellerUrl’s origin.

          2. If interestGroup’s seller capabilities does not contain seller, then set interestGroup’s seller capabilities[seller] to sellerCapabilities.

    13. If group["executionMode"] is "compatibility", "frozen-context", or "group-by-origin", then set interestGroup’s execution mode to it.

    14. For each groupMember and interestGroupField in the following table

      Group member Interest group field
      "biddingLogicURL" bidding url
      "biddingWasmHelperURL" bidding wasm helper url
      "updateURL" update url
      1. If group contains groupMember:

        1. Let parsedUrl be the result of running parse and verify a bidding code or update URL on group[groupMember] and interestGroup’s owner.

        2. If parsedUrl is failure, then throw a TypeError.

        3. Set interestGroup’s interestGroupField to parsedUrl.

    15. If group["trustedBiddingSignalsURL"] exists:

      1. Let parsedUrl be the result of running parse and verify a trusted signals URL on group[trustedBiddingSignalsURL].

      2. If parsedUrl is failure, then throw a TypeError.

      3. Set interestGroup’s trusted bidding signals url to parsedUrl.

    16. If group["trustedBiddingSignalsKeys"] exists, then set interestGroup’s trusted bidding signals keys to group["trustedBiddingSignalsKeys"].

    17. If group["maxTrustedBiddingSignalsURLLength"] exists:

      1. If group["maxTrustedBiddingSignalsURLLength"] ≥ 0, set interestGroup’s max trusted bidding signals url length to group["maxTrustedBiddingSignalsURLLength"], otherwise throw a TypeError.

    18. If group["userBiddingSignals"] exists:

      1. Set interestGroup’s user bidding signals to the result of serializing a JavaScript value to a JSON string, given group["userBiddingSignals"]. This can throw a TypeError.

    19. If group["trustedBiddingSignalsSlotSizeMode"] is one of "none", "slot-size", or "all-slots-requested-sizes", set interestGroup’s trusted bidding signals slot size mode to group["trustedBiddingSignalsSlotSizeMode"].

    20. Let adSizes be a new map whose keys are strings and values are ad sizes.

    21. If group["adSizes"] exists:

      1. For each sizeNamesize of group["adSizes"]:

        1. If sizeName is "", throw a TypeError.

        2. Let parsedSize be the result from running parse an AdRender ad size with size.

        3. If parsedSize is null, throw a TypeError.

        4. Set adSizes[sizeName] to parsedSize.

      2. Set interestGroup’s ad sizes to adSizes.

    22. Let sizeGroups be a new map whose keys are strings and values are lists of strings

    23. If group["sizeGroups"] exists:

      1. For each sizeGroupNamesizeList of group["sizeGroups"]:

        1. If sizeGroupName is "", throw a TypeError.

        2. For each sizeName of sizeList:

          1. If sizeName is "" or adSizes[sizeName] does not exist, throw a TypeError.

        3. Set sizeGroups[sizeGroupName] to sizeList.

      2. Set interestGroup’s size groups to sizeGroups.

    24. For each groupMember and interestGroupField in the following table

      Group member Interest group field
      "ads" ads
      "adComponents" ad components
      1. If group contains groupMember, for each ad of group[groupMember]:

        1. Let igAd be a new interest group ad.

        2. Let renderURL be the result of running the URL parser on ad["renderURL"].

        3. Throw a TypeError if any of the following conditions hold:

        4. Set igAd’s render url to renderURL.

        5. If ad["sizeGroup"] exists:

          1. Let sizeGroup be ad["sizeGroup}].

          2. Throw a TypeError if none of the following conditions hold:

          3. Set igAd’s size group to sizeGroup.

        6. If ad["metadata"] exists, then let igAd’s metadata be the result of serializing a JavaScript value to a JSON string, given ad["metadata"]. This can throw a TypeError.

        7. If ad["adRenderId"] exists:

          1. If ad["adRenderId"]'s length > 12, throw a TypeError.

          2. If any code point in ad["adRenderId"] is not an ASCII code point, throw a TypeError.

          3. Set igAd’s ad render id to ad["adRenderId"].

        8. If groupMember is "ads":

          1. If ad["buyerReportingId"] exists, then set igAd’s buyer reporting ID to it.

          2. If ad["buyerAndSellerReportingId"] exists, then set igAd’s buyer and seller reporting ID to it.

          3. If ad["allowedReportingOrigins"] exists:

            1. Let allowedReportingOrigins be a new list of origins.

            2. For each originStr in ad["allowedReportingOrigins"]:

              1. Let origin be the result of parsing an https origin on originStr.

              2. If origin is failure, then throw a TypeError.

              3. Append origin to allowedReportingOrigins.

              4. If allowedReportingOrigins’s size > 10, throw a TypeError.

            3. Set igAd’s allowed reporting origins to allowedReportingOrigins.

        9. Append igAd to interestGroup’s interestGroupField.

    25. If group["additionalBidKey"] exists:

      1. Let decodedKey be the result of running forgiving-base64 decode with group["additionalBidKey"].

      2. Throw a TypeError if any of the following conditions hold:

      3. Set interestGroup’s additional bid key to decodedKey.

  7. If interestGroup’s estimated size > 1048576 bytes, then throw a TypeError.

  8. Let p be a new promise.

  9. Let queue be the result of starting a new parallel queue.

  10. Enqueue the following steps to queue:

    1. Let permission be the result of checking interest group permissions with interestGroup’s owner, settings, and "join".

    2. If permission is false, then queue a global task on DOM manipulation task source, given global, to reject p with a "NotAllowedError" DOMException and abort these steps.

    3. Queue a global task on DOM manipulation task source, given global, to resolve p with undefined.

    4. If the browser is currently storing an interest group with owner and name that matches interestGroup, then set the bid counts, join counts, and previous wins of interestGroup to the values of the currently stored one and remove the currently stored one from the browser.

    5. Set interestGroup’s joining origin to this's relevant settings object's top-level origin.

    6. Set interestGroup’s join time to the current wall time.

    7. If the most recent entry in interestGroup’s join counts corresponds to the current day in UTC, increment its count. If not, insert a new tuple the time set to the current UTC day and a count of 1.

    8. Store interestGroup in the user agent's interest group set.

    9. Run update k-anonymity cache for interest group for interestGroup.

  11. Return p.

2.2. Interest Group Storage Maintenance

There is a job that periodically performs storage maintenance on the user agent's interest group set. It performs operations such as removing expired or excess interest groups. An interest group set must respect the following limits. Implementations may define their own values for the below constants, however we supply the below values as a starting point, inspired by what the initial implementation of this specification uses:

To perform storage maintenance:
  1. Let ownersAndExpiry be a new ordered map whose keys are origins and values are moments.

    Note: The key is from owner, and value is from expiry. It’s used to determine a set of owners whose interest groups will be removed from the user agent's interest group set because the number of distinct owners exceeds the Interest group set max owners limit. It’s sorted based on their values (expiry) in descending order, in order to remove interest groups of owners expiring soonest first.

  2. Let now be the current wall time.

  3. For each ig of the user agent's interest group set:

    1. Let owner be ig’s owner.

    2. If ig’s expiry is before now, then remove ig from the user agent's interest group set and continue.

    3. If ownersAndExpiry[owner] exists, then set ownersAndExpiry[owner] to ig’s expiry if it comes after ownersAndExpiry[owner].

    4. Otherwise, set ownersAndExpiry[owner] to ig’s expiry.

  4. If ownersAndExpiry’s size > interest group set max owners, then set ownersAndExpiry to ownersAndExpiry sorted in descending order with a being less than b if a’s value comes before b’s value, where values are expiry.

  5. Let owners be the keys of ownersAndExpiry.

  6. For each i in the range from 0 to owners’s size, exclusive:

    1. If iinterest group set max owners, then remove interest groups from the user agent's interest group set whose owner is owners[i], and continue.

    2. Let regularIgs be a list of regular interest groups in the user agent's interest group set whose owner is owners[i].

    3. If regularIgs’s size > max regular interest groups per owner, then clear excess interest groups with regularIgs and max regular interest groups per owner.

    4. Let negativeIgs be a list of negative interest groups in the user agent's interest group set whose owner is owners[i].

    5. If negativeIgs’s size > max negative interest groups per owner, then clear excess interest groups with negativeIgs and max negative interest groups per owner.

  7. For each owner of owners:

    1. Let igs be a list of interest groups in the user agent's interest group set whose owner is owner, sorted in descending order with a being less than b if a’s expiry comes before b’s expiry.

    2. Let cumulativeSize be 0.

    3. For each ig of igs:

      1. If the sum of cumulativeSize and ig’s estimated size > max interest groups total size per owner, then remove ig from the user agent's interest group set.

      2. Otherwise, increment cumulativeSize by ig’s estimated size.

To clear excess interest groups with a list of interest groups igs, and an integer maxIgs:
  1. Let sortedIgs be igs sorted in descending order with a being less than b if a’s expiry comes before b’s expiry.

    Note: In order to remove interest groups expiring soonest first, sort interest groups based on their expiry in descending order.

  2. For each i in the range from maxIgs to igs’s size, exclusive:

    1. Remove sortedIgs[i] from the user agent's interest group set.

3. Leaving Interest Groups

3.1. leaveAdInterestGroup()

This first introductory paragraph is non-normative.

navigator.leaveAdInterestGroup() removes a user from a particular interest group.

[SecureContext]
partial interface Navigator {
  Promise<undefined> leaveAdInterestGroup(optional AuctionAdInterestGroupKey group = {});
};

dictionary AuctionAdInterestGroupKey {
  required USVString owner;
  required USVString name;
};

The leaveAdInterestGroup(group) method steps are:

  1. Let global be this's relevant global object.

  2. Let settings be this's relevant settings object.

  3. Let frameOrigin be settings’s origin.

  4. Assert that frameOrigin is not an opaque origin and its scheme is "https".

  5. Let p be a new promise.

  6. If group is empty:

    1. Let instance be global’s browsing context's fenced frame config instance.

    2. If instance is null, then return.

    3. Let interestGroup be instance’s interest group descriptor.

    4. Run these steps in parallel:

      1. Queue a global task on DOM manipulation task source, given global, to resolve p with undefined.

      2. If interestGroup is not null:

        1. Let owner be interestGroup’s owner.

        2. If owner is same origin with frameOrigin, then remove interest groups from the user agent's interest group set whose owner is owner and name is interestGroup’s name.

  7. Otherwise:

    1. If global’s associated Document is not allowed to use the "join-ad-interest-group" policy-controlled feature, then throw a "NotAllowedError" DOMException.

      Note: Both joining and leaving interest groups use the "join-ad-interest-group" feature.

    2. Let owner be the result of parsing an https origin with group["owner"].

    3. If owner is failure, throw a TypeError.

    4. Run these steps in parallel:

      1. Let permission be the result of checking interest group permissions with owner, settings, and "leave".

      2. If permission is false, then queue a global task on DOM manipulation task source, given global, to reject p with a "NotAllowedError" DOMException and abort these steps.

      3. Queue a global task on DOM manipulation task source, given global, to resolve p with undefined.

      4. Remove interest groups from the user agent's interest group set whose owner is owner and name is group["name"].

  8. Return p.

3.2. clearOriginJoinedAdInterestGroups()

This first introductory paragraph is non-normative.

navigator.clearOriginJoinedAdInterestGroups() removes a user from interest groups whose joining origin is the associated Navigator's relevant settings object's top-level origin.

[SecureContext]
partial interface Navigator {
  Promise<undefined> clearOriginJoinedAdInterestGroups(
      USVString owner, optional sequence<USVString> interestGroupsToKeep = []);
};

The clearOriginJoinedAdInterestGroups(owner, interestGroupsToKeep) method steps are:

  1. Let settings be this's relevant settings object.

  2. Let frameOrigin be settings’s origin.

  3. Assert that frameOrigin is not an opaque origin and its scheme is "https".

  4. Let p be a new promise.

  5. Let global be this's relevant global object.

  6. If global’s associated Document is not allowed to use the "join-ad-interest-group" policy-controlled feature, then throw a "NotAllowedError" DOMException.

    Note: Both joining and leaving interest groups use the "join-ad-interest-group" feature.

  7. Let ownerOrigin be the result of parsing an https origin with owner.

  8. If ownerOrigin is failure, throw a TypeError.

  9. Run these steps in parallel:

    1. Let permission be the result of checking interest group permissions with ownerOrigin, settings, and "leave".

    2. If permission is false, then queue a global task on the DOM manipulation task source given global, reject p with a "NotAllowedError" DOMException and abort these steps.

    3. Queue a global task on the DOM manipulation task source given global, to resolve p with undefined.

    4. Remove interest groups from the user agent's interest group set whose owner is ownerOrigin, whose joining origin is frameOrigin, and whose name is not in interestGroupsToKeep.

  10. Return p.

4. 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 navigator.runAdAuction() function, providing an auction configuration that 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.

4.1. runAdAuction()

[SecureContext]
partial interface Navigator {
  Promise<(USVString or FencedFrameConfig)?> runAdAuction(AuctionAdConfig config);
  readonly attribute boolean deprecatedRunAdAuctionEnforcesKAnonymity;
};

dictionary AuctionRealTimeReportingConfig {
  required DOMString type;
};

dictionary AuctionAdConfig {
  required USVString seller;
  required USVString decisionLogicURL;

  USVString trustedScoringSignalsURL;
  long maxTrustedScoringSignalsURLLength;
  sequence<USVString> interestGroupBuyers;
  Promise<any> auctionSignals;
  Promise<any> sellerSignals;
  Promise<DOMString?> directFromSellerSignalsHeaderAdSlot;
  Promise<record<USVString, USVString>?> deprecatedRenderURLReplacements;
  unsigned long long sellerTimeout;
  unsigned short sellerExperimentGroupId;
  Promise<record<USVString, any>?> perBuyerSignals;
  Promise<record<USVString, unsigned long long>?> perBuyerTimeouts;
  Promise<record<USVString, unsigned long long>?> perBuyerCumulativeTimeouts;
  unsigned long long reportingTimeout;
  USVString sellerCurrency;
  Promise<record<USVString, USVString>?> perBuyerCurrencies;
  record<USVString, unsigned short> perBuyerMultiBidLimits;
  record<USVString, unsigned short> perBuyerGroupLimits;
  record<USVString, unsigned short> perBuyerExperimentGroupIds;
  record<USVString, record<USVString, double>> perBuyerPrioritySignals;
  sequence<DOMString> requiredSellerCapabilities;
  record<DOMString, DOMString> requestedSize;
  sequence<record<DOMString, DOMString>> allSlotsRequestedSizes;
  Promise<undefined> additionalBids;
  DOMString auctionNonce;
  AuctionRealTimeReportingConfig sellerRealTimeReportingConfig;
  record<USVString, AuctionRealTimeReportingConfig> perBuyerRealTimeReportingConfig;
  sequence<AuctionAdConfig> componentAuctions = [];
  AbortSignal? signal;
  Promise<boolean> resolveToConfig;

  Promise<Uint8Array> serverResponse;
  USVString requestId;
};

Note: To help with ease of adoption, the browser will support the attribute navigator.deprecatedRunAdAuctionEnforcesKAnonymity while k-anonymity enforcement is being rolled out. This attribute is true when navigator.runAdAuction() enforces § 7 K-anonymity when running an auction and false otherwise. The attribute is not useful once k-anonymity enforcement is fully rolled out, and hence this attribute will be deprecated and removed some time after this point. See https://developers.google.com/privacy-sandbox/relevance/protected-audience-api/k-anonymity for more up to date information.

The runAdAuction(config) method steps are:

  1. Let global be this's relevant global object.

  2. Let settings be this's relevant settings object.

  3. If global’s associated Document is not allowed to use the "run-ad-auction" policy-controlled feature, then throw a "NotAllowedError" DOMException.

  4. Let auctionConfig be the result of running validate and convert auction ad config with config and true.

  5. If auctionConfig is failure, then throw a TypeError.

  6. Optionally, throw a "NotAllowedError" DOMException.

    Note: This implementation-defined condition is intended to allow user agents to decline for a number of reasons, for example the seller's site not being enrolled.

  7. Let p be a new promise.

  8. Let configMapping be global’s associated Document's node navigable's traversable navigable's fenced frame config mapping.

  9. Let pendingConfig be the result of constructing a pending fenced frame config with auctionConfig.

  10. Let urn be the result of running store a pending config on configMapping with pendingConfig.

  11. If urn is failure, then resolve p with null and return p.

  12. Let bidIgs be a new list of interest groups.

  13. If config["signal"] exists, then:

    1. Let signal be config["signal"].

    2. If signal is aborted, then reject p with signal’s abort reason and return p.

    3. Otherwise add the following abort steps to signal:

      1. Set auctionConfig’s aborted to true.

      2. Set auctionConfig’s abort reason to signal’s abort reason.

      3. For each auction in auctionConfig’s component auctions:

        1. Set auction’s aborted to true.

  14. Assert that settings’s origin is not an opaque origin and its scheme is "https".

  15. Let queue be the result of starting a new parallel queue.

  16. Enqueue the following steps to queue:

    1. Let bidDebugReportInfoList be a new list of bid debug reporting info.

    2. If auctionConfig’s server response is not null:

      1. Let winnerInfo be the result of running parse and validate server response with auctionConfig, null, global, bidIgs, and bidDebugReportInfoList.

    3. Otherwise:

      1. Let realTimeContributionsMap be a new real time reporting contributions map.

      2. Let winnerInfo be the result of running generate and score bids with auctionConfig, null, global, bidIgs, bidDebugReportInfoList, and realTimeContributionsMap.

    4. Let auctionReportInfo be a new auction report info.

    5. If winnerInfo is not failure, then:

      1. Set auctionReportInfo to the result of running collect forDebuggingOnly reports with bidDebugReportInfoList, auctionConfig’s seller, and winnerInfo.

      2. Set auctionReportInfo’s real time reporting contributions map to realTimeContributionsMap.

    6. If auctionConfig’s aborted is true:

      1. Queue a global task on the DOM manipulation task source, given global, to reject p with auctionConfig’s abort reason.

    7. Otherwise if winnerInfo is failure, then queue a global task on DOM manipulation task source, given global, to reject p with a "TypeError".

    8. Otherwise if winnerInfo is null or winnerInfo’s leading bid is null:

      1. Queue a global task on DOM manipulation task source, given global, to resolve p with null.

      2. For each reportUrl of auctionReportInfo’s debug loss report urls:

        1. Send report with reportUrl and settings.

      3. Send real time reports with auctionReportInfo’s real time reporting contributions map and settings.

    9. Otherwise:

      1. Let winner be winnerInfo’s leading bid.

      2. Let fencedFrameConfig be the result of filling in a pending fenced frame config with pendingConfig, auctionConfig, winnerInfo, auctionReportInfo, and settings.

      3. Finalize a pending config on configMapping with urn and fencedFrameConfig.

      4. Wait until auctionConfig’s resolve to config is a boolean.

      5. Let result be fencedFrameConfig.

      6. If auctionConfig’s resolve to config is false, then set result to urn.

      7. Queue a global task on the DOM manipulation task source, given global, to resolve p with result.

      8. Run update previous wins with winner.

    10. Run interest group update with auctionConfig’s interest group buyers and settings’s policy container.

    11. Run update bid counts with bidIgs.

  17. Return p.

To construct a pending fenced frame config given an auction config config:

  1. Return a fenced frame config with the following items:

    mapped url

    a struct with the following items:

    value

    "about:blank"

    visibility

    "opaque"

    container size

    the result of

    converting config’s requested size to pixel values (WICG/turtledove#986)

    content size

    null

    interest group descriptor

    a struct with the following items:

    value

    a struct with the following items:

    owner

    ""

    name

    ""

    visibility

    "opaque"

    on navigate callback

    null

    effective sandboxing flags

    a struct with the following items:

    value

    TODO: fill this in once fenced frame sandbox flags are more fully specified

    visibility

    "opaque"

    effective enabled permissions

    a struct with the following items:

    value

    «"attribution-reporting", "private-aggregation", "shared-storage", "shared-storage-select-url

    visibility

    "opaque"

    fenced frame reporting metadata

    null

    exfiltration budget metadata

    null

    nested configs

    a struct with the following items:

    value

    an empty list «»

    visibility

    "opaque"

    embedder shared storage context

    null

To fill in a pending fenced frame config given a fenced frame config pendingConfig, auction config auctionConfig, leading bid info winningBidInfo, auction report info auctionReportInfo, and an environment settings object settings:

  1. Let winningBid be winningBidInfo’s leading bid.

  2. Let replacements be an ordered map whose keys are strings and whose values are strings.

  3. For each replacement in auctionConfig’s deprecated render url replacements:

    1. Let k be replacement’s match.

    2. Let v be replacement’s replacement.

    3. Set replacements[k] to v.

  4. Let replacedAdURL be the result of running substitute macros with replacements and winningBid’s ad descriptor's url.

  5. Set pendingConfig’s mapped url's value to replacedAdURL.

  6. Let adSize be winningBid’s ad descriptor's size.

  7. Let concreteAdSize be the result of

    resolving screen-relative sizes to pixel values for adSize. (WICG/turtledove#986)

  8. Let adWidthString and adHeightString be the result of

    converting concreteAdSize to strings. (WICG/turtledove#986)

  9. Let sizeMacroMap be the ordered map «[ "{%AD_WIDTH%}" → adWidthString, "{%AD_HEIGHT%}" → adHeightString, "${AD_WIDTH}" → adWidthString, "${AD_HEIGHT}" → adHeightString

  10. Set pendingConfig’s mapped url's value to the result of substituting macros with sizeMacroMap into winningBid’s ad descriptor's url.

  11. If adSize is not null:

    1. Set pendingConfig’s content size to a struct with the following items:

      value

      concreteAdSize.

      Cast concreteAdSize into the format that is eventually chosen for value. (WICG/turtledove#986)

      visibility

      "opaque"

  12. Set pendingConfig’s interest group descriptor's value to a struct with the following items:

    owner

    winningBid’s interest group's owner

    name

    winningBid’s interest group's name

  13. Let fencedFrameReportingMap be the map «[ "buyer" → «», "seller" → «» ]».

  14. If auctionConfig’s component auctions is empty, then set fencedFrameReportingMap["component-seller"] to an empty list «».

  15. Set pendingConfig’s fenced frame reporting metadata to a struct with the following items:

    value

    A struct with the following items:

    fenced frame reporting map

    fencedFrameReportingMap

    direct seller is seller

    true if auctionConfig’s component auctions is empty, false otherwise

    allowed reporting origins

    winningBid’s bid ad's allowed reporting origins

    visibility

    "opaque"

  16. Set pendingConfig’s on navigate callback to an algorithm with these steps:

    1. Asynchronously finish reporting with pendingConfig’s fenced frame reporting metadata's value's fenced frame reporting map, winningBidInfo, auctionReportInfo and settings.

  17. Let adComponentDescriptorsWithReplacements be a new list of ad descriptors.

  18. If winningBid’s ad component descriptors is not null:

    1. For each adComponentDescriptor of winningBid’s ad component descriptors:

      1. Let descriptorWithReplacement be a new ad descriptor.

      2. Set descriptorWithReplacement’s size to adComponentDescriptor’s size.

      3. Set descriptorWithReplacement’s url to the result of running substitute macros with replacements and adComponentDescriptor’s url.

      4. Append descriptorWithReplacement to adComponentDescriptorsWithReplacements.

  19. Set pendingConfig’s nested configs's value to the result of running create nested configs with adComponentDescriptorsWithReplacements.

  20. Return pendingConfig.

To asynchronously finish reporting given a fenced frame reporting map reportingMap, leading bid info leadingBidInfo, auction report info auctionReportInfo, and an environment settings object settings:

  1. Increment a winning bid’s k-anonymity count given leadingBidInfo’s leading bid.

  2. If leadingBidInfo’s leading non-k-anon-enforced bid is not null, and leadingBidInfo’s leading non-k-anon-enforced bid's idleadingBidInfo’s leading bid's id, increment a winning bid’s k-anonymity count given leadingBidInfo’s leading non-k-anon-enforced bid.

  3. Let buyerDone, sellerDone, and componentSellerDone be booleans, initially false.

  4. If leadingBidInfo’s component seller is null, set componentSellerDone to true.

  5. While:

    1. If buyerDone, sellerDone, and componentSellerDone are all true, then break.

    2. Wait until one of the following fields of leadingBidInfo being not null:

    3. If buyerDone is false and leadingBidInfo’s buyer reporting result is not null:

      1. Let buyerMap be leadingBidInfo’s buyer reporting result's reporting beacon map.

      2. If buyerMap is null, set buyerMap to an empty map «[]».

      3. Let macroMap be leadingBidInfo’s buyer reporting result's reporting macro map.

      4. Finalize a reporting destination with reportingMap, buyer, buyerMap, and macroMap.

      5. Send report with leadingBidInfo’s buyer reporting result's report url and settings.

      6. Set buyerDone to true.

    4. If sellerDone is false and leadingBidInfo’s seller reporting result is not null:

      1. Let sellerMap be leadingBidInfo’s seller reporting result's reporting beacon map.

      2. If sellerMap is null, set sellerMap to an empty map «[]».

      3. Finalize a reporting destination with reportingMap, seller, and sellerMap.

      4. Send report with leadingBidInfo’s seller reporting result's report url and settings.

      5. Set sellerDone to true.

    5. If componentSellerDone is false and leadingBidInfo’s component seller reporting result is not null:

      1. Let componentSellerMap be leadingBidInfo’s component seller reporting result's reporting beacon map.

      2. If componentSellerMap is null, set componentSellerMap to an empty map «[]».

      3. Finalize a reporting destination with reportingMap, component-seller, and componentSellerMap.

      4. Send report with leadingBidInfo’s component seller reporting result's report url and settings.

      5. Set componentSellerDone to true.

  6. For each reportUrl of auctionReportInfo’s debug win report urls:

    1. Send report with report and settings.

  7. For each reportUrl of auctionReportInfo’s debug loss report urls:

    1. Send report with report and settings.

  8. Send real time reports with auctionReportInfo’s real time reporting contributions map and settings.

To create nested configs given ad component descriptors adComponentDescriptors:

  1. Let nestedConfigs be an empty list «».

  2. If adComponentDescriptors is null:

    1. Return nestedConfigs.

  3. For each adComponentDescriptor of adComponentDescriptors:

    1. Let fencedFrameConfig be a fenced frame config with the following items:

      mapped url

      a struct with the following items:

      value

      adComponentDescriptor’s url

      visibility

      "opaque"

      container size

      null

      content size

      If adComponentDescriptor’s size is null, then null. Otherwise, a struct with the following items:

      value

      adComponentDescriptor’s size TODO: Resolve screen-relative sizes and macros and cast this properly.

      visibility

      "opaque"

      interest group descriptor

      null

      on navigate callback

      null

      effective sandboxing flags

      a struct with the following items:

      value

      TODO: fill this in once fenced frame sandbox flags are more fully specified

      visibility

      "opaque"

      effective enabled permissions

      a struct with the following items:

      value

      «"attribution-reporting", "private-aggregation", "shared-storage", "shared-storage-select-url

      visibility

      "opaque"

      fenced frame reporting metadata

      null

      exfiltration budget metadata

      null

      nested configs

      a struct with the following items:

      value

      an empty list «»

      visibility

      "opaque"

      embedder shared storage context

      null

    2. Append fencedFrameConfig to nestedConfigs.

  4. Return nestedConfigs.

To validate and convert auction ad config given an AuctionAdConfig config and a boolean isTopLevel:

  1. Assert that these steps are not running in parallel.

  2. Let auctionConfig be a new auction config.

  3. Let seller be the result of parsing an https origin with config["seller"].

  4. If seller is failure, then return failure.

  5. Set auctionConfig’s seller to seller.

  6. If config["serverResponse"] exists:

    1. If config["requestId"] does not exist, then throw a TypeError.

    2. Let resolvedAndTypeChecked be the promise representing performing the following steps upon fulfillment of config["serverResponse"] with result:

      1. Set auctionConfig’s server response to the transferred result.

      2. Set auctionConfig’s server response id to config["requestId"].

    3. Upon rejection of resolvedAndTypeChecked:

      1. Set auctionConfig’s server response to failure.

    Note: The JavaScript code calling runAdAuction() is responsible for not resolving config["serverResponse"] until server responses have been retrieved from one or more `Ad-Auction-Result` headers, as resolving this Promise early would cause a race condition in which the script runners might run without the server responses that it needs. There are two ways that server responses can be retrieved.
    (1) The JavaScript code issues a request whose initiator type is "fetch" and whose adAuctionHeaders option is set to true. The JavaScript code has to resolve config["serverResponse"] after the corresponding call to fetch() has resolved to a response. The JavaScript code can also choose to wait to call runAdAuction() until after the corresponding call to fetch() has resolved to a response, and can then immediately resolve config["serverResponse"];
    (2) issue an iframe navigation request with the adauctionheaders content attribute set to true. In this case, the JavaScript code is retrieved as part of the iframe navigation response, at which point the JavaScript code in the iframe makes the call to runAdAuction(), and config["serverResponse"] can be specified directly without a Promise.

  7. Otherwise:

    1. If config["requestId"] exists, then throw a TypeError.

    2. If config["decisionLogicURL"] does not exist, then throw a TypeError.

  8. If config["decisionLogicURL"] exists:

    1. Let decisionLogicURL be the result of running the URL parser on config["decisionLogicURL"].

    2. If decisionLogicURL is failure, or it is not same origin with auctionConfig’s seller, then return failure.

    3. Set auctionConfig’s decision logic url to decisionLogicURL.

  9. If config["trustedScoringSignalsURL"] exists:

    1. Let trustedScoringSignalsURL be the result of parse and verify a trusted signals URL on config["trustedScoringSignalsURL"].

    2. If trustedScoringSignalsURL is failure, return failure.

    3. Set auctionConfig’s trusted scoring signals url to trustedScoringSignalsURL.

  10. If config["maxTrustedScoringSignalsURLLength"] exists:

    1. If config["maxTrustedScoringSignalsURLLength"] ≥ 0, set auctionConfig’s max trusted scoring signals url length to config["maxTrustedScoringSignalsURLLength"], otherwise throw a TypeError.

  11. If config["interestGroupBuyers"] exists:

    1. Let buyers be a new empty list.

    2. For each buyerString in config["interestGroupBuyers"]:

      1. Let buyer be the result of parsing an https origin with buyerString.

      2. If buyer is failure, then return failure.

      3. Append buyer to buyers.

    3. Set auctionConfig’s interest group buyers to buyers.

  12. If config["auctionSignals"] exists:

    1. Set auctionConfig’s auction signals to config["auctionSignals"].

    2. Handle an input promise in configuration given auctionConfig and auctionConfig’s auction signals:

  13. If config["requestedSize"] exists:

    1. Let adSize be the result from running parse an AdRender ad size with config["requestedSize"]

    2. If adSize is null, throw a TypeError.

    3. Set auctionConfig’s requested size to adSize.

  14. If config["allSlotsRequestedSizes"] exists:

    1. If config["allSlotsRequestedSizes"] is empty, throw a TypeError.

    2. Set auctionConfig’s all slots requested sizes to a new set.

    3. For each adSizeValue in config["allSlotsRequestedSizes"]:

      1. Let adSize be the result of running parse an AdRender ad size with adSizeValue.

      2. If adSize is null, throw a TypeError.

      3. If auctionConfig’s all slots requested sizes contains adSize, throw a TypeError.

      4. Append adSize to auctionConfig’s all slots requested sizes.

    4. If auctionConfig’s requested size is not null, and auctionConfig’s all slots requested sizes does not contain auctionConfig’s requested size, throw a TypeError.

  15. If config["sellerSignals"] exists:

    1. Set auctionConfig’s seller signals to config["sellerSignals"].

    2. Handle an input promise in configuration given auctionConfig and auctionConfig’s seller signals:

  16. If config["auctionNonce"] exists, then set auctionConfig’s auction nonce to the result of running get uuid from string with config["auctionNonce"].

  17. If config["additionalBids"] exists:

    1. Throw a TypeError if any of the following conditions hold:

    2. Set auctionConfig’s expects additional bids to true.

    3. Handle an input promise in configuration given auctionConfig and config["additionalBids"]:

      Note: The JavaScript code calling runAdAuction() is responsible for not resolving config["additionalBids"] until additional bids have been retrieved from one or more `Ad-Auction-Additional-Bid` headers, as resolving this Promise early would cause a race condition in which additional bids might not be included in the auction. There are two ways that additional bids can be retrieved.
      (1) The JavaScript code issues a request whose initiator type is "fetch" and whose adAuctionHeaders option is set to true. The JavaScript code has to resolve config["additionalBids"] after the corresponding call to fetch() has resolved to a response. The JavaScript code can also choose to wait to call runAdAuction() until after the corresponding call to fetch() has resolved to a response, and can then immediately resolve config["additionalBids"];
      (2) issue an iframe navigation request with the adauctionheaders content attribute set to true. In this case, the JavaScript code is retrieved as part of the iframe navigation response, at which point the JavaScript code in the iframe makes the call to runAdAuction(), and config["additionalBids"] can be immediately resolved.

  18. If config["directFromSellerSignalsHeaderAdSlot"] exists:

    1. Handle an input promise in configuration given auctionConfig and config["directFromSellerSignalsHeaderAdSlot"]:

      Note: The JavaScript code calling runAdAuction() is responsible for not resolving config["directFromSellerSignalsHeaderAdSlot"] until direct from seller signals have been retrieved from one or more `Ad-Auction-Signals` headers, as resolving this Promise early would cause a race condition in which the script runners might run without the direct from seller signals that it needs. There are two ways that direct from seller signals can be retrieved.
      (1) The JavaScript code issues a request whose initiator type is "fetch" and whose adAuctionHeaders option is set to true. The JavaScript code has to resolve config["directFromSellerSignalsHeaderAdSlot"] after the corresponding call to fetch() has resolved to a response. The JavaScript code can also choose to wait to call runAdAuction() until after the corresponding call to fetch() has resolved to a response, and can then immediately resolve config["directFromSellerSignalsHeaderAdSlot"];
      (2) issue an iframe navigation request with the adauctionheaders content attribute set to true. In this case, the JavaScript code is retrieved as part of the iframe navigation response, at which point the JavaScript code in the iframe makes the call to runAdAuction(), and config["directFromSellerSignalsHeaderAdSlot"] can be specified directly without a Promise.

  19. If config["deprecatedRenderURLReplacements"] exists:

    1. Set auctionConfig’s deprecated render url replacements to config["deprecatedRenderURLReplacements"].

    2. Handle an input promise in configuration given auctionConfig and auctionConfig’s deprecated render url replacements:

  20. If config["sellerTimeout"] exists, set auctionConfig’s seller timeout to config["sellerTimeout"] in milliseconds or 500, whichever is smaller.

  21. Let reportingTimeout be config["reportingTimeout"] if it exists, 50 otherwise.

  22. Set config["reportingTimeout"] to reportingTimeout in milliseconds or 5000, whichever is smaller.

  23. If config["sellerExperimentGroupId"] exists, then set auctionConfig’s seller experiment group id to config["sellerExperimentGroupId"].

  24. If config["sellerCurrency"] exists:

    1. If the result of checking whether a string is a valid currency tag on config["sellerCurrency"] is false, then return failure.

    2. Set auctionConfig’s seller currency to config["sellerCurrency"].

  25. If config["perBuyerSignals"] exists:

    1. Set auctionConfig’s per buyer signals to config["perBuyerSignals"].

    2. Handle an input promise in configuration given auctionConfig and auctionConfig’s per buyer signals:

  26. For each idlTimeoutMember, perBuyerTimeoutField, allBuyersTimeoutField in the following table

    IDL timeout member Per buyer timeout field All buyers timeout field
    "perBuyerTimeouts" per buyer timeouts all buyers timeout
    "perBuyerCumulativeTimeouts" per buyer cumulative timeouts all buyers cumulative timeout
    1. If config contains idlTimeoutMember:

      1. Set auctionConfig’s perBuyerTimeoutField to config[idlTimeoutMember].

      2. Handle an input promise in configuration given auctionConfig and auctionConfig’s perBuyerTimeoutField:

        • To parse the value result:

          1. If result is null:

            1. Set auctionConfig’s perBuyerTimeoutField to null.

          2. Otherwise:

            1. Set auctionConfig’s perBuyerTimeoutField to a new ordered map whose keys are origins and whose values are durations in milliseconds.

            2. For each keyvalue of result:

              1. If perBuyerTimeoutField is "perBuyerTimeouts", and value > 500, then set value to 500.

              2. If key is "*", then set auctionConfig’s allBuyersTimeoutField to value in milliseconds, and continue.

              3. Let buyer be the result of parsing an https origin with key. If buyer is failure, throw a TypeError.

              4. Set auctionConfig’s perBuyerTimeoutField[buyer] to value in milliseconds.

        • To handle an error, set auctionConfig’s perBuyerTimeoutField to failure.

  27. If config["perBuyerGroupLimits"] exists, for each keyvalue of config["perBuyerGroupLimits"]:

    1. If value is 0, then return failure.

    2. If key is "*", then set auctionConfig’s all buyers group limit to value, and continue.

    3. Let buyer be the result of parsing an https origin with key.

    4. If buyer is failure, then return failure.

    5. Set auctionConfig’s per buyer group limits[buyer] to value.

  28. If config["perBuyerExperimentGroupIds"] exists, for each keyvalue of config["perBuyerExperimentGroupIds"]:

    1. If key is "*", then set auctionConfig’s all buyer experiment group id to value, and continue.

    2. Let buyer the result of parsing an https origin with key.

    3. If buyer is failure, then return failure.

    4. Set auctionConfig’s per buyer experiment group ids[buyer] to value.

  29. If config["perBuyerMultiBidLimits"] exists, for each keyvalue of config["perBuyerMultiBidLimits"]:

    1. If key is "*", then set auctionConfig’s all buyers multi-bid limit to value, and continue.

    2. Let buyer the result of parsing an https origin with key.

    3. If buyer is failure, then return failure.

    4. Set auctionConfig’s per buyer multi-bid limits[buyer] to value.

  30. If config["perBuyerPrioritySignals"] exists, for each keyvalue of config["perBuyerPrioritySignals"]:

    1. Let signals be an ordered map whose keys are strings and whose values are double.

    2. for each kv of value:

      1. If k starts with "browserSignals.", throw a TypeError.

      2. Set signals[k] to v.

    3. If key is "*", then set auctionConfig’s all buyers priority signals to value, and continue.

    4. Let buyer be the result of parsing an https origin with key.

    5. If buyer is failure, then return failure.

    6. Set auctionConfig’s per buyer priority signals[buyer] to signals.

  31. If config["requiredSellerCapabilities"] exists:

    1. Let sellerCapabilities be a new set of seller capabilities.

    2. For each capabilityString of config["requiredSellerCapabilities"]:

      1. If capabilityString is "interest-group-counts" or "latency-stats", then append capabilityString to sellerCapabilities.

        Note: For forward compatibility with new values, don’t throw.

    3. Set auctionConfig’s required seller capabilities to sellerCapabilities.

  32. If config["perBuyerCurrencies"] exists:

    1. Set auctionConfig’s per buyer currencies to config["perBuyerCurrencies"]

    2. Handle an input promise in configuration given auctionConfig and auctionConfig’s per buyer currencies:

  33. If config["sellerRealTimeReportingConfig"] exists:

    1. If config["sellerRealTimeReportingConfig"]["type"] is "default-local-reporting", then set auctionConfig’s seller real time reporting config to "default-local-reporting".

  34. If config["perBuyerRealTimeReportingConfig"] exists, For each keyvalue of config["perBuyerRealTimeReportingConfig"]:

    1. Let buyer the result of parsing an https origin with key.

    2. If buyer is failure, then return failure.

    3. If value["type"] is "default-local-reporting", then set auctionConfig’s per buyer real time reporting config[buyer] to "default-local-reporting".

  35. If config["componentAuctions"] is not empty:

    1. If config["interestGroupBuyers"] exists and is not empty, then return failure.

    2. If config["deprecatedRenderURLReplacements"] exists, then throw a TypeError.

  36. For each component in config["componentAuctions"]:

    1. If isTopLevel is false, then return failure.

    2. Let componentAuction be the result of running validate and convert auction ad config with component and false.

    3. If componentAuction is failure, then return failure.

    4. Append componentAuction to auctionConfig’s component auctions.

  37. Set auctionConfig’s config idl to config.

  38. If config["resolveToConfig"] exists:

    1. Let auctionConfig’s resolve to config be config["resolveToConfig"].

    2. TODO: What should happen if this rejects?

    3. Upon fulfillment of auctionConfig’s resolve to config with resolveToConfig, set auctionConfig’s resolve to config to resolveToConfig.

  39. Return auctionConfig.

To validate a url macro given a string macro:

  1. Return true if any of the following conditions hold:

  2. Otherwise, return false.

To update bid count given a list of interest groups igs:

  1. For each ig in igs:

    1. Let loadedIg be the interest group from the user agent's interest group set whose owner is ig’s owner and whose name is ig’s name, continue if none found.

    2. If the most recent entry in loadedIg’s bid counts corresponds to the current day in UTC, increment its count. If not, insert a new tuple of the time set to the current UTC day and a count of 1.

    3. Replace the interest group that has loadedIg’s owner and name in the user agent's interest group set with loadedIg.

To update previous wins given a generated bid bid:

  1. Let ig be bid’s interest group.

  2. Let loadedIg be the interest group from the user agent's interest group set whose owner is ig’s owner and whose name is ig’s name, return if none found.

  3. Let win be a new previous win.

  4. Set win’s time to the current wall time.

  5. Let ad be a new interest group ad with the following items:

    render url

    bid’s bid ad's render url

    metadata

    bid’s bid ad's metadata

    ad render ID

    bid’s bid ad's ad render ID

  6. Set win’s ad to ad.

  7. Append win to loadedIg’s previous wins.

  8. Replace the interest group that has loadedIg’s owner and name in the user agent's interest group set with loadedIg.

To build bid generators map given an auction config auctionConfig:

  1. Let bidGenerators be a new ordered map whose keys are origins and whose values are per buyer bid generators.

  2. Let negativeTargetInfo be a new negative target info.

  3. For each buyer in auctionConfig’s interest group buyers:

    1. For each ig of the user agent's interest group set whose owner is buyer:

      1. Let igName be ig’s name.

      2. If ig’s additional bid key is not null:

        1. Set negativeTargetInfo[(buyer, igName)] to (ig’s joining origin, ig’s additional bid key).

      3. Continue if any of the following conditions hold:

      4. Let signalsUrl be ig’s trusted bidding signals url.

      5. Let slotSizeQueryParam be the result of calculating the ad slot size query param given ig and auctionConfig.

      6. Let joiningOrigin be ig’s joining origin.

      7. If bidGenerators does not contain buyer:

        1. Let perBuyerGenerator be a new per buyer bid generator.

        2. Let perSignalsUrlGenerator be a new per signals url bid generator.

        3. Set perSignalsUrlGenerator[joiningOrigin] to « ig ».

        4. Set perSlotSizeQueryParam[slotSizeQueryParam] to perSignalsUrlGenerator.

        5. Set perBuyerGenerator[signalsUrl] to perSlotSizeQueryParam.

        6. Set bidGenerators[buyer] to perBuyerGenerator.

        7. TODO: add a perBiddingScriptUrlGenerator layer that replaces the list of IGs with a map from biddingScriptUrl to a list of IGs.

      8. Otherwise:

        1. Let perBuyerGenerator be bidGenerators[buyer].

        2. If perBuyerGenerator does not contain signalsUrl:

          1. Let perSignalsUrlGenerator be a new per signals url bid generator.

          2. Set perSignalsUrlGenerator[joiningOrigin] to « ig ».

          3. Set perSlotSizeQueryParam[slotSizeQueryParam] to perSignalsUrlGenerator.

          4. Set perBuyerGenerator[signalsUrl] to perSlotSizeQueryParam.

        3. Otherwise:

          1. Let perSlotSizeQueryParam be perBuyerGenerator[signalsUrl].

          2. If perSlotSizeQueryParam does not contain slotSizeQueryParam:

            1. Let perSignalsUrlGenerator be a new per signals url bid generator.

            2. Set perSignalsUrlGenerator[joiningOrigin] to « ig ».

            3. Set perSlotSizeQueryParam[slotSizeQueryParam] to perSignalsUrlGenerator.

          3. Otherwise:

            1. Let perSignalsUrlGenerator be perSlotSizeQueryParam[slotSizeQueryParam].

            2. If perSignalsUrlGenerator does not contain joiningOrigin, then set perSignalsUrlGenerator[joiningOrigin] to « ig ».

            3. Otherwise, append ig to perSignalsUrlGenerator[joiningOrigin].

  4. Return « bidGenerators, negativeTargetInfo ».

To check if required seller capabilities are permitted given an auction config auctionConfig and an interest group ig:

  1. Let seller be auctionConfig’s seller.

  2. Let requiredSellerCapabilities be auctionConfig’s required seller capabilities.

  3. Return true if all of the following conditions hold:

  4. Return false.

To generate potentially multiple bids given an ordered map-or-null allTrustedBiddingSignals, and an origin-or-null crossOriginTrustedBiddingSignalsOrigin, a string auctionSignals, a BiddingBrowserSignals browserSignals, a string-or-null perBuyerSignals, a DirectFromSellerSignalsForBuyer directFromSellerSignalsForBuyer, a duration perBuyerTimeout in milliseconds, a currency tag expectedCurrency, an unsigned short multiBidLimit, an interest group ig, and a 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).

  1. Let igGenerateBid be the result of building an interest group passed to generateBid with ig.

  2. Set browserSignals["joinCount"] to the sum of ig’s join counts for all days within the last 30 days.

  3. Set browserSignals["recency"] to the current wall time minus ig’s join time, in milliseconds.

  4. Set browserSignals["bidCount"] to the sum of ig’s bid counts for all days within the last 30 days.

  5. Set browserSignals["adComponentsLimit"] to 40.

  6. Set browserSignals["multiBidLimit"] to multiBidLimit.

  7. Let prevWins be a new sequence<PreviousWin>.

  8. For each prevWin of ig’s previous wins for all days within the the last 30 days:

    1. Let timeDelta be auctionStartTime minus prevWin’s time.

    2. Set timeDelta to 0 if timeDelta is negative, timeDelta’s nearest second (rounding down) otherwise.

    3. Let prevWinAdIDL be a new AuctionAd with the following items:

      renderURL

      the serialization of prevWin’s render url

      metadata

      prevWin’s metadata

      adRenderId

      prevWin’s ad render ID

    4. Let prevWinElement be the sequence<PreviousWinElement> «timeDelta, prevWinAdIDL».

    5. Append prevWinElement to prevWins.

  9. Set browserSignals["prevWinsMs"] to prevWins.

  10. Let biddingScriptFetcher be the result of creating a new script fetcher with ig’s bidding url, and settings.

  11. Let biddingScript be the result of waiting for script body from a fetcher given biddingScriptFetcher.

  12. If biddingScript is failure, return failure.

  13. If ig’s bidding wasm helper url is not null:

    1. Let wasmModuleObject be the result of fetching WebAssembly with ig’s bidding wasm helper url and settings.

    2. If wasmModuleObject is not failure, then set browserSignals["wasmHelper"] to wasmModuleObject.

    3. Otherwise, return failure.

  14. Let trustedBiddingSignals be null.

  15. If allTrustedBiddingSignals is not null:

    1. Assert that ig’s trusted bidding signals keys is not null.

    2. Set trustedBiddingSignals to an ordered map whose keys are strings and whose values are any.

    3. For each key of ig’s trusted bidding signals keys:

      1. If allTrustedBiddingSignals[key] exists, then set trustedBiddingSignals[key] to allTrustedBiddingSignals[key].

  16. Let sameOriginTrustedBiddingSignals be null.

  17. Let crossOriginTrustedBiddingSignals be null.

  18. If trustedBiddingSignals is not null:

    1. If crossOriginTrustedBiddingSignalsOrigin is null, then set sameOriginTrustedBiddingSignals to trustedBiddingSignals.

    2. Otherwise:

      1. Set crossOriginTrustedBiddingSignals to a new ordered map whose keys are strings and whose values are any.

      2. Let originKey be the serialization of crossOriginTrustedBiddingSignalsOrigin.

      3. Set crossOriginTrustedBiddingSignalsOrigin[originKey] to trustedBiddingSignals.

  19. Return the result of evaluating a bidding script with biddingScript, multiBidLimit, ig, expectedCurrency, igGenerateBid, auctionSignals, perBuyerSignals, sameOriginTrustedBiddingSignals, crossOriginTrustedBiddingSignals, browserSignals, directFromSellerSignalsForBuyer. and perBuyerTimeout.

To generate and score bids given an auction config auctionConfig, an auction config-or-null topLevelAuctionConfig, a global object global, a list of interest groups bidIgs, a list of bid debug reporting info bidDebugReportInfoList, and a real time reporting contributions map realTimeContributionsMap:

  1. Assert that these steps are running in parallel.

  2. Let settings be global’s relevant settings object.

  3. Let topLevelOrigin be settings’s top-level origin.

  4. Let seller be auctionConfig’s seller.

  5. Let auctionStartTime be the current wall time.

  6. Let decisionLogicFetcher be the result of creating a new script fetcher with auctionConfig’s decision logic url and settings.

  7. Let trustedScoringSignalsBatcher be the result of creating a trusted scoring signals batcher with auctionConfig’s max trusted scoring signals url length.

  8. Let « bidGenerators, negativeTargetInfo » be the result of running build bid generators map with auctionConfig.

  9. If auctionConfig’s aborted is true, return failure.

  10. Let leadingBidInfo be a new leading bid info.

  11. Let queue be the result of starting a new parallel queue.

  12. Let capturedAuctionHeaders be global’s associated Document’s node navigable’s traversable navigable’s captured ad auction signals headers.

  13. If auctionConfig’s component auctions are not empty:

    1. Assert topLevelAuctionConfig is null.

    2. Let pendingComponentAuctions be auctionConfig’s component auctions's size.

    3. Let topLevelDirectFromSellerSignalsForSeller be null.

    4. Let topLevelDirectFromSellerSignalsRetrieved be false.

    5. For each component in auctionConfig’s component auctions, enqueue the following steps to queue:

      1. If component’s server response is not null:

        1. Let compWinnerInfo be the result of running parse and validate server response with component, auctionConfig, global, bidIgs, and bidDebugReportInfoList.

      2. Otherwise:

        1. Let compWinnerInfo be the result of running generate and score bids with component, auctionConfig, global, bidIgs, bidDebugReportInfoList, and realTimeContributionsMap.

      3. If recursively wait until configuration input promises resolve given auctionConfig does not return failure, and compWinnerInfo is not failure, then:

        1. If topLevelDirectFromSellerSignalsRetrieved is false:

          1. Let topLevelDirectFromSellerSignals be the result of running get direct from seller signals given seller, auctionConfig’s direct from seller signals header ad slot, and capturedAuctionHeaders.

          2. Set topLevelDirectFromSellerSignalsForSeller to the result of running get direct from seller signals for a seller given topLevelDirectFromSellerSignals.

          3. Set topLevelDirectFromSellerSignalsRetrieved to true.

        2. If compWinnerInfo’s leading bid is not null, then run score and rank a bid with auctionConfig, compWinnerInfo’s leading bid, leadingBidInfo, decisionLogicFetcher, trustedScoringSignalsBatcher, null, "top-level-auction", null, and topLevelOrigin.

        3. If compWinnerInfo’s leading non-k-anon-enforced bid is not null, then run score and rank a bid with auctionConfig, compWinnerInfo’s leading non-k-anon-enforced bid, leadingBidInfo, decisionLogicFetcher, trustedScoringSignalsBatcher, topLevelDirectFromSellerSignalsForSeller, null, "top-level-auction", null, topLevelOrigin, and realTimeContributionsMap.

      4. Decrement pendingComponentAuctions by 1.

    6. Wait until pendingComponentAuctions is 0.

    7. If auctionConfig’s aborted is true, return failure.

    8. If leadingBidInfo’s leading bid is null, return null.

    9. Let winningComponentConfig be leadingBidInfo’s auction config.

    10. Set leadingBidInfo’s auction config to auctionConfig.

    11. Set leadingBidInfo’s component seller to winningComponentConfig’s seller.

    12. Let « topLevelSellerSignals, unusedTopLevelReportResultBrowserSignals » be the result of running report result with leadingBidInfo, topLevelDirectFromSellerSignalsForSeller, winningComponentConfig, and global.

    13. Set leadingBidInfo’s auction config to winningComponentConfig.

    14. Set leadingBidInfo’s component seller to null.

    15. Set leadingBidInfo’s top level seller to seller.

    16. Set leadingBidInfo’s top level seller signals to topLevelSellerSignals.

    17. Let directFromSellerSignals be the result of running get direct from seller signals given winningComponentConfig’s seller, winningComponentConfig’s direct from seller signals header ad slot, and capturedAuctionHeaders.

    18. Let directFromSellerSignalsForSeller be the result of running get direct from seller signals for a seller given directFromSellerSignals.

    19. Let directFromSellerSignalsForBuyer be the result of running get direct from seller signals for a buyer with directFromSellerSignals, and leadingBidInfo’s leading bid's interest group's owner.

    20. Let « sellerSignals, reportResultBrowserSignals » be the result of running report result with leadingBidInfo, directFromSellerSignalsForSeller, null, and global.

    21. Run report win with leadingBidInfo, sellerSignals, reportResultBrowserSignals, directFromSellerSignalsForBuyer, and settings.

    22. Return leadingBidInfo.

  14. If waiting until configuration input promises resolve given auctionConfig returns failure, then return failure.

  15. Let allBuyersExperimentGroupId be auctionConfig’s all buyer experiment group id.

  16. Let allBuyersGroupLimit be auctionConfig’s all buyers group limit.

  17. Let auctionSignals be auctionConfig’s auction signals.

  18. Let directFromSellerSignals be the result of running get direct from seller signals given seller, auctionConfig’s direct from seller signals header ad slot, and capturedAuctionHeaders.

  19. Let directFromSellerSignalsForSeller be the result of running get direct from seller signals for a seller given directFromSellerSignals.

  20. Let browserSignals be a BiddingBrowserSignals.

  21. Let topLevelHost be the result of running the host serializer on topLevelOrigin’s host.

  22. Set browserSignals["topWindowHostname"] to topLevelHost.

  23. Set browserSignals["seller"] to the serialization of seller.

  24. If auctionConfig’s requested size is not null, set browserSignals["requestedSize"] to the result of running convert an ad size to a map with auctionConfig’s requested size.

  25. Let auctionLevel be "single-level-auction".

  26. Let componentAuctionExpectedCurrency be null.

  27. If topLevelAuctionConfig is not null:

    1. Set browserSignals["topLevelSeller"]] to the serialization of topLevelAuctionConfig’s seller.

    2. Set auctionLevel to "component-auction".

    3. Set componentAuctionExpectedCurrency to the result of looking up per-buyer currency with topLevelAuctionConfig and seller.

  28. Let pendingBuyers be bidGenerators’s size.

  29. Let additionalBids be the result of running validate and convert additional bids with auctionConfig, topLevelAuctionConfig, negativeTargetInfo and global.

  30. Let pendingAdditionalBids be the size of additionalBids.

  31. For each additionalBid of additionalBids, run the following steps in parallel:

    1. Score and rank a bid with auctionConfig, additionalBid, leadingBidInfo, decisionLogicFetcher, trustedScoringSignalsBatcher, directFromSellerSignalsForSeller, null, auctionLevel, componentAuctionExpectedCurrency, topLevelOrigin, and realTimeContributionsMap.

    2. Decrement pendingAdditionalBids by 1.

  32. For each buyerperBuyerGenerator of bidGenerators, enqueue the following steps to queue:

    1. Let perBuyerCumulativeTimeout be auctionConfig’s all buyers cumulative timeout.

    2. If auctionConfig’s per buyer cumulative timeouts is not null and per buyer cumulative timeouts[buyer] exists, then set perBuyerCumulativeTimeout to auctionConfig’s per buyer cumulative timeouts[buyer].

    3. Let buyerExperimentGroupId be allBuyersExperimentGroupId.

    4. Let perBuyerExperimentGroupIds be auctionConfig’s per buyer experiment group ids.

    5. If perBuyerExperimentGroupIds is not null and perBuyerExperimentGroupIds[buyer] exists, then set buyerExperimentGroupId to perBuyerExperimentGroupIds[buyer].

    6. Apply interest groups limits to prioritized list:

      1. Let buyerGroupLimit be allBuyersGroupLimit.

      2. Let perBuyerGroupLimits be auctionConfig’s per buyer group limits.

      3. If perBuyerGroupLimits is not null and perBuyerGroupLimits[buyer] exists, then set buyerGroupLimit to perBuyerGroupLimits[buyer].

      4. Let igs be a new list of interest groups.

      5. For each signalsUrl → perSlotSizeQueryParam of perBuyerGenerator:

        1. For each slotSizeQueryParam → perSignalsUrlGenerator of perSlotSizeQueryParam:

          1. For each joiningOrigin → groups of perSignalsUrlGenerator:

            1. Extend igs with groups.

      6. Sort in descending order igs, with a being less than b if a’s priority is less than b’s priority.

      7. Remove the first buyerGroupLimit items from igs.

      8. For each signalsUrl → perSlotSizeQueryParam of perBuyerGenerator:

        1. For each slotSizeQueryParam → perSignalsUrlGenerator of perSlotSizeQueryParam:

          1. For each joiningOrigin → groups of perSignalsUrlGenerator:

            1. Remove from groups any interest group contained in igs.

    7. Let perBuyerSignals be null.

    8. If auctionConfig’s per buyer signals is not null and per buyer signals[buyer] exists, then set perBuyerSignals to auctionConfig’s per buyer signals[buyer].

    9. Let perBuyerTimeout be auctionConfig’s all buyers timeout.

    10. If auctionConfig’s per buyer timeouts is not null and per buyer timeouts[buyer] exists, then set perBuyerTimeout to auctionConfig’s per buyer timeouts[buyer].

    11. Let expectedCurrency be the result of looking up per-buyer currency with auctionConfig and buyer.

    12. Let multiBidLimit be the result of looking up per-buyer multi-bid limit with auctionConfig and buyer.

    13. Set browserSignals["forDebuggingOnlyInCooldownOrLockout"] to the result of running is debugging only in cooldown or lockout with buyer.

    14. Let optedInForRealTimeReporting be true if auctionConfig’s per buyer real time reporting config[buyer] is "default-local-reporting", false otherwise.

    15. For each slotSizeQueryParamperSlotSizeQueryParam of perBuyerGenerator:

      1. For each signalsUrlperSignalsUrlGenerator of perSlotSizeQueryParam:

        1. Let crossOriginTrustedBiddingSignalsOrigin be null.

        2. If buyer is not same origin with signalsUrl’s origin, then set crossOriginTrustedBiddingSignalsOrigin to signalsUrl’s origin.

        3. Let trustedBiddingSignalsBatcher be a new trusted bidding signals batcher.

        4. Let fetchSignalStartTime be settings’s current monotonic time.

        5. For each joiningOrigin → groups of perSignalsUrlGenerator:

          1. For each ig of groups:

            1. Batch or fetch trusted bidding signals given trustedBiddingSignalsBatcher, ig, signalsUrl, buyerExperimentGroupId, topLevelOrigin, slotSizeQueryParam, and settings’s policy container.

          2. Fetch the current outstanding trusted signals batch given trustedBiddingSignalsBatcher, signalsUrl, buyer, buyerExperimentGroupId, topLevelOrigin, and slotSizeQueryParam.

        6. Process updateIfOlderThanMs with buyer, and trustedBiddingSignalsBatcher’s all per interest group data.

        7. Let fetchSignalDuration be the duration from fetchSignalStartTime to settings’s current monotonic time, in milliseconds.

        8. If perBuyerCumulativeTimeout is not null, then decrement perBuyerCumulativeTimeout by fetchSignalDuration.

        9. For each joiningOrigin → groups of perSignalsUrlGenerator:

          1. For each ig of groups:

            1. If perBuyerCumulativeTimeout is negative, then break.

            2. If ig’s bidding url is null, continue.

            3. If perBuyerCumulativeTimeout is not null and is less than perBuyerTimeout, then set perBuyerTimeout to perBuyerCumulativeTimeout.

            4. Let generateBidStartTime be settings’s current monotonic time.

            5. Let directFromSellerSignalsForBuyer be the result of running get direct from seller signals for a buyer with directFromSellerSignals, and ig’s owner.

            6. Let dataVersion be null.

            7. Let allTrustedBiddingSignals be an ordered map-or-null, initially null.

            8. Let igName be ig’s name.

            9. If trustedBiddingSignalsBatcher’s no signals flags[igName] does not exist:

              1. Set dataVersion to trustedBiddingSignalsBatcher’s data versions[igName].

              2. Set allTrustedBiddingSignals to all trusted bidding signals.

            10. Otherwise if trustedBiddingSignalsBatcher’s no signals flags[igName] is "fetch-failed", and optedInForRealTimeReporting is true, then:

              1. Add a platform contribution with trusted bidding signals failure bucket, realTimeContributionsMap, and igName.

            11. Remove browserSignals["dataVersion"].

            12. Remove browserSignals["crossOriginDataVersion"].

            13. If dataVersion is not null:

              1. If crossOriginTrustedBiddingSignalsOrigin is not null, then set browserSignals["crossOriginDataVersion"] to dataVersion.

              2. Otherwise, set browserSignals["dataVersion"] to dataVersion.

            14. Let generateBidResult be the result of generate potentially multiple bids given allTrustedBiddingSignals, crossOriginTrustedBiddingSignalsOrigin, auctionSignals, a clone of browserSignals, perBuyerSignals, directFromSellerSignalsForBuyer, perBuyerTimeout, expectedCurrency, multiBidLimit, ig, auctionStartTime, and settings.

            15. If generateBidResult is failure, then:

              1. If optedInForRealTimeReporting is true, then add a platform contribution with bidding script failure bucket, realTimeContributionsMap and buyer.

              2. Continue.

            16. Let (bidsBatch, bidDebugReportInfo, realTimeContributions) be generateBidResult.

            17. Let generateBidDuration be the duration from generateBidStartTime to settings’s current monotonic time, in milliseconds.

            18. If perBuyerCumulativeTimeout is not null, decrement perBuyerCumulativeTimeout by generateBidDuration.

            19. Let bidsToScore be the result of applying adjust bid list based on k-anonymity to bidsBatch.

            20. Let foundKAnonBids be false.

            21. For each generatedBid of bidsToScore:

              1. If generatedBid’s for k-anon auction is true, set foundKAnonBids to true.

            22. If bidsToScore is not empty but foundKAnonBids is false:

              Note: generate potentially multiple bids is now rerun with only k-anonymous ads to give the buyer a chance to generate potentially multiple bids for k-anonymous ads. Allowing the buyer to first generate potentially multiple bids for non-k-anonymous ads provides a mechanism to bootstrap the k-anonymity count, otherwise no ads would ever trigger increment k-anonymity count and all ads would fail query k-anonymity count.

              1. Let originalAds be ig’s ads.

              2. If originalAds is not null:

                1. Set ig’s ads to a new list of interest group ad.

                2. For each ad in originalAds:

                  1. Compute adHashCode by getting the result of compute the key hash of ad given ig and ad.

                  2. If query k-anonymity cache given adHashCode returns true, append ad to ig’s ads.

              3. Let originalAdComponents be ig’s ad components.

              4. If originalAdComponents is not null:

                1. Set ig’s ad components to a new list of interest group ad.

                2. For each adComponent in originalAdComponents:

                  1. Compute componentAdHashCode by getting the result of compute the key hash of component ad given adComponent.

                  2. If query k-anonymity cache given componentAdHashCode returns true, append adComponent to ig’s ad components.

              5. If perBuyerCumulativeTimeout is not null and is < perBuyerTimeout, then set perBuyerTimeout to perBuyerCumulativeTimeout.

              6. Let generateBidStartTime be settings’s current monotonic time.

              7. Set (generatedBids, bidDebugReportInfo, realTimeContributions) to the result of running generate potentially multiple bids with allTrustedBiddingSignals, crossOriginTrustedBiddingSignalsOrigin, auctionSignals, a clone of browserSignals, perBuyerSignals, directFromSellerSignalsForBuyer, perBuyerTimeout, expectedCurrency, 1 (for multiBidLimit), ig, auctionStartTime, and settings.

                Note: passing 1 for multiBidLimit limits the rerun to producing at most a single bid.

              8. Set ig’s ads to originalAds.

              9. Set ig’s ad components to originalAdComponents.

              10. Let generateBidDuration be the duration from generateBidStartTime to settings’s current monotonic time, in milliseconds.

              11. If perBuyerCumulativeTimeout is not null, then decrement perBuyerCumulativeTimeout by generateBidDuration.

              12. Assert that size of generatedBids ≤ 1.

              13. For each generatedBid of generatedBids:

                1. Assert that query generated bid k-anonymity count given generatedBid returns true.

                2. Apply any component ads target to a bid given generatedBid.

                3. Append generatedBid to bidsToScore.

            23. Register bids for forDebuggingOnly reports given bidsToScore, bidDebugReportInfo, and bidDebugReportInfoList.

            24. If auctionConfig’s per buyer real time reporting config[buyer] is "default-local-reporting", then insert entries to map given realTimeContributionsMap, buyer, and realTimeContributions.

            25. For each bidToScore of bidsToScore:

              1. If bidToScore’s for k-anon auction is true, append bidToScore’s interest group to bidIgs.

              2. Score and rank a bid with auctionConfig, bidToScore, leadingBidInfo, decisionLogicFetcher, trustedScoringSignalsBatcher, directFromSellerSignalsForSeller, dataVersion, auctionLevel, componentAuctionExpectedCurrency, topLevelOrigin, and realTimeContributionsMap.

    16. Decrement pendingBuyers by 1.

  33. Wait until both pendingBuyers and pendingAdditionalBids are 0.

  34. If auctionConfig’s aborted is true, return failure.

  35. If leadingBidInfo’s leading bid is null, return null.

  36. If topLevelAuctionConfig is null:

    1. Let « sellerSignals, reportResultBrowserSignals » be the result of running report result with leadingBidInfo, directFromSellerSignalsForSeller, null, and global.

    2. Let directFromSellerSignalsForWinner be the result of running get direct from seller signals for a buyer with directFromSellerSignals, and leadingBidInfo’s leading bid's interest group's owner.

    3. Run report win with leadingBidInfo, sellerSignals, reportResultBrowserSignals, directFromSellerSignalsForWinner, and settings.

  37. Let replacements be an ordered map whose keys are strings and whose values are strings.

    1. For each ad keyword replacement, replacement, within deprecated render url replacements:

      1. Let k be replacement’s match.

      2. Let v be replacement’s replacement.

      3. Set replacements[k] to v.

  38. Set leadingBidInfo’s leading bid's ad descriptor to the result of substitute macros with replacements and leading bid's ad descriptor.

  39. If leadingBidInfo’s leading bid's ad descriptors is not null:

    1. For each ad descriptor, adDescriptor, within leading bid's ad descriptors:

      1. Set adDescriptor to the result of substitute macros with replacements and adDescriptor.

  40. Return leadingBidInfo.

To build an interest group passed to generateBid given an interest group ig:
  1. Let igGenerateBid be a new GenerateBidInterestGroup with the following fields:

    owner
    The serialization of ig’s owner
    name
    ig’s name
    enableBiddingSignalsPrioritization
    ig’s enable bidding signals prioritization
    priorityVector
    ig’s priority vector if not null, otherwise not set.
    executionMode
    ig’s execution mode
    biddingLogicURL
    The serialization-or-undefined of ig’s bidding url
    biddingWasmHelperURL
    The serialization of ig’s bidding wasm helper url
    updateURL
    The serialization of ig’s update url
    trustedBiddingSignalsURL
    The serialization of ig’s trusted bidding signals url
    trustedBiddingSignalsKeys
    ig’s trusted bidding signals keys, if not null, otherwise not set.
    trustedBiddingSignalsSlotSizeMode
    ig’s trusted bidding signals slot size mode
    maxTrustedBiddingSignalsURLLength
    ig’s max trusted bidding signals url length
    userBiddingSignals
    Parse a JSON string to a JavaScript value given ig’s user bidding signals
    ads
    ig’s ads converted to an AuctionAd sequence
    adComponents
    ig’s ad components converted to an AuctionAd sequence
  2. Return igGenerateBid.

To convert to an AuctionAd sequence given a list-or-null ads:
  1. If ads is null, then return undefined.

  2. Let adsIDL be a new sequence<AuctionAd>.

  3. For each ad of ads:

    1. Let adIDL be a new AuctionAd.

    2. Set adIDL["renderURL"] to the serialization of ad’s render url.

    3. If ad’s size group is not null, then set adIDL["sizeGroup"] to ad’s size group.

    4. If ad’s metadata is not null, then set adIDL["metadata"] to the result of parsing a JSON string to a JavaScript value given ad’s metadata.

    5. Append adIDL to adsIDL.

  4. Return adsIDL.

To fetch and decode trusted scoring signals given a trusted scoring signals batcher batcher, an auction config auctionConfig, a generated bid generatedBid, a script fetcher decisionLogicFetcher, an origin topLevelOrigin, a real time reporting contributions map realTimeContributionsMap, and a policy container policyContainer:
  1. Let isCrossOrigin be false.

  2. Let sameOriginTrustedScoringSignals be null.

  3. Let crossOriginTrustedScoringSignals be null.

  4. Let scoringDataVersion be null.

  5. Let renderURL be serialized generatedBid’s ad descriptor's url.

  6. Let adComponentRenderURLs be a new empty list.

  7. If generatedBid’s ad component descriptors is not null:

    1. For each adComponentDescriptor in generatedBid’s ad component descriptors:

      1. Append serialized adComponentDescriptor’s url to adComponentRenderURLs.

  8. If auctionConfig’s trusted scoring signals url is not null:

    1. Let request be a new trusted scoring signals request with the following items:

      seller

      auctionConfig’s seller

      seller script fetcher

      decisionLogicFetcher

      base url

      auctionConfig’s trusted scoring signals url

      seller experiment group id

      auctionConfig’s seller experiment group id

      top level origin

      topLevelOrigin

      render URL

      renderURL

      ad component URLs

      adComponentRenderURLs

      policy container

      policyContainer

    2. If auctionConfig’s trusted scoring signals url's origin is not same origin with auctionConfig’s seller, then set isCrossOrigin to true.

    3. Let result be the result of fetching trusted scoring signals with batching with batcher and request.

    4. If result is not failure:

      1. Let allTrustedScoringSignals be result’s all trusted scoring signals:

      2. Set scoringDataVersion to result’s data version.

      3. Let trustedScoringSignals be a new empty map.

      4. Set trustedScoringSignals["renderURL"] to a new empty map.

      5. If allTrustedScoringSignals["renderURLs"] exists and allTrustedScoringSignals["renderURLs"][renderURL] exists, then set trustedScoringSignals["renderURL"][renderURL] to allTrustedScoringSignals["renderURLs"][renderURL].

      6. If adComponentRenderURLs is not empty:

        1. Let adComponentRenderURLsValue be a new empty map.

        2. If allTrustedScoringSignals["adComponentRenderURLs"] exists, for each adComponentRenderURL in adComponentRenderURLs:

          1. If allTrustedScoringSignals["adComponentRenderURLs"][adComponentRenderURL] exists, then set adComponentRenderURLsValue[adComponentRenderURL] to allTrustedScoringSignals["adComponentRenderURLs"][adComponentRenderURL].

        3. Set trustedScoringSignals["adComponentRenderURLs"] to adComponentRenderURLsValue.

      7. If isCrossOrigin is false, set sameOriginTrustedScoringSignals to trustedScoringSignals.

      8. Otherwise:

        1. Set crossOriginTrustedScoringSignals to a new map.

        2. Let originKey be the serialization given auctionConfig’s trusted scoring signals url's origin.

        3. Set crossOriginTrustedScoringSignals[originKey] to trustedScoringSignals.

    5. Otherwise if auctionConfig’s seller real time reporting config is "default-local-reporting",then:

      1. Add a platform contribution with trusted scoring signals failure bucket, realTimeContributionsMap, and auctionConfig’s seller.

  9. Return «isCrossOrigin, sameOriginTrustedScoringSignals, crossOriginTrustedScoringSignals, scoringDataVersion»

To score and rank a bid given an auction config auctionConfig, a generated bid generatedBid, a bid debug reporting info bidDebugReportInfo, a leading bid info leadingBidInfo, a script fetcher decisionLogicFetcher, a trusted scoring signals batcher trustedScoringSignalsBatcher a DirectFromSellerSignalsForSeller directFromSellerSignalsForSeller, an unsigned long-or-null biddingDataVersion, an enum auctionLevel, which is "single-level-auction", "top-level-auction", or "component-auction", a currency tag componentAuctionExpectedCurrency, an origin topLevelOrigin, and a real time reporting contributions map realTimeContributionsMap:
  1. Let «trustedScoringSignalsAreCrossOrigin, sameOriginTrustedScoringSignals, crossOriginTrustedScoringSignals, scoringDataVersion» be the result of fetch and decode trusted scoring signals given trustedScoringSignalsBatcher, auctionConfig, generatedBid, decisionLogicFetcher, topLevelOrigin, and realTimeContributionsMap.

  2. Let adMetadata be generatedBid’s ad.

  3. Let bidValue be generatedBid’s bid.

  4. If generatedBid’s modified bid is not null, then set bidValue to generatedBid’s modified bid.

  5. Let owner be generatedBid’s interest group's owner.

  6. Let browserSignals be a ScoringBrowserSignals with the following fields:

    topWindowHostname
    The result of running the host serializer on topLevelOrigin’s host
    interestGroupOwner
    Serialized owner
    renderURL
    The result of running the URL serializer on generatedBid’s ad descriptor's url
    renderSize
    The result of running convert an ad size to a map with generatedBid’s ad descriptor's size if it is not null, undefined otherwise
    biddingDurationMsec
    generatedBid’s bid duration
    bidCurrency
    The result of serializing a currency tag with generatedBid’s bid's currency
    dataVersion
    scoringDataVersion if it is not null and trustedScoringSignalsAreCrossOrigin is false, unset otherwise.
    crossOriginDataVersion
    scoringDataVersion if it is not null and trustedScoringSignalsAreCrossOrigin is true, unset otherwise.
    adComponents
    generatedBid’s ad component descriptors converted to a string sequence
    forDebuggingOnlyInCooldownOrLockout
    The result of running is debugging only in cooldown or lockout with seller
  7. Let decisionLogicScript be the result of wait for script body from a fetcher given decisionLogicFetcher.

  8. If decisionLogicScript is failure, then:

    1. If auctionConfig’s seller real time reporting config is "default-local-reporting", then add a platform contribution with scoring script failure bucket, realTimeContributionsMap and seller.

    2. Return.

  9. Let « scoreAdResult, debugWinReportUrl, debugLossReportUrl, realTimeContributions » be the result of evaluating a scoring script with decisionLogicScript, adMetadata, bidValue’s value, auctionConfig’s config idl, sameOriginTrustedScoringSignals, crossOriginTrustedScoringSignals, browserSignals, directFromSellerSignalsForSeller, and auctionConfig’s seller timeout.

  10. If auctionLevel is "top-level-auction":

    1. Set bidDebugReportInfo’s top level seller debug loss report url to debugLossReportUrl.

    2. Set bidDebugReportInfo’s top level seller debug win report url to debugWinReportUrl.

  11. Otherwise:

    1. Set bidDebugReportInfo’s seller debug loss report url to debugLossReportUrl.

    2. Set bidDebugReportInfo’s seller debug win report url to debugWinReportUrl.

  12. Let scoreAdOutput be result of processing scoreAd output with scoreAdResult.

  13. If auctionLevel is "component-auction", then set generatedBid’s component seller and bidDebugReportInfo’s component seller to seller.

  14. If auctionConfig’s seller real time reporting config is "default-local-reporting", then insert entries to map given realTimeContributionsMap, seller, and realTimeContributions.

  15. Return if any of the following conditions hold:

  16. If auctionLevel is "component-auction":

    1. Let bidToCheck be generatedBid’s bid.

    2. If scoreAdOutput["bid"] exists:

      1. Let modifiedBidValue be scoreAdOutput["bid"].

      2. If modifiedBidValue ≤ 0, return.

      3. Let modifiedBidCurrency be null.

      4. If scoreAdOutput["bidCurrency] exists, then set modifiedBidCurrency to scoreAdOutput["bidCurrency].

      5. Set generatedBid’s modified bid to a bid with currency with value modifiedBidValue and currency modifiedBidCurrency.

      6. Set bidToCheck to generatedBid’s modified bid.

    3. If the result of checking a currency tag with componentAuctionExpectedCurrency and bidToCheck’s currency is false, return.

    4. If the result of checking a currency tag with auctionConfig’s seller currency and bidToCheck’s currency is false, return.

  17. If auctionConfig’s seller currency is not null:

    1. If generatedBid’s bid's currency is equal to auctionConfig’s seller currency:

      1. Set generatedBid’s bid in seller currency to generatedBid’s bid's value.

      2. If scoreAdOutput["incomingBidInSellerCurrency"] exists and does not equal generatedBid’s bid in seller currency, return.

    2. Otherwise if scoreAdOutput["incomingBidInSellerCurrency"] exists, then set generatedBid’s bid in seller currency to scoreAdOutput["incomingBidInSellerCurrency"].

  18. Let score be scoreAdOutput["desirability"].

  19. If generatedBid’s for k-anon auction is false:

    1. Let updateLeadingNonKAnonEnforcedBid be false.

    2. If leadingBidInfo’s leading non-k-anon-enforced bid is null, or score > leadingBidInfo’s top non-k-anon-enforced score:

      1. Set updateLeadingNonKAnonEnforcedBid to true.

      2. Set leadingBidInfo’s top non-k-anon-enforced bids count to 1.

    3. If leadingBidInfo’s leading non-k-anon-enforced bid is not null and score = leadingBidInfo’s top non-k-anon-enforced score:

      1. Increment leadingBidInfo’s top non-k-anon-enforced bids count by 1.

      2. Set updateLeadingNonKAnonEnforcedBid to true with 1 in leadingBidInfo’s top non-k-anon-enforced bids count chance.

    4. If updateLeadingNonKAnonEnforcedBid is true:

      1. Set leadingBidInfo’s top non-k-anon-enforced score to score.

      2. Set leadingBidInfo’s leading non-k-anon-enforced bid to generatedBid.

    5. Return.

  20. Let updateLeadingBid be false.

  21. If leadingBidInfo’s leading bid is null, or score > leadingBidInfo’s top score:

    1. Set updateLeadingBid to true.

    2. Set leadingBidInfo’s top bids count to 1.

    3. Set leadingBidInfo’s at most one top bid owner to true.

  22. Otherwise if score equals leadingBidInfo’s top score:

    1. Increment leadingBidInfo’s top bids count by 1.

    2. Set updateLeadingBid to true with 1 in leadingBidInfo’s top bids count chance.

    3. If updateLeadingBid is false, then update highest scoring other bid with score, leadingBidInfo’s leading bid, and leadingBidInfo.

    4. If owner is not same origin with leadingBidInfo’s leading bid's interest group's owner, then set leadingBidInfo’s at most one top bid owner to false.

  23. Otherwise if score is greater than or equal to leadingBidInfo’s second highest score, then update highest scoring other bid with score, bidValue, and leadingBidInfo.

  24. If updateLeadingBid is true:

    1. If leadingBidInfo’s leading bid is not null, then update highest scoring other bid with leadingBidInfo’s top score, leadingBidInfo’s leading bid, and leadingBidInfo.

    2. Set leadingBidInfo’s top score to score.

    3. Set leadingBidInfo’s leading bid to generatedBid.

    4. Set leadingBidInfo’s auction config to auctionConfig.

    5. Set leadingBidInfo’s bidding data version to biddingDataVersion.

    6. Set leadingBidInfo’s scoring data version to scoringDataVersion.

To convert to a string sequence given a list-or-null adComponents:
  1. If adComponents is null, return undefined.

  2. Let result be a new sequence<USVString>.

  3. For each component of adComponents:

    1. Append serialized component’s url to result.

  4. Return result.

To update highest scoring other bid given a double score, a generated bid-or-null bid, and a leading bid info leadingBidInfo:
  1. If bid is null, return.

  2. Let owner be bid’s interest group's owner.

  3. If score is greater than leadingBidInfo’s second highest score:

    1. Set leadingBidInfo’s highest scoring other bid to bid.

    2. Set leadingBidInfo’s highest scoring other bids count to 1.

    3. Set leadingBidInfo’s second highest score to score.

    4. Set leadingBidInfo’s highest scoring other bid owner to owner if leadingBidInfo’s at most one top bid owner is true, null otherwise.

  4. Otherwise if score is equal to leadingBidInfo’s second highest score:

    1. Increment leadingBidInfo’s highest scoring other bids count by 1.

    2. Set leadingBidInfo’s highest scoring other bid to bid with 1 in leadingBidInfo’s highest scoring other bids count chance.

    3. If leadingBidInfo’s highest scoring other bid owner is not null and owner is not same origin with leadingBidInfo’s highest scoring other bid owner, then set leadingBidInfo’s highest scoring other bid owner to null.

The Ad-Auction-Allowed HTTP response header is a structured header whose value must be a boolean.

To validate fetching response headers given a response response:
  1. If getting `Ad-Auction-Allowed` and "item" from response’s header list does not return a true value, return false.

  2. If response’s status is not an ok status, return false.

  3. Return true.

To validate fetching response mime and body given a response response, null, failure, or a byte sequence responseBody, and a string mimeType:
  1. If responseBody is null or failure, return false.

  2. Let headerMimeType be the result of extracting a MIME type from response’s header list.

  3. Return false if any of the following conditions hold:

  4. If mimeType is not "application/wasm":

    1. Let mimeTypeCharset be "utf-8".

    2. If headerMimeType’s parameters["charset"] exists, set mimeTypeCharset to headerMimeType’s parameters["charset"].

    3. Return true if any of the following conditions hold:

      • mimeTypeCharset is "utf-8", and responseBody is UTF-8 encoded;

      • mimeTypeCharset is "us-ascii", and all bytes in responseBody are ASCII bytes.

    4. Return false.

  5. Return true.

To validate fetching response given a response response, null, failure, or a byte sequence responseBody, and a string mimeType:
  1. If the result of validating fetching response headers given response is false, then return false.

  2. If the result of validating fetching response mime and body given response, responseBody, mimeType is false, then return false.

  3. Return true.

To fetch WebAssembly given a URL url and an environment settings object settings:
  1. Let request be a new request with the following properties:

    URL

    url

    header list

    «Accept: application/wasm»

    client

    null

    origin

    settings’s origin

    mode

    "no-cors"

    referrer

    "no-referrer"

    credentials mode

    "omit"

    redirect mode

    "error"

    policy container

    A new policy container whose IP address space is settings’s policy container's IP address space

    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.

    Stop using "no-cors" mode where possible (WICG/turtledove#667).

  2. Let moduleObject be null.

  3. Fetch request with processResponseConsumeBody set to the following steps given a response response and null, failure, or a byte sequence responseBody:

    1. If validate fetching response with response, responseBody and "application/wasm" returns false, set moduleObject to failure and return.

    2. Let module be the result of compiling a WebAssembly module response.

    3. If module is error, set moduleObject to failure.

    4. Otherwise, set moduleObject to module.

  4. Wait for moduleObject to be set.

  5. Return moduleObject.

The Data-Version HTTP response header is a structured header whose value must be an integer. The X-fledge-bidding-signals-format-version HTTP response header is a structured header whose value must be an integer.

To fetch trusted signals given a URL url, an origin scriptOrigin, a policy container policyContainer, and a boolean isBiddingSignal:
  1. Let request be a new request with the following properties:

    URL

    url

    origin

    scriptOrigin

    header list

    «Accept: application/json»

    client

    null

    mode

    "cors"

    referrer

    "no-referrer"

    credentials mode

    "omit"

    redirect mode

    "error"

    policy container

    A new policy container whose IP address space is policyContainer’s IP address space

    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.

  2. Let signals be null.

  3. Let dataVersion be null.

  4. Let formatVersion be null.

  5. Let perInterestGroupData be an ordered map.

  6. Fetch request with useParallelQueue set to true, and processResponseConsumeBody set to the following steps given a response response and null, failure, or a byte sequence responseBody:

    1. If validate fetching response with response, responseBody and "application/json" returns false, set signals to failure and return.

    2. Let headers be response’s header list.

    3. Set dataVersion to the result of getting a structured field value given `Data-Version` and "item" from headers.

    4. If dataVersion is not null:

      1. If dataVersion is not an integer, or is less than 0 or more than 232−1, set signals to failure and return.

    5. If isBiddingSignal is true, then set formatVersion to the result of getting a structured field value given `X-fledge-bidding-signals-format-version` and "item" from headers.

    6. Set signals to the result of parsing JSON bytes to an Infra value responseBody.

  7. Wait for signals to be set.

  8. If signals is a parsing exception, or if signals is not an ordered map, return « null, null, null ».

  9. If formatVersion is 2:

    1. If signals["keys"] does not exist, return « null, null ».

    2. Set signals to signals["keys"].

    3. If signals is not an ordered map, return « null, null ».

    4. If signals["perInterestGroupData"] exists and is an ordered map:

      1. Assert isBiddingSignal is true.

      2. Let perInterestGroupData be signals["perInterestGroupData"].

  10. For each keyvalue of signals:

    1. Set signals[key] to the result of serializing an Infra value to a JSON string given value.

  11. Return « signals, perInterestGroupData, dataVersion ».

To encode trusted signals keys given an ordered set of strings keys:

  1. Let list be a new empty list.

  2. Let keysStr be the result of concatenating keys with separator set to ",".

  3. Append the result of UTF-8 percent-encoding keysStr using component percent-encode set to list.

    The Chrome implementation encodes 0x20 (SP) to U+002B (+), while UTF-8 percent-encoding encodes it to "%20".

  4. Return list.

To convert an ad size to a string given an ad size adSize.

  1. Let sizeList be an empty list.

  2. Let jsWidth be adSize’s width, converted to an ECMAScript value.

  3. Append ToString(jsWidth) to sizeList.

  4. Append adSize’s width to sizeList.

  5. Append adSize’s width units to sizeList.

  6. Append "," to sizeList.

  7. Let jsHeight be adSize’s height, converted to an ECMAScript value.

  8. Append ToString(jsHeight) to sizeList.

  9. Append adSize’s height units to sizeList.

  10. Return the result of concatenating sizeList.

To calculate the ad slot size query param given an interest group ig and auction config config:

  1. Switch on ig’s trusted bidding signals slot size mode:

    "none"
    1. Return "".

    "slot-size"
    1. If config’s requested size is null, then return "".

    2. Otherwise:

      1. Let adSizeString the result of running convert an ad size to a string on config’s requested size.

      2. Return the result of concatenating « "&slotSize=", adSizeString ».

    "all-slots-requested-sizes"
    1. If config’s all slots requested sizes is null, then return "".

    2. Let allSizesList be a new list or strings.

    3. For each adSize in config’s all slots requested sizes:

      1. Let adSizeString the result of running convert an ad size to a string on adSize.

      2. Append adSizeString to allSizesList.

    4. Let allSizes be the result of concatenating allSizesList, with "," as a separator.

    5. Return the result of concatenating « "&allSlotsRequestedSizes=", allSizes ».

To send report given a URL url, and an environment settings object settings:
  1. Let request be a new request with the following properties:

    URL

    url

    client

    null

    origin

    settings’s origin

    mode

    "no-cors"

    referrer

    "no-referrer"

    credentials mode

    "omit"

    redirect mode

    "error"

    policy container

    A new policy container whose IP address space is settings’s policy container's IP address space

    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.

    Stop using "no-cors" mode where possible (WICG/turtledove#667).

  2. Fetch request with useParallelQueue set to true.

To get direct from seller signals given an origin seller, a string-or-null adSlot, and a map capturedAuctionHeaders:
  1. If adSlot is not a string, then return null.

  2. Let directFromSellerSignals be null.

  3. Let directFromSellerSignalsKey be a new direct from seller signals key with its seller set to seller, and ad slot set to adSlot.

  4. If capturedAuctionHeaders[directFromSellerSignalsKey] exists:

    1. Set directFromSellerSignals to capturedAuctionHeaders[directFromSellerSignalsKey].

  5. Return directFromSellerSignals.

To get direct from seller signals for a seller given a direct from seller signals-or-null directFromSellerSignals:
  1. Let directFromSellerSignalsForSeller be a new DirectFromSellerSignalsForSeller.

  2. If directFromSellerSignals is null, then return directFromSellerSignalsForSeller.

  3. Set directFromSellerSignalsForSeller["auctionSignals"] to the result of running parse a JSON string to an Infra value given directFromSellerSignals’s auction signals.

  4. Set directFromSellerSignalsForSeller["sellerSignals"] to the result of running parse a JSON string to an Infra value given directFromSellerSignals’s seller signals.

  5. Return directFromSellerSignalsForSeller.

To get direct from seller signals for a buyer given a direct from seller signals-or-null directFromSellerSignals, and an origin owner:
  1. Let directFromSellerSignalsForBuyer be a new DirectFromSellerSignalsForBuyer.

  2. If directFromSellerSignals is null, then return directFromSellerSignalsForBuyer.

  3. Set directFromSellerSignalsForBuyer["auctionSignals"] to the result of running parse a JSON string to an Infra value given directFromSellerSignals’s auction signals.

  4. If directFromSellerSignals’s per buyer signals[owner] exists:

    1. Set directFromSellerSignalsForBuyer["perBuyerSignals"] to the result of running parse a JSON string to an Infra value given directFromSellerSignals’s per buyer signals[owner].

  5. Return directFromSellerSignalsForBuyer.

To report result given a leading bid info leadingBidInfo, a direct from seller signals-or-null directFromSellerSignals, an auction config-or-null winningComponentConfig, and a global object global:
  1. Let config be leadingBidInfo’s auction config.

  2. Let bidCurrency be null.

  3. If winningComponentConfig is not null:

    1. Assert that leadingBidInfo’s component seller is not null.

    2. Set bidCurrency to winningComponentConfig’s seller currency.

    3. If bidCurrency is null, then set bidCurrency to the result of looking up per-buyer currency with config and leadingBidInfo’s component seller.

  4. Otherwise, set bidCurrency to the result of looking up per-buyer currency with config and leadingBidInfo’s leading bid's interest group's owner.

  5. Let winner be leadingBidInfo’s leading bid.

  6. Let sellerCurrency be leadingBidInfo’s auction config's seller currency.

  7. Let highestScoringOtherBid be leadingBidInfo’s highest scoring other bid's bid in seller currency (or 0 if encountered a null).

  8. If sellerCurrency is null, then set highestScoringOtherBid to leadingBidInfo’s highest scoring other bid's bid's value (or 0 if encountered a null).

  9. Let bid be winner’s bid's value.

  10. Let modifiedBid be null.

  11. If winner’s modified bid is not null:

    1. If leadingBidInfo’s component seller is not null, then set bid to winner’s modified bid.

    2. Otherwise, set modifiedBid to winner’s modified bid.

  12. Let browserSignals be a ReportResultBrowserSignals with the following fields:

    topWindowHostname
    The result of running the host serializer on global’s top-level origin's host.
    interestGroupOwner
    Serialized winner’s interest group's owner.
    renderURL
    Serialized winner’s ad descriptor's url
    bid
    Stochastically rounded bid
    bidCurrency
    The result of serializing a currency tag with bidCurrency
    highestScoringOtherBid
    highestScoringOtherBid
    highestScoringOtherBidCurrency
    sellerCurrency if it is not null, "???" otherwise
    topLevelSeller
    leadingBidInfo’s top level seller if it is not null, undefined otherwise
    componentSeller
    leadingBidInfo’s component seller if it is not null, undefined otherwise
    desirability
    Stochastically rounded leadingBidInfo’s top score
    topLevelSellerSignals
    leadingBidInfo’s top level seller signals if it is not null, undefined otherwise
    modifiedBid
    Stochastically rounded modifiedBid if it is not null, undefined otherwise
    dataVersion
    leadingBidInfo’s scoring data version if it is not null, undefined otherwise
  13. Let igAd be the interest group ad from winner’s interest group's ads whose render url is winner’s ad descriptor's url.

  14. If igAd’s buyer and seller reporting ID exists and the result of query reporting ID k-anonymity count given winner’s interest group and igAd is true, then set browserSignals["buyerAndSellerReportingId"] to igAd’s buyer and seller reporting ID.

  15. Let sellerReportingScriptFetcher be the result of creating a new script fetcher with config’s decision logic url and global’s relevant settings object.

  16. Let sellerReportingScript be the result of waiting for script body from a fetcher given sellerReportingScriptFetcher.

  17. Let « sellerSignals, reportUrl, reportingBeaconMap, ignored » be the result of evaluating a reporting script with sellerReportingScript, "reportResult", config’s config idl's reportingTimeout, and « config’s config idl, browserSignals, directFromSellerSignals ».

  18. Let reportingResult be a reporting result with the following items:

    report url

    reportUrl

    reporting beacon map

    reportingBeaconMap

  19. If leadingBidInfo’s top level seller is null (i.e., if we are reporting for a component seller), set leadingBidInfo’s component seller reporting result to reportingResult.

  20. Otherwise, set leadingBidInfo’s seller reporting result to reportingResult.

  21. Remove browserSignals["desirability"].

  22. Remove browserSignals["modifiedBid"].

  23. Remove browserSignals["topLevelSellerSignals"].

  24. Remove browserSignals["dataVersion"].

    Note: Remove fields specific to ReportResultBrowserSignals which only sellers can learn about, so that they are not passed to "reportWin()".

  25. Return « sellerSignals, browserSignals ».

To report win given a leading bid info leadingBidInfo, a string sellerSignals, a ReportingBrowserSignals browserSignals, a direct from seller signals-or-null directFromSellerSignals, and an environment settings object settings:
  1. Let config be leadingBidInfo’s auction config.

  2. Let winner be leadingBidInfo’s leading bid.

  3. Let perBuyerSignals be config’s per buyer signals.

  4. Let buyer be winner’s interest group's owner.

  5. Let perBuyerSignalsForBuyer be perBuyerSignals[buyer] if that member exists, and null otherwise.

  6. Let reportWinBrowserSignals be a ReportWinBrowserSignals with the members that are declared on ReportingBrowserSignals initialized to their values in browserSignals.

  7. Add the following fields to reportWinBrowserSignals:

    dataVersion
    leadingBidInfo’s bidding data version if it is not null, undefined otherwise.
    adCost
    Rounded winner’s ad cost
    seller
    Serialized config’s seller
    madeHighestScoringOtherBid
    Set to true if leadingBidInfo’s highest scoring other bid owner is not null, and buyer is same origin with leadingBidInfo’s highest scoring other bid owner, false otherwise
    modelingSignals
    winner’s modeling signals if it is not null, undefined otherwise (TODO: noise and bucket this signal)
  8. Let igAd be the interest group ad from winner’s interest group's ads whose render url is winner’s ad descriptor's url.

  9. If igAd’s buyer and seller reporting ID does not exist and the result of query reporting ID k-anonymity count given winner’s interest group and igAd is true:

    1. If igAd’s buyer reporting ID exists, set reportWinBrowserSignals["buyerReportingId"] to igAd’s buyer reporting ID.

    2. Otherwise, Set reportWinBrowserSignals["interestGroupName"] to winner’s interest group name.

  10. Let buyerReportingScriptFetcher be the result of creating a new script fetcher with winner’s interest group's bidding url and settings.

  11. Let buyerReportingScript be the result of waiting for script body from a fetcher given buyerReportingScriptFetcher.

  12. Let reportFunctionName be "reportWin".

  13. If winner’s provided as additional bid is true:

    1. Set reportFunctionName be "reportAdditionalBidWin".

  14. Let « ignored, resultUrl, reportingBeaconMap, reportingMacroMap » be the result of evaluating a reporting script with buyerReportingScript, "reportWin", leadingBidInfo’s auction config's config idl's reportingTimeout, and « leadingBidInfo’s auction config's config idl's auctionSignals, perBuyerSignalsForBuyer, sellerSignals, reportWinBrowserSignals, directFromSellerSignals ».

  15. Set leadingBidInfo’s buyer reporting result to a reporting result with the following items:

    report url

    resultUrl

    reporting beacon map

    reportingBeaconMap

    reporting macro map

    reportingMacroMap

To parse and validate server response given an auction config auctionConfig, an auction config-or-null topLevelAuctionConfig, a global object global, a list of interest groups bidIgs, and a list of bid debug reporting info bidDebugReportInfoList, perform the following steps. They return a leading bid info or a failure.
  1. Assert that these steps are running in parallel.

  2. If waiting until server response promise resolves given auctionConfig returns failure, then return failure.

  3. Let hash be the SHA-256 of auctionConfig’s server response.

  4. Let capturedAuctionHeaders be global’s associated Document’s node navigable’s traversable navigable’s captured ad auction result headers.

  5. Let seller be auctionConfig’s seller.

  6. If capturedAuctionHeaders[seller] does not exist or does not contain hash, then return failure.

  7. Let requestId be the value of auctionConfig’s server response id.

  8. Let requestContexts be the value of global’s associated Document’s node navigable’s traversable navigable’s saved Bidding and Auction request context.

  9. If requestContexts[requestId] does not exist, return null.

  10. Let requestContext be requestContexts[requestId].

  11. Let response be the server auction response which is the result of deserializing auctionConfig’s server response with requestContext according to Section 2.3.5 of the Bidding and Auction Services IETF standard.

  12. If response is failure, then return failure.

  13. If response’s top level seller is not null:

    1. If topLevelAuctionConfig is null return failure.

    2. If topLevelAuctionConfig’s seller is not equal to response’s top level seller, return failure.

  14. Otherwise if topLevelAuctionConfig is not null, return failure.

  15. Let winningGroup be the interest group in the user agent's interest group set whose owner is response’s interest group owner and name is response’s interest group name. Return failure if none found.

  16. If winningGroup’s ads is null, then return failure.

  17. Let winningAd be null.

  18. For each ad of winningGroup’s ads:

    1. If response’s ad render url is ad’s render url, then set winningAd to ad, and break.

  19. Return failure if any of the following conditions hold:

  20. Let winningAdDescriptor be a new ad descriptor whose url is response’s ad render url.

  21. Let winningAdComponents be a new list of ad descriptors.

  22. For each componentAd in response’s ad components:

    1. Let ad be the interest group ad from winningGroup’s ad components where the render url equals componentAd. If there is no matching element, return failure.

    2. Append a new ad descriptor whose url is componentAd to winningAdComponents.

  23. Let winningBid be a new generated bid with the following items:

    id

    TODO

    bid

    response’s bid

    bid in seller currency

    Null

    ad

    response’s ad metadata

    ad descriptor

    winningAdDescriptor

    ad component descriptors

    winningAdComponents

    ad cost

    Null

    modeling signals

    Null

    interest group

    winningGroup

    bid ad

    winningAd

    modified bid

    Null if topLevelAuctionConfig is null, otherwise response’s bid

    bid duration

    0

    component seller

    Null

    number of mandatory ad components

    Size of winningAdComponents

  24. Let buyerReportingResult be a new reporting result with the following items:

    report url

    The value of response’s buyer reporting's reporting url.

    reporting beacon map

    The value of response’s buyer reporting's beacon urls.

  25. Let sellerReportingResult be a new reporting result with the following items:

    report url

    The value of response’s top level seller reporting's reporting url.

    reporting beacon map

    The value of response’s top level seller reporting's beacon urls.

  26. Let componentSellerReportingResult be a new reporting result with the following items:

    report url

    The value of response’s component seller reporting's reporting url.

    reporting beacon map

    The value of response’s component seller reporting's beacon urls.

  27. Let topScore be 0.0 if response’s score is null, response’s score otherwise.

  28. Let winningBidInfo be a new leading bid info with the following items:

    top score

    topScore

    top non-k-anon-enforced score

    topScore

    top bids count

    1

    top non-k-anon-enforced bids count

    1

    leading bid

    winningBid

    leading non-k-anon-enforced bid

    winningBid

    auction config

    auctionConfig

    highest scoring other bid

    Null

    top level seller

    Null if topLevelAuctionConfig is null, otherwise topLevelAuctionConfig’s seller.

    top level seller signals

    Null

    component seller

    Null if topLevelAuctionConfig is null, otherwise auctionConfig’s seller.

    bidding data version

    Null

    scoring data version

    Null

    buyer reporting result

    buyerReportingResult

    seller reporting result

    sellerReportingResult

    component seller reporting result

    componentSellerReportingResult

  29. For each igId in response’s bidding groups:

    1. Let ig be the interest group in the user agent's interest group set whose owner is igId’s owner and name is igId’s name. Continue if none found.

    2. Append ig to bidIgs.

  30. For each igIdupdateIfOlderThan of response’s update groups:

    1. Let ig be the interest group in the user agent's interest group set whose owner is igId’s owner and name is igId’s name. Continue if none found.

    2. If updateIfOlderThan is less than 10 mintues, set it to 10 minutes.

    3. If current wall timeig’s last updatedupdateIfOlderThan, set ig’s next update after to the current wall time + updateIfOlderThan.

  31. Insert the debug reporting URLs from response into bidDebugReportInfoList.

    TODO: Handle forDebuggingOnly reports from server auction. (WICG/turtledove#1254)

  32. Return winningBidInfo.

4.2. canLoadAdAuctionFencedFrame()

This first introductory paragraph is non-normative.

The process of running an ad auction through navigator.runAdAuction() is an expensive operation. To prevent wasted cycles if the resulting ad cannot be loaded in the current context, navigator.canLoadAdAuctionFencedFrame() is provided as a method to determine, before the ad auction begins, whether an ad auction-created fencedframe is allowed to be loaded in the current context. A fencedframe has restrictions on where and how it can be loaded that do not exist with iframes.

[SecureContext]
partial interface Navigator {
  boolean canLoadAdAuctionFencedFrame();
};

The canLoadAdAuctionFencedFrame() method steps are:

  1. Let global be this's relevant global object.

  2. If global’s browsing context's required CSP is not null, then return false.

    Note: CSPEE lets arbitrary data flow from an embedder into a fencedframe via the policies it sets. Since this goes against the privacy guarantees of a Protected Audience-created fencedframe, this is disallowed.

  3. Let CSPList be this's relevant settings object's policy container's CSP list.

  4. For each policy of CSPList:

    1. For each directive of policy’s directive set:

      1. If directive’s name is either "fenced-frame-src", "frame-src", "child-src", or "default-src", and if directive’s value does not contain any of "https:", "https://*:*", or "*", then return false.

  5. Let sandboxFlags be global’s associated Document's active sandboxing flag set.

  6. If the intersection of sandboxFlags and TBD is not empty, then return false.

    TODO: Get the mandatory unsandboxed sandboxing flag set once it exists in the fenced frame spec. This will then determine the intersection between that and the sandboxFlags. (WICG/turtledove#1206)

  7. Return true.

4.3. getInterestGroupAdAuctionData()

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 using a trusted auction server to select an advertisement to display to the user, they can call the navigator.getInterestGroupAdAuctionData() function. This function returns an opaque Uint8Array as the request and a request ID string. The request can be sent to a trusted auction server through the JS [FETCH] API, which returns a response. The response along with the request ID can be then passed as part of an auction configuration to navigator.runAdAuction() to extract the results of the auction.

[SecureContext]
partial interface Navigator {
  Promise<AdAuctionData> getInterestGroupAdAuctionData(AdAuctionDataConfig config);
};

dictionary AdAuctionData {
  required Uint8Array request;
  required USVString requestId;
};
dictionary AdAuctionDataConfig {
  required USVString seller;
  required USVString coordinatorOrigin;
  unsigned long requestSize;
  record<USVString, AdAuctionDataBuyerConfig> perBuyerConfig;
};
seller
The seller that will be used as the seller in the following AuctionAdConfig passed to navigator.runAdAuction().
coordinatorOrigin
The origin of the coordinator hosting public encryption keys for the server running the ad auction. The scheme must be "https". An implementation may select which coordinators are acceptable.
requestSize
The desired size for the returned request. If any buyers are specified in perBuyerConfig, this will be the exact size of the returned request. Otherwise the returned request's size will be at most the requestSize.
perBuyerConfig
Keys are serialized origins of buyers that should be included in the returned request. Values are AdAuctionDataBuyerConfig for that buyer.
dictionary AdAuctionDataBuyerConfig {
  unsigned long targetSize;
};
targetSize
The size of the request to allocate for this buyer. Required when AdAuctionDataConfig's requestSize is not specified.

A server auction interest group is a struct with the following items:

name

A string that uniquely defines each interest group, as in name.

bidding signals keys

Null or a list of string, as in trusted bidding signals keys

user bidding signals

Null or a string, as in user bidding signals

ads

A list of strings containing the corresponding ad render IDs from the ads field.

components

A list of strings containing the corresponding ad render IDs from the ad components field.

browser signals

A server auction browser signals.

priority

A double. Used to select which interest groups for a given buyer are excluded from the serialized request due to space limitations.

A server auction browser signals is a struct with the following items:

bid count

A count of the number of bids for this interest group in the last 30 days. Calculated by summing the bid counts for all days within the last 30 days.

join count

A count of the number of joins for this interest group in the last 30 days. Calculated by summing the join counts.

recency ms

A duration, in milliseconds, representing the current wall time at the time this object was constructed minus the corresponding interest group's join time, in milliseconds.

previous wins

A sequence<server auction previous win>

A server auction previous win is a struct with the following items:

time delta

A duration, in milliseconds, representing the current wall time at the time this object was constructed minus the corresponding previous win's time, in seconds.

ad render ID

A string containing the ad render ID for the ad represented by this entry.

A server auction request context is a struct with the following items:

request ID

A unique identifier associated with this and only this invocation of navigator.getInterestGroupAdAuctionData(). This is used to look-up a specific request context.

request context

An opaque context used to handle the request, such as that returned from Section 2.2.4 of Bidding and Auction Services

A server auction response is a struct that contains auction result from an auction executed on the trusted auction server. It has the following items:

ad render url

URL. The leading bid's ad descriptor's url from the auction.

ad components

A list of URLs. A list of winning bid’s ad component descriptors's urls from the auction.

interest group name

A string. The winning bid’s interest group name.

interest group owner

An origin. The winning bid’s interest group owner.

bidding groups

A list of tuples consisting of an origin owner and a string name for each interest group that bid in the auction.

update groups

A map. Its keys are tuples consisting of an origin for owner and a string for name. Its values are durations indicating the desired maximum time since the interest group was last updated.

score

Null or double. Null if the server auction is not a component auction, otherwise the desirability of component auction’s winning bid.

bid

Null or bid with currency. Null when the server auction is not a component auction. For component auctions, contains the winning bid’s modified bid when not null, otherwise the winning bid’s bid.

top level seller

Null or an origin. Null when the server auction is not a component auction. Otherwise the value is the expected top-level seller origin for that auction. This should match the seller for the top-level auction config.

ad metadata

Null or a JSON string. Null when the server auction is not a component auction. Otherwise the value contains the component auction’s winning bid’s ad.

buyer reporting id

Null or a string. When not null, this will be verified with the winning bid’s ad's buyer reporting ID.

buyer and seller reporting id

Null or a string. When not null, this will be verified with the winning bid’s ad's buyer and seller reporting ID.

error

Null or string. When not null, contains an error message from the auction executed on the trusted auction server. May be used to provide additional context for the result of an auction.

buyer reporting

Server auction reporting info

top level seller reporting

Null or server auction reporting info.

component seller reporting

Null or server auction reporting info.

A server auction reporting info is a struct with the following items:

reporting url

Null or a URL

beacon urls

ordered map whose keys are strings and whose values are URLs whose schemes are "https".

The getInterestGroupAdAuctionData(configIDL) method steps are:

  1. Let global be this's relevant global object.

  2. If global’s associated Document is not allowed to use the "run-ad-auction" policy-controlled feature, then throw a "NotAllowedError" DOMException.

  3. Let settings be this's relevant settings object.

  4. Let config be the result of running parse and verify ad auction data config on configIDL and settings’s top-level origin.

  5. Let p be a new promise.

  6. Let queue be the result of starting a new parallel queue.

  7. Enqueue the following steps to queue:

    1. If config’s coordinator is not one of the implementation-defined coordinators supported by this user agent:

      1. Queue a global task on the DOM manipulation task source, given global, to reject p with a TypeError.

      2. Abort these steps.

    2. Let keyInfo be the result of looking up the server encryption key with config’s seller and config’s coordinator.

    3. If keyInfo is failure:

      1. Queue a global task on the DOM manipulation task source, given global, to reject p with a TypeError.

      2. Abort these steps.

    4. Let (key, keyId) be keyInfo.

    5. Set config’s encryption key to key.

    6. Set config’s encryption key id to keyId.

    7. Let igMap be a new map whose keys are origins and values are lists.

    8. Let startTime be a moment equal to the current wall time.

    9. For each ig of the user agent's interest group set:

      1. If ig’s ads is null or is empty, continue.

      2. Let owner be ig’s owner.

      3. If config’s per buyer config is not empty and config’s per buyer config[owner] does not exist, then continue.

      4. If igMap[owner] does not exist, then set igMap[owner] to a new list.

      5. Let ads be a new list.

      6. For each ad in ig’s ads, append ad’s ad render ID to ads.

      7. Let components be a new list.

      8. For each component in ig’s ad components, append component’s ad render ID to components.

      9. Let prevWins be a new sequence<server auction previous win>.

      10. For each prevWin of ig’s previous wins for all days within the the last 30 days:

        1. Let timeDelta be startTime minus prevWin’s time.

        2. Set timeDelta to 0 if timeDelta is negative, timeDelta’s nearest second (rounding down) otherwise.

        3. Let serverPrevWin be a new server auction previous win with the following items:

          time delta

          timeDelta

          ad render ID

          the value of the ad render ID field in prevWin’s ad, or the empty string if not present

        4. Append serverPrevWin to prevWins.

      11. Let browserSignals be a new server auction browser signals with the following items:

        bid count

        the sum of ig’s bid counts with a bid day within the last 30 days

        join count

        the sum of ig’s join counts with a join day within the last 30 days

        recency ms

        the current wall time minus ig’s join time in millseconds

        previous wins

        prevWins

      12. Let serverIg be a new server auction interest group with the following items:

        name

        ig’s name

        bidding signals keys

        ig’s trusted bidding signals keys

        user bidding signals

        ig’s user bidding signals

        ads

        ads

        components

        components

        browser signals

        browserSignals

        priority

        ig’s priority

      13. Append serverIg to igMap[owner].

    10. Let result be a new AdAuctionData.

    11. Let requestId be the string representation of a version 4 UUID.

    12. Set result["requestId"] to requestId.

    13. Let (requestBlob, context) be the result of serializing igMap using config. The serialization method may follow that described in Section 2.2.4 of Bidding and Auction Services.

    14. Set result["request"] to requestBlob.

    15. Queue a global task on the DOM manipulation task source, given global, to resolve p with result.

    16. Let requestContext be a new server auction request context.

    17. Set requestContext’s request ID field to result["requestId"].

    18. Set requestContext’s request context field to context.

    19. Set global’s associated Document’s node navigable’s traversable navigable’s saved Bidding and Auction request context[requestId] to requestContext.

  8. Return p.

To parse and verify ad auction data config given an AdAuctionDataConfig configIDL and origin top_level_origin:
  1. Let seller be the result of running parse an https origin on configIDL["seller"].

  2. Let coordinator be the result of running parse an https origin on configIDL["coordinatorOrigin"].

  3. If seller or coordinator are failure, then throw a TypeError.

  4. Let config be a new auction data config with the following items:

    publisher

    top_level_origin

    seller

    seller

    coordinator

    coordinator

    request size

    configIDL["requestSize"] if it exists, null otherwise

    per buyer config

    The result of running parse per buyer auction data configs on configIDL["perBuyerConfig"]

  5. If config’s per buyer config is not empty and config’s request size is null:

    1. Let requestSize be 0.

    2. For each buyerConfig of config’s per buyer config's values:

      1. If buyerConfig’s size is null, then throw a TypeError.

      2. Set requestSize to requestSize + buyerConfig’s size.

    3. Set config’s request size to requestSize.

  6. Return config.

To parse per buyer auction data configs given an AdAuctionDataBuyerConfig perBuyerConfigIDL:
  1. Let configs be a new ordered map whose keys are origins and whose values are auction data configs.

  2. For each buyerIDLbuyerConfigIDL of perBuyerConfigIDL:

    1. Let buyerConfig be a new auction data buyer config.

    2. Set buyerConfig’s size to buyerConfigIDL["targetSize"] if it exists, null otherwise.

    3. Let buyer be the result of running parse an https origin on buyerIDL.

    4. If buyer is failure, then throw a TypeError.

    5. Set configs[buyer] to buyerConfig.

  3. Return configs.

To look up the server encryption key given an origin seller and an origin coordinator:
  1. Let keys be a list of (byte sequence, byte) tuples returned from looking up the HPKE public key encryption keys and their corresponding key IDs for seller specified by coordinator. The actual implementation of this lookup is implementation-defined, and may consist of fetching the keys from a known URL.

  2. If keys is failure or keys is empty, return failure.

  3. Return an element from keys, chosen at random.

5. Reporting

5.1. forDebuggingOnly

This first introductory paragraph is non-normative.

"generateBid()" and "scoreAd()" can call forDebuggingOnly's reportAdAuctionWin(url) and reportAdAuctionLoss(url) methods for event-level forDebuggingOnly reports for winning and losing bids.

To collect a single forDebuggingOnly report given a URL reportUrl, an origin invokingOrigin, and a list debugReportUrls:

Note: While the browser is experimenting with third party cookie deprecation (before they have been fully removed), the forDebuggingOnly reporting APIs supply temporary labels indicating when a particular instance would have been downsampled instead of using the following steps to downsample it.

  1. If reportUrl is null, or the result of running is debugging only in cooldown or lockout with invokingOrigin is true, then return.

  2. If the result of running sample a debug report with invokingOrigin is true, then append reportUrl to debugReportUrls.

To collect forDebuggingOnly reports given a list of bid debug reporting info bidDebugReportInfoList, origin seller, and leading bid info-or-null winnerInfo:
  1. Let auctionReportInfo be a new auction report info.

  2. Let winningBid be winnerInfo’s leading bid if winnerInfo is not null, null otherwise.

  3. For each bidDebugReportInfo of bidDebugReportInfoList:

    1. If winningBid is not null and bidDebugReportInfo’s ids contains winningBid’s id:

      1. Assert that winningBid’s id is not null.

      2. Collect a single forDebuggingOnly report with bidDebugReportInfo’s bidder debug win report url, bidDebugReportInfo’s interest group owner, and auctionReportInfo’s debug win report urls.

      3. If bidDebugReportInfo’s component seller is null:

        1. Collect a single forDebuggingOnly report with bidDebugReportInfo’s seller debug win report url, seller, and auctionReportInfo’s debug win report urls.

      4. Otherwise:

        1. Collect a single forDebuggingOnly report with bidDebugReportInfo’s seller debug win report url, bidDebugReportInfo’s component seller, auctionReportInfo’s debug win report urls.

        2. Collect a single forDebuggingOnly report with bidDebugReportInfo’s top level seller debug win report url, seller, and auctionReportInfo’s debug win report urls.

    2. Otherwise:

      1. Collect a single forDebuggingOnly report with bidDebugReportInfo’s bidder debug loss report url, bidDebugReportInfo’s interest group owner, and auctionReportInfo’s debug loss report urls.

      2. If bidDebugReportInfo’s component seller is null:

        1. Collect a single forDebuggingOnly report with bidDebugReportInfo’s seller debug loss report url, seller, and auctionReportInfo’s debug loss report urls.

      3. Otherwise:

        1. Collect a single forDebuggingOnly report with bidDebugReportInfo’s seller debug loss report url, bidDebugReportInfo’s component seller, auctionReportInfo’s debug loss report urls.

        2. Collect a single forDebuggingOnly report with bidDebugReportInfo’s top level seller debug loss report url, seller, and auctionReportInfo’s debug loss report urls.

  4. Return auctionReportInfo.

To register bids for forDebuggingOnly reports given a list of generated bids generatedBids, bid debug reporting info bidDebugReportInfo, a list of bid debug reporting info bidDebugReportInfoList:
  1. Let mainId be bidDebugReportInfoList’s size.

  2. Let subId be 0.

  3. Append bidDebugReportInfo to bidDebugReportInfoList.

  4. For each generatedBid of generatedBids:

    1. If generatedBid’s for k-anon auction is true:

      1. Let id be (mainId, subId).

      2. Set subId to subId + 1.

      3. Set generatedBid’s id to id.

      4. Append id to bidDebugReportInfo’s ids.

Instead of inserting to bidDebugReportInfoList in each component auction that runs in parallel, create a structure InterestGroupAuction that holds data for each auction separately (WICG/turtledove#1021).

5.1.1. Downsampling

This first introductory paragraph is non-normative.

Implementations may define their own values for the below constants based on their privacy goal, however we supply the below values as a starting point, inspired by what the initial implementation of this specification uses:

Note: More details of how these numbers are determined can be found in comments here: (WICG/turtledove#632).

The design of downsampling forDebuggingOnly reports has three main goals:

The user agent has a debug report lockout until, which is null or a moment, and a debug report cooldown, which is null or a map whose keys are origins and values are moments at which the cool down for the origin key expires.

To is debugging only in cooldown or lockout given an origin origin:
  1. If user agent's debug report lockout until is not null, and current wall time is less than user agent's debug report lockout until, return true.

  2. If user agent's debug report cooldown is null, then return false.

  3. If user agent's debug report cooldown[origin] exists and current wall time is less than user agent's debug report cooldown[origin], then return true.

  4. Return false.

To sample a debug report given an origin origin:
  1. Let canSendAfterSampled be false.

  2. Let sampleRand be a random long, 0 ≤ sampleRand < 1000, so each possible long would be chosen with a probability equal to sampling rate.

  3. If sampleRand is 0:

    1. Set canSendAfterSampled to true.

    2. Set user agent's debug report lockout until to current wall time plus lockout period.

  4. Let cooldownRand be a random long ≥ 0 and < 10, which corresponds to

    long cooldown rate.

  5. Let cooldownPeriod be long cooldown period if cooldownRand is 0, short cooldown period otherwise.

  6. Set user agent's debug report cooldown[origin] to current wall time plus cooldownPeriod.

  7. Return canSendAfterSampled.

5.2. realTimeReporting

This first introductory paragraph is non-normative.

The goal of real-time reporting is to get auction monitoring data to the buyer and seller as quickly as possible (e.g. < 5 mins). The primary use-case is rapid error detection i.e. detecting quickly whether there are major problems with unexpected behavior in generateBid(), scoreAd(), or fetching of bidding or scoring scripts or trusted signals.

Initial implementation of this specification defines

Each real time report is a CBOR message using the following data structure:

{
  "version": 1,
  "histogram": {
    "buckets": [5, 32, 0, 1, ..., 129],  // 128 elements.
    "length": 1024    // 1024 buckets before bit packing.
  },
  "platformHistogram": {
    "buckets": [192],
    "length": 4  // 4 buckets before bit packing.
  }
}
To sample real time contributions given a list of real time reporting contributions contributions, perform the following steps. They return an integer or null:
  1. If contributions is empty, then return null.

  2. Let priorityWeightSum be 0.

  3. For each contribution of contributions:

    1. Increment priorityWeightSum by contribution’s priority weight.

  4. Let sampleRand be a random double, 0 ≤ sampleRand < 1.

  5. Set sampleRand to sampleRand multiplied by priorityWeightSum.

  6. Set priorityWeightSum to 0.

  7. Let selectedBucket be -1.

  8. For each contribution of contributions:

    1. Increment priorityWeightSum by contribution’s priority weight.

    2. If priorityWeightSumsampleRand, then set selectedBucket to contribution’s bucket, and break;

  9. Assert selectedBucket is not -1.

  10. Return selectedBucket.

To bit pack a list of booleans input:
  1. Let inputSize be input’s size.

  2. Let packed be a new list of bytes.

  3. Let currentByte be 0.

  4. Let numBits be 0.

  5. For each i in the range from 0 to inputSize, exclusive:

    1. Set currentByte to currentByte * 2 + input[i].

    2. Increment numBits by 1.

    3. If numBits is 8, then:

      1. Append currentByte to packed.

      2. Set currentByte to 0 and numBits to 0.

    4. Otherwise if i is inputSize - 1, then:

      1. While numBits < 8:

        1. Set currentByte to currentByte * 2.

        2. Append currentByte to packed.

        3. Increment numBits by 1.

  6. Assert that packed’s size is (inputSize + 7) / 8.0.

  7. Return packed.

To send a real time report given a URL url, a list of booleans histogram, and an environment settings object settings:
  1. Let totalBuckets be the sum of number of user buckets and number of platform buckets.

  2. Assert histogram’s size is totalBuckets.

  3. Let userHistogram and platformHistogram be new lists of booleans.

  4. For each i in the range 0 to totalBuckets, exclusive:

    1. If i < userHistogram, then append histogram[i] to userHistogram.

    2. Otherwise, append histogram[i] to platformHistogram.

  5. Let body be a new ordered map of the following entries:

    "version"

    1

    "histogram"

    a new ordered map of the following entries:

    "buckets"

    the result of bit packing with userHistogram

    "length"

    number of user buckets

    "platformHistogram"

    a new ordered map of the following entries:

    "buckets"

    the result of bit packing with platformHistogram

    "length"

    number of platform buckets

  6. Let request be a new request with the following properties:

    URL

    url

    header list

    «Content-Type: application/cbor»

    method

    POST

    body

    the byte sequence resulting from CBOR encoding body

    client

    null

    origin

    settings’s origin

    mode

    "no-cors"

    referrer

    "no-referrer"

    credentials mode

    "omit"

    redirect mode

    "error"

    policy container

    A new policy container whose IP address space is settings’s policy container's IP address space

    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.

    Stop using "no-cors" mode where possible (WICG/turtledove#667).

  7. Fetch request with useParallelQueue set to true.

To send real time reports given a real time reporting contributions map contributionsMap and an environment settings object settings:
  1. For each origincontributions of contributionsMap:

    1. Let maybeBucket be the result of sampling real time contributions with contributions.

    2. Let histogram be the result of applying RAPPOR noise with maybeBucket.

    3. Let reportUrl be a new URL with the following items:

      scheme

      origin’s scheme

      host

      origin’s host

      port

      origin’s port

      path

      « ".well-known", "interest-group", "real-time-report" »

    4. Send a real time report with reportUrl, histogram and settings.

      TODO: Spec rate limiting. (WICG/turtledove#1215)

5.2.1. Platform contribution

This first introductory paragraph is non-normative.

There are some errors not visible in either scoreAd() or generateBid(), like failures to fetch the bidding or scoring script, trusted bidding or scoring signals. Certain buckets are used for these errors.

The initial implementation supports four platform contribution buckets starting from number of user buckets:

Platform contributions have a hardcoded platform contribution priority weight. The initial implementation uses a value of 1.

To add a platform contribution given a long bucket, a real time reporting contributions map realTimeContributionsMap, and an origin origin:
  1. Assert bucket is one of platform contribution buckets.

  2. Let contribution be a new real time reporting contribution with the following items:

    bucket

    bucket

    priority weight

    platform contribution priority weight

  3. Insert entries to map given realTimeContributionsMap, origin, and « contribution » .

5.2.2. Apply noise (RAPPOR)

This first introductory paragraph is non-normative.

Basic RAPPOR noises each coordinate of the bit vector independently, and it is parameterized by epsilon, a measure of privacy loss. The initial implementation uses an epsilon value of 1, yielding a flipping probability of ~0.378.

To apply RAPPOR noise given an integer-or-null maybeBucket:
  1. Let totalBuckets be the sum of number of user buckets and number of platform buckets.

  2. Let histogram be a new list of booleans, whose size is totalBuckets.

  3. If maybeBucket is not null:

    1. Assert 0 ≤ maybeBucket < totalBuckets.

    2. Set histogram[maybeBucket] to true.

  4. Let f be 2.0/(1+eepsilon/2.0).

  5. For each i in the range from 0 to totalBuckets, exclusive:

    1. Let rand be a random double, 0 ≤ rand < 1

    2. If rand < f / 2.0, then:

      1. Let flipped be false if histogram[i] is true, otherwise true.

      2. Set histogram[i] to flipped.

  6. Return histogram.

6. Additional Bids and Negative Targeting

6.1. createAuctionNonce()

This first introductory paragraph is non-normative.

navigator.createAuctionNonce() creates an auction nonce, a one-time version 4 UUID uniquely associated with a single call to navigator.runAdAuction(). For multi-seller auctions, this ID is uniquely associated with all componentAuctions. This nonce will need to be passed back in via a subsequent call to 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.

  2. 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 global task on DOM manipulation task source, given this's relevant global object, to resolve p with nonce.

  3. Return p.

6.2. Additional Bids

This first introductory paragraph is non-normative.

In addition to 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 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, a negative target info negativeTargetInfo, and a global object global:
  1. Assert that these steps are running in parallel.

  2. Assert that auctionConfig’s auction nonce is not null.

  3. Let auctionNonce be the string representation of auctionConfig’s auction nonce.

  4. Let capturedAdditionalBidsHeaders be global’s associated Document’s node navigable’s traversable navigable’s captured additional bids headers.

  5. Let additionalBids be a new list of decoded additional bids.

  6. For each encodedSignedAdditionalBid of capturedAdditionalBidsHeaders[auctionNonce]:

    1. Let signedAdditionalBid be the result of running forgiving-base64 decode with encodedSignedAdditionalBid.

    2. If signedAdditionalBid is failure, then continue.

    3. Let additionalBid be the result of running parse a signed additional bid given signedAdditionalBid, auctionConfig, topLevelAuctionConfig, and negativeTargetInfo.

    4. If additionalBid is not null, then append additionalBid to additionalBids.

  7. 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.

  2. Let parsedSignedAdditionalBid be the result of running parse a JSON string to an infra value given signedAdditionalBid.

  3. Return null if any of the following conditions hold:

    • parsedSignedAdditionalBid is not a map;

    • parsedSignedAdditionalBid["bid"] does not exist, or is not a string;

    • parsedSignedAdditionalBid["signatures"] does not exist, or is not a list.

  4. Let signatures be a new list of signed additional bid signatures.

  5. Let decodeSignatureFailed be false.

  6. For each sig of parsedSignedAdditionalBid["signatures"]:

    1. Set decodeSignatureFailed to true and break if any of the following conditions hold:

    2. Let maybeKey be the result of running forgiving-base64 decode with sig["key"].

    3. Let maybeSignature be the result of running forgiving-base64 decode with sig["signature"].

    4. Set decodeSignatureFailed to true and break if any of the following conditions hold:

      • maybeKey is failure, or its length is not 32;

      • maybeSignature is failure, or its length is not 64;

    5. Let signature be a signed additional bid signatures, whose key is maybeKey, and signature is maybeSignature.

    6. Append signature to signatures.

  7. If decodeSignatureFailed is true, then return null.

  8. Let decodedAdditionalBid be the result of decode an additional bid json given parsedSignedAdditionalBid["bid"], auctionConfig and topLevelAuctionConfig.

  9. Return null if any of the following conditions hold:

  10. Let verifiedSignatureKeys be a new set of byte sequences.

  11. For each signature of signatures:

    1. Let isSignatureValid be the result of running verify signature’s signature on message parsedSignedAdditionalBid["bid"] using signature’s key, with 0 for Ed25519ctx.

    2. If isSignatureValid is true, then append signature’s key to verifiedSignatureKeys.

  12. If the result of checking whether negative targeted given decodedAdditionalBid, verifiedSignatureKeys and negativeTargetInfo is true, then return null.

  13. 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.

  2. Let parsedAdditionalBid be the result of parse a JSON string to an infra value given additionalBidJson.

  3. If parsedAdditionalBid is not a map, then return failure.

  4. Let result be a new decoded additional bid.

  5. Return failure if any of the following conditions hold:

  6. If topLevelAuctionConfig is null:

    1. If parsedAdditionalBid["topLevelSeller"] exists, then return failure.

  7. Otherwise:

    1. If parsedAdditionalBid["topLevelSeller"] does not exist, then return failure.

    2. Let bidTopLevelSeller be the result of running the parse an https origin with parsedAdditionalBid["topLevelSeller"].

    3. If bidTopLevelSeller is failure, or bidTopLevelSeller is not same origin with topLevelAuctionConfig’s seller, then return failure.

  8. If parsedAdditionalBid["interestGroup"] does not exist, then return failure.

  9. Let igMap be parsedAdditionalBid["interestGroup"].

  10. Return failure if any the following conditions hold:

    • igMap is not a map;

    • igMap["name"] does not exist, or is not a string;

    • igMap["biddingLogicURL"] does not exist, or is not a string;

    • igMap["owner"] does not exist, or is not a string;

  11. Let igOwner be the result of running parse an https origin given igMap["owner"].

  12. Let igName be igMap["name"].

  13. Let igBiddingUrl be the result of running url parser on igMap["biddingLogicURL"].

  14. Return failure if any of the following conditions hold:

  15. Let ig be a new interest group with the following properties:

    owner

    igOwner

    name

    igName

    bidding url

    igBiddingUrl

  16. If parsedAdditionalBid["bid"] does not exist, or is not a map, return failure.

  17. Let bidMap be parsedAdditionalBid["bid"].

  18. If bidMap["render"] does not exist or is not a string, then return failure.

  19. Let renderUrl be the result of running URL parser on bidMap["render"].

  20. If renderUrl is failure, then return failure.

  21. Let ad be a new interest group ad whose render url is renderUrl.

  22. Set ig’s ads to « ad ».

  23. Let bidVal be bidMap["bid"] if it exists, otherwise return failure.

  24. If bidVal is not a double, or is less than or equal to 0, then return failure.

  25. Let adMetadata be "null".

  26. If bidMap["ad"] exists:

    1. Set adMetadata to the result of running serialize an Infra value to a JSON string with bidMap["ad"].

  27. Let bidCurrency be null.

  28. If bidMap["bidCurrency"] exists:

    1. If bidMap["bidCurrency"] is not a string, or the result of checking whether a string is a valid currency tag is failure, then return failure.

    2. Set bidCurrency to bidMap["bidCurrency"].

  29. Let adCost be null.

  30. If bidMap["adCost"] exists:

    1. If bidMap["adCost"] is not a double, then return failure.

    2. Set adCost to bidMap["adCost"].

  31. Let modelingSignals be null.

  32. If bidMap["modelingSignals"] exists:

    1. If bidMap["modelingSignals"] is not a double, then return failure.

    2. If bidMap["modelingSignals"] ≥ 0, and < 4096, then set modelingSignals to bidMap["modelingSignals"].

  33. Let adComponents be a new list of ad descriptors.

  34. If bidMap["adComponents"] exists:

    1. If bidMap["adComponents"] is not a list, then return failure.

    2. For each component of bidMap["adComponents"]:

      1. If component is not a string, then return failure.

      2. Let componentUrl be the result of running URL parser on component.

      3. If componentUrl is failure, then return failure.

      4. Let componentDescriptor be a new ad descriptor whose url is componentUrl.

      5. Append componentDescriptor to adComponents.

    3. Set ig’s ad components to adComponents.

  35. If parsedAdditionalBid["negativeInterestGroup"] exists:

    1. If parsedAdditionalBid["negativeInterestGroups"] exists, or parsedAdditionalBid["negativeInterestGroup"] is not a string, then return failure.

    2. Append parsedAdditionalBid["negativeInterestGroup"] to result’s negative target interest group names.

  36. If parsedAdditionalBid["negativeInterestGroups"] exists:

    1. Let multipleNegativeIg be parsedAdditionalBid["negativeInterestGroups"].

    2. Return failure if any of the following conditions hold:

      • multipleNegativeIg is not a map;

      • multipleNegativeIg["joiningOrigin"] does not exist, or is not a string;

      • multipleNegativeIg["interestGroupNames"] does not exist, or is not a list.

    3. Let joiningOrigin be the result of running parse an https origin with multipleNegativeIg["joiningOrigin"].

    4. If joiningOrigin is failure, then return failure.

    5. Set result’s negative target joining origin to joiningOrigin.

    6. For each igName of multipleNegativeIg["interestGroupNames"]:

      1. If igName is not a string, then return failure.

      2. Append igName to result’s negative target interest group names.

  37. Set result’s bid to a new generated bid with the following properties:

    bid

    A bid with currency whose value is bidVal, and currency is bidCurrency

    ad

    adMetadata

    ad descriptor

    An ad descriptor whose url is renderUrl

    ad component descriptors

    adComponents

    ad cost

    adCost

    modeling signals

    modelingSignals

    interest group

    ig

    bid ad

    A interest group ad whose render url is renderUrl, and metadata is adMetadata

    provided as additional bid

    true

  38. Return result.

A signed additional bid signature is a struct with the following items:

key

A byte sequence of length 32.

signature

A byte sequence of length 64.

A decoded additional bid is a struct with the following 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 negative target interest group names.

Each traversable navigable has a captured additional bids headers, which is a map whose keys are strings for auction nonces, and whose values are list of strings for encoded additional bids.

6.3. Negative Targeting

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.

To check whether negative targeted 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.

  2. Let negativeTargeted be false.

  3. Let additionalBidBuyer be additionalBid’s bid's interest group's owner.

  4. For each igName of additionalBid’s negative target interest group names:

    1. If negativeTargetInfo[(additionalBidBuyer, igName)] exists:

      1. Let (joiningOrigin, additionalBidKey) be negativeTargetInfo[(additionalBidBuyer, igName)].

      2. If verifiedSignatureKeys contains additionalBidKey:

        1. If joiningOrigin is not null:

          1. If joiningOrigin is not same origin with additionalBid’s negative target joining origin, then continue.

        2. Set negativeTargeted to true, and 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 additionalBidKey, is allowed to negatively target the interest group, and that nobody else can learn whether the interest group set contains the interest group.

  1. Return negativeTargeted.

A negative target info is a map. Its keys are tuples consisting of an origin for owner and a string for name. Its values are tuples consisting of an origin for joining origin and a byte sequence for additional bid key.

6.3.1. Negative Interest Groups

This section is non-normative.

Though negative interest groups are joined using the same joinAdInterestGroup() API as regular interest groups, they remain distinct from one another. Only negative interest groups's additional bid key can be non-null, while only regular interest groups's 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 update url must be null, otherwise a TypeError will be thrown by 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:

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",
    ]
  },
  ...
}

7. K-anonymity

Two goals of this specification rely on applying k-anonymity thresholds:

The browser enforces these k-anonymity requirements by maintaining counts of how many times each ad and ad component has been shown to users. These counts are maintained across users, so the counting must be done on a central k-anonymity server. This specification relies on two operations to query and increment the counts: query k-anonymity count and increment k-anonymity count.

The details of how the k-anonymity server is operated and accessed are implementation-defined but it should be done in a way that prevents the server operator from joining the identity of two query or increment requests. One way to help prevent this is by making accesses to the server go through an HTTP proxy that prevents the server from seeing the browsers' IP addresses.

The browser should choose a k-anonymity threshold, otherwise known as the value for "k", and a k-anonymity duration depending on the projected sizes of interest groups and the browser’s privacy goals. For example an implementation might choose to require a k-anonymity threshold of fifty users over a seven day period. The server will maintain the count over the chosen duration and compare the count to the chosen k-anonymity threshold when responding to query k-anonymity count.

The user agent must maintain a k-anonymity cache as a map whose keys are SHA-256 hashes of the k-anonymity keys for all of the ads and ad components in the user agent's interest group set and whose values are k-anonymity records. This allows the browser to rerun portions of an auction without incurring the delay (and added side channels) from querying the server during an auction.

To query k-anonymity count given a SHA-256 hashCode:
  1. If the k-anonymity server has recorded at least k-anonymity threshold users seeing hashCode over the last k-anonymity duration, return true. Otherwise, return false.

  2. Return true if it is above the threshold, otherwise return false.

To query k-anonymity cache given a SHA-256 hashCode:
  1. If the user agent's k-anonymity cache does not contain hashCode, then return false.

  2. Let record be the user agent's k-anonymity cache[hashCode].

  3. If the difference between current wall time and record’s timestamp is more than 7 days then return false.

  4. Return record’s is k-anonymous.

To compute the key hash of ad given an interest group ig and an interest group ad igAd:
  1. Let keyString be the k-anonymity key formed from the concatenation of the following strings separated with U+000A LF:

  2. Return the SHA-256 hash of the ASCII encoding of keyString.

To compute the key hash of reporting ID given an interest group ig and an interest group ad igAd:
  1. Let keyString be a k-anonymity key formed from the concatenation of the following strings separated with U+000A (LF):

  1. Return the SHA-256 hash of the ASCII encoding of keyString.

To compute the key hash of component ad given an interest group ad igAd:
  1. Let keyString be the concatenation of the following strings separated with U+000A LF:

    1. "ComponentBid"

    2. the serialization of igAd.

  2. Return the SHA-256 hash of the ASCII encoding of keyString.

To query generated bid k-anonymity count given a generated bid bid:
  1. Compute the adHashCode following compute the key hash of ad with the bid’s interest group and bid’s ad descriptor.

  2. If query k-anonymity cache for adHashCode returns false, return false.

  3. If bid’s ad component descriptors is not null:

    1. For each adComponentDescriptor in bid’s ad component descriptors:

      1. Compute the componentAdHashCode by getting the result of compute the key hash of component ad with adComponentDescriptor’s url.

      2. If query k-anonymity cache for componentAdHashCode returns false, return false.

  4. Return true.

To query reporting ID k-anonymity count given an interest group ig and interest group ad igAd:
  1. Let keyHash be the result of computing the key hash of reporting ID given ig and igAd.

  2. Return the result of querying the k-anonymity count given keyHash.

To update k-anonymity cache for key given a SHA-256 hashCode:
  1. Assert that these steps are running in parallel.

  2. Let record be a new k-anonymity record.

  3. Set record’s timestamp field to the current wall time.

  4. Set record’s is k-anonymous field to the result of executing query k-anonymity count for hashCode.

  5. Set record[hashCode] to record.

To update k-anonymity cache for interest group given an interest group ig:
  1. Assert that these steps are running in parallel.

  2. For each igAd of ig’s ads:

    1. Compute the adHashCode following compute the key hash of ad for ig and igAd.

    2. Run update k-anonymity cache for key on adHashCode.

    3. Compute the adReportingHashCode following compute the key hash of reporting ID.

    4. Run update k-anonymity cache for key on adReportingHashCode.

  3. For each componentAd of ig’s ad components:

    1. Compute the componentAdHashCode following compute the key hash of component ad for componentAd.

    2. Run update k-anonymity cache for key on componentAdHashCode.

To increment k-anonymity count given a hashCode:
  1. Ask the k-anonymity server to record that this user agent has seen hashCode.

To increment ad k-anonymity count given an interest group ig and a URL ad:
  1. Let keyString be the concatenation of the following strings separated with U+000A LF:

    1. "AdBid"

    2. the serialization of ig’s owner

    3. the serialization of ig’s bidding url

    4. the serialization of ad.

  2. Let keyHash be the SHA-256 hash of the ASCII encoding of keyString.

  3. Increment k-anonymity count given keyHash.

To increment component ad k-anonymity count given a URL ad:
  1. Let keyString be the concatenation of the following strings separated with U+000A LF:

    1. "ComponentBid"

    2. the serialization of ad.

  2. Let keyHash be the SHA-256 hash of the ASCII encoding of keyString.

  3. Increment k-anonymity count given keyHash.

To increment reporting ID k-anonymity count given an interest group ig and a URL ad:
  1. Let igAd be the interest group ad from ig’s ads whose render url is ad.

  2. Let keyHash be the result of computing the key hash of reporting ID given ig and igAd.

  3. Increment k-anonymity count given keyHash.

To increment a winning bid’s k-anonymity count given a generated bid winner:
  1. Increment ad k-anonymity count given winner’s interest group and winner’s ad descriptor's url.

  2. If winner’s ad component descriptors is not null:

    1. For each adComponentDescriptor in winner’s ad component descriptors:

      1. Increment component ad k-anonymity count given adComponentDescriptor’s url.

  3. Increment reporting ID k-anonymity count given winner’s interest group and winner’s ad descriptor's url.

8. Script Runners

This introduction sub-section is non-normative.

This specification defines a new type of script execution environment called a script runner. On the surface, these are similar to Worklets in that they too are used for running scripts independent of the main execution environment with a flexible implementation model.

However, some key differences from traditional Worklets motivate us to create a new kind of script execution environment. In particular, they:

8.1. Realm and agent

To create a new script runner agent, run these steps:
  1. Let signifier be a new unique internal value.

  2. Let candidateExecution be a new candidate execution.

  3. Return a new agent whose [[CanBlock]] is false, [[Signifier]] is signifier, [[CandidateExecution]] is candidateExecution, and [[IsLockFree1]], [[IsLockFree2]], and [[LittleEndian]] are set at the implementation’s discretion.

Note: This algorithm is almost identical to [HTML]'s create an agent algorithm, with the exception that we do not give the returned agent a new event loop, since it does not process tasks within task sources in the usual way.

To obtain a script runner agent, run these steps:
  1. Let agentCluster be a new agent cluster.

  2. Let agent be the result of creating a new script runner agent.

  3. Add agent to agentCluster.

  4. Return agent.

To create a new script runner realm with a global type globalType, run these steps:
  1. Assert that these steps are running in parallel.

  2. Let agent be the result of obtaining a script runner agent given null, true, and false. Run the rest of these steps in agent.

    This exclusively creates a new agent cluster for a given script to run in, but we should make this work with execution mode somehow.

  3. Let realmExecutionContext be the result of creating a new realm given agent and the following customizations:

    • For the global object, create a new object of type globalType.

  4. Let realm be realmExecutionContext’s Realm component.

  5. Let global be realm’s global object, and run these steps:

    1. Perform !global.[[Delete]]("Date").

    2. If !global.[[HasProperty]]("Temporal") is true, then perform !global.[[Delete]]("Temporal").

    This is not the best way to perform such API neutering (see tc39/ecma262#1357), but at the moment it’s the way that host environments do this.

    Note: Removing time-referencing APIs from the global object is imperative for privacy, as a script might otherwise be able to more easily exfiltrate data by using more accurate time measurements.

  6. Return realm.

8.2. Script evaluation

Concretely, a script runner is a JavaScript execution environment instantiated with one of the following global objects:

To evaluate a bidding script given a string script, an unsigned short multiBidLimit, an interest group ig, 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 crossOriginTrustedBiddingSignals, a BiddingBrowserSignals browserSignals, 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).
  1. Let realm be the result of creating a new script runner realm given InterestGroupBiddingScriptRunnerGlobalScope.

  2. Let global be realm’s global object.

  3. Let settings be realm’s settings object.

    WICG/turtledove#676 needs to be fixed in order to get realm’s settings object.

  4. Set global’s group has ad components to true if ig’s ad components is not null, or false otherwise.

  5. Set global’s expected currency to expectedCurrency.

  6. Let isComponentAuction be true if browserSignals["topLevelSeller"] is not null, or false otherwise.

  7. Set global’s is component auction to isComponentAuction.

  8. Set global’s multi-bid limit to multiBidLimit.

  9. Set global’s interest group to ig.

  10. Let igJS be the result of converting to ECMAScript values given igGenerateBid.

  11. Let auctionSignalsJS be the result of parsing a JSON string to a JavaScript value given auctionSignals if auctionSignals is not null, otherwise undefined.

  12. Let perBuyerSignalsJS be the result of parsing a JSON string to a JavaScript value given perBuyerSignals if perBuyerSignals is not null, otherwise undefined.

  13. Let sameOriginTrustedBiddingSignalsJS be sameOriginTrustedBiddingSignals converted to ECMAScript values.

  14. Let browserSignalsJS be browserSignals converted to ECMAScript values.

  15. Let directFromSellerSignalsJS be directFromSellerSignalsForBuyer converted to ECMAScript values.

  16. Let crossOriginTrustedBiddingSignalsJS be crossOriginTrustedBiddingSignals converted to ECMAScript values.

  17. Let startTime be settings’s current monotonic time.

  18. Let result be the result of evaluating a script with realm, script, "generateBid", « igJS, auctionSignalsJS, perBuyerSignalsJS, sameOriginTrustedBiddingSignalsJS, browserSignalsJS, directFromSellerSignalsJS, crossOriginTrustedBiddingSignalsJS », and timeout.

  19. Let duration be settings’s current monotonic time minus startTime in milliseconds.

  20. If global’s priority is not null and not failure:

    1. Set ig’s priority to global’s priority.

    2. Replace the interest group that has ig’s owner and name in the user agent's interest group set with ig.

  21. If global’s priority signals is not empty:

    1. For each kv of global’s priority signals:

      1. If v is null, remove ig’s priority signals overrides[k].

      2. Otherwise, set ig’s priority signals overrides[k] to v.

    2. Replace the interest group that has ig’s owner and name in the user agent's interest group set with ig.

  22. Let generatedBids be global’s bids.

  23. If result is a normal completion:

    1. Let generatedBidIDL be the result of converting result’s [[Value]] to a (GenerateBidOutput or sequence<GenerateBidOutput>).

    2. If no exception was thrown in the previous step, set generatedBids to the result of converting one or many GenerateBidOutputs to a list of generated bids with generatedBidIDL, multiBidLimit, ig, expectedCurrency, isComponentAuction, and global’s group has ad components.

    3. Otherwise, set generatedBids to failure.

  24. If generatedBids is not a list of generated bids, set generatedBids to a new list of generated bids.

  25. Let bidDebugReportInfo be a new bid debug reporting info.

  26. Set bidDebugReportInfo’s interest group owner to ig’s owner.

  27. Let debugLossReportUrl be global’s debug loss report url if it’s not failure, null otherwise.

  28. Set bidDebugReportInfo’s bidder debug loss report url to debugLossReportUrl.

  29. If generatedBids is not empty:

    1. Let debugWinReportUrl be global’s debug win report url if it’s not failure, null otherwise.

    2. Set bidDebugReportInfo’s bidder debug win report url to debugWinReportUrl.

    3. For each generatedBid in generatedBids:

      1. Set generatedBid’s bid duration to duration, interest group to ig.

  30. Let realTimeContributions be a new list of real time reporting contributions.

  31. For each contribution of global’s real time reporting contributions:

    1. If contribution’s latency threshold is not null, and ≥ duration, then continue.

    2. Append contribution to realTimeContributions.

  32. Return a tuple (generatedBids, bidDebugReportInfo, realTimeContributions).

To evaluate a scoring script given a string script, a string adMetadata, a double bidValue, an AuctionAdConfig auctionConfigIDL, an ordered map-or-null sameOriginTrustedScoringSignals, an ordered map-or-null crossOriginTrustedScoringSignals, ScoringBrowserSignals browserSignals, a DirectFromSellerSignalsForSeller directFromSellerSignalsForSeller, and an integer millisecond duration timeout:
  1. Let realm be the result of creating a new script runner realm given InterestGroupScoringScriptRunnerGlobalScope.

  2. Let browserSignalsJS be browserSignals converted to ECMAScript values.

  3. Let auctionConfigJS be auctionConfigIDL converted to ECMAScript values.

  4. Let sameOriginTrustedScoringSignalsJS be sameOriginTrustedScoringSignals converted to ECMAScript values.

  5. Let crossOriginTrustedScoringSignalsJS be crossOriginTrustedScoringSignals converted to ECMAScript values.

  6. Let directFromSellerSignalsJs be directFromSellerSignalsForSeller converted to ECMAScript values.

  7. Let startTime be settings’s current monotonic time.

  8. Let scoreAdResult be the result of evaluating a script with realm, script, "scoreAd", «adMetadata, bidValue, auctionConfigJS, sameOriginTrustedScoringSignalsJS, browserSignalsJS, directFromSellerSignalsJs, crossOriginTrustedScoringSignalsJS», and timeout.

  9. Let duration be settings’s current monotonic time minus startTime in milliseconds.

  10. Let debugWinReportUrl be global’s debug win report url if it’s not failure, null otherwise.

  11. Let debugLossReportUrl be global’s debug loss report url if it’s not failure, null otherwise.

  12. Let realTimeContributions be a new list of real time reporting contributions.

  13. For each contribution of global’s real time reporting contributions:

    1. If contribution’s latency threshold is not null, and ≥ duration, then continue.

    2. Append contribution to realTimeContributions.

  14. Return « scoreAdResult, debugWinReportUrl, debugLossReportUrl, realTimeContributions ».

To evaluate a reporting script given a string script, a string functionName, an integer millisecond duration timeout, and a list of arguments arguments:
  1. Let realm be the result of creating a new script runner realm given InterestGroupReportingScriptRunnerGlobalScope.

  2. Let global be realm’s global object.

  3. Let argumentsJS be the result of converting arguments to an ECMAScript arguments list. If this throws an exception, return « "null", null, null, null ».

  4. Let result be the result of evaluating a script with realm, script, functionName, argumentsJS, and timeout.

  5. If result is an abrupt completion, return « "null", null, null, null ».

  6. Let resultJSON be "null".

  7. 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 thrown in the previous step, keep resultJSON as "null".

  8. Let reportURL be global’s report url

  9. If reportURL is failure, set reportURL to null.

  10. Let macroMap be global’s reporting macro map if functionName is "reportWin" or "reportAdditionalBidWin", null otherwise.

  11. Return « resultJSON, reportURL, global’s reporting beacon map, macroMap ».

To evaluate a script with a realm realm, string script, string functionName, a list arguments, and an integer millisecond duration timeout, run these steps. They return a Completion Record, which is either an abrupt completion (in the case of a parse failure or execution error), or a normal completion populated with the ECMAScript language value result of invoking functionName.
  1. Assert that these steps are running in parallel.

  2. If timeout ≤ 0, immediately interrupt the execution and set finalCompletion to a new throw completion given null.

  3. Let global be realm’s global object, and run these steps in realm’s agent:

  4. Let result be ParseScript(script, realm, empty).

    Note: The resulting Script Record will have no [[HostDefined]] component, unlike traditional scripts on the web platform.

  5. If result is a list of errors, return Completion { [[Type]]: throw, [[Value]]: result, [[Target]]: empty }.

  6. Assert: result is a Script Record.

  7. Prepare to run script: Push realmExecutionContext onto the JavaScript execution context stack; it is now the running JavaScript execution context.

  8. Let evaluationStatus be the result of ScriptEvaluation(result).

  9. If evaluationStatus is an abrupt completion, jump to the step labeled return.

  10. Let F be Get(global, functionName). If that returns a throw completion, set finalCompletion to F and jump to the step labeled return.

  11. Set finalCompletion be 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 throw completion given null.

  12. Return: at this point finalCompletion will be set to a Completion Record.

    1. Clean up after script: Assert realmExecutionContext is the running JavaScript execution context, and remove it from the JavaScript execution context stack.

    2. Return finalCompletion.

8.3. Global scopes

An additional requirement to the interest group script runner globals defined in this specification is that they must not expose any interfaces from other specifications whose own exposure set is the special value "*". The only interfaces that can be exposed inside of the globals defined in this specification are those that explicitly list the global names provided here.

[Exposed=InterestGroupScriptRunnerGlobalScope]
interface InterestGroupScriptRunnerGlobalScope {
};

[Exposed=InterestGroupBiddingAndScoringScriptRunnerGlobalScope]
interface ForDebuggingOnly {
  undefined reportAdAuctionWin(USVString url);
  undefined reportAdAuctionLoss(USVString url);
};

[Exposed=InterestGroupBiddingAndScoringScriptRunnerGlobalScope]
interface RealTimeReporting {
  undefined contributeToHistogram(RealTimeContribution contribution);
};

dictionary RealTimeContribution {
  required long bucket;
  required double priorityWeight;
  long latencyThreshold;
};

[Exposed=InterestGroupBiddingAndScoringScriptRunnerGlobalScope,
 Global=InterestGroupBiddingAndScoringScriptRunnerGlobalScope]
interface InterestGroupBiddingAndScoringScriptRunnerGlobalScope : InterestGroupScriptRunnerGlobalScope {
  readonly attribute ForDebuggingOnly forDebuggingOnly;
  readonly attribute RealTimeReporting realTimeReporting;
};

Each InterestGroupBiddingAndScoringScriptRunnerGlobalScope has an associated ForDebuggingOnly instance forDebuggingOnly, and an associated RealTimeReporting instance realTimeReporting, which are created alongside the InterestGroupBiddingAndScoringScriptRunnerGlobalScope.

The forDebuggingOnly getter steps are:
  1. Return this's relevant global object's forDebuggingOnly.

Each InterestGroupBiddingAndScoringScriptRunnerGlobalScope has a

debug win report url

Null, failure, or a URL whose scheme is "https". Initially null.

debug loss report url

Null, failure, or a URL whose scheme is "https". Initially null.

The reportAdAuctionWin(url) method steps are:
  1. Let global be this's relevant global object.

  2. Let parsedUrl be the result of running the URL parser on url.

  3. If parsedUrl is failure, or parsedUrl’s scheme is not "https", then set global’s debug win report url to failure.

  4. Optionally, return.

    Note: This implementation-defined condition is intended to allow user agents to decline for a number of reasons, for example the parsedUrl’s site not being enrolled.

  5. Set global’s debug win report url to parsedUrl.

The reportAdAuctionLoss(url) method steps are:
  1. Let global be this's relevant global object.

  2. Let parsedUrl be the result of running the URL parser on url.

  3. If parsedUrl is failure, or parsedUrl’s scheme is not "https", then set global’s debug loss report url to failure.

  4. Optionally, return.

    Note: This implementation-defined condition is intended to allow user agents to decline for a number of reasons, for example the parsedUrl’s site not being enrolled.

  5. Set global’s debug loss report url to parsedUrl.

The realTimeReporting getter steps are:
  1. Return this's relevant global object's realTimeReporting.

Each InterestGroupBiddingAndScoringScriptRunnerGlobalScope has a

real time reporting contributions

A list of real time reporting contributions.

The contributeToHistogram(RealTimeContribution contribution) method steps are:
  1. Let global be this's relevant global object.

  2. If contribution["bucket"] ≥ number of user buckets or is negative, then return;

    Note: For forward compatibility with new values, don’t throw.

  3. If contribution["priorityWeight"] ≤ 0, then throw a TypeError.

  4. Let contributionEntry be a new real time reporting contribution with the following items:

    bucket

    contribution["bucket"]

    priority weight

    contribution["priorityWeight"]

    latency threshold

    contribution["latencyThreshold"] if it exists, null otherwise

  5. Append contributionEntry to global’s real time reporting contributions.

8.3.1. InterestGroupBiddingScriptRunnerGlobalScope

[Exposed=InterestGroupBiddingScriptRunnerGlobalScope,
 Global=(InterestGroupScriptRunnerGlobalScope,
         InterestGroupBiddingScriptRunnerGlobalScope)]
interface InterestGroupBiddingScriptRunnerGlobalScope
        : InterestGroupBiddingAndScoringScriptRunnerGlobalScope {
  boolean setBid(optional (GenerateBidOutput or sequence<GenerateBidOutput>) oneOrManyBids = []);
  undefined setPriority(double priority);
  undefined setPrioritySignalsOverride(DOMString key, optional double? priority);
};

dictionary AdRender {
  required DOMString url;
  DOMString width;
  DOMString height;
};

dictionary GenerateBidOutput {
  double bid = -1;
  DOMString bidCurrency;
  (DOMString or AdRender) render;
  any ad;
  sequence<(DOMString or AdRender)> adComponents;
  double adCost;
  unrestricted double modelingSignals;
  boolean allowComponentAuction = false;
  unsigned long targetNumAdComponents;
  unsigned long numMandatoryAdComponents = 0;
};

Each InterestGroupBiddingScriptRunnerGlobalScope has a

bids

A list of generated bids, initially empty.

priority

Null, failure, or a double, initially null.

priority signals

An ordered map whose keys are strings and whose values are double or null.

interest group

An interest group

expected currency

A currency tag

is component auction

A boolean

group has ad components

A boolean

multi-bid limit

An unsigned short.

To convert one or many GenerateBidOutputs to a list of generated bids given a (GenerateBidOutput or sequence<GenerateBidOutput>) oneOrManyBids, an unsigned short multiBidLimit, an interest group ig, a currency tag expectedCurrency, a boolean

isComponentAuction and a boolean groupHasAdComponents:

  1. Let bidSequence be sequence<GenerateBidOutput>.

  2. If the specific type of oneOrManyBids is GenerateBidOutput, then set bidSequence to « oneOrManyBids ».

  3. Otherwise, set bidSequence to oneOrManyBids.

  4. If bidSequence’s size > multiBidLimit, then return failure.

  5. Let bids be a new list of generated bids.

  6. for each bidOutput of bidSequence:

    1. Let bid be the result of converting GenerateBidOutput to generated bid given bidOutput, ig, expectedCurrency, isComponentAuction, groupHasAdComponents.

    2. If bid is failure, return failure.

    3. If bid is null, continue.

    4. Append bid to bids.

  7. Return bids.

To convert GenerateBidOutput to generated bid given a GenerateBidOutput generateBidOutput, an interest group ig, a currency tag expectedCurrency, a boolean isComponentAuction and a boolean groupHasAdComponents:

  1. If generateBidOutput["bid"] ≤ 0, then return null.

  2. Let bid be a new generated bid.

  3. If generateBidOutput["render"] does not exist, return failure.

  4. If isComponentAuction is true, and generateBidOutput["allowComponentAuction"] is false, then return failure.

  5. Let bidCurrency be null.

  6. If generateBidOutput["bidCurrency"] is specified:

    1. If the result of checking whether a string is a valid currency tag on generateBidOutput["bidCurrency"] is true, then set bidCurrency to generateBidOutput["bidCurrency"], otherwise return failure.

  7. If the result of checking a currency tag with expectedCurrency and bidCurrency is false, return failure.

  8. Set bid’s bid to a bid with currency with value generateBidOutput["bid"] and currency bidCurrency.

  9. If generateBidOutput["ad"] exists:

    1. Let adJSON be the result of serializing a JavaScript value to a JSON string, given generateBidOutput["ad"].

    2. If adJSON is failure, return failure.

    3. Set bid’s ad to adJSON.

  10. Let adDescriptor be a new ad descriptor.

  11. If generateBidOutput["render"] is a DOMString:

    1. Let adUrl be the result of running the URL parser on generateBidOutput["render"].

    2. If adUrl is failure, return failure.

    3. Set adDescriptor’s url to adUrl.

  12. Otherwise:

    1. Set adDescriptor to the result of converting an ad render given generateBidOutput["render"].

    2. If adDescriptor is failure, return failure.

  13. Let bidAd be the result of finding matching ad given adDescriptor, ig, and false.

  14. If bidAd is null, return failure.

  15. Set bid’s ad descriptor to adDescriptor.

  16. Set bid’s bid ad to bidAd.

  17. If generateBidOutput["adComponents"] exists:

    1. Let adComponents be generateBidOutput["adComponents"].

    2. Return failure if any of the following conditions hold:

      • groupHasAdComponents is false;

      • adComponents’s size is greater than 40 and generateBidOutput["targetNumAdComponents"] does not exist.

    3. Let adComponentDescriptors be a new list of ad descriptors.

    4. For component in adComponents:

      1. Let componentDescriptor be a new ad descriptor.

      2. If component is DOMString:

        1. Let componentUrl be the result of running the URL parser on component.

        2. If componentUrl is failure, return failure.

        3. Set componentDescriptor’s url to componentUrl.

      3. Otherwise:

        1. Set componentDescriptor to the result of converting an ad render given component.

        2. If componentDescriptor is failure, return failure.

      4. If finding matching ad given componentUrl, ig, and true returns null, return failure.

      5. Append componentDescriptor to adComponentDescriptors.

    5. Set bid’s ad component descriptors to adComponentDescriptors.

  18. If generateBidOutput["targetNumAdComponents"] exists:

    1. If generateBidOutput["targetNumAdComponents"] is equal to 0 or greater than 40, then return failure.

    2. If bid’s ad component descriptors is null, or bid’s ad component descriptors's size < generateBidOutput["targetNumAdComponents"], return failure.

    3. If generateBidOutput["targetNumAdComponents"] < generateBidOutput["numMandatoryAdComponents"], return failure.

    4. Set bid’s target number of ad components to generateBidOutput["targetNumAdComponents"].

    5. Set bid’s number of mandatory ad components to generateBidOutput["numMandatoryAdComponents"].

  19. If generateBidOutput["adCost"] exists, then set bid’s ad cost to generateBidOutput["adCost"].

  20. If generateBidOutput["modelingSignals"] exists:

    1. Let modelingSignals be generateBidOutput["modelingSignals"].

    2. If modelingSignals ≥ 0 and modelingSignals < 4096, then set bid’s modeling signals to the result of converting the ECMAScript value represented by modelingSignals to an unsigned short.

  21. Return bid.

To parse an AdRender dimension value given a string input:
  1. Let position be a position variable, initially pointing at the start of input.

  2. Strip leading and trailing ASCII whitespace from input.

  3. If input starts with "0" but is not "0" and does not start with "0.", then return null as the dimension and the empty string as the dimension unit.

  4. Collect a sequence of code points that are ASCII digits or U+002E (.), given position. Let that be dimensionString.

  5. If dimensionString is the empty string, then return null as the dimension and the empty string as the dimension unit.

  6. Let dimension be the result of parsing dimensionString using the rules for parsing floating-point number values.

  7. If dimension is an error, then return null as the dimension and the empty string as the dimension unit.

  8. If dimension is less than 0, then return null as the dimension and the empty string as the dimension unit.

  9. Collect a sequence of code points that are ASCII lower alpha, given position. Let that be dimensionUnit.

  10. If position is not past the end of input, then return null as the dimension and the empty string as the dimension unit.

  11. If dimensionUnit is the empty string, then set dimensionUnit to "px".

  12. If dimensionUnit is not "px", "sh", or "sw", then return null as the dimension and the empty string as the dimension unit.

  13. Return dimension as the dimension and dimensionUnit as the dimension unit.

To convert an ad render given an AdRender adRender:
  1. Let adUrl be the result of running the URL parser on adRender["url"].

  2. If adUrl is failure, return failure.

  3. Let adDescriptor be a new ad descriptor.

  4. Set adDescriptor’s url to adUrl.

  5. If adRender["width"] exists:

    1. If adRender["height"] does not exist, return failure.

    2. Let width and widthUnit be the dimension and dimension unit that results from running parse an AdRender dimension value with adRender["width"], respectively.

    3. If width is null, return failure.

    4. Let height and heightUnit be the dimension and dimension unit that results from running parse an AdRender dimension value with adRender["height"], respectively.

    5. If height is null, return failure.

    6. Let adSize be a new ad size.

    7. Set adSize’s width to width, width units to widthUnit, height to height, height units to heightUnit.

    8. Set adDescriptor’s size to adSize.

  6. Return adDescriptor.

To find matching ad given an ad descriptor adDescriptor, an interest group ig, and a boolean isComponent:
  1. Let adUrl be adDescriptor’s url.

  2. If adUrl’s scheme is not "https", return null.

  3. Let adSize be adDescriptor’s size.

  4. Let adList be ig’s ad components if isComponent, otherwise ig’s ads.

  5. For each ad in adList:

    1. If ad’s render url does not equal adUrl, continue.

    2. If ad’s size group is null, return ad.

    3. If adSize is null, return ad.

    4. For each igSize in (ig’s size groups)[ad’s size group]:

      1. If igSize equals adSize, return ad.

  6. Return null.

The setBid(oneOrManyBids) method steps are:
  1. Let global be this's relevant global object.

  2. Set global’s bids to an empty list.

  3. Let ig be global’s interest group.

  4. Let expectedCurrency be global’s expected currency.

  5. Let bidsToSet be the result of converting one or many GenerateBidOutputs to a list of generated bids with oneOrManyBids, global’s multi-bid limit, ig, expectedCurrency, global’s is component auction, and global’s group has ad components.

  6. If bidsToSet is failure, throw a TypeError.

  7. Set global’s bids to bidsToSet.

The setPriority(priority) method steps are:
  1. Let global be this's relevant global object.

  2. If global’s priority is not null, then set global’s priority to failure, and throw a TypeError.

  3. Set global’s priority to priority.

The setPrioritySignalsOverride(key, priority) method steps are:
  1. Set this's relevant global object's priority signals[key] to priority.

8.3.2. InterestGroupScoringScriptRunnerGlobalScope

[Exposed=InterestGroupScoringScriptRunnerGlobalScope,
 Global=(InterestGroupScriptRunnerGlobalScope,
         InterestGroupScoringScriptRunnerGlobalScope)]
interface InterestGroupScoringScriptRunnerGlobalScope
        : InterestGroupBiddingAndScoringScriptRunnerGlobalScope {
};

8.3.3. InterestGroupReportingScriptRunnerGlobalScope

[Exposed=InterestGroupReportingScriptRunnerGlobalScope,
 Global=(InterestGroupScriptRunnerGlobalScope,
         InterestGroupReportingScriptRunnerGlobalScope)]
interface InterestGroupReportingScriptRunnerGlobalScope
        : InterestGroupScriptRunnerGlobalScope {
  undefined sendReportTo(DOMString url);
  undefined registerAdBeacon(record<DOMString, USVString> map);
  undefined registerAdMacro(DOMString name, USVString value);
};

Note: registerAdMacro(name, value) is only available in report win, but not report result.

Each InterestGroupReportingScriptRunnerGlobalScope has a

report url

Null, failure, or a URL whose scheme is "https". Initially null.

reporting beacon map

Null or an ordered map whose keys are strings and whose values are URLs whose schemes are "https". Initially null.

reporting macro map

Null or an ordered map whose keys are strings and whose values are strings. Initially null.

The sendReportTo(url) method steps are:
  1. Let global be this's relevant global object.

  2. If global’s report url is not null, then set global’s report url to failure, and throw a TypeError.

  3. Let parsedUrl be the result of running the URL parser on url.

  4. If parsedUrl is failure, or parsedUrl’s scheme is not "https", set global’s report url to failure, and throw a TypeError.

  5. Optionally, return.

    Note: This implementation-defined condition is intended to allow user agents to decline for a number of reasons, for example the parsedUrl’s site not being enrolled.

  6. Set global’s report url to parsedUrl.

The registerAdBeacon(map) method steps are:
  1. If this's relevant global object's reporting beacon map is not null, then Throw a TypeError.

  2. For each typeurl of map:

    1. If type starts with "reserved." and type does not match one of the automatic beacon event type values, throw a TypeError.

    2. Let parsedURL be the result of running URL parser on url.

    3. Throw a TypeError if any of the following conditions hold:

      • parsedURL is failure;

      • parsedURL’s scheme is not "https".

  3. Set this's relevant global object's reporting beacon map to map.

The registerAdMacro(name, value) method steps are:
  1. For each code unit c in name:

    1. Throw a TypeError if both of the following conditions hold:

  2. For each code unit c in value:

    1. Throw a TypeError if both of the following conditions hold:

  3. Set this's relevant global object's reporting macro map[name] to value.

9. Interest Group Updates

This first introductory paragraph is non-normative.

Interest groups have an update url field that allows updating the interest group definition stored on disk with information periodically retrieved from the update url. The interest group update steps are triggered during runAdAuction() and by calls to updateAdInterestGroups() API:

[SecureContext]
partial interface Navigator {
  undefined updateAdInterestGroups();
};

The updateAdInterestGroups() method steps are:

  1. Let settings be this's relevant settings object.

  2. In parallel, run interest group update with « settings’s top-level origin », and settings’s policy container.

To update interest groups given a list of origins owners, and a policy container policyContainer:

Implementations can consider aborting all updating if updating has been running for too long. This can avoid continuing to reveal coarse IP location information to update servers long after navigating to another page. Some implementations, such as Chromium, have chosen a 10 minute limit.

  1. For each owner of owners:

    1. For each originalInterestGroup of the user agent's interest group set whose owner is owner and next update after is before the current wall time and whose update url is not null:

      Note: Implementations can consider loading only a portion of these interest groups at a time to avoid issuing too many requests at once.

      1. Let ig be a deep copy of originalInterestGroup.

      2. Run update k-anonymity cache for interest group for ig.

      3. Let request be a new request with the following properties:

        URL

        ig’s update url

        header list

        «Accept: application/json»

        client

        null

        origin

        owner

        mode

        "no-cors"

        referrer

        "no-referrer"

        credentials mode

        "omit"

        redirect mode

        "error"

        policy container

        A new policy container whose IP address space is policyContainer’s IP address space

        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.

        Stop using "no-cors" mode where possible (WICG/turtledove#667).

      4. Let update be null.

      5. Fetch request with useParallelQueue set to true, and processResponseConsumeBody set to the following steps given a response response and null, failure, or a byte sequence responseBody:

        1. If validate fetching response with response, responseBody and "application/json" returns false, set update to failure and return.

        2. Set update to responseBody.

      6. Wait for update to be set.

      7. If update is failure, continue.

      8. Let parsedUpdate be the result of parsing JSON bytes to an Infra value, given update.

      9. If parsedUpdate is failure, continue.

      10. If parsedUpdate is not an ordered map, continue.

      11. If parsedUpdate["name"] exists and doesn’t match ig’s name, continue.

      12. If parsedUpdate["owner"] exists and doesn’t match ig’s owner, continue.

      13. For each keyvalue of parsedUpdate:

        1. Switch on key:

          "priority"
          1. If value is a double, set ig’s priority to value.

          2. Otherwise, jump to the step labeled Abort update.

          "enableBiddingSignalsPrioritization"
          1. If value is a boolean, set ig’s enable bidding signals prioritization to value.

          2. Otherwise, jump to the step labeled Abort update.

          "priorityVector"
          1. If value is null or an ordered map whose keys are strings and whose values are double, set ig’s priority vector to value.

          2. Otherwise, jump to the step labeled Abort update.

          "prioritySignalsOverrides"
          1. If value is an ordered map whose keys are strings and whose values are double or null:

            1. For each pvKeypvValue of value:

              1. If pvValue is null, remove ig’s priority signals overrides[pvKey].

              2. Otherwise, set ig’s priority signals overrides[pvKey] to pvValue.

          2. Otherwise, jump to the step labeled Abort update.

          "sellerCapabilities"
          1. If value is an ordered map whose keys are strings and whose values are lists of strings, for each sellerStringcapabilities of value:

            1. Let sellerCapabilities be a new set of seller capabilities.

            2. For each capabilityString of capabilities:

              1. If capabilityString is "interest-group-counts" or "latency-stats", then append capabilityString to sellerCapabilities.

                Note: For forward compatibility with new values, don’t throw.

            3. If sellerString is "*", then set ig’s all sellers capabilities to sellerCapabilities.

            4. Otherwise:

              1. If ig’s seller capabilities is null, then set ig’s seller capabilities to a new ordered map whose keys are origins and whose values are sets of seller capabilities.

              2. Let sellerUrl be the result of running the URL parser on sellerString.

              3. If sellerUrl is not failure:

                1. Let seller be sellerUrl’s origin.

                2. If ig’s seller capabilities does not contain seller, then set ig’s seller capabilities[seller] to sellerCapabilities.

          2. Otherwise, jump to the step labeled Abort update.

          "executionMode"
          1. If value is "compatibility", "frozen-context", or "group-by-origin", then set ig’s execution mode to value.

          2. Otherwise, set ig’s execution mode to "compatibility".

          "biddingLogicURL"
          "biddingWasmHelperURL"
          "updateURL"
          1. For each groupMember and interestGroupField, in the following table

            Group member Interest group field
            "biddingLogicURL" bidding url
            "biddingWasmHelperURL" bidding wasm helper url
            "updateURL" update url
            1. If key is not groupMember, continue.

            2. If value is a string:

              1. Let parsedURL be the result of parse and verify a bidding code or update URL on value and ig’s owner.

              2. If parsedURL is failure, jump to the step labeled Abort update

              3. Set ig’s interestGroupField to parsedURL.

          "trustedBiddingSignalsURL"
          1. If value is a string:

            1. Let parsedURL be the result of parse and verify a trusted signals URL on value.

            2. If parsedURL is failure, jump to the step labeled Abort update

            3. Set ig’s trusted bidding signals url to parsedURL.

          "trustedBiddingSignalsKeys"
          1. If value is a list of strings, set ig’s trusted bidding signals keys to value.

          2. Otherwise, jump to the step labeled Abort update.

          "trustedBiddingSignalsSlotSizeMode"
          1. If value is one of "none", "slot-size", or "all-slots-requested-sizes", set ig’s trusted bidding signals slot size mode to value.

          2. Otherwise, set trusted bidding signals slot size mode to "none".

          "maxTrustedBiddingSignalsURLLength"
          1. If value is a Number:

            1. Let convertedValue be the result of converting value to a long.

            2. If convertedValue ≥ 0, then set ig’s max trusted bidding signals url length to convertedValue.

          2. Otherwise, jump to the step labeled Abort update.

          "userBiddingSignals"
          1. Set ig’s user bidding signals to the result of serialize an Infra value to JSON bytes given value.

          2. Otherwise, jump to the step labeled Abort update.

          Serializing an Infra value to JSON bytes expects to be called within a valid ES realm. See infra/625

          "adSizes"
          1. Let adSizes be a new map whose keys are strings and values are ad sizes.

          2. For each sizeNamesize of value:

            1. If sizeName is "", jump to the step labeled Abort update.

            2. Let adSize be the result from running parse an AdRender ad size with size.

            3. If adSize is null, jump to the step labeled Abort update.

            4. Set adSizes[sizeName] to adSize.

          3. Set ig’s ad sizes to adSizes.

          "sizeGroups"
          1. Let adSizes be ig’s ad sizes.

          2. Let sizeGroups be a new map whose keys are strings and values are strings.

          3. For each sizeGroupNamesizeList of value:

            1. If sizeGroupName is "", jump to the step labeled Abort update.

            2. For each sizeName of sizeList:

              1. If sizeName is "" or adSizes[sizeName] does not exist, jump to the step labeled Abort update.

            3. Set sizeGroups[sizeGroupName] to sizeList.

          4. Set ig’s size groups to sizeGroups.

          "ads"
          "adComponents"
          1. For each groupMember and interestGroupField in the following table

            Group member Interest group field
            "ads" ads
            "adComponents" ad components
            1. If key is not groupMember, continue.

            2. If value is not a list of AuctionAd, jump to the step labeled Abort update.

            3. Let igAds be a new list of interest group ads.

            4. For each ad of value:

              1. Let igAd be a new interest group ad.

              2. Let renderURL be the result of running the URL parser on ad["renderURL"].

              3. Jump to the step labeled Abort update if any of the following conditions hold:

              4. Set igAd’s render url to renderURL.

              5. If ad["sizeGroup"] exists:

                1. Let sizeGroup be ad["sizeGroup}].

                2. Jump to the step labeled Abort update if none of the following conditions hold:

                3. Set igAd’s size group to sizeGroup.

              6. If ad["metadata"] exists, then let igAd’s metadata be the result of serializing a JavaScript value to a JSON string, given ad["metadata"]. If this throws, jump to the step labeled Abort update.

              7. If groupMember is "ads":

                1. If ad["buyerReportingId"] exists then set igAd’s buyer reporting ID to it.

                2. If ad["buyerAndSellerReportingId"] exists then set igAd’s buyer and seller reporting ID to it.

              8. Append igAd to igAds.

            5. If igAds is not is empty:

              1. Set ig’s interestGroupField to igAds.

          "additionalBidKey"
          1. Let decodedKey be the result of running forgiving-base64 decode with value.

          2. Jump to the step labeled Abort update if any of the following conditions hold:

            • decodedKey is a failure;

            • decodedKey’s length is not 32;

          3. Set ig’s additional bid key to decodedKey.

      14. Jump to the step labeled Abort update if any of the following conditions hold:

      15. Set ig’s next update after to the current wall time plus 24 hours.

      16. Set ig’s last updated to the current wall time.

      17. Replace the interest group that has ig’s owner and name in the user agent's interest group set with ig.

      18. Abort update: We jump here if some part of the interest group update failed. Continue to the next interest group update.

To process updateIfOlderThanMs given an origin buyer, and an ordered map perInterestGroupData whose keys are name strings and whose values are bidding signals per interest group data:

  1. For each igNameperIgData of perInterestGroupData, the following steps should be done:

    1. If perIgData’s updateIfOlderThanMs is null, continue.

    2. Let updateIfOlderThan be a duration of perIgData’s updateIfOlderThanMs milliseconds.

    3. Let ig be the interest group of the user agent's interest group set whose owner is buyer and whose name is igName, or null if interest group set does not have such an interest group.

    4. If ig is not null and the current wall timeig’s last updatedupdateIfOlderThan:

      1. Set ig’s next update after to current wall time + updateIfOlderThan.

      2. Replace the interest group that has ig’s owner and name in the user agent's interest group set with ig.

10. Feature Detection

The queryFeatureSupport() method permits checking what functionality is available in the current implementation, in order to help deploy new features. The return values specified in this specification are for an implementation that fully implements it.

[SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute ProtectedAudience protectedAudience;
};

[SecureContext, Exposed=Window]
interface ProtectedAudience {
  any queryFeatureSupport(DOMString feature);
};

The queryFeatureSupport(feature) method steps are:

  1. Let featuresTable be an ordered map whose keys are DOMStrings and whose values are booleans or longs, with the following entries:

    "adComponentsLimit"

    40

    "deprecatedRenderURLReplacements"

    true

    "permitCrossOriginTrustedSignals"

    true

    "realTimeReporting"

    true

    "reportingTimeout"

    true

  2. If feature is "*", then return featuresTable.

  3. If featuresTable[feature] exists, then return featuresTable[feature].

  4. Return undefined.

11. Common Algorithms

The following algorithms are some helper algorithms used in this specification.

The estimated size of an interest group ig is the sum of:

  1. The length of the serialization of ig’s owner.

  2. The length of ig’s name.

  3. 8, which is the size of ig’s priority.

  4. 4, which is the size of ig’s execution mode.

    Note: Each of execution mode's value represents a long integer, so 4 bytes.

  5. 2, which is the size of ig’s enable bidding signals prioritization.

  6. If ig’s priority vector is not null, for each keyvalue of priority vector:

    1. The length of key.

    2. 8, which is the size of value.

  7. If ig’s priority signals overrides is not null, for each keyvalue of priority signals overrides:

    1. The length of key.

    2. 8, which is the size of value.

  8. If ig’s seller capabilities is not null, then for each sellercapabilities of ig’s seller capabilities:

    1. The length of the serialization of seller.

    2. 4, which is capabilities’s underlying number of enum bytes.

  9. The length of the serialization of ig’s bidding url, if the field is not null.

  10. The length of the serialization of ig’s bidding wasm helper url, if the field is not null.

  11. The length of the serialization of ig’s update url, if the field is not null.

  12. The length of the serialization of ig’s trusted bidding signals url, if the field is not null.

  13. 4, which is the size of ig’s trusted bidding signals slot size mode.

    Note: Each of trusted bidding signals slot size mode's value represents a long integer, so 4 bytes.

  14. If ig’s trusted bidding signals keys is not null, for each key of it:

    1. The length of key.

  15. If ig’s max trusted bidding signals url length is not null:

    1. 4, which is the size of ig’s max trusted bidding signals url length.

  16. The length of ig’s user bidding signals.

  17. If ig’s ads is not null, for each ad of it:

    1. The length of the serialization of ad’s render url.

    2. The length of ad’s size group if the field is not null.

    3. The length of ad’s metadata if the field is not null.

    4. The length of ad’s buyer reporting ID if the field is not null.

    5. The length of ad’s buyer and seller reporting ID if the field is not null.

    6. If ad’s allowed reporting origins is not null, for each origin of it:

      1. The length of the serialization of origin.

  18. If ig’s ad components is not null, for each ad of it:

    1. The length of the serialization of ad’s render url.

    2. The length of ad’s size group if the field is not null.

    3. The length of ad’s metadata if the field is not null.

  19. If ig’s ad sizes is not null, for each sizeNamesize of it:

    1. The sum of the length of sizeName and 18 (representing the fixed length of size).

    Note: 10 represents the size of 2 doubles for width and height (each 8 bytes), and 2 bytes for enums for width units and height units (each 1 byte).

  20. If ig’s size groups is not null, for each sizeGroupNamesizeList of it:

    1. The sum of the length of sizeGroupName and the following:

    2. For each sizeName of sizeList:

      1. The length of sizeName.

  21. If ig’s additional bid key is not null:

    1. 32, which is its size (number of bytes).

To parse an https origin given a string input:
  1. Let url be the result of running the URL parser on input.

  2. If url is failure, or its scheme is not "https", then return failure.

  3. Return url’s origin.

To serialize a URL given a URL-or-null url:
  1. If url is null, then return undefined.

  2. Return the serialization of url.

To parse and verify a bidding code or update URL given a string input and an origin igOwner:
  1. Let parsedUrl be the result of running the URL parser on input.

  2. Return failure if any of the following conditions hold:

  3. Assert: parsedUrl’s scheme is "https".

  4. Return parsedUrl.

To parse and verify a trusted signals URL given a string input:
  1. Let parsedUrl be the result of running the URL parser on input.

  2. Return failure if any of the following conditions hold:

  3. Return parsedUrl.

To round a value given a double value:
  1. If value is not a valid floating-point number, return value.

  2. Let valueExp be value’s IEEE 754 biased exponent field minus 1023.

  3. Let normValue be value multiplied by 2(−1 × valueExp).

  4. If valueExp < −128:

    1. If value < 0, return −0.

    2. Otherwise, return 0.

  5. If valueExp > 127:

    1. If value < 0, return −∞.

    2. Otherwise, return ∞.

  6. Let precisionScaledValue be normValue multiplied by 256.

  7. Let noisyScaledValue be precisionScaledValue plus a random double value greater than or equal to 0 but less than 1.

  8. Let truncatedScaledValue be the largest integer not greater than noisyScaledValue.

  9. Return truncatedScaledValue multiplied by 2(valueExp − 8).

To serialize an integer, represent it as a string of the shortest possible decimal number.

This would ideally be replaced by a more descriptive algorithm in Infra. See infra/201

To get uuid from string given a string input:
  1. If input’s length is not 36, return an empty string.

  2. Let uuidStr be an empty string.

  3. For each i in the range from 1 to 36, inclusive:

    1. Let unit be input’s ith code unit.

    2. If « 8, 13, 18, 23 » contains i:

      1. If unit is not 0x002D (-), then return an empty string.

    3. Otherwise:

      1. If unit is not an ASCII lower hex digit, then return an empty string.

      2. Append unit to the end of uuidStr.

  4. Return ASCII encoded uuidStr.

To insert entries to map given an ordered map map, a key which is in same type as map’s key, and a list entries which is in same type as map’s value:
  1. If map[key] exists, then extend map[key] with entries.

  2. Otherwise, set map[key] to entries.

12. Permissions Policy Integration

This specification defines two policy-controlled features identified by the strings "join-ad-interest-group", and "run-ad-auction". Their default allowlists are "*".

Move from "*" to "self" (WICG/turtledove#522).

To check interest group permissions given an origin ownerOrigin, an environment settings object settings, and an enum joinOrLeave which is "join" or "leave":

  1. If ownerOrigin is same origin with frameOrigin, then return true.

  2. Let encodedFrameOrigin be the result of