共享存储 API

社区组报告草案,

此版本:
https://github.com/WICG/shared-storage
问题跟踪:
GitHub
规范内联
编辑:
(Google)

摘要

共享存储是一个有意不按顶级可遍历站点分区的存储 API(当然,它仍按上下文源分区!)。 为限制对用户的跨站点重新识别,共享存储中的数据只能在具有精心构造输出门控的受限环境中读取。

本文档状态

本规范由 Web 平台孵化器 社区组发布。 它不是 W3C 标准,也不在 W3C 标准化轨道上。 请注意,根据 W3C 社区 贡献者许可协议(CLA),适用有限的退出权及其他条件。 了解更多关于 W3C 社区组和商务组的信息。

1. 简介

本节不是规范性的。

为了防止跨站点用户跟踪,浏览器正在按顶级可遍历站点来分区所有形式的存储;参见客户端存储分区。但是,目前有许多 合法用例依赖未分区存储。

本文档引入一种新的存储 API,它有意不按顶级可遍历站点分区(但仍按上下文源分区),以服务于若干需要未分区存储的用例。为限制 对用户的跨站点重新识别,共享存储中的数据只能在两个受限环境中读取。一个这样的环境称为 worklet,而来自 worklet 的任何输出都以围栏框架私有聚合报告的形式给出。随着时间推移,标准中可能会包含其他 worklet 输出 门控。 另一个受限环境位于围栏框架的内容中,即它解析对 disableUntrustedNetwork() 的调用之后,该调用会防止读取到的数据被共享到该框架之外。

a.example 以跨站点保持一致的方式随机将用户分配到组。

a.example iframe 内:

function generateSeed() {}
await window.sharedStorage.worklet.addModule('experiment.js');

// 仅当尚不存在跨站点种子时,才将它写入 a.example 的存储。
window.sharedStorage.set('seed', generateSeed(), { ignoreIfPresent: true });

let fencedFrameConfig = await window.sharedStorage.selectURL(
  'select-url-for-experiment',
  [
    {url: "blob:https://a.example/123…", reportingMetadata: {"click": "https://report.example/1..."}},
    {url: "blob:https://b.example/abc…", reportingMetadata: {"click": "https://report.example/a..."}},
    {url: "blob:https://c.example/789…"}
  ],
  { data: { name: 'experimentA' } }
);

// 假设围栏框架 'my-fenced-frame' 已经被附加。
document.getElementById('my-fenced-frame').config = fencedFrameConfig;

experiment.js worklet 脚本内部:

class SelectURLOperation {
  hash(experimentName, seed) {}

  async run(urls, data) {
    const seed = await this.sharedStorage.get('seed');
    return hash(data.name, seed) % urls.length;
  }
}
register('select-url-for-experiment', SelectURLOperation);

2. SharedStorageWorklet 接口

SharedStorageWorklet 对象允许开发者提供模块脚本来处理共享存储数据,然后通过一个或多个 输出门控输出结果。目前有两个输出门控,即私有 聚合输出门控和 URL 选择 输出门控。
typedef (USVString or FencedFrameConfig) SharedStorageResponse;
[Exposed=(Window)]
interface SharedStorageWorklet : Worklet {
  Promise<SharedStorageResponse> selectURL(DOMString name,
                               sequence<SharedStorageUrlWithMetadata> urls,
                               optional SharedStorageRunOperationMethodOptions options = {});
  Promise<any> run(DOMString name,
                   optional SharedStorageRunOperationMethodOptions options = {});
};

每个 SharedStorageWorklet 都有一个关联的布尔值 addModule initiated,初始化为 false。

每个 SharedStorageWorklet 都有一个关联的 USVString data origin,初始化为 "context-origin"

每个 SharedStorageWorklet 都有一个关联的布尔值 has cross-origin data origin, 初始化为 false。

因为通过 addModule() 为同一个 SharedStorageWorklet 添加多个模块脚本,会让调用者能够将来自共享存储的数据存储在模块脚本中定义的全局变量里,然后通过之后对 addModule() 的调用泄露这些数据,因此每个 SharedStorageWorklet 只能调用一次 addModule()addModule initiated 布尔值使得强制执行此限制成为可能。

当为某个 worklet 调用 addModule() 时,它将运行check if addModule is allowed and update state,并且如果结果是 "DisallowedDueToNonPreferenceError",或者如果结果是 "DisallowedDueToPreferenceError" 且该 worklet 的has cross-origin data origin 为 false,它将导致 addModule() 失败,详见 § 2.2.6 针对 addModule() 的猴子补丁

要在给定环境设置对象 environment origin 的情况下,check if user preference setting allows access to shared storage,运行以下步骤:
  1. 根据需要使用 environmentorigin 中可用的值,执行一个实现定义的算法,以返回 true 或 false。

要在给定环境设置对象 environment origin 和布尔值 allowedInOpaqueOriginContext 的情况下,determine whether shared storage is allowed by context,运行这些步骤:
  1. 如果 environment 不是安全上下文,则返回 false。

  2. 如果 allowedInOpaqueOriginContext 为 false,且 environment不透明源,则返回 false。

  3. 如果 origin不透明源,则返回 false。

  4. globalObject当前 Realm全局对象

  5. 断言globalObjectWindowSharedStorageWorkletGlobalScope

  6. 如果 globalObjectWindow, 并且在 "shared-storage"、globalObject关联文档origin 上运行Is feature enabled in document for origin? 的结果 返回 false,则返回 false。

  7. 如果以 origin 运行 obtain a site 的结果没有登记,则返回 false。

  8. 返回 true。

下面是使用算法 determine whether shared storage is allowed by contextcheck if user preference setting allows access to shared storage 的场景:
要在给定 SharedStorageWorklet workletURL moduleURLRecord 的情况下,check if addModule is allowed and update state, 运行以下步骤:
  1. 如果 workletaddModule initiated 为 true, 返回 "DisallowedDueToNonPreferenceError"。

  2. workletaddModule initiated 设置为 true。

  3. workletDataOrigin当前设置对象

  4. 如果 workletdata origin"script-origin",将 workletDataOrigin 设置为 moduleURLRecord

  5. 否则,如果 workletdata origin 不是 "context-origin"

    1. customOriginUrl 为在 workletdata origin 上运行 URL 解析器的结果。

    2. 如果 customOriginUrl 不是有效的 URL, 返回 "DisallowedDueToNonPreferenceError"。

    3. workletDataOrigin 设置为 customOriginUrl

  6. hasCrossOriginDataOrigin 为 false。

  7. 如果 workletDataOrigin当前设置对象不是同源,则将 hasCrossOriginDataOrigin 设置为 true。

  8. allowedInOpaqueOriginContexthasCrossOriginDataOrigin

  9. 如果在给定当前设置对象workletDataOriginallowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则返回 "DisallowedDueToNonPreferenceError"。

  10. worklethas cross-origin data origin 设置为 hasCrossOriginDataOrigin

  11. 如果在给定当前设置对象workletDataOrigin 的情况下运行 check if user preference setting allows access to shared storage 的结果为 false,则返回 "DisallowedDueToPreferenceError"。

  12. 返回 "Allowed"。

此外,每个 SharedStorageWorklet全局作用域列表(初始为空)最多只能包含其worklet 全局作用域类型的一个实例,即 SharedStorageWorkletGlobalScope

2.1. SharedStorageWorklet 上运行操作方法

要在给定 SharedStorageWorklet workletDOMString operationNamelist of strings urlList workletDataOriginnavigable navigableSharedStorageRunOperationMethodOptions options预指定报告参数或 null preSpecifiedParams 以及聚合协调者或 null aggregationCoordinator 的情况下,get the select-url result index,运行以下步骤。此算法将返回一个由如下内容组成的元组: 一个会解析为 unsigned longpromise,其值是从 urlList 中选出的 URL 的索引;以及一个布尔值,指示是否应对顶级可遍历的预算进行计费
  1. promise 为一个新的promise

  2. windowworklet相关设置对象

  3. 断言windowWindow

  4. 如果 window浏览上下文为 null,则返回由(以 TypeError 拒绝的 promise,true)组成的元组

  5. 如果 window关联文档不是完全活动的,返回由(以 TypeError 拒绝的 promise,true)组成的元组

  6. 断言worklet全局作用域大小为 1。

  7. globalScopeworklet全局作用域[0]。

  8. moduleMapKeyTuples 为在 globalScope相关设置对象模块映射上运行 get the keys 的结果。

  9. 断言moduleMapKeyTuples 具有大小 1。

  10. moduleURLRecordmoduleMapKeyTuples[0][0]。

  11. savedQueryNameoptions["savedQuery"]。

  12. 如果 savedQueryName 是一个字符串且不是 空字符串,则:

    1. callbackTask 为在给定 windowurlListpromise 的情况下运行 obtain a callback to process the saved index result 的结果。

    2. savedIndex 为在 navigableworkletDataOriginmoduleURLRecordoperationNamesavedQueryNamecallbackTask 上运行 get the index for a saved query 的结果。

    3. 如果 savedIndex 是 "pending callback",则返回元组 (promise, false)。

      注: callbackTask 现在被 存储起来,以便在先前获得的 worklet 代理完成其为此查询选择索引的操作时运行。当运行 callbackTask 的步骤时,promise 将被解析。

    4. 如果 savedIndexunsigned long, 则:

      1. DOM 操作任务源上,给定 window排队一个全局任务来运行 callbackTask 的步骤,并给定 savedIndex

        注: 运行 callbackTask 的步骤将解析 promise

      2. 返回元组 (promise, false)。

    5. 断言 savedIndex 是 "pending current operation"。

  13. globalScopeworklet 事件循环排队一个任务来执行以下步骤:

    1. operationMapglobalScope操作映射

    2. 如果 operationMap包含 operationName,则在DOM 操作任务源上,给定 window排队一个全局任务以用 TypeError 拒绝 promise,并中止这些步骤。

      注: 如果 register() 从未以 operationName 调用,则可能发生这种情况。

    3. 断言operationMap[operationName] 的 关联 Realmthis相关 Realm

    4. operationoperationMap[operationName],并转换RunFunctionForSharedStorageSelectURLOperation

    5. privateAggregationCompletionTask 为在给定 workletDataOriginpreSpecifiedParamsaggregationCoordinator 的情况下设置私有 聚合作用域的结果。

    6. argumentsList列表 « urlList »。

    7. 如果 options["data"] 存在,将它附加argumentsList

    8. indexPromise 为以 argumentsList 调用 operation 的结果。

    9. indexPromise 作出反应

      如果它以值 index 兑现:
      1. 如果 index 大于 urlList大小,则:

        1. 如果 savedQueryName 是一个字符串且不是空 字符串,则使用 windownavigableworkletDataOriginmoduleURLRecordoperationNamesavedQueryName默认 selectURL 索引运行 store the index for a saved query

        2. DOM 操作 任务源上,给定 window排队一个全局任务以用 TypeError 拒绝 promise, 并中止这些步骤。

        注: 结果 索引超出了输入 urls 的大小。这违反了 selectURL() 协议,我们不知道应选择哪个 url。

        否则:

        1. 如果 savedQueryName 是一个字符串且不是空 字符串,则使用 windownavigableworkletDataOriginmoduleURLRecordoperationNamesavedQueryNameindex 运行 store the index for a saved query

        2. DOM 操作 任务源上,给定 window排队一个全局任务以用 index 解析 promise

        3. 运行 privateAggregationCompletionTask

      如果它被拒绝:
      1. 如果 savedQueryName 是一个字符串且不是空字符串,则使用 windownavigableworkletDataOriginmoduleURLRecordoperationNamesavedQueryName默认 selectURL 索引运行 store the index for a saved query

      2. DOM 操作任务 源上,给定 window排队一个全局任务以用 TypeError 拒绝 promise

        注: 这表示 operationCtor 的 run() 方法遇到错误 (其中 operationCtorregister() 中的参数),或者结果 index 是非整数值,这违反了 selectURL() 协议,我们不知道应选择哪个 url。

      3. 运行 privateAggregationCompletionTask

  14. 返回元组 (promise, true)。

要在给定 SharedStorageWorklet worklet环境设置对象 environmentDocument documentsequence of SharedStorageUrlWithMetadata urlslist of strings urlListnavigable navigableSharedStorageRunOperationMethodOptions options围栏框架配置映射 fencedFrameConfigMappingurn uuid urn布尔值 shouldChargeTopLevelBudgets布尔值 shouldUseDefaultIndex,以及 unsigned long resultIndex 的情况下,handle the result of selecting an index,执行以下步骤:
  1. site 为使用 document运行 obtain a site 的结果。

  2. remainingBudget 为使用 environmentsite 运行 determine remaining navigation budget 的结果。

  3. pendingBitsurlList大小的以 2 为底的对数。

  4. 如果 shouldChargeTopLevelBudgets 为 true:

    1. pageBudgetResult 为使用 navigablesitependingBits 运行 charge shared storage top-level traversable budgets 的结果。

    2. 如果 pageBudgetResult 为 false,则将 shouldUseDefaultIndex 设置为 true。

  5. 如果 pendingBits 大于 remainingBudget,则将 shouldUseDefaultIndex 设置为 true。

  6. 如果 shouldUseDefaultIndex 为 true,则将 resultIndex 设置为默认 selectURL 索引

  7. finalConfig 为一个新的围栏框架配置

  8. finalConfig映射 URL设置为 urlList[resultIndex]。

  9. finalConfiga "pending shared storage budget debit" field 设置为 pendingBits

  10. fencedFrameConfigMapping 上使用 urnfinalConfig Finalize a pending config

  11. resultURLWithMetadataurls[resultIndex]。

  12. 如果 resultURLWithMetadata 具有字段 "reportingMetadata",则使用 resultURLWithMetadata["reportingMetadata"] 运行 register reporting metadata

  13. 如果 options["keepAlive"] 为 false,则使用 worklet 运行 terminate a worklet global scope

