能力委托

社区组报告草案,

本版本:
https://wicg.github.io/capability-delegation/spec.html
问题跟踪:
GitHub
编辑:
(Google Canada)

摘要

将使用受限 API 的能力转移给另一个窗口。

本文档的状态

本规范由网页平台孵化器社区组发布。它既不是 W3C 标准,也不在 W3C 标准轨道上。请注意,W3C 社区贡献者许可协议(CLA)规定了有限的退出机制,并适用其他条件。进一步了解W3C 社区组和商务组。

1. 引言

本节为非规范性内容。

本规范定义了一种机制,使脚本能够将调用受限 API 的能力委托给另一个它信任的浏览上下文。这里关注的是一种动态委托机制,它在有限时间内向目标浏览上下文提供所委托的能力。

1.1. 什么是能力委托?

网页中的许多能力只能以受限方式从 JS 使用。例如:

能力委托允许框架动态放弃其调用受限 API 的能力,并将该能力转移给另一个可信的(子)框架。这里的“动态”意味着委托效果仅在所委托能力定义的有限时间内持续。这与通过 浏览上下文所用的 iframe 的 allow 属性静态地(在加载时)暴露能力不同;后一种方式会不受时间限制地向子框架暴露能力。

1.2. 发起委托与使用能力

能力委托需要两个不同的步骤才能生效。第一步是“发起”:一个浏览上下文通知另一个浏览上下文,某项能力正在委托给它。发起之后,第二个(即接收方)浏览上下文会“使用”所委托的能力,通常是调用由该能力定义的方法。本能力委托规范不定义第二步所用的 API 接口,但会重新定义该 API 的内部行为。

因此,本规范分为两个不同的部分:先定义发起步骤的 API,再为一个具体的“使用方”API 定义委托行为。第二部分重点说明支付请求 API所需的行为变更,为将来利用能力委托的其他 API 提供类似变更的指南。

1.3. 短暂可用性

上述两个步骤本质上都受时间限制:

  1. 发起步骤会消耗激活,因此只有在最近发生用户激活之后才允许执行。此外,这里的用户激活消耗确保每次用户激活最多只能使用一次委托机制。这可以防止恶意使用能力委托,例如反复向多个框架尝试委托,从而实际上绕过所委托 API 的用户激活限制。

  2. 成功完成发起步骤后,所委托的 API 仅在目标浏览上下文中可用数秒。确切时限取决于该 API 的规范如何定义委托行为。如果 API 未定义自己的时限,则默认时限与用户激活的过期时限相同。

2. 示例

当网站希望在鼠标点击后,向子框架委托调用以下 API 的能力时:[payment-request] show(),它会向子框架发送消息,并附带一个指定委托能力的选项:
window.onclick = () => {
  targetWindow.postMessage('a_message', {delegate: "payment"});
};

收到消息后,子框架即使没有获得用户激活,也能够使用 show():

window.onmessage = () => {
  const payRequest = new PaymentRequest(...);
  const payResponse = await payRequest.show();
  ...
}

3. 发起能力委托

当一个浏览上下文希望将能力委托给另一个浏览上下文时,它向第二个浏览上下文发送消息,并附加一个 WindowPostMessageOptions,名为 delegate,用于指定能力。该选项的值必须是功能标识符。如果该值不对应任何用户代理支持的功能,则必须忽略此选项。

可用的功能标识符值列表见此处.

3.1. 对 HTML 规范的临时修补

WindowPostMessageOptions 的 IDL 定义将增加如下字段:

DOMString? delegate;

在窗口发送消息的算法中,以下步骤:

  1. 令 transfer 为 options["transfer"]。

