From 1a19d68507eb42a0cf7faf88701fa319c9f16a27 Mon Sep 17 00:00:00 2001 From: Qingxin Wu Date: Thu, 12 Oct 2023 13:51:23 -0400 Subject: [PATCH 1/7] Add an algorithm of storage maintenance. It includes several IG caps. --- spec.bs | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 11 deletions(-) diff --git a/spec.bs b/spec.bs index bed9f68ef..c4156e50b 100644 --- a/spec.bs +++ b/spec.bs @@ -139,6 +139,7 @@ advertisement relevant to this interest to this user in the future. The user age interest group set, a [=list=] of [=interest groups=] in which [=interest group/owner=] / [=interest group/name=] pairs are unique. +

navigator.joinAdInterestGroup()

[SecureContext] @@ -310,7 +311,7 @@ This is detectable because it can change the set of fields that are read from th a {{TypeError}}. 1. Set |igAd|'s [=interest group ad/allowed reporting origins=] to |allowedReportingOrigins|. 1. [=list/Append=] |igAd| to |interestGroup|'s |interestGroupField|. -1. If |interestGroup|'s [=interest group/estimated size=] is greater than 50 KB, then +1. If |interestGroup|'s [=interest group/estimated size=] is greater than 1048576, then [=exception/throw=] a {{TypeError}}. 1. Let |p| be [=a new promise=]. 1. Let |queue| be the result of [=starting a new parallel queue=]. @@ -331,7 +332,7 @@ This is detectable because it can change the set of fields that are read from th 1. If the most recent entry in |interestGroup|'s [=interest group/join counts=] corresponds to the current day in UTC, increment its count. If not, [=list/insert=] a new [=tuple=] the time set to the current UTC day and a count of 1. - 1. Store |interestGroup| in the browser’s [=interest group set=]. + 1. Store |interestGroup| in the user agent's [=interest group set=]. 1. Return |p|. </div> @@ -447,6 +448,82 @@ To <dfn>build an interest group permissions url</dfn> given a [=origin=] |ownerO </div> +<h3 id="interest-group-storage-maintenance">Interest Group Storage Maintenance</h3> + +There is a job that periodically performs maintenance on the user agent's [=interest group set=], +such as [=list/removing=] expired, or more than allowed [=interest groups=] which include the +following limitations: + * no more than 2000 owners; + * no more than 1000 [=regular interest groups=] per owner; + * no more than 20000 [=negative interest groups=] per owner; + * no more than <code>10\*1024\*1024</code> total size of interest groups per owner. + +<div algorithm='maintenance'> +To perform storage maintenance: + +1. Let |ownersAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=origins=] + [=interest group/owner=] and [=map/values=] are [=moments=] [=interest group/expiry=]. +1. [=list/For each=] |ig| of the user agent's [=interest group set=]: + 1. Let |owner| be |ig|'s [=interest group/owner=]. + 1. If |ownersAndExpiry|[|owner|] [=map/exists=], then [=map/set=] |ownersAndExpiry|[|owner|] to + |ig|'s [=interest group/expiry=] if it comes after |ownersAndExpiry|[|owner|]. + 1. Otherwise, [=map/set=] |ownersAndExpiry|[|owner|] to |ig|'s [=interest group/expiry=]. +1. If [=map/size=] of |ownersAndExpiry| |i| &gt; 2000, then set |ownersAndExpiry| to + |ownersAndExpiry| [=map/sorted in descending order=] with |a| being less than |b| if |a|'s + [=map/value=] comes before |b|'s [=map/value=]. + + Note: Sorting owners based on their expiry in descending order so that if interest groups of some + owners need to be removed, the interest groups of owners expiring soonest are the ones to be + removed first. + +1. Let |owners| be the [=map/get the keys|keys=] of |ownersAndExpiry|. +1. [=list/For each=] |i| in [=the range=] from 0 to the [=set/size=] of |owners|, exclusive: + 1. If |i| &ge; 2000, then [=list/remove=] [=interest groups=] from the user agent's + [=interest group set=] whose [=interest group/owner=] is |owners|[|i|], and [=iteration/continue=]. + 1. Let |regularIgs| be a [=list=] of [=regular interest groups=] in the user agent's + [=interest group set=] whose [=interest group/owner=] is |owners|[|i|]. + 1. If [=list/size=] of |regularIgs| &gt; 1000, then [=clear excess interest groups=] with + |regularIgs|, |owners|[|i|], and 1000. + 1. Let |negativeIgs| be a [=list=] of [=negative interest groups=] in the user agent's + [=interest group set=] whose [=interest group/owner=] is |owners|[|i|]. + 1. If [=list/size=] of |regularIgs| &gt; 20000, then [=Clear excess interest groups=] with + |negativeIgs|, |owners|[|i|], and 20000. +1. TODO: remove expired IGs. +1. [=list/For each=] |owner| of |owners|: + 1. Let |igs| be a [=list=] of [=interest groups=] in the user agent's [=interest group set=] whose + [=interest group/owner=] is |owner|, [=list/sorted in descending order=] with |a| being less + than |b| if |a|[=interest group/expiry=] comes before |b|[=interest group/expiry=]. + 1. Let |cumulativeSize| be 0. + 1. [=list/For each=] |ig| of |igs|: + 1. If the sum of |cumulativeSize| and |ig|'s [=interest group/estimated size=] + &gt; <code>10\*1024\*1024</code>, then [=list/remove=] |ig| from the user + agent's [=interest group set=]. + 1. Otherwise, increment |cumulativeSize| by |ig|'s [=interest group/estimated size=]. + +</div> + +<div algorithm> +To <dfn>clear excess interest groups</dfn> with a [=list=] of [=interest groups=] |igs|, +an [=origin=] |owner|, and an integer |maxIgs|: + +1. Let |namesAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=strings=] + [=interest group/name=] and [=map/values=] are [=moments=] [=interest group/expiry=]. +1. [=list/For each=] |ig| of |igs|: + 1. Let |name| be |ig|'s [=interest group/name=]. + 1. [=map/Set=] |namesAndExpiry|[|name|] to |ig|'s [=interest group/expiry=]. +1. Set |namesAndExpiry| to |namesAndExpiry| [=map/sorted in descending order=] with |a| being less + than |b| if |a|'s [=map/value=] comes before |b|'s [=map/value=]. + + Note: Sorting interest groups based on their expiry in descending order so that if interest groups + need to be removed, the interest groups expiring soonest are the ones to be removed first. + +1. Let |names| be the [=map/get the keys|keys=] of |namesAndExpiry|. +1. [=list/For each=] |i| in [=the range=] from |maxIgs| to the [=set/size=] of |names|, exclusive: + 1. [=list/Remove=] the [=interest group=] from the user agent's [=interest group set=] whose + [=interest group/owner=] is |owner| and [=interest group/name=] is |names|[|i|]. + +</div> + <h2 id="leaving-interest-groups">Leaving Interest Groups</h2> {{Window/navigator}}.{{Navigator/leaveAdInterestGroup()}} removes a user from a particular interest @@ -467,8 +544,7 @@ dictionary AuctionAdInterestGroupKey { <div algorithm> -The <dfn for=Navigator method>leaveAdInterestGroup(group)</dfn> method steps -are: +The <dfn for=Navigator method>leaveAdInterestGroup(group)</dfn> method steps are: 1. Let |frameOrigin| be [=this=]'s [=relevant settings object=]'s [=environment settings object/origin=]. @@ -1071,7 +1147,7 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|: the current day in UTC, increment its count. If not, [=list/insert=] a new [=tuple=] of the time set to the current UTC day and a count of 1. 1. [=list/Replace=] the [=interest group=] that has |loadedIg|'s [=interest group/owner=] and - [=interest group/name=] in the browser’s [=interest group set=] with |loadedIg|. + [=interest group/name=] in the user agent's [=interest group set=] with |loadedIg|. </div> @@ -1091,7 +1167,7 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|: [=serializing an Infra value to a JSON string=] given |ad|. 1. [=list/Append=] |win| to |loadedIg|'s [=interest group/previous wins=]. 1. [=list/Replace=] the [=interest group=] that has |loadedIg|'s [=interest group/owner=] and - [=interest group/name=] in the browser’s [=interest group set=] with |loadedIg|. + [=interest group/name=] in the user agent's [=interest group set=] with |loadedIg|. </div> @@ -1940,8 +2016,8 @@ null |winningComponentConfig|: 1. [=map/Remove=] |browserSignals|["`topLevelSellerSignals`"]. 1. [=map/Remove=] |browserSignals|["`dataVersion`"]. - Note: Remove fields specific to {{ReportResultBrowserSignals}} which only sellers can learn about, - so that they are not passed to "`reportWin()`". + Note: Remove fields specific to {{ReportResultBrowserSignals}} which only sellers can learn about, + so that they are not passed to "`reportWin()`". 1. Return « |sellerSignals|, |browserSignals| ». </div> @@ -2280,7 +2356,7 @@ of the following global objects: 1. Set |ig|'s [=interest group/priority=] to |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/priority=]. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and - [=interest group/name=] in the browser’s [=interest group set=] with |ig|. + [=interest group/name=] in the user agent's [=interest group set=] with |ig|. 1. If |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/priority signals=] [=map/is not empty=]: 1. [=map/For each=] |k| → |v| of |global|'s @@ -2288,7 +2364,7 @@ of the following global objects: 1. If |v| is null, [=map/remove=] |ig|'s [=interest group/priority signals overrides=][|k|]. 1. Otherwise, [=map/set=] |ig|'s [=interest group/priority signals overrides=][|k|] to |v|. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and - [=interest group/name=] in the browser’s [=interest group set=] with |ig|. + [=interest group/name=] in the user agent's [=interest group set=] with |ig|. 1. Let |generatedBid| be |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/bid=]. 1. If |result| is a [=ECMAScript/normal completion=]: 1. Let |generatedBidIDL| be the result of [=converted to an IDL value|converting=] @@ -2928,7 +3004,7 @@ The <dfn for=Navigator method>updateAdInterestGroups()</dfn> method steps are: 1. Set |ig|'s [=interest group/next update after=] to the [=current wall time=] plus 24 hours. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and - [=interest group/name=] in the browser’s [=interest group set=] with |ig|. + [=interest group/name=] in the user agent's [=interest group set=] with |ig|. 1. <i id=abort-update>Abort update</i>: We jump here if some part of the [=interest group=] update failed. [=iteration/Continue=] to the next [=interest group=] update. @@ -3182,6 +3258,12 @@ An interest group is a [=struct=] with the following [=struct/items=]: </dl> +A <dfn>regular interest group</dfn> is an [=interest group=] whose +additional bid key (TODO: add link once the PR defines it is merged) is null. + +A <dfn>negative interest group</dfn> is an [=interest group=] whose +additional bid key (TODO: add link once the PR defines it is merged) is not null. + <h3 dfn-type=dfn>Interest group ad</h3> An interest group ad is a [=struct=] with the following [=struct/items=]: From 50c9f41c529b9d5c763e4a608e0b70aee333f95f Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Thu, 12 Oct 2023 14:45:35 -0400 Subject: [PATCH 2/7] Small re-wording. --- spec.bs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec.bs b/spec.bs index c4156e50b..b420ba276 100644 --- a/spec.bs +++ b/spec.bs @@ -450,16 +450,16 @@ To <dfn>build an interest group permissions url</dfn> given a [=origin=] |ownerO <h3 id="interest-group-storage-maintenance">Interest Group Storage Maintenance</h3> -There is a job that periodically performs maintenance on the user agent's [=interest group set=], -such as [=list/removing=] expired, or more than allowed [=interest groups=] which include the -following limitations: - * no more than 2000 owners; - * no more than 1000 [=regular interest groups=] per owner; - * no more than 20000 [=negative interest groups=] per owner; - * no more than <code>10\*1024\*1024</code> total size of interest groups per owner. +There is a job that periodically [=performs storage maintenance=] on the user agent's +[=interest group set=]. It performs operations such as [=list/removing=] expired or excess +[=interest groups=]. It's required that [=interest group set=] has no more than: + * 2000 owners; + * 1000 [=regular interest groups=] per owner; + * 20000 [=negative interest groups=] per owner; + * <code>10\*1024\*1024</code> total size of interest groups per owner. <div algorithm='maintenance'> -To perform storage maintenance: +To <dfn>perform storage maintenance</dfn>: 1. Let |ownersAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=origins=] [=interest group/owner=] and [=map/values=] are [=moments=] [=interest group/expiry=]. From c816b76478d5a0833abff28316dba98d8824e9a7 Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Thu, 12 Oct 2023 14:47:37 -0400 Subject: [PATCH 3/7] links to defs. --- spec.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec.bs b/spec.bs index b420ba276..fb2136e7d 100644 --- a/spec.bs +++ b/spec.bs @@ -453,10 +453,10 @@ To <dfn>build an interest group permissions url</dfn> given a [=origin=] |ownerO There is a job that periodically [=performs storage maintenance=] on the user agent's [=interest group set=]. It performs operations such as [=list/removing=] expired or excess [=interest groups=]. It's required that [=interest group set=] has no more than: - * 2000 owners; - * 1000 [=regular interest groups=] per owner; - * 20000 [=negative interest groups=] per owner; - * <code>10\*1024\*1024</code> total size of interest groups per owner. + * 2000 [=interest group/owners=]; + * 1000 [=regular interest groups=] per [=interest group/owner=]; + * 20000 [=negative interest groups=] per [=interest group/owner=]; + * <code>10\*1024\*1024</code> total size of [=interest groups=] per [=interest group/owner=]. <div algorithm='maintenance'> To <dfn>perform storage maintenance</dfn>: From cf7ebcb0a82b22ea689ce0802f0057924803a66c Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Thu, 12 Oct 2023 14:49:53 -0400 Subject: [PATCH 4/7] Link to estimated size. --- spec.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec.bs b/spec.bs index fb2136e7d..82f453e45 100644 --- a/spec.bs +++ b/spec.bs @@ -456,7 +456,8 @@ There is a job that periodically [=performs storage maintenance=] on the user ag * 2000 [=interest group/owners=]; * 1000 [=regular interest groups=] per [=interest group/owner=]; * 20000 [=negative interest groups=] per [=interest group/owner=]; - * <code>10\*1024\*1024</code> total size of [=interest groups=] per [=interest group/owner=]. + * <code>10\*1024\*1024</code> total [=interest group/estimated size=] of [=interest groups=] per + [=interest group/owner=]. <div algorithm='maintenance'> To <dfn>perform storage maintenance</dfn>: From 82cbc1aa7b44b51a50789220d0e121196c1a68c9 Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Mon, 16 Oct 2023 17:54:02 -0400 Subject: [PATCH 5/7] Address comments. --- spec.bs | 136 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 61 deletions(-) diff --git a/spec.bs b/spec.bs index 82f453e45..caab6a42a 100644 --- a/spec.bs +++ b/spec.bs @@ -65,6 +65,10 @@ spec: Shared Storage API; urlPrefix: https://wicg.github.io/shared-storage text: shared-storage-select-url; url: #permissionspolicy-shared-storage-select-url </pre> +<pre class=link-defaults> +spec:infra; type:dfn; text:user agent +</pre> + <style> /* Put nice boxes around each algorithm. */ [data-algorithm]:not(.heading) { @@ -135,7 +139,7 @@ When a user's interactions with a website indicate that the user may have a part 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 {{Window/navigator}}.{{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 +advertisement relevant to this interest to this user in the future. The [=user agent=] has an <dfn>interest group set</dfn>, a [=list=] of [=interest groups=] in which [=interest group/owner=] / [=interest group/name=] pairs are unique. @@ -217,9 +221,9 @@ This is detectable because it can change the set of fields that are read from th 1. If |interestGroup|'s [=interest group/owner=] is failure, then [=exception/throw=] a {{TypeError}}. 1. Optionally, [=exception/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 [=interest group/owner=]'s [=site=] - not being <a href="https://github.com/privacysandbox/attestation">enrolled</a>. + Note: This [=implementation-defined=] condition is intended to allow [=user agents=] to decline + for a number of reasons, for example the [=interest group/owner=]'s [=site=] not being + <a href="https://github.com/privacysandbox/attestation">enrolled</a>. 1. Set |interestGroup|'s [=interest group/name=] to |group|["{{GenerateBidInterestGroup/name}}"]. 1. Set |interestGroup|'s [=interest group/priority=] to |group|["{{AuctionAdInterestGroup/priority}}"]. @@ -307,12 +311,12 @@ This is detectable because it can change the set of fields that are read from th 1. Let |origin| be the result of [=parsing an https origin=] on |originStr|. 1. If |origin| is failure, then [=exception/throw=] a {{TypeError}}. 1. [=list/Append=] |origin| to |allowedReportingOrigins|. - 1. If [=list/size=] of |allowedReportingOrigins| is greater than 10, [=exception/throw=] + 1. If |allowedReportingOrigins|'s [=list/size=] &gt; 10, [=exception/throw=] a {{TypeError}}. 1. Set |igAd|'s [=interest group ad/allowed reporting origins=] to |allowedReportingOrigins|. 1. [=list/Append=] |igAd| to |interestGroup|'s |interestGroupField|. -1. If |interestGroup|'s [=interest group/estimated size=] is greater than 1048576, then - [=exception/throw=] a {{TypeError}}. +1. If |interestGroup|'s [=interest group/estimated size=] &gt; 1048576, then [=exception/throw=] a + {{TypeError}}. 1. Let |p| be [=a new promise=]. 1. Let |queue| be the result of [=starting a new parallel queue=]. 1. [=parallel queue/enqueue steps|Enqueue the following steps=] to |queue|: @@ -332,7 +336,7 @@ This is detectable because it can change the set of fields that are read from th 1. If the most recent entry in |interestGroup|'s [=interest group/join counts=] corresponds to the current day in UTC, increment its count. If not, [=list/insert=] a new [=tuple=] the time set to the current UTC day and a count of 1. - 1. Store |interestGroup| in the user agent's [=interest group set=]. + 1. Store |interestGroup| in the [=user agent=]'s [=interest group set=]. 1. Return |p|. </div> @@ -450,55 +454,60 @@ To <dfn>build an interest group permissions url</dfn> given a [=origin=] |ownerO <h3 id="interest-group-storage-maintenance">Interest Group Storage Maintenance</h3> -There is a job that periodically [=performs storage maintenance=] on the user agent's +There is a job that periodically [=performs storage maintenance=] on the [=user agent=]'s [=interest group set=]. It performs operations such as [=list/removing=] expired or excess -[=interest groups=]. It's required that [=interest group set=] has no more than: +[=interest groups=]. An [=interest group set=] must have no more than: * 2000 [=interest group/owners=]; * 1000 [=regular interest groups=] per [=interest group/owner=]; * 20000 [=negative interest groups=] per [=interest group/owner=]; * <code>10\*1024\*1024</code> total [=interest group/estimated size=] of [=interest groups=] per [=interest group/owner=]. -<div algorithm='maintenance'> +<div algorithm> To <dfn>perform storage maintenance</dfn>: -1. Let |ownersAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=origins=] - [=interest group/owner=] and [=map/values=] are [=moments=] [=interest group/expiry=]. -1. [=list/For each=] |ig| of the user agent's [=interest group set=]: +1. Let |ownersAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and + [=map/values=] are [=moments=]. + + Note: The [=map/key=] is from [=interest group/owner=], and [=map/value=] is from + [=interest group/expiry=]. + +1. Let |now| be the [=current wall time=]. +1. [=list/For each=] |ig| of the [=user agent=]'s [=interest group set=]: 1. Let |owner| be |ig|'s [=interest group/owner=]. + 1. If |ig|'s [=interest group/expiry=] is before |now|, then [=list/remove=] |ig| from the + [=user agent=]'s [=interest group set=] and [=iteration/continue=]. 1. If |ownersAndExpiry|[|owner|] [=map/exists=], then [=map/set=] |ownersAndExpiry|[|owner|] to |ig|'s [=interest group/expiry=] if it comes after |ownersAndExpiry|[|owner|]. 1. Otherwise, [=map/set=] |ownersAndExpiry|[|owner|] to |ig|'s [=interest group/expiry=]. -1. If [=map/size=] of |ownersAndExpiry| |i| &gt; 2000, then set |ownersAndExpiry| to +1. If |ownersAndExpiry|'s [=map/size=] &gt; 2000, then [=map/set=] |ownersAndExpiry| to |ownersAndExpiry| [=map/sorted in descending order=] with |a| being less than |b| if |a|'s - [=map/value=] comes before |b|'s [=map/value=]. + [=map/value=] comes before |b|'s [=map/value=], where [=map/values=] are [=interest group/expiry=]. - Note: Sorting owners based on their expiry in descending order so that if interest groups of some - owners need to be removed, the interest groups of owners expiring soonest are the ones to be - removed first. + Note: In order to remove interest groups of owners expiring soonest first, sort owners based on + their expiry in descending order, 1. Let |owners| be the [=map/get the keys|keys=] of |ownersAndExpiry|. -1. [=list/For each=] |i| in [=the range=] from 0 to the [=set/size=] of |owners|, exclusive: - 1. If |i| &ge; 2000, then [=list/remove=] [=interest groups=] from the user agent's +1. [=list/For each=] |i| in [=the range=] from 0 to |owners|'s [=set/size=], exclusive: + 1. If |i| &ge; 2000, then [=list/remove=] [=interest groups=] from the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owners|[|i|], and [=iteration/continue=]. - 1. Let |regularIgs| be a [=list=] of [=regular interest groups=] in the user agent's + 1. Let |regularIgs| be a [=list=] of [=regular interest groups=] in the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owners|[|i|]. - 1. If [=list/size=] of |regularIgs| &gt; 1000, then [=clear excess interest groups=] with + 1. If |regularIgs|'s [=list/size=] &gt; 1000, then [=clear excess interest groups=] with |regularIgs|, |owners|[|i|], and 1000. - 1. Let |negativeIgs| be a [=list=] of [=negative interest groups=] in the user agent's + 1. Let |negativeIgs| be a [=list=] of [=negative interest groups=] in the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owners|[|i|]. - 1. If [=list/size=] of |regularIgs| &gt; 20000, then [=Clear excess interest groups=] with + 1. If |negativeIgs|'s [=list/size=] &gt; 20000, then [=Clear excess interest groups=] with |negativeIgs|, |owners|[|i|], and 20000. -1. TODO: remove expired IGs. 1. [=list/For each=] |owner| of |owners|: - 1. Let |igs| be a [=list=] of [=interest groups=] in the user agent's [=interest group set=] whose - [=interest group/owner=] is |owner|, [=list/sorted in descending order=] with |a| being less - than |b| if |a|[=interest group/expiry=] comes before |b|[=interest group/expiry=]. + 1. Let |igs| be a [=list=] of [=interest groups=] in the [=user agent=]'s [=interest group set=] + whose [=interest group/owner=] is |owner|, [=list/sorted in descending order=] with |a| being + less than |b| if |a|[=interest group/expiry=] comes before |b|[=interest group/expiry=]. 1. Let |cumulativeSize| be 0. 1. [=list/For each=] |ig| of |igs|: 1. If the sum of |cumulativeSize| and |ig|'s [=interest group/estimated size=] - &gt; <code>10\*1024\*1024</code>, then [=list/remove=] |ig| from the user - agent's [=interest group set=]. + &gt; <code>10\*1024\*1024</code>, then [=list/remove=] |ig| from the [=user agent=]'s + [=interest group set=]. 1. Otherwise, increment |cumulativeSize| by |ig|'s [=interest group/estimated size=]. </div> @@ -507,20 +516,25 @@ To <dfn>perform storage maintenance</dfn>: To <dfn>clear excess interest groups</dfn> with a [=list=] of [=interest groups=] |igs|, an [=origin=] |owner|, and an integer |maxIgs|: -1. Let |namesAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=strings=] - [=interest group/name=] and [=map/values=] are [=moments=] [=interest group/expiry=]. +1. Let |namesAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=strings=] and + [=map/values=] are [=moments=]. + + Note: The [=map/key=] is from [=interest group/name=], and [=map/value=] is from + [=interest group/expiry=]. + 1. [=list/For each=] |ig| of |igs|: 1. Let |name| be |ig|'s [=interest group/name=]. 1. [=map/Set=] |namesAndExpiry|[|name|] to |ig|'s [=interest group/expiry=]. 1. Set |namesAndExpiry| to |namesAndExpiry| [=map/sorted in descending order=] with |a| being less - than |b| if |a|'s [=map/value=] comes before |b|'s [=map/value=]. + than |b| if |a|'s [=map/value=] comes before |b|'s [=map/value=], where [=map/values=] are + [=interest group/expiry=]. - Note: Sorting interest groups based on their expiry in descending order so that if interest groups - need to be removed, the interest groups expiring soonest are the ones to be removed first. + Note: In order to remove interest groups expiring soonest first, sort interest groups based on + their expiry in descending order. 1. Let |names| be the [=map/get the keys|keys=] of |namesAndExpiry|. -1. [=list/For each=] |i| in [=the range=] from |maxIgs| to the [=set/size=] of |names|, exclusive: - 1. [=list/Remove=] the [=interest group=] from the user agent's [=interest group set=] whose +1. [=list/For each=] |i| in [=the range=] from |maxIgs| to |names|'s [=set/size=], exclusive: + 1. [=list/Remove=] the [=interest group=] from the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owner| and [=interest group/name=] is |names|[|i|]. </div> @@ -562,7 +576,7 @@ The <dfn for=Navigator method>leaveAdInterestGroup(group)</dfn> method steps are 1. Let |owner| be |interestGroup|'s [=interest group descriptor/owner=]. 1. Let |name| be |interestGroup|'s [=interest group descriptor/name=]. 1. If |owner| is [=same origin=] with |frameOrigin|: - 1. [=list/Remove=] [=interest groups=] from the user agent's [=interest group set=] whose + 1. [=list/Remove=] [=interest groups=] from the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owner| and [=interest group/name=] is |name|. 1. Otherwise: 1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the @@ -580,7 +594,7 @@ The <dfn for=Navigator method>leaveAdInterestGroup(group)</dfn> method steps are 1. If |permission| is false, then [=queue a task=] to [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and do not run the remaining steps. 1. [=Queue a task=] to [=resolve=] |p| with `undefined`. - 1. [=list/Remove=] [=interest groups=] from the user agent's [=interest group set=] whose + 1. [=list/Remove=] [=interest groups=] from the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owner| and [=interest group/name=] is |name|. 1. Return |p|. @@ -638,9 +652,9 @@ The <dfn for=Navigator method>runAdAuction(|config|)</dfn> method steps are: 1. If |auctionConfig| is failure, then [=exception/throw=] a {{TypeError}}. 1. Optionally, [=exception/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 [=auction config/seller=]'s - [=site=] not being <a href="https://github.com/privacysandbox/attestation">enrolled</a>. + Note: This [=implementation-defined=] condition is intended to allow [=user agents=] to decline + for a number of reasons, for example the [=auction config/seller=]'s [=site=] not being + <a href="https://github.com/privacysandbox/attestation">enrolled</a>. 1. Let |p| be [=a new promise=]. 1. Let |configMapping| be |global|'s [=associated Document=]'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/fenced frame config mapping=]. @@ -1141,14 +1155,14 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|: To <dfn>update bid count</dfn> given a [=list=] of [=interest group=]s |igs|: 1. [=list/For each=] |ig| in |igs|: - 1. Let |loadedIg| be the [=interest group=] from the user agent’s [=interest group set=] + 1. Let |loadedIg| be the [=interest group=] from the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |ig|'s [=interest group/owner=] and whose [=interest group/name=] is |ig|'s [=interest group/name=], [=iteration/continue=] if none found. 1. If the most recent entry in |loadedIg|'s [=interest group/bid counts=] corresponds to the current day in UTC, increment its count. If not, [=list/insert=] a new [=tuple=] of the time set to the current UTC day and a count of 1. 1. [=list/Replace=] the [=interest group=] that has |loadedIg|'s [=interest group/owner=] and - [=interest group/name=] in the user agent's [=interest group set=] with |loadedIg|. + [=interest group/name=] in the [=user agent=]'s [=interest group set=] with |loadedIg|. </div> @@ -1156,7 +1170,7 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|: To <dfn>update previous wins</dfn> given a [=generated bid=] |bid|: 1. Let |ig| be |bid|'s [=generated bid/interest group=]. - 1. Let |loadedIg| be the [=interest group=] from the user agent’s [=interest group set=] + 1. Let |loadedIg| be the [=interest group=] from the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |ig|'s [=interest group/owner=] and whose [=interest group/name=] is |ig|'s [=interest group/name=], return if none found. 1. Let |win| be a new [=previous win=]. @@ -1168,7 +1182,7 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|: [=serializing an Infra value to a JSON string=] given |ad|. 1. [=list/Append=] |win| to |loadedIg|'s [=interest group/previous wins=]. 1. [=list/Replace=] the [=interest group=] that has |loadedIg|'s [=interest group/owner=] and - [=interest group/name=] in the user agent's [=interest group set=] with |loadedIg|. + [=interest group/name=] in the [=user agent=]'s [=interest group set=] with |loadedIg|. </div> @@ -1178,7 +1192,7 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose [=map/values=] are [=per buyer bid generators=]. 1. [=list/For each=] |buyer| in |auctionConfig|'s [=auction config/interest group buyers=]: - 1. [=list/For each=] |ig| of the user agent's [=interest group set=] whose + 1. [=list/For each=] |ig| of the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |buyer|: 1. Let |signalsUrl| be |ig|'s [=interest group/trusted bidding signals url=]. 1. Let |joiningOrigin| be |ig|'s [=interest group/joining origin=]. @@ -1265,8 +1279,8 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig 1. Let |queue| be the result of [=starting a new parallel queue=]. 1. If |auctionConfig|'s [=auction config/component auctions=] are not [=list/is empty|empty=]: 1. [=Assert=] |topLevelAuctionConfig| is null. - 1. Let |pendingComponentAuctions| be the [=list/size=] of |auctionConfig|'s - [=auction config/component auctions=]. + 1. Let |pendingComponentAuctions| be |auctionConfig|'s [=auction config/component auctions=]'s + [=list/size=]. 1. [=list/For each=] |component| in |auctionConfig|'s [=auction config/component auctions=], [=parallel queue/enqueue steps|enqueue the following steps=] to |queue|: 1. Let |compWinner| be the result of running [=generate and score bids=] with |component|, @@ -1318,7 +1332,7 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig 1. Set |auctionLevel| to "component-auction". 1. Set |componentAuctionExpectedCurrency| to the result of [=looking up per-buyer currency=] with |topLevelAuctionConfig| and |auctionConfig|'s [=auction config/seller=]. -1. Let |pendingBuyers| be the [=map/size=] of |bidGenerators|. +1. Let |pendingBuyers| be |bidGenerators|'s [=map/size=]. 1. [=map/For each=] |buyer| → |perBuyerGenerator| of |bidGenerators|, [=parallel queue/enqueue steps|enqueue the following steps=] to |queue|: 1. Let |buyerExperimentGroupId| be |allBuyersExperimentGroupId|. @@ -2187,7 +2201,7 @@ threshold when responding to [=query k-anonymity count=]. <div algorithm> To <dfn>increment k-anonymity count</dfn> given a |hashCode|: - 1. Ask the [=k-anonymity server=] to record that this user agent has seen |hashCode|. + 1. Ask the [=k-anonymity server=] to record that this [=user agent=] has seen |hashCode|. </div> <div algorithm> @@ -2231,8 +2245,8 @@ flexible implementation model. However, some key differences from traditional Worklets motivate us to create a new kind of script execution environment. In particular, they: - * Are not scoped to a particular {{Document}}, but are rather scoped to a user agent, as they are - spun up by [=interest groups=] in the user agent's [=interest group set=]. + * Are not scoped to a particular {{Document}}, but are rather scoped to a [=user agent=], as they + are spun up by [=interest groups=] in the [=user agent=]'s [=interest group set=]. * Consequently have a different, more flexible [=ECMAScript/agent cluster=] allocation model — specifically, they need not execute in the same [=ECMAScript/agent cluster=] as any {{Document}}, and for privacy reasons implementations may be motivated to enjoy this flexibility. @@ -2357,7 +2371,7 @@ of the following global objects: 1. Set |ig|'s [=interest group/priority=] to |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/priority=]. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and - [=interest group/name=] in the user agent's [=interest group set=] with |ig|. + [=interest group/name=] in the [=user agent=]'s [=interest group set=] with |ig|. 1. If |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/priority signals=] [=map/is not empty=]: 1. [=map/For each=] |k| → |v| of |global|'s @@ -2365,7 +2379,7 @@ of the following global objects: 1. If |v| is null, [=map/remove=] |ig|'s [=interest group/priority signals overrides=][|k|]. 1. Otherwise, [=map/set=] |ig|'s [=interest group/priority signals overrides=][|k|] to |v|. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and - [=interest group/name=] in the user agent's [=interest group set=] with |ig|. + [=interest group/name=] in the [=user agent=]'s [=interest group set=] with |ig|. 1. Let |generatedBid| be |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/bid=]. 1. If |result| is a [=ECMAScript/normal completion=]: 1. Let |generatedBidIDL| be the result of [=converted to an IDL value|converting=] @@ -2776,9 +2790,9 @@ Each {{InterestGroupReportingScriptRunnerGlobalScope}} has a and [=exception/Throw=] a {{TypeError}}. 1. 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 <a href="https://github.com/privacysandbox/attestation">enrolled</a>. + 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 + <a href="https://github.com/privacysandbox/attestation">enrolled</a>. 1. Set [=this=]'s [=relevant global object=]'s [=InterestGroupReportingScriptRunnerGlobalScope/report url=] to |parsedUrl|. </div> @@ -2837,7 +2851,7 @@ The <dfn for=Navigator method>updateAdInterestGroups()</dfn> method steps are: |owners|: 1. [=list/For each=] |owner| of |owners|: - 1. [=list/For each=] |originalInterestGroup| of the user agent's [=interest group set=] whose + 1. [=list/For each=] |originalInterestGroup| of the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owner| and [=interest group/next update after=] is before the [=current wall time=]: @@ -3005,7 +3019,7 @@ The <dfn for=Navigator method>updateAdInterestGroups()</dfn> method steps are: 1. Set |ig|'s [=interest group/next update after=] to the [=current wall time=] plus 24 hours. 1. [=list/Replace=] the [=interest group=] that has |ig|'s [=interest group/owner=] and - [=interest group/name=] in the user agent's [=interest group set=] with |ig|. + [=interest group/name=] in the [=user agent=]'s [=interest group set=] with |ig|. 1. <i id=abort-update>Abort update</i>: We jump here if some part of the [=interest group=] update failed. [=iteration/Continue=] to the next [=interest group=] update. From 77d1559f8b0a073b3bca1ff6d0cd703e2a5644e4 Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Wed, 18 Oct 2023 14:46:03 -0400 Subject: [PATCH 6/7] Address Orr's comments. --- spec.bs | 75 +++++++++++++++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/spec.bs b/spec.bs index c3fec75e0..3a3f94e3a 100644 --- a/spec.bs +++ b/spec.bs @@ -461,12 +461,19 @@ To <dfn>build an interest group permissions url</dfn> given a [=origin=] |ownerO There is a job that periodically [=performs storage maintenance=] on the [=user agent=]'s [=interest group set=]. It performs operations such as [=list/removing=] expired or excess -[=interest groups=]. An [=interest group set=] must have no more than: - * 2000 [=interest group/owners=]; - * 1000 [=regular interest groups=] per [=interest group/owner=]; - * 20000 [=negative interest groups=] per [=interest group/owner=]; - * <code>10\*1024\*1024</code> total [=interest group/estimated size=] of [=interest groups=] per +[=interest groups=]. An [=interest group set=] must respect the following limits: + * <dfn>Interest group set max owners</dfn> is 1000, which defines the max number of + [=interest group/owners=] in the [=user agent=]'s [=interest group set=]. + * <dfn>Max regular interest groups per owner</dfn> is 2000, which defines the max number of + [=regular interest groups=] in the [=user agent=]'s [=interest group set=] for an [=interest group/owner=]. + * <dfn>Max negative interest groups per owner</dfn> is 20000, which defines the max number of + [=negative interest groups=] in the [=user agent=]'s [=interest group set=] for an + [=interest group/owner=]. + * <dfn>Max interest groups total size per owner</dfn> is <code>10\*1024\*1024</code>, which + defines the max total [=interest group/estimated size|sizes=] of [=interest groups=] in the + [=user agent=]'s [=interest group set=] for an [=interest group/owner=]. It includs both + [=regular interest groups=] and [=negative interest groups=]. <div algorithm> To <dfn>perform storage maintenance</dfn>: @@ -475,7 +482,11 @@ To <dfn>perform storage maintenance</dfn>: [=map/values=] are [=moments=]. Note: The [=map/key=] is from [=interest group/owner=], and [=map/value=] is from - [=interest group/expiry=]. + [=interest group/expiry=]. It's used to determine a set of [=interest group/owners=] whose + [=interest groups=] will be removed from the [=user agent=]'s [=interest group set=] because the + number of distinct [=interest group/owners=] exceeds the [=Interest group set max owners=] limit. + It's sorted based on their [=map/values=] ([=interest group/expiry=]) in descending order, in + order to remove [=interest groups=] of [=interest group/owners=] expiring soonest first. 1. Let |now| be the [=current wall time=]. 1. [=list/For each=] |ig| of the [=user agent=]'s [=interest group set=]: @@ -485,25 +496,23 @@ To <dfn>perform storage maintenance</dfn>: 1. If |ownersAndExpiry|[|owner|] [=map/exists=], then [=map/set=] |ownersAndExpiry|[|owner|] to |ig|'s [=interest group/expiry=] if it comes after |ownersAndExpiry|[|owner|]. 1. Otherwise, [=map/set=] |ownersAndExpiry|[|owner|] to |ig|'s [=interest group/expiry=]. -1. If |ownersAndExpiry|'s [=map/size=] &gt; 2000, then [=map/set=] |ownersAndExpiry| to - |ownersAndExpiry| [=map/sorted in descending order=] with |a| being less than |b| if |a|'s - [=map/value=] comes before |b|'s [=map/value=], where [=map/values=] are [=interest group/expiry=]. - - Note: In order to remove interest groups of owners expiring soonest first, sort owners based on - their expiry in descending order, - +1. If |ownersAndExpiry|'s [=map/size=] &gt; [=interest group set max owners=], then [=map/set=] + |ownersAndExpiry| to |ownersAndExpiry| [=map/sorted in descending order=] with |a| being less than + |b| if |a|'s [=map/value=] comes before |b|'s [=map/value=], where [=map/values=] are + [=interest group/expiry=]. 1. Let |owners| be the [=map/get the keys|keys=] of |ownersAndExpiry|. 1. [=list/For each=] |i| in [=the range=] from 0 to |owners|'s [=set/size=], exclusive: - 1. If |i| &ge; 2000, then [=list/remove=] [=interest groups=] from the [=user agent=]'s - [=interest group set=] whose [=interest group/owner=] is |owners|[|i|], and [=iteration/continue=]. + 1. If |i| &ge; [=interest group set max owners=], then [=list/remove=] [=interest groups=] from + the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owners|[|i|], and + [=iteration/continue=]. 1. Let |regularIgs| be a [=list=] of [=regular interest groups=] in the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owners|[|i|]. - 1. If |regularIgs|'s [=list/size=] &gt; 1000, then [=clear excess interest groups=] with - |regularIgs|, |owners|[|i|], and 1000. + 1. If |regularIgs|'s [=list/size=] &gt; [=max regular interest groups per owner=], then + [=clear excess interest groups=] with |regularIgs| and [=max regular interest groups per owner=]. 1. Let |negativeIgs| be a [=list=] of [=negative interest groups=] in the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owners|[|i|]. - 1. If |negativeIgs|'s [=list/size=] &gt; 20000, then [=Clear excess interest groups=] with - |negativeIgs|, |owners|[|i|], and 20000. + 1. If |negativeIgs|'s [=list/size=] &gt; [=max negative interest groups per owner=], then + [=clear excess interest groups=] with |negativeIgs| and [=max negative interest groups per owner=]. 1. [=list/For each=] |owner| of |owners|: 1. Let |igs| be a [=list=] of [=interest groups=] in the [=user agent=]'s [=interest group set=] whose [=interest group/owner=] is |owner|, [=list/sorted in descending order=] with |a| being @@ -511,36 +520,24 @@ To <dfn>perform storage maintenance</dfn>: 1. Let |cumulativeSize| be 0. 1. [=list/For each=] |ig| of |igs|: 1. If the sum of |cumulativeSize| and |ig|'s [=interest group/estimated size=] - &gt; <code>10\*1024\*1024</code>, then [=list/remove=] |ig| from the [=user agent=]'s - [=interest group set=]. + &gt; [=max interest groups total size per owner=], then [=list/remove=] |ig| from the + [=user agent=]'s [=interest group set=]. 1. Otherwise, increment |cumulativeSize| by |ig|'s [=interest group/estimated size=]. </div> <div algorithm> -To <dfn>clear excess interest groups</dfn> with a [=list=] of [=interest groups=] |igs|, -an [=origin=] |owner|, and an integer |maxIgs|: +To <dfn>clear excess interest groups</dfn> with a [=list=] of [=interest groups=] |igs|, and an +integer |maxIgs|: -1. Let |namesAndExpiry| be a new [=ordered map=] whose [=map/keys=] are [=strings=] and - [=map/values=] are [=moments=]. - - Note: The [=map/key=] is from [=interest group/name=], and [=map/value=] is from - [=interest group/expiry=]. - -1. [=list/For each=] |ig| of |igs|: - 1. Let |name| be |ig|'s [=interest group/name=]. - 1. [=map/Set=] |namesAndExpiry|[|name|] to |ig|'s [=interest group/expiry=]. -1. Set |namesAndExpiry| to |namesAndExpiry| [=map/sorted in descending order=] with |a| being less - than |b| if |a|'s [=map/value=] comes before |b|'s [=map/value=], where [=map/values=] are - [=interest group/expiry=]. +1. Let |sortedIgs| be |igs| [=list/sorted in descending order=] with |a| being less + than |b| if |a|'s [=interest group/expiry=] comes before |b|'s [=interest group/expiry=]. Note: In order to remove interest groups expiring soonest first, sort interest groups based on their expiry in descending order. -1. Let |names| be the [=map/get the keys|keys=] of |namesAndExpiry|. -1. [=list/For each=] |i| in [=the range=] from |maxIgs| to |names|'s [=set/size=], exclusive: - 1. [=list/Remove=] the [=interest group=] from the [=user agent=]'s [=interest group set=] whose - [=interest group/owner=] is |owner| and [=interest group/name=] is |names|[|i|]. +1. [=list/For each=] |i| in [=the range=] from |maxIgs| to |igs|'s [=list/size=], exclusive: + 1. [=list/Remove=] |sortedIgs|[|i|] from the [=user agent=]'s [=interest group set=]. </div> From 8c0969ada20c8a64920ed5b3c36a7abdb5a006b8 Mon Sep 17 00:00:00 2001 From: Qingxin Wu <qingxinwu@google.com> Date: Tue, 24 Oct 2023 23:11:10 -0400 Subject: [PATCH 7/7] Explain that impls can choose their own limits. --- spec.bs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec.bs b/spec.bs index eed444ecf..997642c91 100644 --- a/spec.bs +++ b/spec.bs @@ -481,7 +481,9 @@ To <dfn>build an interest group permissions url</dfn> given a [=origin=] |ownerO There is a job that periodically [=performs storage maintenance=] on the [=user agent=]'s [=interest group set=]. It performs operations such as [=list/removing=] expired or excess -[=interest groups=]. An [=interest group set=] must respect the following limits: +[=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: * <dfn>Interest group set max owners</dfn> is 1000, which defines the max number of [=interest group/owners=] in the [=user agent=]'s [=interest group set=]. * <dfn>Max regular interest groups per owner</dfn> is 2000, which defines the max number of