selectURL(name, urls, options) 方法步骤为:
  1. resultPromise 为一个新的promise

  2. 如果 thisaddModule initiated 为 false, 则返回一个以 TypeError 拒绝的 promise

  3. windowthis相关设置对象

  4. 断言windowWindow

  5. contextwindow浏览上下文

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  7. preSpecifiedParams 为在给定 optionscontext 的情况下 获得预指定报告 参数的结果。

  8. 如果 preSpecifiedParamsDOMException, 则返回一个 preSpecifiedParams 拒绝的 promise。

  9. aggregationCoordinator 为在给定 options 的情况下获得聚合协调者的结果。

  10. 如果 aggregationCoordinatorDOMException, 则返回一个 aggregationCoordinator 拒绝的 promise。

  11. documentcontext活动文档

  12. 如果 this全局作用域,则 返回一个以 TypeError 拒绝的 promise

    注: 如果在 selectURL() 之前调用 addModule(), 就可能发生这种情况。

  13. 断言this全局作用域大小为 1。

  14. globalScopethis全局作用域[0]。

  15. workletDataOriginglobalScopeRealm设置对象

  16. 如果在 "shared-storage-select-url"、 documentworkletDataOrigin 上运行Is feature enabled in document for origin? 的结果返回 false, 则返回一个以 TypeError 拒绝的 promise

  17. 如果对 globalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  18. 如果 urls 为空,或者 urls大小大于 8,则返回一个以 TypeError 拒绝的 promise

    注: 此处选择 8,是为了使每次 selectURL() 调用在结果围栏框架被点击时,最多只能泄露 log2(8) = 3 位信息。每次调用的信息量并不多。

  19. urlList 为一个空列表

  20. urls 中的每个 urlWithMetadata 执行

    1. 如果 urlWithMetadata 没有字段 "url",则返回一个以 TypeError 拒绝的 promise

    2. 否则,令 urlStringurlWithMetadata["url"]。

    3. serializedUrl 为使用 urlString 运行 get the canonical URL string if valid 的结果。

    4. 如果 serializedUrl 为 undefined,则返回一个以 TypeError 拒绝的 promise

    5. 否则,将 serializedUrl 附加urlList

    6. 如果 urlWithMetadata 具有字段 "reportingMetadata":

      1. reportingMetadataurlWithMetadata["reportingMetadata"]。

      2. 如果使用 reportingMetadata 运行 validate reporting metadata 的结果为 false,则以 TypeError 拒绝 resultPromise 并中止这些步骤。

  21. navigablewindow关联文档节点 navigable

  22. fencedFrameConfigMappingnavigable可遍历 navigable围栏框架配置 映射

  23. pendingConfig 为一个新的围栏框架配置

  24. urn 为在 fencedFrameConfigMapping 上使用 pendingConfig 运行 store a pending config 的结果。

  25. 如果 urn 是 failure,则返回一个以 TypeError 拒绝的 promise

  26. environmentwindow相关设置对象

  27. allowedInOpaqueOriginContextthishas cross-origin data origin

  28. 如果在给定 environmentworkletDataOriginallowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  29. 如果在给定 environmentworkletDataOrigin 的情况下运行 check if user preference setting allows access to shared storage 的结果为 false:

    1. 如果 thishas cross-origin data origin 为 false,则返回一个以 TypeError 拒绝的 promise

  30. 如果 options["resolveToConfig"] 为 true,则用 pendingConfig 解析 resultPromise

  31. 否则,用 urn 解析 resultPromise

  32. 令 (indexPromise, shouldChargeTopLevelBudgets) 为在给定 thisnameurlListworkletDataOriginnavigableoptionspreSpecifiedParamsaggregationCoordinator 的情况下运行 get the select-url result index 的结果。

  33. indexPromiseresultIndex 兑现时,在给定 workletenvironmentdocumenturlsurlListnavigableoptionsfencedFrameConfigMappingurnshouldChargeTopLevelBudgets、 false 和 resultIndex 的情况下,运行 handle the result of selecting an index

  34. indexPromise 被拒绝时,在给定 workletenvironmentdocumenturlsurlListnavigableoptionsfencedFrameConfigMappingurnshouldChargeTopLevelBudgets、 true 和默认 selectURL 索引的情况下,运行 handle the result of selecting an index

  35. 返回 resultPromise

run(name, options) 方法 步骤为:
  1. promise 为一个新的promise

  2. 如果 thisaddModule initiated 为 false, 则返回一个以 TypeError 拒绝的 promise

  3. windowthis相关设置对象

  4. 断言windowWindow

  5. contextwindow浏览上下文

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise。

  7. preSpecifiedParams 为在给定 optionscontext 的情况下获得预指定报告 参数的结果。

  8. 如果 preSpecifiedParamsDOMException, 则返回一个以 preSpecifiedParams 拒绝的 promise。

  9. aggregationCoordinator 为在给定 options 的情况下获得聚合协调者的结果。

  10. 如果 aggregationCoordinatorDOMException, 则返回一个以 aggregationCoordinator 拒绝的 promise。

  11. 如果 this全局作用域,则 返回一个以 TypeError 拒绝的 promise

    注: 如果在 run() 之前调用 addModule(), 就可能发生这种情况。

  12. 断言this全局作用域大小为 1。

  13. globalScopethis全局作用域[0]。

  14. 如果对 globalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  15. workletDataOriginglobalScopeRealm设置对象

  16. allowedInOpaqueOriginContextthishas cross-origin data origin

  17. 如果在给定 windowworkletDataOriginallowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则以 TypeError 拒绝 promise

  18. 如果在给定 windowworkletDataOrigin 的情况下运行 check if user preference setting allows access to shared storage 的结果为 false:

    1. 如果 thishas cross-origin data origin 为 false,则以 TypeError 拒绝 promise

    2. 否则,用 undefined 解析 promise

    3. 返回 promise

  19. 返回 promise,并立即在给定 window 的情况下获得 worklet 代理,并在该代理中运行这些步骤的其余部分:

    注: promise 的解析 应该早于 SharedStorageWorkletGlobalScope 内部的执行,并且不依赖于该执行。 这是因为共享存储是一种未分区存储,而 SharedStorageWorkletGlobalScope 可以访问跨站点数据,这些数据不应通过 run() (通过其成功/错误结果)泄露。

    1. DOM 操作任务源上,给定 window排队一个全局任务,以用 undefined 解析 promise

    2. 如果 globalScope相关设置对象模块映射不是

      1. operationMapthisSharedStorageWorkletGlobalScope操作 映射

      2. 如果 operationMap 包含 name

        1. 断言operationMap[name] 的关联 Realmthis相关 Realm

        2. operationoperationMap[name], 并转换Function

        3. privateAggregationCompletionTask 为在给定 workletDataOriginpreSpecifiedParamsaggregationCoordinator 的情况下设置私有聚合 作用域的结果。

        4. argumentsList 为一个新的列表

        5. 如果 options["data"] 存在,将它附加argumentsList

        6. argumentsList 和 "report" 调用 operation

        7. 等待 operation 完成运行(如适用)。

        8. 运行 privateAggregationCompletionTask

    3. 如果 options["keepAlive"] 为 false:

      1. 等待 operation 完成运行(如适用)。

      2. this 运行 terminate a worklet global scope

要在给定 SharedStorageRunOperationMethodOptions options 的情况下,obtain the aggregation coordinator,执行以下 步骤。它们返回一个聚合协调者、null 或 DOMException
  1. 如果 options["privateAggregationConfig"] 不存在,返回 null。

  2. 如果 options["privateAggregationConfig"]["aggregationCoordinatorOrigin"] 不存在,返回 null。

  3. 返回在给定 options["privateAggregationConfig"]["aggregationCoordinatorOrigin"] 的情况下获得私有聚合 协调者的结果。

要在给定 SharedStorageRunOperationMethodOptions options浏览上下文 context 的情况下,obtain the pre-specified report parameters,执行以下步骤。它们 返回预指定报告参数、null,或 DOMException
  1. 如果 options["privateAggregationConfig"] 不存在,返回 null。

  2. privateAggregationConfigoptions["privateAggregationConfig"]。

  3. contextId 为 null。

  4. 如果 privateAggregationConfig["contextId"] 存在,将 contextId 设置为 privateAggregationConfig["contextId"]。

  5. 如果 contextId长度大于 64,则返回一个名称为 "DataError" 的新 DOMException

  6. filteringIdMaxBytes默认过滤 ID 最大字节数

  7. 如果 privateAggregationConfig["filteringIdMaxBytes"] 存在,将 filteringIdMaxBytes 设置为 privateAggregationConfig["filteringIdMaxBytes"]。

  8. 如果 filteringIdMaxBytes包含有效过滤 ID 最大字节数范围中, 返回一个名称为 "DataError" 的新 DOMException

  9. 如果 context围栏框架配置实例 不是 null:

    1. 如果 filteringIdMaxBytes 不是默认过滤 ID 最大字节数,或者 contextId 不是 null,则返回一个名称为 "DataError" 的新 DOMException

  10. maxContributions 为 null。

  11. 如果 privateAggregationConfig["maxContributions"] 存在,将 maxContributions 设置为 privateAggregationConfig["maxContributions"]。

  12. 如果 maxContributions 为零,返回一个名称为 "DataError" 的新 DOMException

  13. 返回一个新的预指定报告参数,其项目为:

    上下文 ID

    contextId

    过滤 ID 最大字节数

    filteringIdMaxBytes

    最大贡献数

    maxContributions

要在给定 workletDataOrigin预指定报告参数或 null preSpecifiedParams 以及聚合协调者或 null aggregationCoordinator 的情况下,set up the Private Aggregation scopes,执行以下步骤。它们返回一个 算法。

注: 返回的算法应在关联操作 完成时运行。

  1. batchingScope 为一个新的批处理作用域

  2. debugScope 为一个新的调试作用域

  3. privateAggregationTimeout 为 null。

  4. hasRunPrivateAggregationCompletionTask 为 false。

  5. privateAggregationCompletionTask 为一个执行 以下步骤的算法:

    1. 如果 hasRunPrivateAggregationCompletionTask,返回。

    2. hasRunPrivateAggregationCompletionTask 设置为 true。

    3. 给定 debugScopeMark a debug scope complete

    4. 给定 batchingScopeworkletDataOrigin、 "shared-storage" 和 privateAggregationTimeoutProcess contributions for a batching scope

  6. 如果 aggregationCoordinator 不为 null,给定 aggregationCoordinatorbatchingScope set the aggregation coordinator for a batching scope

  7. 如果 preSpecifiedParams 不为 null:

    1. isDeterministicReport 为给定 preSpecifiedParams 运行 determine if a report should be sent deterministically 的结果。

    2. 如果 isDeterministicReport

      1. privateAggregationTimeout 设置为当前挂钟时间加上 确定性 操作超时持续时间

    3. 给定 preSpecifiedParamsbatchingScopeSet the pre-specified report parameters for a batching scope

    4. 如果 isDeterministicReport并行运行以下步骤:

      1. 等待直到 privateAggregationTimeout

      2. 运行 privateAggregationCompletionTask

  8. 返回 privateAggregationCompletionTask

deterministic operation timeout duration 是一个实现定义的非负持续时间,它控制 如果共享存储操作正在触发确定性报告,那么该操作可以进行私有聚合贡献的时长,以及等价地, 该报告应在操作开始后何时发送。

2.2. 针对 Worklets 的猴子补丁

本规范将对 Worklet 标准作出一些修改,以适应共享存储的需要。

2.2.1. 针对 set up a worklet environment settings object 的猴子补丁

set up a worklet environment settings object 算法将需要包含一个额外的参数:Worklet worklet。定义 settingsObject的步骤应修改如下:

  1. settingsObject 为一个新的环境设置对象,其算法定义如下:

    ......

    1. workletGlobalScoperealmExecutionContext 的 Realm 组件的全局对象

    2. 如果 workletGlobalScope 不是 SharedStorageWorkletGlobalScope, 返回 origin

    3. 断言 worklet 是一个 SharedStorageWorklet

    4. 如果 workletdata origin"context-origin",返回 outsideSettings

    5. 否则,如果 data origin"script-origin"

      1. pendingAddedModulesworklet已添加模块列表的一个克隆

      2. 断言pendingAddedModules大小为 1。

      3. moduleURLpendingAddedModules[0]。

      4. 返回 moduleURL

    6. 否则,令 customOriginUrl 为在 data origin 上运行 URL 解析器的结果。

    7. 断言 customOriginUrl 是一个有效的 URL

    8. 返回 customOriginUrl

    ......

2.2.2. 针对 create a worklet global scope 的猴子补丁

create a worklet global scope 算法将需要被修改,以传入 worklet 参数:

  1. insideSettings 为在给定 realmExecutionContextoutsideSettingsworklet 的情况下,设置 worklet 环境 设置对象的结果。

2.2.3. 针对 fetch a worklet script graph 的猴子补丁

算法 fetch a worklet script graph 会调用 fetch a worklet/module worker script graph 算法,后者接受一个算法参数 processCustomFetchResponse。该 processCustomFetchResponse 参数的定义将需要在步骤 "5. Fetch request, ..." 之前包含以下步骤:

  1. 如果 fetchClient全局对象SharedStorageWorkletGlobalScope

    1. request重定向模式设置为 "error"。

      注: 对于共享存储,模块脚本请求不允许 重定向。有了这个限制,就有可能在 SharedStorageWorkletGlobalScope 被创建后立即定义并使用获取 realm设置对象的算法(如 § 2.2.1 针对 set up a worklet environment settings object 的猴子补丁 中所述),因为该源不会改变。 此限制可能会在该设计未来的迭代中移除。如果允许重定向,大概获取 realm设置对象的算法应被更新为在收到最终请求的 响应之后,返回最终请求的URL,并且用户偏好检查应只在该时点之后进行。

    2. 如果 fetchClientsettingsObject不是同源

      1. dataOriginValuesettingsObject序列化

      2. 断言 dataOriginValue 不是 null。

      3. 标头 (`Sec-Shared-Storage-Data-Origin`, dataOriginValue) 附加request标头列表

2.2.4. `Shared-Storage-Cross-Origin-Worklet-Allowed` HTTP 响应标头

`Shared-Storage-Cross-Origin-Worklet-Allowed` HTTP 响应标头连同传统 CORS 标头,可用于授予跨源站点从模块脚本的 URL创建 worklet 的权限,并允许在 worklet 上运行后续操作时,使用 模块脚本的 URL作为 用于访问共享存储数据的 data partition origin,即在 § 2.2.1 针对 set up a worklet environment settings object 的猴子补丁中设置的,它会成为所有 SharedStorage 调用 obtain a shared storage bottle map 时使用的

