1. 推测规则
修改 HTML § 7.6.1 推测规则 以支持预渲染。
1.1. 解析
-
target navigable name hint,一个 字符串 或 null
-
移除 typesToTreatAsPrefetch 构造,并将 parsed["
prerender"] 解析到 prerender rules 列表,parsed["prerender_until_script"] 解析到 prerender_until_script rules 列表,方式与 parsed["prefetch"] 及 prefetch rules 相同。 -
丢弃 parsed["
prefetch"] 解析得到的规则,如果 target navigable name hint 非 null。 -
丢弃 parsed["
prerender"] 和 parsed["prerender_until_script"] 解析得到的规则,如果 要求包含 "anonymous-client-ip-when-cross-origin"。
实现仍允许将 prerender 候选视为 prefetch,详见 § 1.2 处理模型。
-
令 targetHint 为 null。
-
如 input["
target_hint"] 存在:-
如 input["
target_hint"] 不是 有效可导航目标名或关键字:-
用户代理可以 报告警告到控制台,指示该 target hint 无效。
-
返回 null。
-
-
设 targetHint 为 input["
target_hint"]。
-
并将最终返回 推测规则的一步,更新为包含设置 target navigable name hint 为 targetHint。
1.2. 处理模型
-
target navigable name hint,有效可导航目标名或关键字 或 null
-
should block scripts,一个 布尔值,初始为 false
-
令 prerenderCandidates 为一个空 列表。
-
遍历 document 的 推测规则集 的每一项 ruleSet:
-
遍历 « (ruleSet 的 prerender rules, false)、(ruleSet 的 prerender_until_script rules, true) » 的每一对(rules, shouldBlockScripts):
-
遍历 rules 中的每个 rule:
-
-
令 referrerPolicy 为 计算推测加载 referrer policy 的结果,参数为 rule 和 null。
-
- URL
-
url
- No-Vary-Search提示
-
rule 的 No-Vary-Search提示
- eagerness
-
rule 的 eagerness
- referrer policy
-
referrerPolicy
- 标签
-
rule 的 标签
- target navigable name hint
-
rule 的 target navigable name hint
- should block scripts
-
shouldBlockScripts
-
-
如 rule 的 谓词 非 null,则:
-
遍历 links 的每一项 link:
-
令 target 为 rule 的 target navigable name hint。
-
如 target 为 null,则设置为 获取元素 target 的结果,参数为 link。
-
令 referrerPolicy 为 计算推测加载 referrer policy 的结果,参数为 rule 和 link。
-
- URL
-
link 的 url
- No-Vary-Search 提示
-
rule 的 No-Vary-Search 提示
- eagerness
-
rule 的 eagerness
- referrer policy
-
referrerPolicy
- 标签
-
rule 的 标签
- target navigable name hint
-
target
- should block scripts
-
shouldBlockScripts
-
-
-
-
-
令 speculativeLoadCandidates 为 prefetchCandidates 与 prerenderCandidates 的并集。
修改后续取消非 仍在推测中 的 prefetch record 的步骤,使其操作对象为 speculativeLoadCandidates,不再是 prefetchCandidates。
像处理 prefetchCandidateGroups 那样,创建 prerenderCandidateGroups,但底层数据为 prerenderCandidates。
用如下内容替换对 prefetchCandidateGroups 实际预取循环的步骤:
-
令 speculativeLoadCandidateGroups 为 prefetchCandidateGroups 与 prerenderCandidateGroups 的并集。
-
遍历 speculativeLoadCandidateGroups 的每个 group:
-
用户代理可运行如下步骤:
-
令 candidate 为 group[0]。
-
令 tagsToSend 为 收集推测加载候选标签 的结果,参数为 group。
-
令 prefetchRecord 为新建的 预取记录,内容:
- source
-
"
speculation rules" - URL
-
candidate 的 URL
- No-Vary-Search提示
-
candidate 的 No-Vary-Search提示
- referrer policy
-
candidate 的 referrer policy
- 标签
-
tagsToSend
-
如 candidate 是 预取候选,则设 prefetchRecord 的 匿名策略 为 candidate 的 匿名策略。
-
如 candidate 是 预渲染候选,用户代理可运行如下步骤:
-
设 prefetchRecord 的 预渲染可遍历对象 为 "
to be created"。 -
设 prefetchRecord 的 预渲染目标可导航名称提示 为 candidate 的 target navigable name hint。
-
启动referrer发起的导航预渲染,参数为 document,prefetchRecord,candidate 的 should block scripts。
-
-
如未执行上一步,则 启动referrer发起的导航预取,参数为document和prefetchRecord。
(后续执行"may"步骤的时机讨论保留)
-
-
1.3. 触发
更新
属性变更步骤,使其在
a
和
area
元素上也监听
target
属性的变化,并在其改变时 重新考虑推测加载。
2. 预渲染基础设施
2.1. 对 Document
接口的扩展
我们将这样修改 [HTML] 中 Document
的集中定义:
partial interface Document {readonly attribute boolean prerendering ; // Under "special event handler IDL attributes that only apply to Document objects"attribute EventHandler onprerenderingchange ; };
onprerenderingchange 属性是一个 事件处理器 IDL 属性 ,对应于 prerenderingchange 事件类型。(我们会更新 [HTML] 中相关表,目前只有 onreadystatechange。)
如同 [HTML] 的习惯,prerendering
的定义会放在规范的其它章节;我们将在下方新章节中定义:
2.2. 预渲染可导航对象
下述章节将作为 [HTML] 中 Navigables 部分的新子节添加。
每个 可导航对象都有一个 加载模式,可取以下值之一:
- "
default" -
在该可导航对象中加载内容无特殊限制
- "
prerender" -
该可导航对象正显示预渲染内容
默认情况下,可导航对象的 加载模式 为 "default"。 加载模式为 "prerender"
的可导航对象,被称为 预渲染可导航对象。 预渲染可导航对象,若同时为 顶级可遍历对象,称为 预渲染可遍历对象。
预渲染可导航对象的 活跃浏览环境 永远不会是 辅助浏览环境。
虽然 加载模式 目前只有两个值,但结构设计上为将来如 fenced frame、portal、uncredentialed(跨站)预渲染等其它模式预留了拓展空间。如果后续正式规范中不需要,可替换为布尔型实现。
每个 可导航对象有一个 脚本模式,可取以下值:
- "
enabled" -
脚本可正常执行。
- "
blocked-until-activation" -
在对象被激活前,脚本被阻止执行。
脚本模式作为可导航对象属性(而非文档的),以便在同一 navigable 内导航时持久保持。例如处理首次
about:blank,或者在激活前发生的客户端重定向。
每个 预渲染可遍历对象有一个 预渲染初始响应搜索变体,类型为 URL搜索变体 或 null,默认 null。
document.prerendering-
若页面正处于非交互式预渲染环境下,则返回 true。
其值可由 true 变为 false,并伴随
Document触发prerenderingchange事件。(不会再从 false 变回 true。)
prerendering getter 步骤为:如果 this 有一个非 null 的 节点可导航对象且为 预渲染可导航对象,则返回 true,否则为 false。
每个 Document
有一个 预渲染激活后步骤列表,是一个 列表
每个 项是一系列算法步骤。此处为方便,也定义对任意平台对象 platformObject 的 预渲染激活后步骤列表:
- 如果 platformObject 是 节点
-
-
返回 platformObject 的 node document 的 预渲染激活后步骤列表。
-
- 否则
-
-
返回 platformObject 的 相关全局对象 的 关联 Document 的 预渲染激活后步骤列表。
每个 Document
有一个 激活开始时间,初始为一个时间值为零的 DOMHighResTimeStamp。
每个 Document
有一个 允许预渲染时跨域 iframe 导航,类型为 布尔值,初始值为
false。
2.3. 预渲染算法
若要启动用户代理发起的预渲染,给定一个URL startingURL:
-
令prerenderingTraversable为创建新的顶级可遍历对象的结果,参数为null与空字符串。
-
设prerenderingTraversable的加载模式为"
prerender"。 -
令prefetchRecord为新的预取记录,其中URL为startingURL,匿名化策略为null,referrer policy为空字符串,No-Vary-Search提示为默认URL搜索变体,source为"
browser UI",prerendering traversable为prerenderingTraversable。 -
启动referrer发起的导航预取,参数为prerenderingTraversable的活跃文档与prefetchRecord。
-
导航 prerenderingTraversable到startingURL,使用prerenderingTraversable的活跃文档。
我们将此初始导航视为prerenderingTraversable自身的导航,从而确保所有安全检查通过。
-
当用户表明希望提交到activationURL,其为URL,且prefetchRecord URL匹配activationURL时:
用户可能永远不发起此类提交,也可能等待太久导致用户代理需回收预渲染用资源用于更紧急任务。在这种情况下用户代理可以销毁 prerenderingTraversable。
Document
referrerDoc、预取记录 prefetchRecord、布尔blockScripts:
-
断言:prefetchRecord的URL的scheme为HTTP(S) 方案。
-
如referrerDoc的节点可导航对象不是顶级可遍历对象,则返回。
当前不支持或实现从子可导航对象发起预渲染,否则会涉及预渲染可导航对象如何插入到树中的复杂性。
-
如referrerDoc的源与prefetchRecord的URL的源不同站,则返回。
当前未支持或实现跨站预渲染,相关设计想法见本库 explainer。
-
如referrerDoc 已有匹配的预取记录,参数为prefetchRecord且checkPrerender为true,则返回。
-
断言:prefetchRecord的prerendering traversable为"
to be created"。 -
令prerenderingTraversable为创建新的顶级可遍历对象的结果。
用户代理可以用prefetchRecord的预渲染目标可导航名称提示作为创建新顶级可遍历对象的实现提示,此提示表明开发者期望后续激活时用哪个前驱遍历对象替换。即用预渲染目标可导航名称提示和referrerDoc的节点可导航对象调用选择可导航对象规则。
这只是提示,没有规范行为要求,将来同样可以在未给予提示的前驱对象上激活。
-
设prerenderingTraversable的加载模式为"
prerender"。 -
如blockScripts为true,则设prerenderingTraversable的脚本模式为"
blocked-until-activation"。 -
设prefetchRecord的prerendering traversable为prerenderingTraversable。
-
设prerenderingTraversable的remove from referrer为:将prefetchRecord的prerendering traversable设为null的算法。
与所有顶级可遍历对象一样,prerendering traversable可因各种原因销毁。比如无响应、执行受限操作、或用户代理判断资源占用过高。这种情况下,prefetch record 仍会保留,以便后续导航复用,即使prerendering traversable已销毁。
-
启动referrer发起的导航预取,参数为referrerDoc与prefetchRecord。
-
导航 prerenderingTraversable到prefetchRecord的URL,使用referrerDoc,referrerPolicy为prefetchRecord的referrer policy。
若要更新激活用后继,参数为prerendering traversable successorTraversable,URL startingURL,以及URL activationURL:
-
令successorDocument为successorTraversable的活跃文档。
-
若startingURL等于successorDocument的URL且startingURL不等于activationURL:
-
令navigation为successorDocument的相关全局对象的navigation API。
-
令continue为触发 push/replace/reload
navigate事件的结果,参数为navigation,"replace"、activationURL和true。 -
如continue为true,则用successorDocument和activationURL运行URL及历史更新步骤。
这保证了预渲染的URL可与实际导航到的URL一致,适用于基于 `
No-Vary-Search`的非精确匹配情形。
若要激活 prerendering traversable successorTraversable 替换顶级可遍历对象 predecessorTraversable,参数为历史处理行为 historyHandling、URL startingURL、URL activationURL、可选navigation ID navigationId:
-
断言:successorTraversable的活跃文档的是否初始about:blank为false。
-
如未指定navigationId,则令其为生成的随机UUID。
-
设predecessorTraversable的正在进行的导航为navigationId。
这样做会终止predecessorTraversable的其他并行导航。
-
并行执行:
-
令unloadPromptCanceled为对predecessorTraversable的活跃文档的包含自身的后代可导航对象执行检查卸载是否被用户取消的结果。
-
如unloadPromptCanceled为true,或predecessorTraversable的进行中导航已不等于navigationId,则中止后续步骤。
-
全局任务排队在predecessorTraversable的活跃窗口上,执行中止predecessorTraversable的活跃文档。
-
更新激活用后继,参数为successorTraversable、startingURL、activationURL。
-
为predecessorTraversable追加会话历史遍历步骤,执行如下操作:
-
断言:successorTraversable的当前会话历史步骤索引为0。
-
令successorEntry为successorTraversable的会话历史条目[0]。
-
移除successorEntry从successorTraversable的会话历史条目。
此时successorTraversable已为空,可无副作用地销毁。(只要实现确保successorEntry携带的文档及其
Document的 浏览环境存活用于后续。) -
完成跨文档导航,参数为predecessorTraversable、historyHandling、successorEntry。
由于移动了整个会话历史条目(包含文档及其 浏览环境),这意味着相关会话条目的遍历将导致浏览环境组切换,类似于COOP头行为。这会断开opener关系,无论是激活还是后撤操作(如用户点击后退或
history.back())。 -
更新用户代理界面,以体现该变化,如更新标签/窗口内容与浏览器Chrome。
-
完成激活,参数为successorTraversable、referrerOrigin。
-
此处或许应配合WebDriver BiDi?参见 w3c/webdriver-bidi#321。当前,在实际并行执行前,navigate算法会发起WebDriver BiDi navigation started,但导航完成不会有事件。
-
-
-
遍历traversable的活跃文档的包含自身的后代可导航对象,对每个navigable,全局任务排队在navigable的活跃窗口上,依次执行以下步骤:
-
遍历navigable的预渲染范围Accept-CH缓存中origin→hintSet:
-
设Accept-CH缓存[origin]为hintSet。
-
-
如navigable的脚本模式为"
blocked-until-activation":脚本恢复后实际执行细节,特别是先前入队的任务如何处理,细节待补充。
-
令doc为navigable的活跃文档。
-
这里应在投递任务内传播加载模式变更,实现会更新
document.prerendering的返回值。不过实现上加载模式目前挂在traversable上,因此移动会话历史条目后会“自动”变。后续应迁移到Document对象。 -
触发事件,事件名为
prerenderingchange,目标为doc。 -
遍历 doc的预渲染激活后步骤列表的每一项steps:
-
执行steps。
这些步骤可有返回,如
Promise,但按规范可忽略。 -
断言:执行steps未抛异常。
-
-
为确保prerendering traversable被销毁时能清除引用,修改销毁顶级可遍历对象,追加如下步骤:
-
如traversable是prerendering traversable,且其remove from referrer非null,则调用之。
2.4. 创建可导航对象的修改
-
将 navigable 的 loading mode 设置为 element 的 node navigable 的 loading mode。
3. 导航与会话历史
3.1. 允许用激活替代导航
Document
predecessorDocument 和 URL url 的情况下 查找匹配的已预渲染预取记录:
-
令 recordToUse 为 null。
-
遍历 predecessorDocument 的 prefetch records 中的每个 record:
-
若 record 的 prerendering traversable 不是一个 prerendering traversable,则 继续(continue)。
-
若 record 的 prerendering traversable 的 active document 的 is initial about:blank 为 true,则 继续。
-
若 record 的 URL 等于 url:
-
将 recordToUse 设为 record。
-
跳出循环(Break)。
-
-
若 recordToUse 为 null 且 record 在给定 url 的情况下匹配某 URL:
-
将 recordToUse 设为 record。
-
-
-
若 recordToUse 非 null:
-
从列表中移除 recordToUse,从而将其从 predecessorDocument 的 prefetch records 中移除。
-
-
返回 recordToUse。
-
若下列任一条件成立:
-
navigable 不是一个 顶级可遍历对象;
-
navigable 是一个 预渲染可遍历对象;
-
navigable 是一个 fenced frame;
-
cspNavigationType 不是 "
other";或 -
documentResource 非 null
则返回 null。
-
-
令 predecessorDocument 为 navigable 的 active document。
-
令 cutoffTime 为 null。
-
当 true 时循环:
-
令 completeRecord 为调用 查找匹配的已预渲染预取记录,参数为 predecessorDocument 与 url 的结果。
-
若 completeRecord 非 null,则返回 completeRecord。
-
令 potentialRecords 为一个空的 列表。
-
遍历 predecessorDocument 的 prefetch records 中的每个 record:
-
若下列所有条件均为真,则将 record 追加 到 potentialRecords:
-
record 的 prerendering traversable 是一个 prerendering traversable;
-
record 的 prerendering traversable 的 active document 的 is initial about:blank 为 true;
-
record 在给定 url 的情况下 被期望匹配某 URL;且
-
cutoffTime 为 null 或者 record 的 start time 小于 cutoffTime。
-
-
-
若 potentialRecords 为空,则返回 null。
-
等待 predecessorDocument 的 prefetch records 中任一元素的 prerendering traversable 的 ongoing navigation 发生变化。
-
如果 cutoffTime 为 null,且 potentialRecords 中有任一元素的 prerendering traversable 的 active document 的 is initial about:blank 为 false,则将 cutoffTime 设为 predecessorDocument 的 当前高分辨率时间(相对于其 相关全局对象)。
-
对 navigate 算法进行补丁,使其允许将 预渲染可遍历对象的激活 用作正常导航的替代,具体如下:
-
令 record 为调用 等待匹配的已预渲染预取记录,参数为 navigable, url, cspNavigationType, 和 documentResource 的结果。
-
若 record 非 null,则:
-
令 matchingPrerenderedNavigable 为 record 的 prerendering traversable。
-
令 startingURL 为 record 的 URL。
-
激活 matchingPrerenderedNavigable 以替代 navigable,参数为 historyHandling, startingURL, url, 和 navigationId。
-
中止这些步骤。
-
3.2. 导航获取变更
-
令 initiatorOrigin 为 entry 的 document state 的 initiator origin。
在 "While true:" 的首个子步骤之后追加如下步骤:
-
若 navigable 为 预渲染可导航对象,且 currentURL 的 origin 与 initiatorOrigin 不为 同站,则:
-
若 navigable 为 顶级可遍历对象,则返回 null。
-
否则,若 navigable 的 top-level traversable 的 active document 的 allow cross origin iframes navigation while prerendering 为 false, 则用户代理必须等待,直到 navigable 的 loading mode 变为 "
normal" 才能继续本算法。在等待期间(包括立即)它也可选择 销毁 navigable 的 顶级可遍历对象 并从本算法返回 null。
-
在算法末尾、检查 locationURL 的步骤之后追加如下步骤:
-
若 navigable 为 预渲染可导航对象,且 responseOrigin 与 initiatorOrigin 并非 同源,则:
-
令 loadingModes 为对 response 调用 获取支持的加载模式 的结果。
-
若 loadingModes 未 包含 `
credentialed-prerender`, 则返回 null。将来我们也可能允许
uncredentialed-prerender在此处生效。但鉴于它主要针对跨站情形(目前尚未规范或实现),我们不希望鼓励在实际中使用,因此当前规定如果仅有uncredentialed-prerender标记则预渲染失败。
-
Refresh header, then:" 之后添加下列步骤:
-
若 navigationParams 的 navigable 是一个 预渲染可导航对象,则:
-
令 loadingModes 为对 response 调用 获取支持的加载模式 的结果。
-
若 loadingModes 包含 `
prerender-cross-origin-frames`, 则将 document 的 allow cross origin iframes navigation while prerendering 设为 true。
-
-
若 navigable 为 预渲染可导航对象,且下列任一成立:
-
failure 为 true;
-
navigationParams 的 request 为 null;
-
navigationParams 的 request 的 current URL 的 scheme 不是 HTTP(S) scheme;
-
navigationParams 的 request 的 current URL 不是 潜在可信 URL;
-
navigationParams 的 response 不 支持预取;
那些不符合预取资格的响应(例如状态为错误)同样不符合预渲染资格。将来版本可能允许响应更清晰地划分两者。 -
navigationParams 的 response 含有指定为
attachment的Content-Disposition头;或
则:
-
-
若 navigable 为 预渲染可遍历对象 且其 prerender 初始响应搜索变体 为 null:
-
将 navigable 的 prerender 初始响应搜索变体 设为对 navigationParams 的 response 调用 obtaining a URL search variance 的结果。
-
-
若 navigable 为 预渲染可导航对象,则不调用外部软件包并直接返回。
我们也可以允许在重定向时使用预渲染激活, 而不仅仅是在导航替代时使用。为简化且考虑到当前尚无实现,我们暂未包含该行为。
3.3. 维护简化会话历史
-
如果 navigable 是 预渲染可导航对象,则将 historyHandling 设为 "
replace"。
-
如果 navigable 是 预渲染可导航对象,则将 historyHandling 设为 "
replace"。
3.4. 与 worker 生命周期的交互
若一个 worker 的任一 owner 为 Document
且 完全活跃,且其 节点可导航对象 不是 预渲染可导航对象,或 active needed worker,则该 worker 被认为是 active needed worker。
这意味着 worker 脚本会加载,但其执行会被挂起,直到文档被激活。
3.5. 丢弃 Document
时的清理
-
清空 document 的 预渲染激活后步骤列表。
4. 与其他规范和概念的交互
4.1. 与页面可见性的交互
处于 预渲染可导航对象
内的文档始终具有 visibility state 为 "hidden"。
此处类似 document.prerendering,我们也许应该显式更新这一属性。
4.2. 与系统焦点的交互
4.3. 对 PerformanceNavigationTiming
接口的扩展
如下扩展 PerformanceNavigationTiming
接口:
partial interface PerformanceNavigationTiming {readonly attribute DOMHighResTimeStamp activationStart ; };
activationStart
的 getter 步骤为:
-
返回 this 的 相关全局对象 的 关联 Document 的 激活开始时间。
4.4. 与客户端提示缓存的交互
我们需要确保作为全局存储的 Accept-CH 缓存 在预渲染期间不被修改,但在激活后能正确更新。下面的修改保证这一点。
每个 预渲染可导航对象有一个 prerender 范围 Accept-CH 缓存,其为一个 有序映射,key 为 origin,value 为 client hints 集合。
这用于临时存储各 origin 期望接收的 client hints,直至在激活时复制到全局 Accept-CH 缓存。
-
令 originMatchingEntries 为 Accept-CH 缓存 中 origin 与 settingsObject 的 origin 为 同源 的条目集合。
-
如果 navigable 是 预渲染可导航对象,则:
-
令 prerenderAcceptClientHintsCache 为 navigable 的 prerender 范围 Accept-CH 缓存。
-
令 origin 为 settingsObject 的 origin。
-
如 prerenderAcceptClientHintsCache[origin] 存在,则令 originMatchingEntries 为 prerenderAcceptClientHintsCache 中所有与 origin 同源 的条目。
-
-
如果 navigable 是 预渲染可导航对象,则 设置 navigable 的 prerender 范围 Accept-CH 缓存[origin] = hintSet。
-
否则,设置 Accept-CH 缓存[origin] = hintSet。
4.5. 与 Clear Site Data 的交互
在 Clear Site Data § 3.1 The Clear-Site-Data HTTP Response Header Field 中增加一个额外的头部值说明:
- "
prerenderCache" -
"
prerenderCache" 类型指示服务器希望移除由该 origin 启动的所有预渲染,针对特定 响应的 URL。该类型是 "
cache" 类型的子集。实现细节见下。
对 parse response’s Clear-Site-Data header 的解析步骤进行修改:
-
遇到
`"cache"`或`"*"`时,将 "prerenderCache" 追加到 types(在这些分支已追加其它值的基础上); -
遇到
`"prerenderCache"`时,将 "prerenderCache" 追加到 types。
对 clear site data for response 的 switch
追加分支,处理
"prerenderCache"
并调用 clear
prerender cache,参数为 origin。
-
遍历 用户代理的 顶级可遍历对象集合中的每个 顶级可遍历对象 traversable:
-
令 navigables 为 traversable 的 活跃文档 的 包含自身的后代可导航对象。
-
遍历 navigables 中的每个 navigable:
-
令 activeDocument 为 navigable 的 活跃文档。
-
遍历 activeDocument 的 prefetch records 的每一项 prefetchRecord:
-
若 prefetchRecord 的 prerendering traversable 为 null,则 继续。
-
取消并丢弃 prefetchRecord,参数为activeDocument。
-
-
-
4.6. 与 Fetch 的交互
Sec-Purpose`
的现有步骤后添加如下步骤:
5. `Supports-Loading-Mode` HTTP
响应头
在某些情况下,跨域网页可能尚未做好在新型上下文下加载的准备。为了允许它们以这种方式加载,
`Supports-Loading-Mode`
HTTP 响应头可被使用。该头是结构化头;如存在,其值必须是下述列出的一个或多个token。
实际上解析时作为token列表解析,未知的 token 会被忽略。
`credentialed-prerender`
token 表示该响应可用于创建 预渲染可导航对象,即使预渲染是由同站但跨源的引用页发起。若未主动声明,则此类预渲染会失败,详见 § 3.2 导航获取变更。
`prerender-cross-origin-frames`
token 表示该响应可用于预渲染所有跨域 iframe。若未主动声明,则此类预渲染会被延后,详见 § 3.2 导航获取变更。
要获取某 响应 response 支持的加载模式:
-
如果 response 是 网络错误,则返回空列表。
-
令 slmHeader 为对 response 的 获取结构化字段值,参数为 `
Supports-Loading-Mode` 和 "list",从 response 的 header list。
6. 防止侵扰性行为
下述行为在 预渲染可导航对象 内被禁止,因为这些行为会侵扰用户,毕竟预渲染内容下用户不会主动互动。
6.1. 暂停脚本执行
如果 可导航对象的 脚本模式为"blocked-until-activation",
用户代理不得在该可导航对象的活跃文档中执行脚本。这包括但不限于执行 <script> 元素。
关于如何阻止脚本执行,以及与脚本相关的各类任务如何排队而非被执行,具体机制需要更详细规范。
这实际上会暂停所有 JavaScript 直到页面被激活。对于内联事件处理程序,预期行为仍在讨论中。
6.2. 下载资源
修改 下载超链接 算法,确保在 预渲染可导航对象 内的下载被延后到 激活 后进行,在进入 并行 前插入如下步骤:
-
如果 subject 的 节点可导航对象是 预渲染可导航对象, 则将如下步骤追加到 subject 的 预渲染激活后步骤列表,然后返回。
6.3. 用户提示
Window
window,在最前面插入如下步骤:
6.4. 延迟异步 API 结果
许多规范需要被补丁,以便当某个算法在 预渲染可导航对象 中调用时,其大部分工作被延后到该可导航对象的顶级可遍历对象被激活时再执行。 这在规范上较难统一,因为许多规范对事件循环使用规范并不严谨。不过,以下子节尽量给出我们的建议。
注意如果 脚本模式 为"blocked-until-activation",
那么会调用这些算法的脚本本身就不会在激活前被执行。
6.4.1.
[DelayWhilePrerendering]
扩展属性
为简化异步方法在 激活前延迟执行的模板,我们引入 [DelayWhilePrerendering] Web IDL
扩展属性。其表示:当在预渲染可导航对象中调用标注该属性的方法时,应立刻返回一个pending的promise,其他任何操作均等待激活后再执行,并用实际结果填充或拒绝该promise。
[DelayWhilePrerendering]
扩展属性不带参数,仅适用于返回类型为 Promise 类型
或 undefined
的 regular 或 static
operation,且其exposure set仅包含Window。
[DelayWhilePrerendering]注解的方法,其操作步骤被如下内容替代:
-
令 realm 为 当前 realm。
-
如果该操作为 regular operation,则将 realm 设为 相关 realm,其主体为 this。
-
如果 realm 的 全局对象的 navigable 是 预渲染可导航对象,则:
-
令 promise 为 新 Promise,在 realm 内创建。
-
将如下步骤追加到 this 的 预渲染激活后步骤列表:
-
令 result 为用相同 this 与实参运行该操作的原始指定步骤的结果。
-
以 result resolve promise。
-
-
若该操作的 返回类型为 Promise 类型,则返回 promise。
-
-
否则,返回用相同 this 和实参运行原始指定步骤的结果。
6.4.2. Service Workers
为 [DelayWhilePrerendering]
添加到 update()、
unregister()、
register(scriptURL, options)、
postMessage(message, transfer)、
postMessage(message, options)。
这样预渲染页面可以利用已有的 service worker,但不会影响 service worker 注册的状态。
6.4.3. BroadcastChannel
为 [DelayWhilePrerendering]
添加到 postMessage()。
6.4.4. 地理定位 API
getCurrentPosition()
方法步骤,在最前面插入如下步骤:
watchPosition()
方法步骤,在最前面插入如下步骤:
-
如果 this 的 相关全局对象 的 navigable 是 预渲染可导航对象,则:
-
令 watchId 是 实现自定义 的
unsigned long, 并记作 预渲染激活后地理定位观察进程ID。 -
将如下步骤追加到 this 的 预渲染激活后步骤列表,特殊在于为该生成的 watchId 使用同一ID,并返回 watchId。
-
clearWatch(watchId)
方法步骤,在最前面插入如下步骤:
-
如果 this 的 相关全局对象 的 navigable 是 预渲染可导航对象,则:
-
如果 watchId 是 预渲染激活后地理定位观察进程ID,则将其对应的步骤从 this 的 预渲染激活后步骤列表 移除。
-
返回。
-
6.4.5. Web 串口 API
为 [DelayWhilePrerendering]
添加到 requestPort()。
TODO:下述内容或许可以通过将 [DelayWhilePrerendering]
泛化到专用 worker 下的 owner document 来实现。
getPorts()
方法步骤,在初始创建 promise 后插入如下步骤:
-
令 document 为 this 的 相关全局对象 的 关联 Document,若 this 的 相关全局对象 为
Window, 或 this 的 相关全局对象 的 owner document,若 this 的 相关全局对象 为DedicatedWorkerGlobalScope。 -
如果 document 为 null,则返回 一个以
SecurityErrorDOMException拒绝的 promise。 -
如果 document 的 节点可导航对象 是 预渲染可导航对象,则将如下步骤追加到 document 的 预渲染激活后步骤列表 并返回 promise。
6.4.6. 通知 API
为 [DelayWhilePrerendering]
添加到 requestPermission()。
Notification()
构造函数步骤,将执行 并行 的那一步替换为如下内容:
permission
静态 getter 步骤,替换为如下内容:
6.4.7. Web MIDI API
为 [DelayWhilePrerendering]
添加到 requestMIDIAccess()。
6.4.8. 空闲检测 API
为 [DelayWhilePrerendering]
添加到 start()。
另一个相关方法 IdleDetector.requestPermission()
受 瞬时激活 限制。但即便相关 origin 已授权,预渲染期间仍会延后所有 idle detector 启动。
6.4.9. 通用传感器 API
start()
方法步骤,在状态设为 "activating" 后插入如下步骤:
6.4.10. Web NFC
为 [DelayWhilePrerendering]
添加到 write()
和 scan()。
6.4.11. 电池状态 API
getBattery()
方法步骤,在最前面插入如下步骤:
-
如果 this 的 相关全局对象 的 navigable 是 预渲染可导航对象,则将如下步骤追加到 this 的 预渲染激活后步骤列表,并返回 this.[[BatteryPromise]]。
6.4.12. 屏幕方向 API
-
令 promise 为新建的 Promise。
-
如果 this 的 相关全局对象 的 navigable 是 预渲染可导航对象,则将如下步骤追加到 this 的 预渲染激活后步骤列表 并返回 promise。
-
如果 用户代理 不支持锁定屏幕方向,则 拒绝 promise,原因为 "
NotSupportedError"DOMException并返回 promise。 -
如果 document 的 活动沙箱标志集合 包含 已沙箱化方向锁定浏览环境标志, 或者 用户代理 不满足 预锁定条件,则 拒绝 promise,原因为 "
SecurityError"DOMException并返回 promise。 -
将 document 的 [[orientationPendingPromise]] 设为 promise。
为 [DelayWhilePrerendering]
添加到 unlock()。
该补丁保证调用 screen.orientation.lock()
紧接着 screen.orientation.unlock()
能获得预期效果。
6.4.13. 游戏手柄
getGamepads()
方法步骤,在最前面插入如下步骤:
修改 gamepadconnected
和 gamepaddisconnected
事件的讨论,要求当 Window
对象的 navigable 是 预渲染可导航对象 时,用户代理不得派发这些事件。
gamepadconnected
章节,说明每个 Document
document 的 预渲染激活后步骤列表 应增加如下步骤:
-
如果 document 被 允许使用 "
gamepad" 功能,且 document 的 相关设置对象 是 安全上下文,并且有手柄已连接,则对每个已连接的手柄,派发一个名为gamepadconnected的事件到 document 的 相关全局对象,事件类型为GamepadEvent, 其gamepad属性初始化为一个代表此已连接手柄的新的Gamepad对象。
6.4.14. 加密媒体扩展
为[DelayWhilePrerendering]
添加到 requestMediaKeySystemAccess()。
6.4.15. 媒体自动播放
修改 播放媒体资源 的相关内容,要求 HTMLMediaElement
的 当前播放位置 仅当其 Document
非 prerendering
时单调递增。
6.4.16. 媒体捕获与流
为 [DelayWhilePrerendering]
添加到 getUserMedia()、
getUserMedia()
和 enumerateDevices()。
6.4.17. Web 音频 API
规范中使用了 允许启动 的概念,但细节留作 实现自定义。
为限制预渲染期间的自动播放,在 AudioContext
接口章节增加如下规则。
AudioContext
在预渲染期间永远 不允许启动。
还需修改 AudioContext()
构造函数步骤,在返回已构建对象前增加如下步骤。
-
否则,如 context 仅因预渲染未被允许启动,则将如下步骤追加到 context 的 预渲染激活后步骤列表。
-
排队发送控制消息以 resume context。
开发者可能会在预渲染期间调用 resume()。
此时 context 仍未允许启动,
但会将 Promise
追加到 [[pending resume promises]]。
上述激活步骤会 resolve 此 Promise。
AudioScheduledSourceNode
接口的 start(when)
方法及 AudioBufferSourceNode
接口的 start(when, offset, duration)
方法理论上可影响 [[control thread state]],
但预渲染期间 context 并未允许启动,算法也无法设置该状态。
实际上无影响,因为节点可在 context 启动时自动开始。
6.4.18. 音频输出设备 API
为 [DelayWhilePrerendering]
添加到 selectAudioOutput()。
6.4.19. 推送 API
为 [DelayWhilePrerendering]
添加到 subscribe()。
6.4.20. 后台fetch
为 [DelayWhilePrerendering]
添加到 fetch()。
6.4.21. 后台同步
为 [DelayWhilePrerendering]
添加到 register()。
6.4.22. 存储 API
为 [DelayWhilePrerendering]
添加到 persist()。
6.4.23. WebUSB API
为 [DelayWhilePrerendering]
添加到 getDevices()
和 requestDevice()。
6.4.24. Web蓝牙
为 [DelayWhilePrerendering]
添加到 getDevices()
和 requestDevice()。
6.4.25. WebHID API
为 [DelayWhilePrerendering]
添加到 getDevices()
和 requestDevice()。
6.4.26. WebXR设备API
为 [DelayWhilePrerendering]
添加到 requestSession()。
6.4.27. 凭据管理
为 [DelayWhilePrerendering]
添加到 get()、
store()、
和 create()。
6.4.28. Web语音API
为 [DelayWhilePrerendering]
添加到 speak(utterance)、
cancel()、
pause()、
以及 resume()。
为 [DelayWhilePrerendering]
添加到 start()、
stop()、
和 abort()。
6.4.29. Web锁API
为 [DelayWhilePrerendering]
添加到 request(name, callback)、
request(name, options, callback)、
以及 query()。
6.4.30. 自定义协议方案处理器
registerProtocolHandler(scheme, url)
方法步骤,重写其开始前的若干步骤,直到进入 并行,如下:
unregisterProtocolHandler(scheme, url)
方法步骤,重写其开始前的若干步骤,直到进入 并行,如下:
6.5. 隐式受限 API
有些 API 无需专门修改,因为只要 预渲染可导航对象 或其 活跃窗口 或 活跃文档 永远不会具备某些属性,它们就会自动失败或无操作。这些属性包括:
我们在此处列出已知 API,以示哪些接口曾被审计。
-
由
beforeunload事件生成的提示 [HTML] -
element.requestFullscreen()[FULLSCREEN]-
navigator.keyboard.lock()[KEYBOARD-LOCK],需全屏才能生效[KEYBOARD-LOCK] 允许浏览上下文方便启用键盘锁,但只有在全屏、且需用户手势时才有效。如有变化,需要重新考量。
-
-
showOpenFilePicker()、showSaveFilePicker()、showDirectoryPicker()[FILE-SYSTEM-ACCESS] -
触发剪贴板事件。[CLIPBOARD-APIS]
要求 系统焦点的 API 包括:
-
异步剪贴板 API:
navigator.clipboard.read()、navigator.clipboard.readText()、navigator.clipboard.write()、navigator.clipboard.writeText()[CLIPBOARD-APIS]
要求 "visible" 可见性状态的 API 包括:
更复杂的情况:
-
请求画中画算法(例如
video.requestPictureInPicture())要求需有瞬时激活,或 可见性状态曾为 "visible"。[PICTURE-IN-PICTURE]
7. 安全性考虑
本规范与 No-Vary-Search 的集成请参见 No-Vary-Search 安全性考虑。
需补充 prerendering 相关的安全性内容。参见 issue #319。
8. 隐私性考虑
本规范与 No-Vary-Search 的集成请参见 No-Vary-Search 隐私性考虑。
需补充 prerendering 相关的隐私性说明。参见 issue #319。尤其值得指出的是:跨分区 prerendering 因隐私复杂性被直接禁止。