之后将增加以下两个步骤:

  1. 令 delegate 为 options["delegate"]。

  2. 如果 delegate 不为 null,则:

    1. 如果用户代理不支持委托 delegate 指示的功能,则抛出 "NotSupportedError" DOMException。

    2. 如果 targetWindow 的关联 Document未被允许使用由 delegate 指示的功能,则抛出 "NotAllowedError" DOMException。

    3. 如果 targetOrigin 为单个 U+002A 星号字符(*),则抛出 "NotAllowedError" DOMException。

      选项 targetOrigin 的默认值为 "/",将消息限制在同源目标内。另行要求使用 "*" 之外的字符串,意味着跨源消息必须指定其预期接收方的具体源。
    4. 令 source 为 incumbentSettings 的全局对象。

    5. 如果 source 没有瞬时激活,则抛出 "NotAllowedError" DOMException。

    6. 消耗用户激活,所针对的是 source。

4. 跟踪委托的能力

委托给浏览上下文的能力将使用名为 Window.DELEGATED_CAPABILITY_TIMESTAMPS 的映射来跟踪。每次将能力委托给 Window 时,都在 DELEGATED_CAPABILITY_TIMESTAMPS 中添加一个条目,其键等于表示该能力的功能标识符,值等于当前的 DOMHighResTimeStamp。如果映射已包含同键条目,则将已有值更新为当前的 DOMHighResTimeStamp。

4.1. 对 HTML 规范的临时修补

在窗口发送消息算法的紧前方插入如下新段落:

为了跟踪委托给浏览上下文的能力,每个 Window 都有一个映射,名为 DELEGATED_CAPABILITY_TIMESTAMPS,将功能标识符映射到 DOMHighResTimeStamp。该映射初始化为空映射。

在窗口发送消息算法中,为当前第 8 步添加两个子步骤。第一个新增子步骤插入以下子步骤之后:

  1. 将一个全局任务加入队列……

    1. 令 origin 为 incumbentSettings 的源的序列化结果。

如下:

  1. 将一个全局任务加入队列……(不变)

    1. 令 delegate 为 options["delegate"]。

第二个新增子步骤插入以下子步骤之后:

  1. 将一个全局任务加入队列……

    1. 令 newPorts 为由……组成的新冻结数组。

如下:

  1. 将一个全局任务加入队列……(不变)

    1. 令 newPorts 为由……组成的新冻结数组。(除编号外不变)

    2. 如果 delegate 不为 null,并且用户代理支持委托 delegate,则将 DELEGATED_CAPABILITY_TIMESTAMPS[delegate] 设为当前高精度时间。

5. 定义委托能力的行为

任何定义了委托行为的能力,都以适合该能力的方式使用 Window.DELEGATED_CAPABILITY_TIMESTAMPS 中的对应条目。以下列出某项具体能力所需的规范变更。

5.1. 对支付请求规范的临时修补

在 show() 的算法中,将替换以下步骤以实现委托行为:

以下两个步骤:

  1. 如果 request 的相关全局对象没有瞬时激活:

    1. 返回以 "SecurityError" DOMException 拒绝的 promise。

  2. 消耗相关全局对象的用户激活。

将被替换为以下三个步骤:

  1. 如果 request 的相关全局对象没有瞬时激活,并且该相关全局对象中的时间戳 DELEGATED_CAPABILITY_TIMESTAMPS["payment"] 为 undefined 或已过期:

    1. 返回以 "SecurityError" DOMException 拒绝的 promise。

  2. 如果 request 的相关全局对象没有瞬时激活,则清除映射条目 DELEGATED_CAPABILITY_TIMESTAMPS["payment"]。

  3. 否则,消耗用户激活,所针对的是相关全局对象。

5.2. 对全屏规范的临时修补

在 requestFullscreen() 的算法中,进行以下修改以实现委托行为:

第 5 步的最后一个条件:

  1. 如果以下任一条件为 false,则将 error 设为 true:……

    • this 的相关全局对象具有瞬时激活,或者该算法由用户产生的方向变化触发。