加载跨源脚本的 worklet 依赖 CORS 作为基线权限机制,以指示受信任的外部源。然而,仅有 CORS 不足以创建一个带有跨源脚本且其data partition origin 为脚本源的 worklet。 不同于简单资源共享,worklet 允许创建者站点在目标源的上下文中执行 JavaScript。为了确保安全, 需要脚本源额外提供一个响应标头: `Shared-Storage-Cross-Origin-Worklet-Allowed`。

2.2.5. 针对 HTTP fetch 的猴子补丁

将需要把步骤添加到 HTTP fetch 算法中。

注: 谨慎考虑安全影响是服务模块脚本的站点的责任: 当模块脚本的 URL和 worklet 的创建者 Window 源不是同源时, 通过在模块脚本响应上发送宽松的 CORS 标头和 `Shared-Storage-Cross-Origin-Worklet-Allowed` 标头,服务器将授予 worklet 的创建及 worklet 上的后续操作,同时允许 worklet 使用该 worklet 的脚本的作为 用于访问共享存储数据的,即data partition origin。例如,worklet 的 创建者 Window 可通过调用 selectURL()run(), 污染并耗尽 worklet 源的站点剩余导航预算,其中 worklet 源是全局作用域的Realm设置对象

2.2.6. 针对 addModule() 的猴子补丁

addModule() 方法对于 Worklet 的步骤将需要在步骤 "Let promise be a new promise" 之前包含以下步骤:

  1. 如果 this 的类型为 SharedStorageWorklet

    1. addModuleAllowedResult 为在给定 thismoduleURLRecord 的情况下运行 check if addModule is allowed and update state 的结果。

    2. 如果 addModuleAllowedResult 是 "DisallowedDueToNonPreferenceError":

      1. 返回一个以 TypeError 拒绝的 promise。

    3. 否则,如果 addModuleAllowedResult 是 "DisallowedDueToPreferenceError":

      1. 如果 thishas cross-origin data origin 为 false,则返回一个以 TypeError 拒绝的 promise。

    4. 否则:

      1. 断言addModuleAllowedResult 是 "Allowed"。

发生用户偏好错误时,addModule() 将在早期阶段被中止。然而,只有对于同源 worklet(即发起者文档的源与模块脚本的源同源的情况), 该错误才会暴露给调用者。对于跨源 worklet,该错误将被隐藏。这是为了防止调用者知道用户已通过偏好 为哪些源禁用了共享存储(如果该浏览器供应商存在按源的偏好)。

调用者仍可能使用计时攻击来获知此信息,但这是一个较小的安全/隐私问题,因为现实中很少有用户会设置 这样的偏好,而进行大范围搜索会因启动 worklet 而产生显著性能成本。

此理由同样适用于 selectURL()run() 对用户偏好错误的处理。

在步骤 "Let addedSuccessfully be false" 之后,我们需要包含以下步骤:

  1. 如果 this 的类型为 SharedStorageWorklethas cross-origin data origin 为 true,且 data origin 不是 "script-origin"

    1. 断言 pendingTasks 为 1。

    2. pendingTasks 设置为 2。

    3. 网络任务源上,给定 workletGlobalScope排队一个全局任务来执行以下步骤:

      1. customOriginUrl 为在 data origin 上运行 URL 解析器的结果。

      2. 断言 customOriginUrl 是一个有效的 URL

      3. customOriginUrl路径设置为 ≪".well-known", "shared-storage", "trusted-origins"≫。

      4. request 为一个新的请求,其URLcustomOriginUrl模式"cors"引荐来源"client"目标"json"发起者类型"script", 且客户端outsideSettings

      5. Fetch request,并将 processResponseConsumeBody 设为 以下算法,给定响应 response 和 null、failure 或字节序列 bodyBytes

        1. 如果以下任一为 true:

          • bodyBytes 为 null 或 failure;或者

          • response状态不是ok 状态

          则:

          1. pendingTasks 设置为 −1。

          2. 用 "TypeError" DOMException 拒绝 promise

          3. 中止这些步骤。

        2. mimeType 为从 response标头列表提取 MIME 类型的结果。

        3. 如果 mimeType 不是 JSON MIME 类型,则:

          1. pendingTasks 设置为 −1。

          2. 用 "TypeError" DOMException 拒绝 promise

          3. 中止这些步骤。

        4. sourceText 为对 bodyBytes 进行 UTF-8 解码的结果。

        5. parsed 为在给定 sourceText 的情况下,将 JSON 字符串解析为 Infra 值的结果。

        6. 如果 parsed 不是列表 或者 parsed,则:

          1. pendingTasks 设置为 −1。

          2. 用 "TypeError" DOMException 拒绝 promise

          3. 中止这些步骤。

        7. doesMatch 为 false。

        8. 对于 parsed 的每个 item

          1. 如果 item 不是有序映射,或者 item包含 scriptOrigin, 或者 item包含 contextOrigin

            1. pendingTasks 设置为 −1。

            2. 用 "TypeError" DOMException 拒绝 promise

            3. 中止这些步骤。

          2. doesMatch 为在 item[scriptOrigin]、 moduleURLRecorditem[contextOrigin] 和 outsideSettings上运行 check for script and context origin match 的结果。

          3. 如果 doesMatch 为 true:

            1. 网络任务 源上,给定 this相关全局 对象排队一个全局 任务来执行以下步骤:

              1. 如果 pendingTasks 不是 −1,则:

                1. pendingTasks 设置为 pendingTasks − 1。

                2. 如果 pendingTasks 为 0,执行 以下步骤:

                  1. 如果 workletGlobalScope 有 一个关联的布尔值 addModule success,将 workletGlobalScopeaddModule success 设置为 true。

                  2. 解析 promise

            2. 中断。

        9. 如果 doesMatch 为 false,则:

          1. pendingTasks 设置为 −1。

          2. 用 "TypeError" DOMException 拒绝 promise

注: 如果 worklet 数据源不同于当前上下文和脚本源, 则会执行一项额外检查。这涉及从 worklet 数据源获取配置文件,以验证当前上下文是否被允许加载带有该脚本的 worklet 并执行操作。

倒数第二个步骤(即最后一个缩进的步骤),目前为 "If pendingTasks is 0, then resolve promise.",应更新为:

  1. 如果 pendingTasks 为 0,执行以下步骤:

    1. 如果 workletGlobalScope 有一个关联的布尔值 addModule success, 将 workletGlobalScopeaddModule success 设置为 true。

    2. 解析 promise

在最后一个步骤(目前为 "Return promise.")之前,添加以下步骤:

  1. 如果 this 是一个 SharedStorageWorklet, 则在 promise 兑现时promise 被拒绝时,运行以下步骤:

    1. globalScopesthis全局作用域

    2. 断言globalScopes大小等于 1。

    3. privateAggregationObjglobalScopes[0] 的 privateAggregation

    4. privateAggregationObjallowed to use 设置为确定 this相关全局对象关联文档是否允许使用 "private-aggregation" 策略控制特性的结果。

      如果先进行权限 策略检查,请考虑在这里添加早期返回。

    5. privateAggregationObj作用域细节设置为一个 新的作用域细节,其项目为:

      get batching scope steps

      一个算法,返回在 scope 中当前执行的调用返回时,计划传递给process contributions for a batching scope批处理作用域

      get debug scope steps

      一个算法,返回在 scope 中当前执行的调用返回时,计划传递给mark a debug scope complete调试作用域

      注: 多个操作调用可以同时 进行,每个调用都有不同的批处理作用域和调试作用域。然而, 当前只能有一个正在执行。

trusted origin type字符串列表 of strings

要在给定 trusted origin type itemScriptOrigin actualScriptOrigintrusted origin type itemContextOrigin actualContextOrigin 的情况下,check for script and context origin match,执行以下步骤:
  1. 如果在给定 itemScriptOriginactualScriptOrigin 的情况下运行 check for trusted origin match 的结果为 false,则返回 false。

  2. 返回在给定 itemContextOriginactualContextOrigin 的情况下运行 check for trusted origin match 的结果。

要在给定 trusted origin type itemOrigin actualOrigin 的情况下,check for trusted origin match,执行以下步骤:
  1. 如果 itemOrigin字符串,返回在给定 itemOriginactualOrigin 的情况下运行 check for trusted origin match on a string 的结果。

  2. 否则,对于 itemOrigin 中的每个 originString

    1. 如果在给定 originStringactualOrigin 的情况下运行 check for trusted origin match on a string 的结果为 true,则返回 true。

  3. 返回 false。

要在给定 字符串 itemOrigin actualOrigin 的情况下,check for trusted origin match on a string,执行以下步骤:
  1. 如果 itemOrigin"*",返回 true。

  2. itemOriginUrl 为在 itemOrigin 上运行 URL 解析器的结果。

  3. 如果 itemOriginUrl 不是有效的 URL,则 返回 false。

  4. 如果 itemOriginUrlactualOrigin同源,返回 true。

  5. 否则,返回 false。

为进程外 worklet 添加额外的猴子补丁部分。

2.3. SharedStorageWorkletGlobalScope

SharedStorageWorkletworklet 全局作用域类型SharedStorageWorkletGlobalScope

SharedStorageWorkletworklet 目标类型是 "sharedstorageworklet"。

2.3.1. 针对 request destination 的猴子补丁

fetch 请求的 destination 字段还应将 "sharedstorageworklet" 作为一个有效值包含进来。

callback RunFunctionForSharedStorageSelectURLOperation = Promise<unsigned long>(sequence<USVString> urls, optional any data);
[Exposed=SharedStorageWorklet, Global=SharedStorageWorklet]
interface SharedStorageWorkletGlobalScope : WorkletGlobalScope {
  undefined register(DOMString name,
                     Function operationCtor);

  readonly attribute SharedStorage sharedStorage;
  readonly attribute PrivateAggregation privateAggregation;

  Promise<sequence<StorageInterestGroup>> interestGroups();

  readonly attribute SharedStorageWorkletNavigator navigator;
};

每个 SharedStorageWorkletGlobalScope 都有一个关联的环境设置对象 outside settings,它是关联的 SharedStorageWorklet相关设置对象

每个 SharedStorageWorkletGlobalScope 都有一个关联的布尔值 addModule success,它初始化为 false。

每个 SharedStorageWorkletGlobalScope 还有一个关联的 operation map,它是一个映射,初始为空,从字符串(表示操作 名称)到函数对象

每个 SharedStorageWorkletGlobalScope 都有一个关联的 SharedStorage 实例 shared storage instance

每个 SharedStorageWorkletGlobalScope 都有一个关联的 SharedStorageWorkletNavigator 实例 navigator instance

2.3.2. SharedStorageWorkletGlobalScope 算法

register(name, operationCtor) 方法步骤为:
  1. 如果 name 缺失或为空,则抛出一个 TypeError

  2. operationMap 为此 SharedStorageWorkletGlobalScopeoperation map

  3. 如果 operationMap 包含一个 条目,其name, 则抛出一个 TypeError

  4. 如果 operationCtor 缺失,则抛出一个 TypeError

  5. operationClassInstance 为不带参数构造 operationCtor 的结果。

  6. runFunctionGet(operationClassInstance, "run")。 重新抛出任何异常。

  7. 如果 IsCallable(runFunction) 为 false,则抛出一个 TypeError

  8. operationMap[name] 的值设置runFunction

