1. 引言
本节为非规范性内容。
本规范定义了一种机制,脚本可通过该机制将其调用受限 API 的 能力委托给它信任的另一个浏览上下文。这里 关注的是一种动态委托机制,它以受时间约束的方式,将被委托的 能力暴露给目标浏览上下文。
1.1. 什么是能力委托?
Web 中的许多能力可以通过 JS 以受限方式使用。例如:
-
大多数浏览器只有在用户最近与页面发生过交互, 或者允许浏览器从该页面的源打开弹出窗口时,才允许弹出窗口(通过
Window.open())。 -
一个被沙箱化的 iframe 不能使自身全屏 (尽管可以调用
requestFullscreen()), 除非具有特定的沙箱属性 或该框架内发生了用户交互。
能力委托意味着允许一个框架动态放弃其调用受限 API 的
能力,并将该能力转移给它可以信任的另一个(子)框架。
这里的“dynamic”一词表示委托的效果会持续
一段由被委托能力定义的有限时间。这不同于通过 浏览
上下文中的 iframe
allow
属性,对子框架进行能力的静态(加载时)暴露,在这种情况下,该能力会以
不受时间约束的方式暴露给子框架。
1.2. 发起委托与使用能力
能力委托需要两个不同的步骤才能生效。第一步 是“发起”,其中一个浏览上下文通知另一个浏览 上下文有某个特定能力正在被委托。发起之后, 第二个(即接收者)浏览上下文将“使用”被委托的 能力,这通常意味着调用一个由能力定义的方法。虽然 此处的能力委托规范未定义第二步中使用的 API 接口, 但它重新定义了该 API 的内部行为。
因此,本规范由两个不同部分组成:定义用于 发起步骤的 API,然后为一个特定的“使用者”API 定义被委托行为。 对于第二部分,本规范重点关注 Payment Request API 中所需的行为变更,该 API 将 作为未来其他任何会利用能力委托的 API 中类似变更的指南。
1.3. 瞬态可用性
上述两个步骤本质上都受时间约束:
-
发起步骤会消耗激活, 因此该步骤只允许在最近发生用户激活之后执行。 此外,此处对用户激活的消耗保证了 委托机制不能在每次用户激活中被使用多于一次。这 防止能力委托被恶意使用,例如反复向多个框架进行委托 尝试,从而有效绕过被委托 API 的用户激活 限制。
-
发起步骤成功完成之后,被委托的 API 只会在目标浏览上下文中可供使用几 秒。这里的确切时间限制取决于被委托 API 如何在其自身规范中定义被委托行为。对于 未定义自身时间限制的 API,默认限制将与用户 激活 过期相同。
2. 示例
show()
的能力委托给子框架时,它将向该子框架发布消息,
并带有一个额外的
选项来指定被委托的能力:
window. onclick= () => { targetWindow. postMessage( 'a_message' , { delegate: "payment" }); };
收到该消息后,即使该框架没有收到用户
激活,子框架也能够使用 show():
window. onmessage= () => { const payRequest= new PaymentRequest(...); const payResponse= await payRequest. show(); ... }
3. 发起能力委托
当某个浏览上下文想要将某项能力委托给另一个浏览
上下文时,它会向第二个浏览上下文发布消息,并带有一个名为 delegate 的额外 WindowPostMessageOptions
来指定该能力。此选项的值必须是一个feature-identifier。
如果该值不对应任何用户代理支持的
特性,则必须
忽略该选项。
3.1. 对 HTML 规范的猴子补丁
WindowPostMessageOptions
IDL 定义将包含一个额外字段,如下所示:
DOMString? delegate;
在window post message 的算法中, 以下步骤:
令 transfer 为 options["transfer"]。
后面将跟随如下两个额外步骤:
-
令 delegate 为 options["delegate"]。
-
如果 delegate 不为 null,则:
-
如果用户代理不支持委托由 delegate 指示的特性, 则抛出一个 "NotSupportedError" DOMException。
-
如果 targetWindow 的关联 Document 不被allowed-to-use 由 delegate 指示的特性,则抛出一个 "NotAllowedError" DOMException。
-
如果 targetOrigin 是单个 U+002A ASTERISK 字符 (*),则抛出一个 "NotAllowedError" DOMException。
targetOrigin 的默认值为 "/",它将消息限制为同源目标。使用 "*" 之外字符串的额外要求意味着 跨源消息必须指定其所针对的具体源。 -
令 source 为 incumbentSettings 的全局对象。
-
如果 source 不具有瞬态 激活, 则抛出一个 "NotAllowedError" DOMException。
-
在 source 中消耗 用户 激活。
-
4. 跟踪被委托的能力
委托给某个浏览上下文的能力将使用一个名为
Window.DELEGATED_CAPABILITY_TIMESTAMPS 的映射来跟踪。每次将能力
委托给某个 Window
时,
都会在 DELEGATED_CAPABILITY_TIMESTAMPS 中添加一个条目,其键等于
表示该
能力的 feature-identifier,
值等于当前 DOMHighResTimeStamp。
如果该映射
已经有相同键的条目,则现有值将更新为
当前 DOMHighResTimeStamp。
4.1. 对 HTML 规范的猴子补丁
在window post message 的算法之前, 将插入一个新段落,如下所示:
为了跟踪委托给某个浏览上下文的能力, 每个
Window都有一个从 feature-identifier 到DOMHighResTimeStamp的映射,名为 DELEGATED_CAPABILITY_TIMESTAMPS。 该映射初始化为空映射。
在window post message 的算法中, 将向当前步骤 8 添加两个额外子步骤。第一个额外 子步骤将插入在以下子步骤之后:
排入一个全局任务 ...
令 origin 为 incumbentSettings 的 origin 的序列化。
如下所示:
-
排入一个全局任务 ... (不变)
-
令 delegate 为 options["delegate"]。
-
第二个额外子步骤将插入在以下子步骤之后:
排入一个全局任务 ...
令 newPorts 为一个由 ... 组成的新冻结数组
如下所示:
-
排入一个全局任务 ... (不变)
-
令 newPorts 为一个由 ... 组成的新冻结数组 (除 编号外不变)
-
如果 delegate 不为 null,且用户代理支持 委托 delegate,则将 DELEGATED_CAPABILITY_TIMESTAMPS[delegate] 设置为当前 高分辨率 时间。
-
5. 定义被委托能力的行为
任何定义了被委托行为的能力,都会以适合该
能力的方式使用 Window.DELEGATED_CAPABILITY_TIMESTAMPS 中的相应条目。
下面是某个特定能力所需的规范变更。
5.1. 对 Payment Request 规范的猴子补丁
在 show()
的算法中,
以下步骤将被
替换,以实现被委托行为:
这两个步骤:
如果 request 的相关全局对象不具有瞬态激活:
返回一个以 "SecurityError" DOMException 拒绝的 promise。
消耗相关全局对象的用户激活。
将由以下三个步骤替换:
-
如果 request 的相关全局对象不具有瞬态 激活, 且相关全局对象中的时间戳 DELEGATED_CAPABILITY_TIMESTAMPS["payment"] 要么未定义,要么已过期:
-
返回一个以 "SecurityError" DOMException 拒绝的 promise。
-
-
如果 request 的相关全局对象不具有瞬态激活, 则清除映射条目 DELEGATED_CAPABILITY_TIMESTAMPS["payment"]。
-
否则,消耗 相关全局对象的用户 激活。
5.2. 对 Fullscreen 规范的猴子补丁
在 requestFullscreen()
的算法中,
将进行以下变更,
以实现被委托行为:
步骤 5 中的最后一个条件:
如果以下任一条件为 false,则将 error 设置为 true:...
this 的相关全局对象具有瞬态激活,或者 该算法由用户生成的方向变化触发。
将被替换为:
-
如果以下任一条件为 false,则将 error 设置为 true:... (不变)
-
this 的相关全局对象具有瞬态激活,或者 this 的相关全局对象中的时间戳 DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"] 既不是 undefined,也没有过期, 或者 该算法由用户生成的方向变化触发。
-
在步骤 10 之前:
令 fullscreenElements 为一个有序集,初始仅包含 this。
将插入以下新步骤:
-
如果 this 的相关全局对象不具有瞬态激活,则清除 this 的相关全局对象中的映射条目 DELEGATED_CAPABILITY_TIMESTAMPS["fullscreen"]。
-
令 fullscreenElements 为一个有序集,初始仅包含 this。(除 编号外不变)
5.3. 对 [SCREEN-CAPTURE] 规范的猴子补丁
在 getDisplayMedia()
的算法中,
将进行以下变更,
以实现被委托行为:
步骤 3 中的条件:
如果 this 的相关全局对象不具有瞬态激活,则返回一个以 name 属性值为 InvalidStateError 的 DOMException 对象拒绝的 promise。
将被替换为:
-
如果 this 的相关全局对象不具有瞬态激活,且 this 的相关全局对象中的时间戳 DELEGATED_CAPABILITY_TIMESTAMPS["display-capture"] 要么未定义,要么已过期, 则返回一个以 拒绝的 promise,其拒绝原因是一个 DOMException 对象,且其
name属性的值为InvalidStateError。