将被替换为:

  1. 如果以下任一条件为 false,则将 error 设为 true:……(不变)

    • this 的相关全局对象具有瞬时激活,或者该对象中的时间戳 DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"] 既不是 undefined 也未过期,或者该算法由用户产生的方向变化触发。

在第 10 步的紧前方:

  1. 令 fullscreenElements 为初始仅包含 this 的有序集合。

将插入以下新步骤:

  1. 如果 this 的相关全局对象没有瞬时激活,则清除该对象中的映射条目 DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"]。

  2. 令 fullscreenElements 为初始仅包含 this 的有序集合。(除编号外不变)

5.3. 临时修补:[SCREEN-CAPTURE] 规范

在 getDisplayMedia() 的算法中,进行以下修改以实现委托行为:

第 3 步的条件:

  1. 如果 this 的相关全局对象没有瞬时激活,则返回一个 promise,其以 name 属性值为 InvalidStateError 的 DOMException 对象拒绝。

将被替换为:

  1. 如果 this 的相关全局对象没有瞬时激活,并且时间戳 DELEGATED_CAPABILITY_TIMESTAMPS["display-capture"] 在 this 的相关全局对象中为 undefined 或已过期,则返回一个已拒绝的 promise,拒绝原因是一个 DOMException 对象,其 name 属性值为 InvalidStateError。

符合性

文档约定

符合性要求通过描述性断言与 RFC 2119 术语相结合来表达。本文档规范性部分中的关键词“必须(MUST)”“不得(MUST NOT)”“必需(REQUIRED)”“必须(SHALL)”“不得(SHALL NOT)”“应(SHOULD)”“不应(SHOULD NOT)”“推荐(RECOMMENDED)”“可以(MAY)”和“可选(OPTIONAL)”,应按 RFC 2119 的描述解释。不过,为了便于阅读,这些词在本规范中并非全部以大写字母呈现。

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

本规范的示例以“例如”引出,或通过 class="example" 与规范性文本区分,如下所示:

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

资料性注释以“注”开头,并通过 class="note" 与规范性文本区分,如下所示:

注:这是一条资料性注释。

符合规范的算法

作为算法一部分、以祈使语气表述的要求(例如“移除所有前导空格字符”或“返回 false 并终止这些步骤”),应按照引入该算法时所用关键词(“必须”“应”“可以”等)的含义解释。

以算法或具体步骤表述的符合性要求,可以用任何方式实现,只要最终结果等价即可。特别是,本规范定义的算法旨在易于理解,而非追求高性能。鼓励实现者进行优化。

索引

本规范定义的术语

通过引用定义的术语

参考文献

规范性参考文献

[FULLSCREEN]
Philip Jägenstedt. 全屏 API 标准。持续更新标准。网址:https://fullscreen.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. 高精度时间。网址:https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren 等。HTML 标准。持续更新标准。网址:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准。持续更新标准。网址:https://infra.spec.whatwg.org/
[PAYMENT-REQUEST-1.1]
Marcos Caceres; Rouslan Solomakhin; Ian Jacobs. 支付请求 API 1.1。网址:https://w3c.github.io/payment-request/
[PERMISSIONS-POLICY-1]
Ian Clelland. 权限策略。网址:https://w3c.github.io/webappsec-permissions-policy/
[RFC2119]
S. Bradner. 用于 RFC 中表示要求级别的关键词。1997 年 3 月。当前最佳实践。网址:https://datatracker.ietf.org/doc/html/rfc2119
[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. 屏幕捕获。网址:https://w3c.github.io/mediacapture-screen-share/
[WebCryptoAPI]
Mark Watson. 网页密码学 API。网址:https://w3c.github.io/webcrypto/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准。持续更新标准。网址:https://webidl.spec.whatwg.org/

资料性参考文献

[PAYMENT-REQUEST]
Marcos Caceres; Rouslan Solomakhin; Ian Jacobs. 支付请求 API。网址:https://w3c.github.io/payment-request/