鉴于 WebIDL,这里的 "name" 和 "operationCtor" 不可能缺失。应当只检查默认/空值。[Issue #151]

interestGroups() 方法步骤为:
  1. promise 为一个新的promise

  2. 如果对 SharedStorage 的关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  3. globalObject当前 Realm全局对象

  4. contextglobalObject浏览上下文

  5. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  6. documentcontext活动 window关联文档

  7. 如果 document 不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  8. workletDataOrigin当前 Realm设置对象

  9. 并行运行以下步骤:

    1. interestGroups 为在给定 workletDataOrigin 的情况下运行 get storage interest groups for owner 的结果。

    2. 如果 interestGroups 是 failure:

      1. DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

    3. 否则:

      1. DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 interestGroups 解析 promise

  10. 返回 promise

sharedStorage getter 步骤为:
  1. 如果 thisaddModule success 为 true,则返回 thisshared storage instance

  2. 否则,抛出一个 TypeError

navigator getter 步骤为:
  1. 返回 thisnavigator instance

privateAggregation getter 步骤为:
  1. 给定 thisGet the privateAggregation

check whether addModule is finished,步骤为:
  1. 返回 addModule success 的值。

2.4. SharedStorageUrlWithMetadata 和报告

dictionary SharedStorageUrlWithMetadata {
  required USVString url;
  object reportingMetadata;
};

如果一个 SharedStorageUrlWithMetadata 字典包含非reportingMetadata object, 形式为一个字典,其FenceEventeventType, 且其是会解析为有效 URL字符串, 则这些 eventType-URL 对将被注册,以便之后在加载由此 selectURL() 调用产生的 SharedStorageResponse 的任何围栏框架内访问。

reportingMetadata 应该是一个字典[Issue #141]

在一个围栏框架内,如果其中通过带有 reportingMetadata objectselectURL() 已经注册eventType-URL 对,当在一个 FenceEvent 上调用 reportEvent(), 且其 destination 包含 "shared-storage-select-url" 并且该 FenceEvent 的 对应 eventType 被触发时,则该 FenceEventeventData 将作为一个 beacon 发送到为该 eventType 注册的 URL

要在给定一个 object reportingMetadata 的情况下,validate reporting metadata,运行以下步骤:
  1. 如果 reportingMetadata 不是字典,返回 false。

  2. 如果 reportingMetadata, 返回 true。

  3. 对于每个 reportingMetadataeventTypeurlString,如果用 urlString 运行 get the canonical URL string if valid 的结果为 undefined,则返回 false。

  4. 返回 true。

要在给定字符串 urlString 的情况下,get the canonical URL string if valid,运行 以下步骤:
  1. url 为在 urlString 上运行 URL 解析器的结果。

  2. 如果 url 不是有效的 URL,返回 undefined。

  3. 否则,返回在 url 上运行 URL 序列化器的结果。

要在给定一个 object reportingMetadata 和一个围栏 框架配置 fencedFrameConfigStruct 的情况下,register reporting metadata,运行以下步骤:
  1. 如果 reportingMetadata, 返回。

  2. 断言reportingMetadata 是一个字典

  3. reportingUrlMap 为一个映射

  4. 对于每个 reportingMetadataeventTypeurlString

    1. url 为在 urlString 上运行 URL 解析器的结果。

    2. 断言url 是有效的 URL

    3. reportingUrlMap[eventType] 设置url

reportingUrlMap 存储在与 fencedFrameConfigStruct 关联的围栏框架报告器类中。二者仍需添加到草案 [Fenced-Frame] 中。[Issue #144]

2.5. 熵预算

由于熵位可以通过 selectURL() 泄露,用户 代理将需要维护预算以限制这些泄露。

在调用 selectURL() 时,当这些预算中的任一耗尽,默认 selectURL 索引将被用于确定要选择哪个 URL。

要在给定 sequence<USVString> urls 的情况下,get the default selectURL index,运行以下步骤:
  1. 返回 0。

    注: 我们本可以选择返回从 0 到 urls大小排他范围内的任意 unsigned long, 只要返回的索引独立于已注册操作类的 "run" 方法即可。

默认 selectURL 索引是在给定 sequence<USVString> urls 的情况下,运行 get the default selectURL index 所获得的索引。

如果用户激活一个围栏框架,其节点 文档浏览上下文围栏框架配置实例selectURL() 生成,并因此发起一个顶级可遍历 导航,这将向落地页面揭示其 URL 被选中,这是一种熵位泄露,最多为对 selectURL() 调用的输入 URL 数量以 2 为底的对数。为缓解此问题,用户代理将设置按站点导航熵限额

导航 熵限额是允许在给定调用站点的给定导航预算时期期间,通过围栏框架发起顶级可遍历导航而泄露的最大熵位限额。此限额用户代理定义, 且与站点无关。

用户 代理将定义一个固定的预定持续时间 navigation budget lifetime

导航预算 时期是任何时间间隔,其持续时间navigation budget lifetime

为跟踪此导航熵限额如何被使用,用户 代理使用一个 共享存储导航预算表,它是从站点导航熵账本映射

导航 熵账本列表,由bit debit 组成。

bit debit 是一个 结构,具有 以下

bits

一个 double

timestamp

一个 DOMHighResTimeStamp (来自 Unix Epoch

Bit debits,其timestamps早于 当前导航预算时期的开始,被称为过期

当发生泄露时,其熵位值会被计算,并与当前时间作为timestamp 一起,作为bit debit 存储到该站点共享存储导航预算表中。

每个站点都有一个关联的 double remaining navigation budget,其值为导航熵限额减去所有bit debits,这些 timestamps位于 当前导航预算时期内。

当一个站点没有足够的remaining navigation budget 时,selectURL() 将返回 SharedStorageResponse (即 FencedFrameConfigurn uuid),用于 SharedStorageUrlWithMetadata 中位于默认 selectURL 索引处的 url

要在给定环境设置对象 environment 和一个站点 site 的情况下,determine remaining navigation budget,运行以下步骤:
  1. 断言site 不是不透明源

  2. maxBits用户代理导航熵限额

  3. 如果用户代理共享存储导航预算 表包含 site,则返回 maxBits

  4. 否则,令 ledger用户代理共享存储导航预算 表[site]。

  5. debitSum 为 0。

  6. 对于每个 ledger 中的 bitDebit,执行以下步骤:

    1. debitbitDebitbits

    2. 如果使用 environmentbitDebit 运行 check whether a bit debit is expired 的结果为 false,则将 debitSum 增加 debit

  7. 返回 maxBitsdebitSum

要在给定环境设置对象 environment 和一个 bit debit bitDebit 的情况下,check whether a bit debit is expired,运行 以下步骤:
  1. epochLength用户代理navigation budget lifetime

  2. currentTimeenvironment当前挂钟时间

  3. thresholdcurrentTimeepochLength

  4. 如果 bitDebittimestamp 小于 threshold,则返回 true。

  5. 否则,返回 false。

对于每个由围栏框架发起的顶级可遍历导航,如果其节点 文档浏览上下文围栏框架配置实例是通过 selectURL() 生成的,则需要向共享存储导航预算表计费一个 bit debit, 因为这可能会泄露跨站点数据。由于要计费的 bits 是在调用 selectURL() 期间计算的,但只有当且仅当所得的围栏框架发起顶级可遍历导航时,才会实际记录到共享存储导航预算表中,因此在此之前,bits 必须作为 pending shared storage budget debit 存储在 相应围栏框架的节点 文档浏览上下文围栏框架配置实例中。

pending shared storage budget debit 的定义移至 围栏框架配置实例,在草案 [Fenced-Frame] 规范中。[Issue #148]

开始导航结束导航之间,用户代理将执行 charge shared storage navigation budget 算法。

需要找到一种更好的方式 来指定导航预算计费的时机。[Issue #138]

要在具有navigable navigableDocument sourceDocument导航期间,charge shared storage navigation budget,运行以下步骤:
  1. 如果 navigable 不是顶级可遍历,则返回。

  2. currentNavigablesourceDocument节点 navigable

  3. 只要 currentNavigable 不为 null:

    1. site 为使用 currentNavigable活动文档运行 obtain a site 的结果。

    2. instancecurrentNavigable节点文档浏览上下文围栏框架配置 实例

    3. currentNavigable 设置为 currentNavigable父级

    4. 如果 instance 为 null 或 site不透明源,则继续

    5. pendingBitsinstancepending shared storage budget debit

    6. 如果 pendingBits 不大于 0,则继续

    7. ledger用户 代理共享存储导航 预算表[site]。

    8. bitDebit 为一个新的 bit debit

    9. bitDebitbits 设置为 pendingBits

    10. currentTime当前挂钟时间

    11. bitDebittimestamp 设置为 currentTime

    12. bitDebit 附加ledger

    13. pendingBits 设置为 0。

用户 代理可能希望设置一个定时器,以定期purge expired bit debits from all navigation entropy ledgers,因为过期bit debits 将不再需要。

purge expired bit debits from all navigation entropy ledgers,运行以下步骤:
  1. 对于每个用户代理共享存储导航预算 表originledger

    1. 对于每个 ledger 中的 bitDebit,如果 用 bitDebit 运行 check whether a bit debit is expired 的结果为 true,则从 ledger移除 bitDebit

2.5.2. 顶级可遍历熵预算

短期内,当我们拥有限制较少的围栏框架时,有必要施加如下额外限制。

每个用户 代理将指定最大 整体页面熵限额和最大 站点页面熵 限额,其中前者是每个顶级可遍历允许因 selectURL() 泄露的总位数,后者是每个站点在每个顶级可遍历中允许因 selectURL() 泄露的总位数

共享存储页面预算是一个结构,具有 以下

overall budget

一个 double

site budget map

一个从站点double映射

saved query map

一个从元组 data originURL worklet script URL字符串 operation name字符串 query name)到saved query data映射

saved query data 是一个结构,具有以下

index

一个 long

callbacks

一个由任务组成的队列

2.5.2.1. 针对可遍历 Navigable 的猴子补丁

[HTML]Traversable navigables 节中,添加以下内容:

除了navigable 的属性之外,可遍历 navigable 还具有:

2.5.2.2. 针对 Navigables 的猴子补丁

通过在末尾添加以下步骤,修改 initialize the navigable 算法:

  1. 如果 parent 为 null 且 navigable可遍历 navigable,则:

    1. newPageBudget 为一个具有空site budget map共享存储页面 预算

    2. newPageBudgetoverall budget设置为整体页面熵限额

    3. navigablepage budget设置为 newPageBudget

2.5.2.3. 已保存查询
要在给定navigable navigable originURL moduleURLRecord字符串 operationName字符串 savedQueryName任务 callbackTask 的情况下,get the index for a saved query
  1. topLevelTraversable 为对 navigable 运行 get the top-level traversable 的结果。

  2. 断言 topLevelTraversablepage budget不是 null。

  3. 如果 topLevelTraversablepage budgetsaved query map包含origin, moduleURLRecord, operationName, savedQueryName),则:

    1. topLevelTraversablepage budgetsaved query map[(origin, moduleURLRecord, operationName, savedQueryName)] 设置为一个新的saved query data 结构 queryData

    2. queryDataindex值设置为 -1。

    3. 返回 "pending current operation"。

  4. savedIndextopLevelTraversablepage budgetsaved query map[(origin, moduleURLRecord, operationName, savedQueryName)] 的index

  5. 如果 savedIndex 为 -1:

    1. callbackTask 入队queryDatacallbacks

    2. 返回 "pending callback"。

  6. 返回 savedIndex

注: get the index for a saved query 算法返回 "pending current operation",表示索引值正在等待在 SharedStorageWorkletGlobalScopeworklet 事件循环排队任务以执行已注册操作的结果。

注: get the index for a saved query 算法返回 "pending callback",表示一个用于确定索引结果的任务此前已在 SharedStorageWorkletGlobalScopeworklet 事件循环上被排队,并且我们现在正在排队一个额外的 callbackTask,以便在原始任务完成时运行。

要在给定 Window windownavigable navigable originURL moduleURLRecord字符串 operationName字符串 savedQueryNameunsigned long index 的情况下,store the index for a saved query
  1. topLevelTraversable 为对 navigable 运行 get the top-level traversable 的结果。

  2. 断言 topLevelTraversablepage budget不是 null。

  3. queryDatatopLevelTraversablepage budgetsaved query map[(origin, moduleURLRecord, operationName, savedQueryName)]。

  4. queryDataindex设置index

  5. queryDatacallbacks 不为空时:

    1. queryDatacallbacks出队下一个任务 callbackTask,并在DOM 操作任务源上,给定 window排队一个全局任务以运行 callbackTask 的步骤,给定 index

要在给定 Window window列表 of SharedStorageUrlWithMetadatas urlListpromise promise 的情况下,obtain a callback to process the saved index result,执行以下步骤。它们返回一个算法。
  1. processIndexTask 为一个执行以下步骤的算法,给定一个 unsigned long index

    1. 如果 index 大于 urlList大小, 则在DOM 操作任务源上,给定 window排队一个全局任务以用 TypeError 拒绝 promise

      注: 结果索引超出了输入 urls 的大小。这违反了 selectURL() 协议,我们不知道应选择哪个 url。

    2. 否则,在DOM 操作任务源上,给定 window排队一个全局任务以用 index 解析 promise

  2. 返回 processIndexTask

2.5.2.4. 顶级可遍历熵预算计费
要在给定navigable navigable站点 site 和 double pendingBits 的情况下,charge shared storage top-level traversable budgets,运行以下步骤:
  1. topLevelTraversable 为对 navigable 运行 get the top-level traversable 的结果。

  2. 断言 topLevelTraversablepage budget不是 null。

  3. 如果 pendingBits 大于 topLevelTraversablepage budgetoverall budget,则返回 false 并中止这些步骤。

  4. 如果 topLevelTraversablepage budgetsite budget map包含 site,则将 topLevelTraversablepage budgetsite budget map [site] 设置站点页面熵限额

  5. 如果 pendingBits 大于 topLevelTraversablepage budgetsite budget map [site],则返回 false 并中止这些步骤。

  6. topLevelTraversablepage budgetsite budget map [site] 减少 pendingBits

  7. topLevelTraversablepage budgetoverall budget减少 pendingBits

  8. 返回 true。

3. 共享存储的后端

共享存储 API 将按如下方式通过注册新的存储端点,集成到 Storage API 中。

3.1. 针对 存储模型的猴子补丁

本标准将向存储模型添加一个新的存储类型 "shared"。

用户 代理会为类型为 "shared" 的存储 端点持有一个共享存储棚

本标准还将以 存储 标识符 "sharedStorage" 和配额 54 * 216 字节(即 39.0625 mebibytes),注册一个存储 端点,其类型为 "shared"。

配额是根据当前实现计算出来的。 请考虑使当前实现与 "localStorage" 和 "sessionStorage" 存储 端点的规范保持一致,即 5 * 220 字节。例如,将每源条目限制 从 10,000 降低到 1,280 即可实现这一点。

共享存储 棚是从存储架映射。它初始为空。

注: 不同于以存储键为键的存储棚共享存储棚直接使用作为键。共享存储将被有意排除在客户端存储分区之外。

对于共享存储棚中的每个存储架,该存储架桶 映射目前只有一个键 "default"。

用户 代理共享 存储棚持有所有 共享存储数据。

要在给定共享存储棚 shed环境设置对象 environment origin 的情况下,obtain a shared storage shelf,运行这些步骤:
  1. allowedInOpaqueOriginContext 为 false。

  2. 如果在给定 environmentoriginallowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则返回 failure。

  3. 如果在给定 environmentorigin 的情况下运行 check if user preference setting allows access to shared storage 的结果为 false,则返回 failure。

  4. 如果 shed[origin] 不存在,则将 shed[origin] 设置为使用类型 "shared" 运行 create a shared storage shelf 的结果。

  5. 返回 shed[origin]。

create a shared storage shelf,运行这些步骤:
  1. shelf 为一个新的存储 架

  2. shelf桶映射["default"] 设置为运行 create a shared storage bucket 的结果。

  3. 返回 shelf

共享存储 桶共享存储棚的某个中的存储桶

create a shared storage bucket,运行这些步骤:
  1. endpoint 为具有存储标识符 "sharedStorage" 的存储端点

  2. bucket 为一个新的共享存储桶

  3. bucket瓶 映射["sharedStorage"] 设置为一个新的存储 瓶,其配额endpoint配额

  4. 返回 bucket

注: 目前,共享存储桶瓶 映射大小1,因为只有一个存储 端点类型 "shared" 注册

要在给定环境设置对象 environment origin 的情况下,obtain a shared storage bottle map,运行这些步骤:
  1. shed用户代理共享 存储棚

  2. shelf 为使用 shedenvironmentorigin 运行 obtain a shared storage shelf 的结果。

  3. 如果 shelf 为 failure,则返回 failure。

  4. bucketshelf桶 映射["default"]。

  5. bottlebucket瓶 映射["sharedStorage"]。

  6. proxyMap 为一个新的存储代理映射,其后备映射bottle映射

  7. proxyMap 附加bottle代理映射引用集

  8. 返回 proxyMap

3.2. 共享存储数据库

浏览上下文具有一个关联的 共享存储数据库,它提供用于存储检索删除清除清除过期数据的方法, 以及如下所述的其他方法。数据库中的数据采用条目的形式。

每个共享 存储数据库都有一个 shared storage database queue,它是启动一个新的并行队列的结果。此队列用于运行共享存储 数据库的每个方法,当这些方法的调用从该浏览上下文发起时。

每个 entry 由一个key和一个value struct组成。

entrykey 是一个字符串

用户 代理可以指定 keymaximum length

由于 keys 用于组织和 高效检索entrieskeys 在任何给定的共享存储数据库中最多只能出现一次。

entryvalue struct 是一个结构, 由字符串 valueDOMHighResTimeStamp last updated(来自 Unix Epoch)组成。

用户 代理可以指定 valuemaximum length

用户 代理可以指定 default entry lifetime,即一个entry存储到其过期之间的默认持续时间。如果 用户 代理指定了default entry lifetime,则它应当有一个定时器定期从数据库中清除过期条目

3.3. 核心数据库算法

要在给定shared storage database queue queue存储代理映射 databaseMap环境设置对象 environmentkey keyvalue value 的情况下,store an entry in the database, 在 queue 上运行以下步骤:
  1. valueStruct 为一个新的value struct

  2. valueStructvalue设置为 value

  3. currentTimeenvironment当前挂钟时间

  4. valueStructlast updated设置为 currentTime

  5. databaseMap[key] 设置valueStruct

  6. 如果抛出了异常,则返回 false。

    注: 根据实现,存储代理映射 databaseMap 的方法可能会产生错误。

  7. 否则,返回 true。

要在给定shared storage database queue queue存储代理映射 databaseMap环境设置对象 environmentkey key 的情况下,retrieve an entry from the database,在 queue 上运行以下步骤:
  1. 如果 databaseMap包含 key,则返回 undefined。

  2. valueStruct 为在 databaseMap 上以 key 运行 Get 的结果。

  3. 如果抛出了异常,则返回 failure。

    注: 根据实现,存储代理映射 databaseMap 的方法可能会产生错误。

  4. 如果使用 environmentvalueStruct 运行 determine whether an entry is expired 的结果为 true,则返回 undefined。

  5. 返回 valueStructvalue

要在给定shared storage database queue queue存储代理映射 databaseMapkey key 的情况下,delete an entry from the database, 在 queue 上运行以下步骤:
  1. 移除 databaseMap[key]。

  2. 如果抛出了异常,则返回 false。

    注: 根据实现,存储代理映射 databaseMap 的方法可能会产生错误。

  3. 返回 true。

要在给定shared storage database queue queue存储代理映射 databaseMap 的情况下,clear all entries in the database, 在 queue 上运行以下步骤:
  1. databaseMap 上运行 Clear

  2. 如果抛出了异常,则返回 false。

    注: 根据实现,存储代理映射 databaseMap 的方法可能会产生错误。

  3. 返回 true。

要在给定shared storage database queue queue存储代理映射 databaseMap 的情况下,retrieve all entries from the database,在 queue 上运行以下步骤:
  1. values 为在 databaseMap 上运行获取值的结果。

  2. 如果抛出了异常,则返回 failure。

    注: 根据实现,存储代理映射 databaseMap 的方法可能会产生错误。

  3. 返回 values

要在给定 shared storage database queue queue存储代理映射 databaseMap 的情况下,count entries in the database, 在 queue 上运行以下步骤:
  1. sizedatabaseMap大小

  2. 如果抛出了异常,则返回 failure。

    注: 根据实现,存储代理映射 databaseMap 的成员可能会产生错误。

  3. 返回 size

要在给定shared storage database queue queue存储代理映射 databaseMap环境设置对象 environment 的情况下,purge expired entries from the database,在 queue 上运行以下步骤:
  1. 对于每个 databaseMap 中的key key

    1. valueStruct 为在 databaseMap 上以 key 运行 Get 的结果。

    2. 如果抛出了异常,则返回 false。

    3. 如果使用 environmentvalueStruct 运行 determine whether an entry is expired 的结果为 true,则移除 databaseMap[key]。

    4. 如果抛出了异常,则返回 false。

  2. 返回 true。

要在给定环境设置对象 environmentvalue struct valueStruct 的情况下,determine whether an entry is expired,运行以下步骤:

  1. lastUpdatedvalueStructlast updated

  2. lifetime用户代理default entry lifetime

  3. expirationlastUpdatedlifetime 之和。

  4. currentTimeenvironment当前挂钟时间

  5. 如果 expiration 小于或等于 currentTime,返回 true。

  6. 否则,返回 false。

3.4. 专门化数据库算法

这些算法不同于 § 3.3 核心数据库算法中的核心算法, 它们接受带有更专门启发式的参数,或者采用多步骤 过程,或者二者兼有,以处理更复杂的数据库操作。

要在给定 共享存储数据库 队列 queue存储 代理映射 databaseMap环境设置对象 environment、一个 key、一个 value 和一个布尔值 ignoreIfPresent 的情况下,set an entry in the database,在 queue 上运行以下步骤:

  1. 如果 ignoreIfPresent

    1. currentValue 为使用 queuedatabaseMapenvironmentkey 运行 retrieve an entry from the database 的结果。

    2. 如果 currentValue 为 failure,则返回 false。

    3. 如果 currentValue 不是 undefined,则返回 true。

  2. 返回使用 queuedatabaseMapenvironmentkeyvalue 运行 store an entry in the database 的结果。

要在给定 共享存储数据库 队列 queue存储 代理映射 databaseMap环境设置对象 environment、一个 key 和一个 value 的情况下,append an entry in the database, 在 queue 上运行以下步骤:

  1. currentValue 为使用 queuedatabaseMapenvironmentkey 运行 retrieve an entry from the database 的结果。

  2. 如果 currentValue 为 failure,则返回 false。

  3. 如果 currentValue 不是 undefined:

    1. list 为一个新的列表

    2. currentValue 附加list

    3. value 附加list

    4. value 设置为在 list 上运行串联的结果。

  4. 返回使用 queuedatabaseMapenvironmentkeyvalue 运行 store an entry in the database 的结果。

要在给定 共享存储数据库 队列 queue存储 代理映射 databaseMap环境设置对象 environment, 和一个由 SharedStorageModifierMethod 组成的列表 methods 的情况下,batch update entries in the database,在 queue 上运行以下步骤:

  1. originalDatabaseMapdatabaseMap

  2. innerMethodFailed 为 false。

  3. 对于 methods 中的每个 method

    1. 如果 method 是一个 SharedStorageSetMethod

      1. keymethodkey

      2. valuemethodvalue

      3. ignoreIfPresentmethodignore if present

      4. result 为使用 queuedatabaseMapenvironmentkeyvalueignoreIfPresent 运行 set an entry in the database 的结果。

      5. 如果 result 为 false:

        1. innerMethodFailed 设置为 true。

        2. 中断。

    2. 否则,如果 method 是一个 SharedStorageAppendMethod

      1. keymethodkey

      2. valuemethodvalue

      3. result 为使用 queuedatabaseMapenvironmentkeyvalue 运行 append an entry in the database 的结果。

      4. 如果 result 为 false:

        1. innerMethodFailed 设置为 true。

        2. 中断。

    3. 否则,如果 method 是一个 SharedStorageDeleteMethod

      1. keymethodkey

      2. result 为使用 queuedatabaseMapenvironmentkey 运行 delete an entry from the database 的结果。

      3. 如果 result 为 false:

        1. innerMethodFailed 设置为 true。

        2. 中断。

    4. 否则:

      1. 断言method 是一个 SharedStorageClearMethod

      2. result 为使用 queuedatabaseMapenvironment 运行 clear all entries in the database 的结果。

      3. 如果 result 为 false:

        1. innerMethodFailed 设置为 true。

        2. 中断。

  4. 如果 innerMethodFailed

    1. databaseMap 设置为 originalDatabaseMap

    2. 返回 false。

  5. 返回 true。

此算法使用简单的回滚机制。对于生产环境, 请考虑使用避免完整数据库副本的更高效技术。

4. Window 接口的扩展

每个 Window 对象都有一个关联的 SharedStorage 实例 sharedStorage, 如果共享存储已启用, 它将随 Window 一同创建,并具有如下getter
partial interface Window {
  [SecureContext] readonly attribute SharedStorage? sharedStorage;
};
sharedStorage getter 步骤为:
  1. 如果 this完全活动的,则返回 thissharedStorage

  2. 否则,返回 null。

5. SharedStorageModifierMethod 接口组

SharedStorageSetMethodSharedStorageAppendMethodSharedStorageDeleteMethodSharedStorageClearMethod 接口分别对应于 set()append()delete()clear() 修改器方法。它们都继承基础 SharedStorageModifierMethod 接口。
[Exposed=(Window,SharedStorageWorklet)]
interface SharedStorageModifierMethod {};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageSetMethod : SharedStorageModifierMethod {
  constructor(DOMString key, DOMString value, optional SharedStorageSetMethodOptions options = {});
};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageAppendMethod : SharedStorageModifierMethod {
  constructor(DOMString key, DOMString value, optional SharedStorageModifierMethodOptions options = {});
};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageDeleteMethod : SharedStorageModifierMethod {
  constructor(DOMString key, optional SharedStorageModifierMethodOptions options = {});
};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageClearMethod : SharedStorageModifierMethod {
  constructor(optional SharedStorageModifierMethodOptions options = {});
};

dictionary SharedStorageModifierMethodOptions {
  DOMString withLock;
};

dictionary SharedStorageSetMethodOptions : SharedStorageModifierMethodOptions {
  boolean ignoreIfPresent;
};

SharedStorageModifierMethod 具有以下关联字段:

with lock

Null 或一个字符串。初始为 null。

SharedStorageSetMethod 具有以下关联字段:

key

一个字符串。初始为空。

value

一个字符串。初始为空。

ignore if present

一个布尔值。初始为 false。

SharedStorageAppendMethod 具有以下关联字段:

key

一个字符串。初始为空。

value

一个字符串。初始为空。

SharedStorageDeleteMethod 具有以下关联字段:

key

一个字符串。初始为空。

new SharedStorageSetMethod(key, value, options) 构造器步骤为:

  1. globalObject当前 Realm全局对象

  2. context 为 null。

  3. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  4. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则抛出一个 TypeError

  5. 如果 context 为 null,则抛出一个 TypeError

  6. 如果 context活动 window关联文档不是完全活动的,则抛出一个 TypeError

  7. 如果 key长度超过最大长度,则抛出一个 TypeError

  8. 如果 value长度超过最大长度,则抛出一个 TypeError

  9. environmentcontext活动 window相关设置对象

  10. databaseMap 为在给定 environmentenvironment的情况下运行 obtain a shared storage bottle map 的结果。

  11. 如果 databaseMap 为 failure,则抛出一个 TypeError

  12. thiskey设置为 key

  13. thisvalue设置为 value

  14. thisignore if present设置为 options["ignoreIfPresent"]。

  15. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则抛出一个 TypeError

    2. thiswith lock设置为 options["withLock"]。

new SharedStorageAppendMethod(key, value, options) 构造器步骤为:

  1. globalObject当前 Realm全局对象

  2. context 为 null。

  3. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  4. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则抛出一个 TypeError

  5. 如果 context 为 null,则抛出一个 TypeError

  6. 如果 context活动 window关联文档不是完全活动的,则抛出一个 TypeError

  7. 如果 key长度超过最大长度,则抛出一个 TypeError

  8. 如果 value长度超过最大长度,则抛出一个 TypeError

  9. environmentcontext活动 window相关设置对象

  10. databaseMap 为在给定 environmentenvironment的情况下运行 obtain a shared storage bottle map 的结果。

  11. 如果 databaseMap 为 failure,则抛出一个 TypeError

  12. thiskey设置为 key

  13. thisvalue设置为 value

  14. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则抛出一个 TypeError

    2. thiswith lock设置为 options["withLock"]。

new SharedStorageAppendMethod(key, options) 构造器步骤为:

  1. globalObject当前 Realm全局对象

  2. context 为 null。

  3. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  4. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则抛出一个 TypeError

  5. 如果 context 为 null,则抛出一个 TypeError

  6. 如果 context活动 window关联文档不是完全活动的,则抛出一个 TypeError

  7. 如果 key长度超过最大长度,则抛出一个 TypeError

  8. environmentcontext活动 window相关设置对象

  9. databaseMap 为在给定 environmentenvironment的情况下运行 obtain a shared storage bottle map 的结果。

  10. 如果 databaseMap 为 failure,则抛出一个 TypeError

  11. thiskey设置为 key

  12. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则抛出一个 TypeError

    2. thiswith lock设置为 options["withLock"]。

new SharedStorageClearMethod(options) 构造器步骤为:

  1. globalObject当前 Realm全局对象

  2. context 为 null。

  3. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  4. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则抛出一个 TypeError

  5. 如果 context 为 null,则抛出一个 TypeError

  6. 如果 context活动 window关联文档不是完全活动的,则抛出一个 TypeError

  7. environmentcontext活动 window相关设置对象

  8. databaseMap 为在给定 environmentenvironment的情况下运行 obtain a shared storage bottle map 的结果。

  9. 如果 databaseMap 为 failure,则抛出一个 TypeError

  10. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则抛出一个 TypeError

    2. thiswith lock设置为 options["withLock"]。

6. SharedStorage 接口

SharedStorage 接口暴露给 WindowSharedStorageWorklet

允许设置和/或删除数据的方法同时暴露给 WindowSharedStorageWorklet, 尽管它们的实现可能会因其环境而异。这使得可以从多个上下文修改共享存储中的数据。

与此同时,用于发布操作以在 SharedStorageWorkletGlobalScope 内运行的方法(即 selectURL()run()), 以及用于调用 addModule()worklet 属性,仅暴露给 Window, 因为这些是 WindowSharedStorageWorklet 交互的手段。

另一方面,从共享存储数据库获取数据的方法仅暴露给 SharedStorageWorklet, 以便谨慎控制从数据库读取的数据流。唯一的例外 是 get() 暴露给 Window, 但仅当 determine if a navigable has fully revoked network 算法的结果为 true 时才会成功。

注: determine if a navigable has fully revoked network 算法确保 get() 仅对已成功解析对 disableUntrustedNetwork() 调用的围栏框架成功。

[Exposed=(Window,SharedStorageWorklet)]
interface SharedStorage {
  Promise<DOMString> get(DOMString key);
  Promise<any> set(DOMString key,
                   DOMString value,
                   optional SharedStorageSetMethodOptions options = {});
  Promise<any> append(DOMString key,
                      DOMString value,
                      optional SharedStorageModifierMethodOptions options = {});
  Promise<any> delete(DOMString key, optional SharedStorageModifierMethodOptions options = {});
  Promise<any> clear(optional SharedStorageModifierMethodOptions options = {});
  Promise<any> batchUpdate(sequence<SharedStorageModifierMethod> methods,
                           optional SharedStorageModifierMethodOptions options = {});

  [Exposed=Window]
  Promise<SharedStorageResponse> selectURL(DOMString name,
                               sequence<SharedStorageUrlWithMetadata> urls,
                               optional SharedStorageRunOperationMethodOptions options = {});

  [Exposed=Window]
  Promise<any> run(DOMString name,
                   optional SharedStorageRunOperationMethodOptions options = {});

  [Exposed=Window]
  Promise<SharedStorageWorklet> createWorklet(USVString moduleURL, optional SharedStorageWorkletOptions options = {});

  [Exposed=Window]
  readonly attribute SharedStorageWorklet worklet;

  [Exposed=SharedStorageWorklet]
  Promise<unsigned long> length();

  [Exposed=SharedStorageWorklet]
  Promise<double> remainingBudget();

  [Exposed=SharedStorageWorklet]
  async iterable<DOMString, DOMString>;
};

dictionary SharedStoragePrivateAggregationConfig {
  USVString aggregationCoordinatorOrigin;
  USVString contextId;
  [EnforceRange] unsigned long long filteringIdMaxBytes;
  [EnforceRange] unsigned long long maxContributions;
};

dictionary SharedStorageRunOperationMethodOptions {
  object data;
  boolean resolveToConfig = false;
  boolean keepAlive = false;
  SharedStoragePrivateAggregationConfig privateAggregationConfig;
  DOMString savedQuery;
};

dictionary SharedStorageWorkletOptions : WorkletOptions {
  USVString dataOrigin = "context-origin";
};

6.1. SharedStorage 上的运行操作方法

selectURL(name, urls, options) 方法步骤为:
  1. sharedStoragethis

  2. 返回 sharedStorage.worklet.selectURL(name, urls, options)。

run(name, options) 方法步骤为:
  1. sharedStoragethis

  2. 返回 sharedStorage.worklet.run(name, options)。

6.2. 通过 SharedStorage 创建新的 worklet

createWorklet(moduleURL, options) 方法步骤为:
  1. sharedStorageWorklet 为一个新的 SharedStorageWorklet

  2. 如果 options 包含 "dataOrigin", 则将 sharedStorageWorkletdata origin 设置为 options["dataOrigin"]。

  3. addModulePromise 为调用 sharedStorageWorklet.addModule(moduleURL, options) 的结果。

  4. resultPromise 为一个新的 promise

  5. addModulePromise 兑现时,将 resultPromise 解析sharedStorageWorklet

  6. addModulePromise 被拒绝时,用 TypeError 拒绝 resultPromise

  7. 返回 resultPromise

6.3. BatchUpdate 方法

batchUpdate(methods, options) 方法步骤为:
  1. promise 为一个新的 promise

  2. globalObject当前 Realm全局对象

  3. context 为 null。

  4. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  5. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  7. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  8. 对于 methods 中的每个 method

    1. 如果 method["withLock"] 存在

      1. 返回一个以 TypeError 拒绝的 promise

      注: batchUpdate() 作为事务性操作执行。为避免更细粒度锁定导致的潜在死锁, batchUpdate() 内部的方法不能使用 withLock 选项。不是忽略此选项,而是抛出错误以 强制执行该限制并防止误用。

  9. environmentcontext活动 window相关设置对象

  10. databaseMap 为在给定 environmentenvironment的情况下,运行 obtain a shared storage bottle map 的结果。

  11. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  12. onLockGrantedCallback 为执行以下步骤的算法:

    1. 将以下步骤入队queue

      1. result 为使用 queuedatabaseMapenvironmentmethods 运行 batch update entries in the database 的结果。

      2. 如果 result 为 false,且如果 globalObject 是一个 SharedStorageWorkletGlobalScope

        1. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

        2. 中止这些步骤。

      3. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 undefined 解析 promise

  13. 如果 options["withLock"] 存在,则在给定 environmentoptions["withLock"]、 onLockGrantedCallback 的情况下,运行 handle callback within a shared storage lock

  14. 否则,运行 onLockGrantedCallback

  15. 返回 promise

6.4. 设置器/删除器方法

set(key, value, options) 方法步骤为:
  1. promise 为一个新的 promise

  2. globalObject当前 Realm全局对象

  3. context 为 null。

  4. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  5. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  7. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  8. 如果 key长度超过最大长度,则返回一个以 TypeError 拒绝的 promise

  9. 如果 value长度超过最大长度,则返回一个以 TypeError 拒绝的 promise

  10. environmentcontext活动 window相关设置对象

  11. databaseMap 为在给定 environmentenvironment的情况下,运行 obtain a shared storage bottle map 的结果。

  12. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  13. queuecontext 的关联数据库shared storage database queue

  14. realm当前 Realm

  15. onLockGrantedCallback 为执行以下步骤的算法:

    1. 将以下步骤入队queue

      1. result 为使用 queuedatabaseMapenvironmentkeyvalueoptions["ignoreIfPresent"] 运行 set an entry in the database 的结果。

      2. 如果 result 为 false,且如果 globalObject 是一个 SharedStorageWorkletGlobalScope

        1. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

        2. 中止这些步骤。

      3. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 undefined 解析 promise

  16. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则返回一个以 TypeError 拒绝的 promise

    2. 在给定 environmentoptions["withLock"]、 onLockGrantedCallback 的情况下,运行 handle callback within a shared storage lock

  17. 否则,运行 onLockGrantedCallback

  18. 返回 promise

append(key, value, options) 方法步骤为:
  1. promise 为一个新的 promise

  2. globalObject当前 Realm全局对象

  3. context 为 null。

  4. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  5. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  7. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  8. 如果 key长度超过最大长度,则返回一个以 TypeError 拒绝的 promise

  9. 如果 value长度超过最大长度,则返回一个以 TypeError 拒绝的 promise

  10. environmentcontext活动 window相关设置对象

  11. databaseMap 为在给定 environmentenvironment的情况下,运行 obtain a shared storage bottle map 的结果。

  12. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  13. queuecontext 的关联数据库shared storage database queue

  14. realm当前 Realm

  15. onLockGrantedCallback 为执行以下步骤的算法:

    1. 将以下步骤入队queue

      1. result 为使用 queuedatabaseMapenvironmentkeyvalue 运行 append an entry in the database 的结果。

      2. 如果 result 为 false,且如果 globalObject 是一个 SharedStorageWorkletGlobalScope

        1. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

        2. 中止这些步骤。

      3. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 undefined 解析 promise

  16. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则返回一个以 TypeError 拒绝的 promise

    2. 在给定 environmentoptions["withLock"]、 onLockGrantedCallback 的情况下,运行 handle callback within a shared storage lock

  17. 否则,运行 onLockGrantedCallback

  18. 返回 promise

delete(key, options) 方法步骤 为:
  1. promise 为一个新的 promise

  2. globalObject当前 Realm全局对象

  3. context 为 null。

  4. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  5. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  7. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  8. 如果 key长度超过最大长度,则返回一个以 TypeError 拒绝的 promise

  9. environmentcontext活动 window相关设置对象

  10. databaseMap 为在给定 environmentenvironment的情况下,运行 obtain a shared storage bottle map 的结果。

  11. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  12. queuecontext 的关联数据库shared storage database queue

  13. realm当前 Realm

  14. onLockGrantedCallback 为执行以下步骤的算法:

    1. 将以下步骤入队queue

      1. result 为使用 queuedatabaseMapkey 运行 delete an entry from the database 的结果。

      2. 如果 result 为 false,且如果 globalObject 是一个 SharedStorageWorkletGlobalScope

        1. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

        2. 中止这些步骤。

      3. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 undefined 解析 promise

  15. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则返回一个以 TypeError 拒绝的 promise

    2. 在给定 environmentoptions["withLock"]、 onLockGrantedCallback 的情况下,运行 handle callback within a shared storage lock

  16. 否则,运行 onLockGrantedCallback

  17. 返回 promise

clear(options) 方法步骤为:
  1. promise 为一个新的 promise

  2. globalObject当前 Realm全局对象

  3. context 为 null。

  4. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

  5. 否则:

    1. context 设置为 globalObjectoutside settings目标浏览 上下文

    2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  6. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  7. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  8. environmentcontext活动 window相关设置对象

  9. databaseMap 为在给定 environmentenvironment的情况下,运行 obtain a shared storage bottle map 的结果。

  10. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  11. queuecontext 的关联数据库shared storage database queue

  12. realm当前 Realm

  13. onLockGrantedCallback 为执行以下步骤的算法:

    1. 将以下步骤入队queue

      1. result 为使用 queuedatabaseMap 运行 clear all entries in the database 的结果。

      2. 如果 result 为 false,且如果 globalObject 是一个 SharedStorageWorkletGlobalScope

        1. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

        2. 中止这些步骤。

      3. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 undefined 解析 promise

  14. 如果 options["withLock"] 存在

    1. 如果 options["withLock"] 以 U+002D HYPHEN-MINUS (-) 开头,则返回一个以 TypeError 拒绝的 promise

    2. 在给定 environmentoptions["withLock"]、 onLockGrantedCallback 的情况下,运行 handle callback within a shared storage lock

  15. 否则,运行 onLockGrantedCallback

  16. 返回 promise

6.5. Getter 方法

get(key) 方法步骤为:
  1. promise 为一个新的 promise

  2. globalObject当前 Realm全局对象

  3. context 为 null。

  4. environment 为 null。

  5. 如果 globalObject 是一个 Window

    1. context 设置为 globalObject浏览上下文

    2. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

    3. environment 设置为 context活动 window相关设置对象

    4. allowedInOpaqueOriginContext 为 false。

    5. 如果在给定 environmentenvironmentallowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则返回一个以 TypeError 拒绝的 promise

    6. 如果在给定 environmentenvironment的情况下运行 check if user preference setting allows access to shared storage 的结果为 false,则返回一个以 OperationError 拒绝的 promise

    7. documentcontext活动文档

    8. 如果在 "fenced-unpartitioned-storage-read"、 documentenvironment上运行 Is feature enabled in document for origin? 的结果为 false,则返回一个以 OperationError 拒绝的 promise

    9. navigabledocument节点 navigable

    10. 如果在给定 navigable 的情况下运行 determine if a navigable has fully revoked network 的结果为 false,则返回一个以 OperationError 拒绝的 promise

  6. 否则: 1 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

    1. context 设置为 SharedStorageSharedStorageWorkletGlobalScopeoutside settings目标浏览 上下文

    2. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

    3. environment 设置为 context活动 window相关设置对象

  7. 如果 key长度超过最大长度,则返回一个以 TypeError 拒绝的 promise

  8. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  9. realm当前 Realm

  10. databaseMap 为在给定 environmentrealm设置对象的情况下,运行 obtain a shared storage bottle map 的结果。

  11. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  12. queuecontext 的关联数据库shared storage database queue

  13. 将以下步骤入队queue

    1. value 为使用 queuedatabaseMapenvironmentkey 运行 retrieve an entry from the database 的结果。

    2. 如果 value 为 failure,则在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

    3. 否则,如果 value 为 undefined,则在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 undefined 解析 promise

    4. 否则,在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 value 解析 promise

  14. 返回 promise

length() 方法步骤为:
  1. promise 为一个新的 promise

  2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  3. contextSharedStorageSharedStorageWorkletGlobalScopeoutside settings目标浏览上下文

  4. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  5. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  6. environmentcontext活动 window相关设置对象

  7. realm当前 Realm

  8. databaseMap 为在给定 environmentrealm设置对象的情况下,运行 obtain a shared storage bottle map 的结果。

  9. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  10. queuecontext 的关联数据库shared storage database queue

  11. 将以下步骤入队queue

    1. numEntries 为使用 queueenvironment 运行 count entries in the database 的结果。

    2. 如果 numEntries 为 failure,则在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

    3. 否则,在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 numEntries 解析 promise

  12. 返回 promise

remainingBudget() 方法步骤为:
  1. promise 为一个新的 promise

  2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  3. contextSharedStorageSharedStorageWorkletGlobalScopeoutside settings目标浏览上下文

  4. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  5. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  6. environmentcontext活动 window相关设置对象

  7. realm当前 Realm

  8. allowedInOpaqueOriginContext 为 false。

  9. 如果在给定 environmentrealm设置对象allowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  10. 如果在给定 environmentrealm设置对象的情况下运行 check if user preference setting allows access to shared storage 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  11. site 为使用 realm设置对象运行 obtain a site 的结果。

  12. 断言site 不是不透明源

  13. queuecontext 的关联数据库shared storage database queue

  14. 将以下步骤入队queue

    1. remainingBudget 为使用 site 运行 determine remaining navigation budget 的结果。

    2. 解析DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 remainingBudget 解析 promise

  15. 返回 promise

6.6. 迭代

每个 SharedStorage 异步迭代器实例都有一个由条目组成的队列 pending entries,初始为

每个 SharedStorage 异步迭代器实例还都有一个 boolean error,初始为 false。

下面定义的 asynchronous iterator initialization stepsget the next iteration result 算法对应于 Web IDL 标准中称为 asynchronous iterator initialization stepsget the next iteration result 的算法。

SharedStorage 异步迭代器 iteratorasynchronous iterator initialization steps 为:
  1. promise 为一个新的 promise

  2. 如果对 SharedStorage 的 关联 SharedStorageWorkletGlobalScope 运行 check whether addModule is finished 的结果为 false,则返回一个以 TypeError 拒绝的 promise

  3. contextSharedStorageSharedStorageWorkletGlobalScopeoutside settings目标浏览上下文

  4. 如果 context 为 null,则返回一个以 TypeError 拒绝的 promise

  5. 如果 context活动 window关联文档不是完全活动的,则返回一个以 TypeError 拒绝的 promise

  6. environmentcontext活动 window相关设置对象

  7. realm当前 Realm

  8. databaseMap 为在给定 environmentrealm设置对象的情况下,运行 obtain a shared storage bottle map 的结果。

  9. 如果 databaseMap 为 failure,则返回一个以 TypeError 拒绝的 promise

  10. queuecontext 的关联数据库shared storage database queue

  11. 将以下步骤入队queue

    1. entries 为使用 queueenvironment 运行 retrieve all entries from the database 的结果。

    2. 如果 entries 为 failure,则在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 TypeError 拒绝 promise

    3. 否则,在DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 entries 解析 promise

  12. promise 兑现时,运行以下步骤:

    1. promiseEntriespromise 的值。

    2. 对于每个 promiseEntries 中的条目 entry,将 entry 入队iteratorpending entries

  13. promise 被拒绝时,将 iteratorerror 设置为 true。

要在给定 SharedStorage异步迭代器 iterator 的情况下,get the next iteration result,运行以下步骤:
  1. promise 为一个新的 promise

  2. 将以下步骤入队

    1. 如果 iteratorerror 为 true,则返回一个以 TypeError 拒绝的 promise

    2. 如果 iteratorpending entries

      1. 创建一个对象 doneObject

      2. DOM 操作任务 源上,给定 realm全局对象排队一个全局任务以用 doneObject 解析 promise

      3. 中止这些步骤。

    3. 否则,令 entry 为从 iteratorpending entries出队的结果。

    4. DOM 操作任务源上,给定 realm全局对象排队一个全局任务以用 entry 解析 promise

  3. 返回 promise

7. 通过 HTTP 响应标头触发操作

虽然设置器和删除器操作(例如 set()append()delete()clear()) 可以通过上述用于 WindowSharedStorageWorkletGlobalScope 的 API 发起,但设置器/删除器操作也可以通过 HTTP 响应标头触发。

这将需要对 HTML 和 Fetch 规范进行猴子补丁。

8. HTML 猴子补丁

8.1. sharedStorageWritablesharedstoragewritable 属性

定义以下接口 mixin,并将其包含到 HTMLIFrameElementHTMLImageElement 的 IDL 接口中:

interface mixin HTMLSharedStorageWritableElementUtils {
  [CEReactions, SecureContext] attribute boolean sharedStorageWritable;
};

HTMLIFrameElement includes HTMLSharedStorageWritableElementUtils;
HTMLImageElement includes HTMLSharedStorageWritableElementUtils;

添加以下布尔内容属性

iframe

sharedstoragewritable

img

sharedstoragewritable

IDL 属性 sharedStorageWritable 必须反映相应的同名内容属性

8.2. HTML 算法修改

8.2.1. Update the image data 算法的修改

按如下方式修改 update the image data

在以下步骤之后

request优先级设置为当前状态...

添加步骤

  1. 如果元素存在 sharedstoragewritable 属性,则将 requestshared storage writable 设置为 true。

8.2.2. Create navigation params by fetching 算法的修改

按如下方式修改 create navigation params by fetching

在以下步骤之后

request 为一个新的请求,其中 ...

添加步骤

  1. 如果 navigable容器是一个 iframe 元素,并且如果它具有 sharedstoragewritable 内容属性,则将 requestshared storage writable 设置为 true。

9. Fetch 猴子补丁

9.1. sharedStorageWritable

请求具有一个关联的布尔值 shared storage writable。除非另有说明,否则它为 false。

RequestInit 字典包含一个 sharedStorageWritable 键:

partial dictionary RequestInit {
  boolean sharedStorageWritable;
};

9.2. Fetch 算法修改

9.2.1. Request Constructor 算法的修改

按如下方式修改 new Request(input, init) constructor

在以下步骤之前

将 this 的请求设置为 request

添加步骤

  1. 如果 init["sharedStorageWritable"] 存在,则将 requestshared storage writable 设置为它。

9.2.2. HTTP network or cache fetch 算法的修改

按如下方式修改 HTTP network or cache fetch 算法:

在以下步骤之前

按 HTTP 修改 httpRequest标头列表。...

添加步骤

  1. httpRequest Append or modify a Sec-Shared-Storage-Writable request header

9.2.3. HTTP fetch 算法的修改

按如下方式修改 HTTP fetch 算法:

在以下步骤之前

如果 internalResponse状态重定向 状态:...

添加步骤

  1. 如果 request目标为 "sharedstorageworklet":

    1. dataOriginValue 为从 request标头列表获取 `Sec-Shared-Storage-Data-Origin` 的结果。

    2. 如果 dataOriginValue 不是 null,则:

      1. dataOriginUrl 为在 dataOriginValue 上运行 URL 解析器的结果。

      2. 断言 dataOriginUrl 不是 failure。

      3. 断言 request不是 "client"。

      4. 断言 requestrequestURL不是同源的。

      5. allowed 为 true。

      6. 如果 dataOriginUrlrequestURL同源的:

        1. responseHeadersinternalResponse标头列表

        2. allowed 为在给定 `Shared-Storage-Cross-Origin-Worklet-Allowed`、 "item" 和 responseHeaders 作为输入的情况下运行 get a structured field value 算法的结果。

      7. 如果 allowed 为 false,则返回一个网络错误

  2. 在给定响应 internalResponse请求 request 作为输入的情况下,Handle a Shared-Storage-Write response

9.3. 共享存储 HTTP 标头

9.3.1. `Sec-Shared-Storage-Data-Origin` 请求标头

本规范定义了一个 Sec-Shared-Storage-Data-Origin HTTP 请求标头

`Sec-Shared-Storage-Data-Origin` 请求标头是一个字符串

当 `Sec-Shared-Storage-Data-Origin` 在 fetch a worklet/module worker script graph 算法期间发送时,其 被设置为拥有该 worklet 的共享存储数据的的序列化形式。

9.3.2. `Shared-Storage-Cross-Origin-Worklet-Allowed` 响应标头

本规范定义了一个 Shared-Storage-Cross-Origin-Worklet-Allowed HTTP 响应标头

`Shared-Storage-Cross-Origin-Worklet-Allowed` 响应标头是一个结构化标头,其值必须是一个布尔值

当一个响应具有 `Shared-Storage-Cross-Origin-Worklet-Allowed` 且值为 true 时,worklet 脚本的服务器已授权跨源站点使用来自 worklet 脚本的共享存储数据来创建 worklet。

9.3.3. `Sec-Shared-Storage-Writable` 请求标头

本规范定义了一个 Sec-Shared-Storage-Writable HTTP 请求标头

`Sec-Shared-Storage-Writable` 请求标头是一个结构化标头,其值必须是一个布尔值

当一个请求将 `Sec-Shared-Storage-Writable` 设置为 true 时,其响应将能够写入共享存储

9.3.4. `Shared-Storage-Write` 响应标头

本规范定义了一个 Shared-Storage-Write HTTP 响应标头

`Shared-Storage-Write` 响应标头是一个结构化标头,其值必须是一个列表。定义了以下列表 成员。持有相同字符序列的Token字符串 被视为等价。还允许表示UTF-8 编码字节的字节序列,以便通过 HTTP 标头提供写入和删除非 ASCII Unicode 键和值的功能。

未知的列表成员,包括既不是字符串也不是字节序列的类型, 会被跳过,列表的其余部分会像它们不存在一样被处理。如果成员缺少必需的参数,或这些参数具有意外类型,也会被跳过。

注: 我们允许字节序列以便 容纳 Unicode 键和值。任何字节序列都将被 假定为UTF-8 编码,否则将解析失败。

添加示例。

9.4. 共享存储 Fetch 相关算法

要在给定请求 request 的情况下,determine whether a request can currently use shared storage,执行以下步骤:
  1. windowrequestwindow

  2. 如果 window 不是一个其全局对象Window环境设置对象, 则返回 false。

  3. allowedInOpaqueOriginContext 为 true。

  4. 如果在给定 windowrequest当前 URLallowedInOpaqueOriginContext 的情况下运行 determine whether shared storage is allowed by context 的结果为 false,则返回 false。

  5. 如果在给定 windowrequest当前 URL的情况下运行 check if user preference setting allows access to shared storage 的结果为 false,则返回 false。

determine whether a request can currently use shared storage 算法需要考虑“选择加入特性”, 如 https://github.com/w3c/webappsec-permissions-policy/pull/499 中所阐述。

要在给定请求 request 的情况下,append or modify a Sec-Shared-Storage-Writable request header,执行以下步骤:
  1. 如果 requestshared storage writable 不是 true, 则返回。

    注: 在重定向时, requestshared storage writable 可能为 true, 但重定向可能没有使用共享存储的权限,从而使对 request 运行 determine whether a request can currently use shared storage 的结果为 false。

  2. 如果对 request 运行 determine whether a request can currently use shared storage 的结果为 false,则从 request标头列表删除 `Sec-Shared-Storage-Writable`。

  3. 否则,在 request标头列表设置结构化字段值 (`Sec-Shared-Storage-Writable`, true)。

要在给定响应 response请求 request 的情况下,handle a Shared-Storage-Write response,执行以下步骤:
  1. sharedStorageWritable 为在给定 `Sec-Shared-Storage-Writable`、 "item" 和 request标头列表作为输入的情况下运行 get a structured field value 算法的结果。

  2. 如果 sharedStorageWritable 为 null,或者 sharedStorageWritable 不是一个布尔值,或者 sharedStorageWritable 的值为 false,则返回。

  3. windowrequestwindow

  4. 断言window 是一个其全局对象Window环境设置对象

  5. sharedStoragewindow全局对象sharedStorage

  6. 如果 sharedStorage 为 null,则返回。

  7. listresponse标头列表

  8. operationsToParse 为在给定 `Shared-Storage-Write`、 "list" 和 list 作为输入的情况下运行 get a structured field value 算法的结果。

  9. 如果 operationsToParse 为 null 或,则 返回。

  10. methods 为空列表

  11. batchWithLock 为 null。

  12. 对于 operationsToParse 中的每个元组(item, parameters),执行 以下步骤:

    1. 如果 item 是一个内部列表,则继续。

    2. 断言item 是一个裸项

    3. methodOrOptionsString 为对 item 运行 get the string value 的结果。

    4. 如果 methodOrOptionsString 为 failure,则继续。

    5. methodOrOptionsString 进行切换:

      如果 methodOrOptionsString 为 "clear":
      执行以下步骤:
      1. options 为一个新的 SharedStorageModifierMethodOptions

      2. withLock 为使用 parameters 和 "with_lock" 运行 obtain a string-like parameter value 的结果。

      3. 如果 withLock 不是 null,且 withLock 不以 U+002D HYPHEN-MINUS (-) 开头,则将 options["withLock"] 设置withLock

      4. method 为 new SharedStorageClearMethod(options)。

      5. 如果抛出了异常, 则继续。

      6. method 附加methods

      7. 继续。

      如果 methodOrOptionsString 为 "delete":
      执行以下步骤:
      1. key 为使用 parameters 和 "key" 运行 obtain a string-like parameter value 的结果。

      2. 如果 key 为 null,则继续。

      3. options 为一个新的 SharedStorageModifierMethodOptions

      4. withLock 为使用 parameters 和 "with_lock" 运行 obtain a string-like parameter value 的结果。

      5. 如果 withLock 不是 null,且 withLock 不以 U+002D HYPHEN-MINUS (-) 开头,则将 options["withLock"] 设置withLock

      6. method 为 new SharedStorageDeleteMethod(key, options)。

      7. 如果抛出了异常, 则继续。

      8. method 附加methods

      9. 继续。

      如果 methodOrOptionsString 为 "append":
      执行以下步骤:
      1. key 为使用 parameters 和 "key" 运行 obtain a string-like parameter value 的结果。

      2. 如果 key 为 null,则继续。

      3. value 为使用 parameters 和 "value" 运行 obtain a string-like parameter value 的结果。

      4. 如果 value 为 null,则继续。

      5. options 为一个新的 SharedStorageModifierMethodOptions

      6. withLock 为使用 parameters 和 "with_lock" 运行 obtain a string-like parameter value 的结果。

      7. 如果 withLock 不是 null,且 withLock 不以 U+002D HYPHEN-MINUS (-) 开头,则将 options["withLock"] 设置withLock

      8. method 为 new SharedStorageAppendMethod(key, value, options)。

      9. 如果抛出了异常, 则继续。

      10. method 附加methods

      11. 继续。

      如果 methodOrOptionsString 为 "set":
      执行以下步骤:
      1. key 为使用 parameters 和 "key" 运行 obtain a string-like parameter value 的结果。

      2. 如果 key 为 null,则继续。

      3. value 为使用 parameters 和 "value" 运行 obtain a string-like parameter value 的结果。

      4. 如果 value 为 null,则继续。

      5. options 为一个新的 SharedStorageSetMethodOptions

      6. 如果使用 parameters 和 "ignore_if_present" 运行 obtain a boolean parameter value 的结果为 true,则将 options["ignoreIfPresent"] 设置为 true。

      7. withLock 为使用 parameters 和 "with_lock" 运行 obtain a string-like parameter value 的结果。

      8. 如果 withLock 不是 null,且 withLock 不以 U+002D HYPHEN-MINUS (-) 开头,则将 options["withLock"] 设置withLock

      9. method 为 new SharedStorageSetMethod(key, value, options)。

      10. 如果抛出了异常, 则继续。

      11. method 附加methods

      12. 继续。

      如果 methodOrOptionsString 为 "options":
      执行以下步骤:
      1. batchWithLock 设置为使用 parameters 和 "with_lock" 运行 obtain a string-like parameter value 的结果。

      2. 继续。

      如果 methodOrOptionsString 是其他任何内容:
      继续。
  13. batchOptions 为一个新的 SharedStorageModifierMethodOptions

  14. 如果 batchWithLock 不是 null,且 batchWithLock 不以 U+002D HYPHEN-MINUS (-) 开头,则将 batchOptions["withLock"] 设置为 batchWithLock

  15. 运行 sharedStorage.batchUpdate(methods, batchOptions)。

要为裸项 item check if string-like,执行以下操作:
  1. 如果 item 是一个字符串Token字节序列, 则返回 true。

  2. 否则,返回 false。

要为裸项 item get the string value,执行以下操作:
  1. 如果对 item 运行 check if string-like 的结果为 false, 则返回 failure。

  2. item 的类型进行切换:

    如果 item 是一个Token
    如果 item 是一个字符串
    执行以下步骤:
    1. 断言item 是一个ASCII 字符串

    2. 返回 item

    如果 item 是一个字节 序列
    执行以下步骤:
    1. fromUTF8 为在 item 上运行 UTF-8 解码的结果。

    2. 如果 fromUTF8 是一个 error,则返回 null。

    3. 返回 fromUTF8

要在给定参数 映射 parametersToken paramKey 的情况下,obtain a string-like parameter value,执行以下步骤:
  1. 如果 parameters包含 paramKey,则返回 null。

  2. 如果对 parameters[paramKey] 运行 check if string-like 的结果为 false,则返回 null。

  3. 返回为 parameters[paramKey] 运行 get the string value 的结果。

要在给定参数映射 parametersToken paramKey 的情况下,obtain a boolean parameter value,执行以下步骤:
  1. 如果 parameters包含 paramKey,则返回 null。

  2. 如果 parameters[paramKey] 不是一个布尔值, 则返回 null。

  3. 返回 parameters[paramKey]。

10. Web Locks 集成

10.1. 用户代理关联状态

shared storage lock managers map 是从锁管理器映射。 它初始为空。

用户 代理具有一个关联的shared storage lock managers map

注: 类似于其数据分区,共享存储具有自己的锁管理作用域, 独立于 Storage Buckets API。这些 web locks 不会与通过现有旧版 Web Locks API 从 Window 或 Worker 创建的 web locks 交互。

10.2. SharedStorageWorkletNavigator 接口

[
  Exposed=SharedStorageWorklet
] interface SharedStorageWorkletNavigator {};

10.3. Web Locks IDL 猴子补丁

NavigatorLocks mixin 包含到 SharedStorageWorkletNavigator 中(即,让 SharedStorageWorkletNavigator 包含一个 LockManager 实例):

SharedStorageWorkletNavigator includes NavigatorLocks;

LockManagerLock 额外暴露给 SharedStorageWorklet:

[SecureContext, Exposed=(Window,Worker,SharedStorageWorklet)]
interface LockManager {};
[SecureContext, Exposed=(Window,Worker,SharedStorageWorklet)]
interface Lock {};

10.4. 对 lock manager 描述的猴子补丁

在定义 lock manager 的段落末尾添加以下句子: "此外,每个用户代理都包含一个shared storage lock managers map,用于 Web Locks API 与 Shared Storage API 的集成。"

10.5. 对 locks getter 步骤的猴子补丁

locks getter steps 应更新为以下步骤:

  1. globalObject当前 Realm全局对象

  2. 如果 globalObject 是一个 SharedStorageWorkletGlobalScope

    1. 如果 globalObjectaddModule success 为 false,则抛出一个 TypeError

  3. 返回 this相关设置对象LockManager 对象

10.6. 对 "obtain a lock manager" 算法的猴子补丁

应在 obtain a lock manager 算法前置以下步骤:

  1. 如果当前 Realm全局对象是一个 SharedStorageWorkletGlobalScope

    1. workletDataOriginenvironment

    2. 返回 shared storage lock managers map[workletDataOrigin]。

10.7. "Handle callback within a shared storage lock" 算法

要在给定 workletDataOrigin字符串 name任务 callback 的情况下,handle callback within a shared storage lock, 执行以下步骤:
  1. environmentthis相关设置对象

  2. lockManagershared storage lock managers map[workletDataOrigin]。

  3. promise一个新的 promise

  4. defaultOptions 为一个新的 LockOptions

  5. 使用 promise、当前agentenvironmentidlockManagercallbacknamedefaultOptions["mode"]、 defaultOptions["ifAvailable"]、 defaultOptions["steal"]、 defaultOptions["signal"] 请求一个锁

注: 使用默认的 LockOptions 时, callback 最终会在锁被授予时被调用(即,锁请求不会失败)。

11. Permissions Policy 集成

本规范定义了三个策略控制特性,由以下字符串标识:

shared-storage" 门控对 Shared Storage 的一般访问。

"shared-storage-select-url" 为 selectURL() 增加一层额外权限

"fenced-unpartitioned-storage-read" 为 get() 增加一层额外权限,以确保它只有在从 Window 调用,且从 disableUntrustedNetwork() 返回的 Promise解析时,才能成功被调用。

对于这些特性中的每一个,默认允许列表都是 *。

12. Clear Site Data 集成

添加 Clear Site Data 集成的细节。

13. 隐私考量

Shared Storage API 试图提供一种将跨站点数据用于一系列用例的能力, 其方式相比使用第三方 cookie 更能保护用户隐私。共享存储的主要隐私 保障是,对存储在其存储中的数据的读取访问只能在嵌入者的 SharedStorageWorklet 内发生。明确定义的限制将从 SharedStorageWorklet 输出的数据限制到最低限度。

特别是,嵌入者可以基于其共享存储中的数据,从一个简短的URL 列表中选择一个 URL, 然后在围栏框架中显示结果。除非通过将在长期内得到更好缓解的特定机制, 否则嵌入者将无法知道选择了哪个 URL。 目前,每当用户点击围栏框架以发起顶级可遍历导航,和/或围栏框架调用 reportEvent() API 时,都可能泄漏少量熵。

嵌入者还能够通过私有聚合 API 发送可聚合报告,该 API 为实现差分隐私而添加噪声, 使用时间延迟来发送报告,对发送的报告数量施加限制,并将报告处理为聚合数据,从而保护个人隐私。

一致性

文档 约定

一致性要求通过描述性断言 和 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、 “MAY”和“OPTIONAL” 应按 RFC 2119 中的描述来解释。 但是,为了可读性, 这些词在本规范中不会全部以大写字母出现。

本规范的所有文本都是规范性的, 除非某些章节明确标记为非规范性、示例和注。[RFC2119]

本规范中的示例会以“例如”这些词引入, 或通过 class="example" 从规范性文本中分隔出来, 如下所示:

这是一个资料性示例的例子。

资料性注以“注”开头, 并通过 class="note" 从规范性文本中分隔出来, 如下所示:

注,这是一个资料性注。

索引

由本 规范定义的术语

由 引用定义的术语

参考文献

规范性参考文献

[BEACON]
Ilya Grigorik; Alois Reitbauer. Beacon. URL: https://w3c.github.io/beacon/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[Fenced-Frame]
Fenced Frame. Draft Community Group Report. URL: https://wicg.github.io/fenced-frame/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[I18N-GLOSSARY]
Richard Ishida; Addison Phillips. Internationalization Glossary. URL: https://w3c.github.io/i18n-glossary/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[MEDIA-SOURCE-2]
Jean-Yves Avenard; Mark Watson. Media Source Extensions™. URL: https://w3c.github.io/media-source/
[MIMESNIFF]
Gordon P. Hemsley. MIME Sniffing Standard. Living Standard. URL: https://mimesniff.spec.whatwg.org/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[PRIVATE-AGGREGATION-API]
Private Aggregation API. Unofficial Proposal Draft. URL: https://patcg-individual-drafts.github.io/private-aggregation-api/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC8941]
M. Nottingham; P-H. Kamp. Structured Field Values for HTTP. February 2021. Proposed Standard. URL: https://httpwg.org/specs/rfc8941.html
[STORAGE]
Anne van Kesteren. Storage Standard. Living Standard. URL: https://storage.spec.whatwg.org/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEB-LOCKS]
Joshua Bell; Kagami Rosylight. Web Locks API. URL: https://w3c.github.io/web-locks/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

资料性参考文献

[SERVICE-WORKERS]
Yoshisato Yanagisawa; Monica CHINTALA. Service Workers. URL: https://w3c.github.io/ServiceWorker/

IDL 索引

typedef (USVString or FencedFrameConfig) SharedStorageResponse;

[Exposed=(Window)]
interface SharedStorageWorklet : Worklet {
  Promise<SharedStorageResponse> selectURL(DOMString name,
                               sequence<SharedStorageUrlWithMetadata> urls,
                               optional SharedStorageRunOperationMethodOptions options = {});
  Promise<any> run(DOMString name,
                   optional SharedStorageRunOperationMethodOptions options = {});
};

callback RunFunctionForSharedStorageSelectURLOperation = Promise<unsigned long>(sequence<USVString> urls, optional any data);

[Exposed=SharedStorageWorklet, Global=SharedStorageWorklet]
interface SharedStorageWorkletGlobalScope : WorkletGlobalScope {
  undefined register(DOMString name,
                     Function operationCtor);

  readonly attribute SharedStorage sharedStorage;
  readonly attribute PrivateAggregation privateAggregation;

  Promise<sequence<StorageInterestGroup>> interestGroups();

  readonly attribute SharedStorageWorkletNavigator navigator;
};

dictionary SharedStorageUrlWithMetadata {
  required USVString url;
  object reportingMetadata;
};

partial interface Window {
  [SecureContext] readonly attribute SharedStorage? sharedStorage;
};

[Exposed=(Window,SharedStorageWorklet)]
interface SharedStorageModifierMethod {};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageSetMethod : SharedStorageModifierMethod {
  constructor(DOMString key, DOMString value, optional SharedStorageSetMethodOptions options = {});
};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageAppendMethod : SharedStorageModifierMethod {
  constructor(DOMString key, DOMString value, optional SharedStorageModifierMethodOptions options = {});
};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageDeleteMethod : SharedStorageModifierMethod {
  constructor(DOMString key, optional SharedStorageModifierMethodOptions options = {});
};

[Exposed=(Window, SharedStorageWorklet)]
interface SharedStorageClearMethod : SharedStorageModifierMethod {
  constructor(optional SharedStorageModifierMethodOptions options = {});
};

dictionary SharedStorageModifierMethodOptions {
  DOMString withLock;
};

dictionary SharedStorageSetMethodOptions : SharedStorageModifierMethodOptions {
  boolean ignoreIfPresent;
};

[Exposed=(Window,SharedStorageWorklet)]
interface SharedStorage {
  Promise<DOMString> get(DOMString key);
  Promise<any> set(DOMString key,
                   DOMString value,
                   optional SharedStorageSetMethodOptions options = {});
  Promise<any> append(DOMString key,
                      DOMString value,
                      optional SharedStorageModifierMethodOptions options = {});
  Promise<any> delete(DOMString key, optional SharedStorageModifierMethodOptions options = {});
  Promise<any> clear(optional SharedStorageModifierMethodOptions options = {});
  Promise<any> batchUpdate(sequence<SharedStorageModifierMethod> methods,
                           optional SharedStorageModifierMethodOptions options = {});

  [Exposed=Window]
  Promise<SharedStorageResponse> selectURL(DOMString name,
                               sequence<SharedStorageUrlWithMetadata> urls,
                               optional SharedStorageRunOperationMethodOptions options = {});

  [Exposed=Window]
  Promise<any> run(DOMString name,
                   optional SharedStorageRunOperationMethodOptions options = {});

  [Exposed=Window]
  Promise<SharedStorageWorklet> createWorklet(USVString moduleURL, optional SharedStorageWorkletOptions options = {});

  [Exposed=Window]
  readonly attribute SharedStorageWorklet worklet;

  [Exposed=SharedStorageWorklet]
  Promise<unsigned long> length();

  [Exposed=SharedStorageWorklet]
  Promise<double> remainingBudget();

  [Exposed=SharedStorageWorklet]
  async iterable<DOMString, DOMString>;
};

dictionary SharedStoragePrivateAggregationConfig {
  USVString aggregationCoordinatorOrigin;
  USVString contextId;
  [EnforceRange] unsigned long long filteringIdMaxBytes;
  [EnforceRange] unsigned long long maxContributions;
};

dictionary SharedStorageRunOperationMethodOptions {
  object data;
  boolean resolveToConfig = false;
  boolean keepAlive = false;
  SharedStoragePrivateAggregationConfig privateAggregationConfig;
  DOMString savedQuery;
};

dictionary SharedStorageWorkletOptions : WorkletOptions {
  USVString dataOrigin = "context-origin";
};

interface mixin HTMLSharedStorageWritableElementUtils {
  [CEReactions, SecureContext] attribute boolean sharedStorageWritable;
};

HTMLIFrameElement includes HTMLSharedStorageWritableElementUtils;
HTMLImageElement includes HTMLSharedStorageWritableElementUtils;

partial dictionary RequestInit {
  boolean sharedStorageWritable;
};

[
  Exposed=SharedStorageWorklet
] interface SharedStorageWorkletNavigator {};

SharedStorageWorkletNavigator includes NavigatorLocks;

[SecureContext, Exposed=(Window,Worker,SharedStorageWorklet)]
interface LockManager {};

[SecureContext, Exposed=(Window,Worker,SharedStorageWorklet)]
interface Lock {};

问题索引

如果先进行 permissions policy 检查,请考虑在这里添加一个提前返回。
鉴于 WebIDL,这里的 "name" 和 "operationCtor" 不可能缺失。应当只检查 默认/空值。 [Issue #151]
reportingMetadata 应当是一个 dictionary[Issue #141]
reportingUrlMap 存储在与 fencedFrameConfigStruct 关联的 fenced frame reporter 类中。这两者仍都需要被 添加到草案 [Fenced-Frame] 中。 [Issue #144]
pending shared storage budget debit 的定义移动到草案 fenced frame config instance 中,即草案 [Fenced-Frame] 规范中。 [Issue #148]
需要找到一种更好的方式来指定导航预算扣除的时机。 [Issue #138]
determine whether a request can currently use shared storage 算法需要考虑“选择加入特性”,如 https://github.com/w3c/webappsec-permissions-policy/pull/499 中所阐述。