预渲染重构

社区组草案报告

本版本:
https://wicg.github.io/nav-speculation/prerendering.html
问题追踪:
GitHub
规范内问题
编辑:
(谷歌)
(谷歌)

摘要

本文档包含面向明确定义预渲染的一组规范补丁。

本文档状态

本规范由Web Platform Incubator Community Group发布。 它不是W3C标准,也不属于W3C标准制定流程。 请注意,根据 W3C Community Contributor License Agreement (CLA) 规定,有有限的选择退出权,且适用其他条件。 详细了解 W3C社区和业务组

1. 推测规则

修改 HTML § 7.6.1 推测规则 以支持预渲染。

1.1. 解析

扩展 推测规则集 结构体,增加两个 成员

扩展 推测规则 结构体,增加一个 成员

修改 解析推测规则集字符串 如下:

实现仍允许将 prerender 候选视为 prefetch,详见 § 1.2 处理模型

修改 解析推测规则,增加如下步骤:
  1. targetHint 为 null。

  2. input["target_hint"] 存在

    1. input["target_hint"] 不是 有效可导航目标名或关键字

      1. 用户代理可以 报告警告到控制台,指示该 target hint 无效。

      2. 返回 null。

    2. targetHintinput["target_hint"]。

并将最终返回 推测规则的一步,更新为包含设置 target navigable name hinttargetHint

1.2. 处理模型

预渲染候选 是带有以下额外 成员推测加载候选

更新 推测加载处理算法,如果 document节点可导航对象预渲染 navigable,则提前中止,并给出说明,表明在推测加载页面内再进行推测加载是资源浪费。
更新 内部推测加载处理步骤 算法,在组装 prefetchCandidates 后加入如下步骤:
  1. prerenderCandidates 为一个空 列表

  2. 遍历 document推测规则集 的每一项 ruleSet

    1. 遍历 « (ruleSetprerender rules, false)、(ruleSetprerender_until_script rules, true) » 的每一对(rules, shouldBlockScripts):

      1. 遍历 rules 中的每个 rule

        1. 遍历 ruleURLs 的每个 url

          1. referrerPolicy计算推测加载 referrer policy 的结果,参数为 rule 和 null。

          2. 追加一个 预渲染候选,内容:

            URL

            url

            No-Vary-Search提示

            ruleNo-Vary-Search提示

            eagerness

            ruleeagerness

            referrer policy

            referrerPolicy

            标签

            rule标签

            target navigable name hint

            ruletarget navigable name hint

            should block scripts

            shouldBlockScripts

            prerenderCandidates
        2. rule谓词 非 null,则:

          1. links查找匹配链接 的结果,参数为 documentrule谓词

          2. 遍历 links 的每一项 link

            1. targetruletarget navigable name hint

            2. target 为 null,则设置为 获取元素 target 的结果,参数为 link

            3. referrerPolicy计算推测加载 referrer policy 的结果,参数为 rulelink

            4. 追加 一个 预渲染候选,内容:

              URL

              linkurl

              No-Vary-Search 提示

              ruleNo-Vary-Search 提示

              eagerness

              ruleeagerness

              referrer policy

              referrerPolicy

              标签

              rule标签

              target navigable name hint

              target

              should block scripts

              shouldBlockScripts

              prerenderCandidates
  3. speculativeLoadCandidatesprefetchCandidatesprerenderCandidates 的并集。

修改后续取消非 仍在推测中 的 prefetch record 的步骤,使其操作对象为 speculativeLoadCandidates,不再是 prefetchCandidates

像处理 prefetchCandidateGroups 那样,创建 prerenderCandidateGroups,但底层数据为 prerenderCandidates

用如下内容替换对 prefetchCandidateGroups 实际预取循环的步骤:

  1. speculativeLoadCandidateGroupsprefetchCandidateGroupsprerenderCandidateGroups 的并集。

  2. 遍历 speculativeLoadCandidateGroups 的每个 group

    1. 用户代理可运行如下步骤:

      1. candidategroup[0]。

      2. tagsToSend收集推测加载候选标签 的结果,参数为 group

      3. prefetchRecord 为新建的 预取记录,内容:

        source

        "speculation rules"

        URL

        candidateURL

        No-Vary-Search提示

        candidateNo-Vary-Search提示

        referrer policy

        candidatereferrer policy

        标签

        tagsToSend

      4. candidate预取候选,则设 prefetchRecord匿名策略candidate匿名策略

      5. candidate预渲染候选,用户代理可运行如下步骤:

        1. prefetchRecord预渲染可遍历对象 为 "to be created"。

        2. prefetchRecord预渲染目标可导航名称提示candidatetarget navigable name hint

        3. 启动referrer发起的导航预渲染,参数为 documentprefetchRecordcandidateshould block scripts

      6. 如未执行上一步,则 启动referrer发起的导航预取,参数为documentprefetchRecord

      (后续执行"may"步骤的时机讨论保留)

1.3. 触发

更新 属性变更步骤,使其在 aarea 元素上也监听 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"

在对象被激活前,脚本被阻止执行。

默认情况下,可导航对象脚本模式为 "enabled"。

脚本模式作为可导航对象属性(而非文档的),以便在同一 navigable 内导航时持久保持。例如处理首次 about:blank,或者在激活前发生的客户端重定向。

每个 预渲染可遍历对象有一个 预渲染初始响应搜索变体,类型为 URL搜索变体 或 null,默认 null。

document.prerendering

若页面正处于非交互式预渲染环境下,则返回 true。

其值可由 true 变为 false,并伴随 Document 触发 prerenderingchange 事件。(不会再从 false 变回 true。)

prerendering getter 步骤为:如果 this 有一个非 null 的 节点可导航对象且为 预渲染可导航对象,则返回 true,否则为 false。


每个 Document 有一个 预渲染激活后步骤列表,是一个 列表 每个 是一系列算法步骤。此处为方便,也定义对任意平台对象 platformObject预渲染激活后步骤列表

如果 platformObject节点
  1. 返回 platformObjectnode document预渲染激活后步骤列表

否则
  1. 断言:platformObject相关全局对象Window 对象。

  1. 返回 platformObject相关全局对象关联 Document预渲染激活后步骤列表

每个 Document 有一个 激活开始时间,初始为一个时间值为零的 DOMHighResTimeStamp

每个 Document 有一个 允许预渲染时跨域 iframe 导航,类型为 布尔值,初始值为 false。

2.3. 预渲染算法

用户代理可选择在没有引用文档的情况下启动预渲染,例如来源于地址栏或其它浏览器用户交互。

若要启动用户代理发起的预渲染,给定一个URL startingURL

  1. 断言startingURLschemeHTTP(S)方案

  2. prerenderingTraversable创建新的顶级可遍历对象的结果,参数为null与空字符串。

  3. prerenderingTraversable加载模式为"prerender"。

  4. prefetchRecord为新的预取记录,其中URLstartingURL匿名化策略为null,referrer policy为空字符串,No-Vary-Search提示默认URL搜索变体source为"browser UI",prerendering traversableprerenderingTraversable

  5. 启动referrer发起的导航预取,参数为prerenderingTraversable活跃文档prefetchRecord

  6. 导航 prerenderingTraversablestartingURL,使用prerenderingTraversable活跃文档

    我们将此初始导航视为prerenderingTraversable自身的导航,从而确保所有安全检查通过。

  7. 当用户表明希望提交到activationURL,其为URL,且prefetchRecord URL匹配activationURL时:

    1. 如用户表明希望创建新的用户可见顶级可遍历对象(如在地址栏输入完后按Shift+Enter):

      1. 更新激活用后继,参数为prerenderingTraversablestartingURLactivationURL

      2. 更新用户代理界面以展示prerenderingTraversable,例如创建新标签页或窗口。

      3. 完成激活,参数为prerenderingTraversablestartingURL

    2. 否则,若用户表明希望在已有顶级可遍历对象 predecessorTraversable中提交导航(如输入完后按Enter):

      1. 激活 prerenderingTraversable替换predecessorTraversable,参数为"push"、startingURLactivationURL

用户可能永远不发起此类提交,也可能等待太久导致用户代理需回收预渲染用资源用于更紧急任务。在这种情况下用户代理可以销毁 prerenderingTraversable

若要启动referrer发起的导航预渲染,参数为Document referrerDoc预取记录 prefetchRecord、布尔blockScripts
  1. 断言prefetchRecordURLschemeHTTP(S) 方案

  2. referrerDoc节点可导航对象不是顶级可遍历对象,则返回。

    当前不支持或实现从子可导航对象发起预渲染,否则会涉及预渲染可导航对象如何插入到树中的复杂性。

  3. referrerDoc浏览环境辅助浏览环境,则返回。

    可避免预渲染可遍历对象与referrerDoc浏览环境opener关系问题。

  4. referrerDocprefetchRecordURL不同站,则返回。

    当前未支持或实现跨站预渲染,相关设计想法见本库 explainer。

  5. referrerDoc 已有匹配的预取记录,参数为prefetchRecordcheckPrerender为true,则返回。

  6. 断言prefetchRecordprerendering traversable为"to be created"。

  7. prerenderingTraversable创建新的顶级可遍历对象的结果。

    用户代理可以用prefetchRecord预渲染目标可导航名称提示作为创建新顶级可遍历对象的实现提示,此提示表明开发者期望后续激活时用哪个前驱遍历对象替换。即用预渲染目标可导航名称提示referrerDoc节点可导航对象调用选择可导航对象规则

    这只是提示,没有规范行为要求,将来同样可以在未给予提示的前驱对象上激活

  8. prerenderingTraversable加载模式为"prerender"。

  9. blockScripts为true,则设prerenderingTraversable脚本模式为"blocked-until-activation"。

  10. prefetchRecordprerendering traversableprerenderingTraversable

  11. prerenderingTraversableremove from referrer为:将prefetchRecordprerendering traversable设为null的算法。

    与所有顶级可遍历对象一样,prerendering traversable可因各种原因销毁。比如无响应、执行受限操作、或用户代理判断资源占用过高。这种情况下,prefetch record 仍会保留,以便后续导航复用,即使prerendering traversable已销毁。

  12. 启动referrer发起的导航预取,参数为referrerDocprefetchRecord

  13. 导航 prerenderingTraversableprefetchRecordURL,使用referrerDocreferrerPolicyprefetchRecordreferrer policy

若要更新激活用后继,参数为prerendering traversable successorTraversableURL startingURL,以及URL activationURL

  1. successorDocumentsuccessorTraversable活跃文档

  2. startingURL等于successorDocumentURLstartingURL不等于activationURL

    1. 断言successorDocument 可重写URLactivationURL

    2. navigationsuccessorDocument相关全局对象navigation API

    3. continue触发 push/replace/reload navigate事件的结果,参数为navigation,"replace"、activationURL和true。

    4. continue为true,则用successorDocumentactivationURL运行URL及历史更新步骤

    这保证了预渲染的URL可与实际导航到的URL一致,适用于基于 `No-Vary-Search`的非精确匹配情形。

若要激活 prerendering traversable successorTraversable 替换顶级可遍历对象 predecessorTraversable,参数为历史处理行为 historyHandlingURL startingURLURL activationURL、可选navigation ID navigationId

  1. 断言successorTraversable活跃文档是否初始about:blank为false。

  2. 如未指定navigationId,则令其为生成的随机UUID

  3. referrerOriginpredecessorTraversable活跃文档

  4. predecessorTraversable正在进行的导航navigationId

    这样做会终止predecessorTraversable的其他并行导航。

  5. 并行执行:

    1. unloadPromptCanceled为对predecessorTraversable活跃文档包含自身的后代可导航对象执行检查卸载是否被用户取消的结果。

    2. unloadPromptCanceled为true,或predecessorTraversable进行中导航已不等于navigationId,则中止后续步骤。

    3. 全局任务排队predecessorTraversable活跃窗口上,执行中止predecessorTraversable活跃文档

    4. 更新激活用后继,参数为successorTraversablestartingURLactivationURL

    5. predecessorTraversable追加会话历史遍历步骤,执行如下操作:

      1. 断言successorTraversable当前会话历史步骤索引为0。

      2. 断言successorTraversable会话历史条目长度为1。

      3. successorEntrysuccessorTraversable会话历史条目[0]。

      4. 移除successorEntrysuccessorTraversable会话历史条目

        此时successorTraversable已为空,可无副作用地销毁。(只要实现确保successorEntry携带的文档及其Document浏览环境存活用于后续。)

      5. 完成跨文档导航,参数为predecessorTraversablehistoryHandlingsuccessorEntry

        由于移动了整个会话历史条目(包含文档及其 浏览环境),这意味着相关会话条目的遍历将导致浏览环境组切换,类似于COOP头行为。这会断开opener关系,无论是激活还是后撤操作(如用户点击后退或history.back())。

      6. 更新用户代理界面,以体现该变化,如更新标签/窗口内容与浏览器Chrome。

      7. 完成激活,参数为successorTraversablereferrerOrigin

      8. 此处或许应配合WebDriver BiDi?参见 w3c/webdriver-bidi#321。当前,在实际并行执行前,navigate算法会发起WebDriver BiDi navigation started,但导航完成不会有事件。

若要完成激活顶级可遍历对象traversable,参数为 origin
  1. 遍历traversable活跃文档包含自身的后代可导航对象,对每个navigable全局任务排队navigable活跃窗口上,依次执行以下步骤:

    1. 遍历navigable预渲染范围Accept-CH缓存originhintSet

      1. Accept-CH缓存[origin]为hintSet

    2. navigable脚本模式为"blocked-until-activation":

      1. navigable脚本模式为"enabled"。

      2. 解除navigable活跃文档脚本执行阻塞。

      脚本恢复后实际执行细节,特别是先前入队的任务如何处理,细节待补充。

    3. docnavigable活跃文档

    4. 这里应在投递任务内传播加载模式变更,实现会更新document.prerendering的返回值。不过实现上加载模式目前挂在traversable上,因此移动会话历史条目后会“自动”变。后续应迁移到Document对象。

    5. doc等于origin,则设doc激活开始时间doc当前高分辨率时间

    6. 触发事件,事件名为prerenderingchange,目标为doc

    7. 遍历 doc预渲染激活后步骤列表的每一项steps

      1. 执行steps

        这些步骤可有返回,如Promise,但按规范可忽略。

      2. 断言:执行steps未抛异常。

      对于同一事件循环内的Document执行顺序可感知,跨事件循环不可感知。

prerendering traversable 有一个关联的 remove from referrer,为null或无参数算法,默认设置为null。

为确保prerendering traversable销毁时能清除引用,修改销毁顶级可遍历对象,追加如下步骤:

  1. traversableprerendering traversable,且其remove from referrer非null,则调用之。

2.4. 创建可导航对象的修改

为确保任何 子可导航对象 继承其 父对象加载模式,通过追加下列步骤来修改 create a new child navigable
  1. navigableloading mode 设置为 elementnode navigableloading mode

若要在给定 Document predecessorDocumentURL url 的情况下 查找匹配的已预渲染预取记录
  1. recordToUse 为 null。

  2. 遍历 predecessorDocumentprefetch records 中的每个 record

    1. recordprerendering traversable 不是一个 prerendering traversable,则 继续(continue)。

    2. recordprerendering traversableactive documentis initial about:blank 为 true,则 继续

    3. recordURL 等于 url

      1. recordToUse 设为 record

      2. 跳出循环(Break)。

    4. recordToUse 为 null 且 record 在给定 url 的情况下匹配某 URL

      1. recordToUse 设为 record

  3. recordToUse 非 null:

    1. 从列表中移除 recordToUse,从而将其从 predecessorDocumentprefetch records 中移除。

  4. 返回 recordToUse

若要在给定 navigable navigableURL url、字符串 cspNavigationType 与可为 POST 资源、字符串或 null 的 documentResource 的情况下 等待匹配的已预渲染预取记录
  1. 断言:此处在并行运行。

  2. 若下列任一条件成立:

    • navigable 不是一个 顶级可遍历对象

    • navigable 是一个 预渲染可遍历对象

    • navigable 是一个 fenced frame

      目前尚未定义 fenced frame navigable 的概念,但一旦存在我们需要链接到它。

    • cspNavigationType 不是 "other";或

    • documentResource 非 null

    则返回 null。

  3. predecessorDocumentnavigableactive document

  4. cutoffTime 为 null。

  5. 当 true 时循环:

    1. completeRecord 为调用 查找匹配的已预渲染预取记录,参数为 predecessorDocumenturl 的结果。

    2. completeRecord 非 null,则返回 completeRecord

    3. potentialRecords 为一个空的 列表

    4. 遍历 predecessorDocumentprefetch records 中的每个 record

      1. 若下列所有条件均为真,则将 record 追加potentialRecords

    5. potentialRecords 为空,则返回 null。

    6. 等待 predecessorDocumentprefetch records 中任一元素的 prerendering traversableongoing navigation 发生变化。

    7. 如果 cutoffTime 为 null,且 potentialRecords 中有任一元素的 prerendering traversableactive documentis initial about:blank 为 false,则将 cutoffTime 设为 predecessorDocument当前高分辨率时间(相对于其 相关全局对象)。

navigate 算法进行补丁,使其允许将 预渲染可遍历对象的激活 用作正常导航的替代,具体如下:

navigate 中,在进入 并行 后的第一组步骤中插入如下步骤:
  1. record 为调用 等待匹配的已预渲染预取记录,参数为 navigable, url, cspNavigationType, 和 documentResource 的结果。

  2. record 非 null,则:

    1. matchingPrerenderedNavigablerecordprerendering traversable

    2. startingURLrecordURL

    3. 激活 matchingPrerenderedNavigable 以替代 navigable,参数为 historyHandling, startingURL, url, 和 navigationId

    4. 中止这些步骤。

create navigation params by fetching 中,在算法顶部附近添加下列步骤:
  1. initiatorOriginentrydocument stateinitiator origin

在 "While true:" 的首个子步骤之后追加如下步骤:

  1. navigable预渲染可导航对象,且 currentURLorigininitiatorOrigin 不为 同站,则:

    1. navigable顶级可遍历对象,则返回 null。

    2. 否则,若 navigabletop-level traversableactive documentallow cross origin iframes navigation while prerendering 为 false, 则用户代理必须等待,直到 navigableloading mode 变为 "normal" 才能继续本算法。在等待期间(包括立即)它也可选择 销毁 navigable顶级可遍历对象 并从本算法返回 null。

在算法末尾、检查 locationURL 的步骤之后追加如下步骤:

  1. navigable预渲染可导航对象,且 responseOrigininitiatorOrigin 并非 同源,则:

    1. loadingModes 为对 response 调用 获取支持的加载模式 的结果。

    2. loadingModes包含 `credentialed-prerender`, 则返回 null。

      将来我们也可能允许 uncredentialed-prerender 在此处生效。但鉴于它主要针对跨站情形(目前尚未规范或实现),我们不希望鼓励在实际中使用,因此当前规定如果仅有 uncredentialed-prerender 标记则预渲染失败。

create and initialize a Document object 中,在 "If navigationParams’s response has a Refresh header, then:" 之后添加下列步骤:
  1. navigationParamsnavigable 是一个 预渲染可导航对象,则:

    1. loadingModes 为对 response 调用 获取支持的加载模式 的结果。

    2. loadingModes 包含 `prerender-cross-origin-frames`, 则将 documentallow cross origin iframes navigation while prerendering 设为 true。

attempt to populate the history entry’s document 中,在确定 failure 值的步骤之后,但在根据该值执行其它动作之前追加下列内容:
  1. navigable预渲染可导航对象,且下列任一成立:

    则:

    1. 销毁 navigable顶级可遍历对象

    2. 返回。

  2. navigable预渲染可遍历对象 且其 prerender 初始响应搜索变体 为 null:

    1. navigableprerender 初始响应搜索变体 设为对 navigationParamsresponse 调用 obtaining a URL search variance 的结果。

hand-off to external software 中,前置加入如下步骤:
  1. navigable预渲染可导航对象,则不调用外部软件包并直接返回。

我们也可以允许在重定向时使用预渲染激活, 而不仅仅是在导航替代时使用。为简化且考虑到当前尚无实现,我们暂未包含该行为。

3.3. 维护简化会话历史

navigate 算法进行补丁,确保 预渲染可导航对象 的会话历史始终为简化型,在所有其他步骤之前预先添加以下步骤:
  1. 如果 navigable预渲染可导航对象,则将 historyHandling 设为 "replace"。

URL 和历史记录更新步骤 进行补丁,在第1步之后添加下列步骤:
  1. 如果 navigable预渲染可导航对象,则将 historyHandling 设为 "replace"。

3.4. 与 worker 生命周期的交互

worker 生命周期 的定义里,修改 active needed worker 的定义,使拥有 预渲染可导航对象 owner 的 worker 不被视为 active

若一个 worker 的任一 ownerDocument完全活跃,且其 节点可导航对象 不是 预渲染可导航对象,或 active needed worker,则该 worker 被认为是 active needed worker。

这意味着 worker 脚本会加载,但其执行会被挂起,直到文档被激活。

3.5. 丢弃 Document 时的清理

Document销毁 算法,在末尾添加以下步骤:

  1. 清空 document预渲染激活后步骤列表

4. 与其他规范和概念的交互

4.1. 与页面可见性的交互

处于 预渲染可导航对象 内的文档始终具有 visibility state 为 "hidden"。

此处类似 document.prerendering,我们也许应该显式更新这一属性。

4.2. 与系统焦点的交互

预渲染可遍历对象从不拥有系统焦点

4.3. PerformanceNavigationTiming 接口的扩展

如下扩展 PerformanceNavigationTiming 接口:

partial interface PerformanceNavigationTiming {
    readonly attribute DOMHighResTimeStamp activationStart;
};

activationStart 的 getter 步骤为:

  1. 返回 this相关全局对象关联 Document激活开始时间

4.4. 与客户端提示缓存的交互

我们需要确保作为全局存储的 Accept-CH 缓存 在预渲染期间不被修改,但在激活后能正确更新。下面的修改保证这一点。

每个 预渲染可导航对象有一个 prerender 范围 Accept-CH 缓存,其为一个 有序映射,key 为 origin,value 为 client hints 集合

这用于临时存储各 origin 期望接收的 client hints,直至在激活时复制到全局 Accept-CH 缓存

修改 update the client hints set from cache 算法,将第二步替换为如下步骤。
  1. navigablesettingsObject全局对象navigable

  2. originMatchingEntriesAccept-CH 缓存originsettingsObjectorigin同源 的条目集合。

  3. 如果 navigable预渲染可导航对象,则:

    1. prerenderAcceptClientHintsCachenavigableprerender 范围 Accept-CH 缓存

    2. originsettingsObjectorigin

    3. prerenderAcceptClientHintsCache[origin] 存在,则令 originMatchingEntriesprerenderAcceptClientHintsCache 中所有与 origin 同源 的条目。

修改 create or override the cached client hints set 算法,更新最后一步。
  1. navigablesettingsObject全局对象navigable

  2. 如果 navigable预渲染可导航对象,则 设置 navigableprerender 范围 Accept-CH 缓存[origin] = hintSet

  3. 否则,设置 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 的解析步骤进行修改:

clear site data for response 的 switch 追加分支,处理 "prerenderCache" 并调用 clear prerender cache,参数为 origin

若要 清除预渲染缓存,参数为 origin origin
  1. 遍历 用户代理的 顶级可遍历对象集合中的每个 顶级可遍历对象 traversable

    1. navigablestraversable活跃文档包含自身的后代可导航对象

    2. 遍历 navigables 中的每个 navigable

      1. activeDocumentnavigable活跃文档

      2. activeDocumentorigin同源origin,则 继续

      3. 遍历 activeDocumentprefetch records 的每一项 prefetchRecord

        1. prefetchRecordprerendering traversable 为 null,则 继续

        2. 取消并丢弃 prefetchRecord,参数为activeDocument

4.6. 与 Fetch 的交互

修改 HTTP-network-or-cache fetch,在设置 `Sec-Purpose` 的现有步骤后添加如下步骤:
  1. 否则,如果 httpRequestclient 是一个 环境设置对象,其 全局对象Window, 并且该 Windownavigable预渲染可导航对象

    1. purposeList,包含 Token prefetch

    2. purpose 中的 prefetch token 添加参数,参数名为 "prerender",值为 true。

    3. 设置结构化字段值 给定 (`Sec-Purpose`, purpose) 在 httpRequest’s header list.

    这包括预渲染可导航对象中的子资源,以及该对象自身导航的情况。

5. `Supports-Loading-Mode` HTTP 响应头

本节将作为 [HTML]加载网页 子节添加。

在某些情况下,跨域网页可能尚未做好在新型上下文下加载的准备。为了允许它们以这种方式加载, `Supports-Loading-Mode` HTTP 响应头可被使用。该头是结构化头;如存在,其值必须是下述列出的一个或多个token

实际上解析时作为token列表解析,未知的 token 会被忽略。

`credentialed-prerender` token 表示该响应可用于创建 预渲染可导航对象,即使预渲染是由同站但跨源的引用页发起。若未主动声明,则此类预渲染会失败,详见 § 3.2 导航获取变更

`prerender-cross-origin-frames` token 表示该响应可用于预渲染所有跨域 iframe。若未主动声明,则此类预渲染会被延后,详见 § 3.2 导航获取变更

获取某 响应 response 支持的加载模式

  1. 如果 response网络错误,则返回空列表。

  2. slmHeader 为对 response获取结构化字段值,参数为 `Supports-Loading-Mode` 和 "list",从 responseheader list

  3. 返回slmHeader中所有为token的元素组成的列表

6. 防止侵扰性行为

下述行为在 预渲染可导航对象 内被禁止,因为这些行为会侵扰用户,毕竟预渲染内容下用户不会主动互动。

6.1. 暂停脚本执行

如果 可导航对象脚本模式为"blocked-until-activation", 用户代理不得在该可导航对象的活跃文档中执行脚本。这包括但不限于执行 <script> 元素。

关于如何阻止脚本执行,以及与脚本相关的各类任务如何排队而非被执行,具体机制需要更详细规范。

这实际上会暂停所有 JavaScript 直到页面被激活。对于内联事件处理程序,预期行为仍在讨论中。

6.2. 下载资源

修改 下载超链接 算法,确保在 预渲染可导航对象 内的下载被延后到 激活 后进行,在进入 并行 前插入如下步骤:

  1. 如果 subject节点可导航对象预渲染可导航对象, 则将如下步骤追加到 subject预渲染激活后步骤列表,然后返回。

6.3. 用户提示

修改 cannot show simple dialogs 算法,参数为 Window window,在最前面插入如下步骤:
  1. 如果 windownavigable预渲染可导航对象,则返回 true。

修改 print() 方法步骤,在最前面插入如下步骤:
  1. 如果 thisnavigable预渲染可导航对象,则返回。

6.4. 延迟异步 API 结果

许多规范需要被补丁,以便当某个算法在 预渲染可导航对象 中调用时,其大部分工作被延后到该可导航对象的顶级可遍历对象激活时再执行。 这在规范上较难统一,因为许多规范对事件循环使用规范并不严谨。不过,以下子节尽量给出我们的建议。

注意如果 脚本模式 为"blocked-until-activation", 那么会调用这些算法的脚本本身就不会在激活前被执行。

6.4.1. [DelayWhilePrerendering] 扩展属性

为简化异步方法在 激活前延迟执行的模板,我们引入 [DelayWhilePrerendering] Web IDL 扩展属性。其表示:当在预渲染可导航对象中调用标注该属性的方法时,应立刻返回一个pending的promise,其他任何操作均等待激活后再执行,并用实际结果填充或拒绝该promise。

[DelayWhilePrerendering] 扩展属性不带参数,仅适用于返回类型为 Promise 类型 或 undefinedregularstatic operation,且其exposure set仅包含Window

任何被[DelayWhilePrerendering]注解的方法,其操作步骤被如下内容替代:
  1. realm当前 realm

  2. 如果该操作为 regular operation,则将 realm 设为 相关 realm,其主体为 this

  3. 如果 realm全局对象navigable预渲染可导航对象,则:

    1. promise新 Promise,在 realm 内创建。

    2. 将如下步骤追加到 this预渲染激活后步骤列表

      1. result 为用相同 this 与实参运行该操作的原始指定步骤的结果。

      2. result resolve promise

    3. 若该操作的 返回类型Promise 类型,则返回 promise

  4. 否则,返回用相同 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() 方法步骤,在最前面插入如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则将如下步骤追加到 this预渲染激活后步骤列表 并返回。

修改 watchPosition() 方法步骤,在最前面插入如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则:

    1. watchId实现自定义unsigned long, 并记作 预渲染激活后地理定位观察进程ID

    2. 将如下步骤追加到 this预渲染激活后步骤列表,特殊在于为该生成的 watchId 使用同一ID,并返回 watchId

修改 clearWatch(watchId) 方法步骤,在最前面插入如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则:

    1. 如果 watchId预渲染激活后地理定位观察进程ID,则将其对应的步骤从 this预渲染激活后步骤列表 移除。

    2. 返回。

6.4.5. Web 串口 API

[DelayWhilePrerendering] 添加到 requestPort()

TODO:下述内容或许可以通过将 [DelayWhilePrerendering] 泛化到专用 worker 下的 owner document 来实现。

修改 getPorts() 方法步骤,在初始创建 promise 后插入如下步骤:
  1. documentthis相关全局对象关联 Document,若 this相关全局对象Window, 或 this相关全局对象 的 owner document,若 this相关全局对象DedicatedWorkerGlobalScope

  2. 如果 document 为 null,则返回 一个以SecurityError DOMException 拒绝的 promise。

  3. 如果 document节点可导航对象预渲染可导航对象,则将如下步骤追加到 document预渲染激活后步骤列表 并返回 promise

6.4.6. 通知 API

[DelayWhilePrerendering] 添加到 requestPermission()

修改 Notification() 构造函数步骤,将执行 并行 的那一步替换为如下内容:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则将这些步骤追加到 this预渲染激活后步骤列表。否则在 并行 中执行这些步骤。

修改 permission 静态 getter 步骤,替换为如下内容:
  1. 如果 当前全局对象navigable预渲染可导航对象,则返回 "default"。

    这样做是为实现避免查询实际权限状态,特别是在 预渲染可导航对象 情况下,这也许无法同步获取。Web开发者可以调用 Notification.requestPermission(), 且上文修改已确保只有激活后才会实际执行。此时我们可能发现权限为 "granted" 或 "denied",浏览器无需再提示用户,这与通常"default"时行为不同。但这是允许的: 对 web 代码来说不可观察。

  2. 否则,获取通知权限状态并返回之。

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" 后插入如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则将如下步骤追加到 this预渲染激活后步骤列表 并返回。

  2. 如果 this.[[state]] 为 "idle",则返回。

    这样确保如本算法因预渲染而延后、期间又调用了 stop(),则激活预渲染后什么都不做。

  3. 断言:this.[[state]] 为 "activating"。

6.4.10. Web NFC

[DelayWhilePrerendering] 添加到 write()scan()

6.4.11. 电池状态 API

修改 getBattery() 方法步骤,在最前面插入如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则将如下步骤追加到 this预渲染激活后步骤列表,并返回 this.[[BatteryPromise]]

6.4.12. 屏幕方向 API

修改 应用方向锁算法步骤,重写其开始的若干步骤,在进入 并行 前如下:
  1. promise 为新建的 Promise。

  2. 如果 this相关全局对象navigable预渲染可导航对象,则将如下步骤追加到 this预渲染激活后步骤列表 并返回 promise

  3. 如果 用户代理 不支持锁定屏幕方向,则 拒绝 promise,原因为 "NotSupportedError" DOMException 并返回 promise

  4. 如果 document活动沙箱标志集合 包含 已沙箱化方向锁定浏览环境标志, 或者 用户代理 不满足 预锁定条件,则 拒绝 promise,原因为 "SecurityError" DOMException 并返回 promise

  5. document 的 [[orientationPendingPromise]] 设为 promise

[DelayWhilePrerendering] 添加到 unlock()

该补丁保证调用 screen.orientation.lock() 紧接着 screen.orientation.unlock() 能获得预期效果。

6.4.13. 游戏手柄

修改 getGamepads() 方法步骤,在最前面插入如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则返回空序列。

修改 gamepadconnectedgamepaddisconnected 事件的讨论,要求当 Window 对象的 navigable预渲染可导航对象 时,用户代理不得派发这些事件。

修改 gamepadconnected 章节,说明每个 Document document预渲染激活后步骤列表 应增加如下步骤:
  1. 如果 document允许使用 "gamepad" 功能,且 document相关设置对象安全上下文,并且有手柄已连接,则对每个已连接的手柄,派发一个名为 gamepadconnected 的事件到 document相关全局对象,事件类型为 GamepadEvent, 其 gamepad 属性初始化为一个代表此已连接手柄的新的 Gamepad 对象。

6.4.14. 加密媒体扩展

[DelayWhilePrerendering] 添加到 requestMediaKeySystemAccess()

6.4.15. 媒体自动播放

修改 播放媒体资源 的相关内容,要求 HTMLMediaElement当前播放位置 仅当其 Documentprerendering 时单调递增。

6.4.16. 媒体捕获与流

[DelayWhilePrerendering] 添加到 getUserMedia()getUserMedia()enumerateDevices()

修改 MediaDevices 章节,对 设备变更通知步骤 前添加如下步骤:
  1. 如果 this相关全局对象navigable预渲染可导航对象,则返回。

6.4.17. Web 音频 API

规范中使用了 允许启动 的概念,但细节留作 实现自定义。 为限制预渲染期间的自动播放,在 AudioContext 接口章节增加如下规则。

AudioContext 在预渲染期间永远 不允许启动

还需修改 AudioContext() 构造函数步骤,在返回已构建对象前增加如下步骤。

  1. 否则,如 context 仅因预渲染未被允许启动,则将如下步骤追加到 context预渲染激活后步骤列表

    1. [[control thread state]] 设为 running

    2. 排队发送控制消息以 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) 方法步骤,重写其开始前的若干步骤,直到进入 并行,如下:
  1. 令 (normalizedScheme, normalizedURLString) 为用 scheme, url 和 规范化协议处理参数处理,所用设置对象为 this相关设置对象

  2. 如果 this相关全局对象navigable预渲染可导航对象,则将如下步骤追加到 this预渲染激活后步骤列表 并返回。

修改 unregisterProtocolHandler(scheme, url) 方法步骤,重写其开始前的若干步骤,直到进入 并行,如下:
  1. 令 (normalizedScheme, normalizedURLString) 为用 scheme, url 和 规范化协议处理参数处理,所用设置对象为 this相关设置对象

  2. 如果 this相关全局对象navigable预渲染可导航对象,则将如下步骤追加到 this预渲染激活后步骤列表 并返回。

6.5. 隐式受限 API

有些 API 无需专门修改,因为只要 预渲染可导航对象 或其 活跃窗口活跃文档 永远不会具备某些属性,它们就会自动失败或无操作。这些属性包括:

我们在此处列出已知 API,以示哪些接口曾被审计。

要求 瞬时激活持久激活的 API 包括:

要求 系统焦点的 API 包括:

要求 "visible" 可见性状态的 API 包括:

更复杂的情况:

7. 安全性考虑

参见 HTML § 7.6.5 安全性考虑

本规范与 No-Vary-Search 的集成请参见 No-Vary-Search 安全性考虑

需补充 prerendering 相关的安全性内容。参见 issue #319

8. 隐私性考虑

参见 HTML § 7.6.6 隐私性考虑

本规范与 No-Vary-Search 的集成请参见 No-Vary-Search 隐私性考虑

需补充 prerendering 相关的隐私性说明。参见 issue #319。尤其值得指出的是:跨分区 prerendering 因隐私复杂性被直接禁止。

索引

本规范定义的术语

由引用定义的术语

参考文献

规范性引用

[AUDIO-OUTPUT]
Guido Urdaneta; youenn fablet. 音频输出设备 API。URL: https://w3c.github.io/mediacapture-output/
[BACKGROUND-FETCH]
后台获取。社区组草案报告。URL: https://wicg.github.io/background-fetch/
[BACKGROUND-SYNC]
Web 后台同步。社区组草案报告。URL: https://wicg.github.io/background-sync/spec/
[BATTERY-STATUS]
Anssi Kostiainen. 电池状态 API。URL: https://w3c.github.io/battery/
[CLEAR-SITE-DATA]
Mike West. 清除站点数据。URL: https://w3c.github.io/webappsec-clear-site-data/
[CLIENT-HINTS-INFRASTRUCTURE]
客户端提示基础设施。社区组草案报告。URL: https://wicg.github.io/client-hints-infrastructure/
[CLIPBOARD-APIS]
Rakesh Goulikar; Rohan Raja. 剪贴板 API 及事件。URL: https://w3c.github.io/clipboard-apis/
[CONSOLE]
Dominic Farolino; Robert Kowalski; Terin Stock. 控制台标准。Living Standard。URL: https://console.spec.whatwg.org/
[CREDENTIAL-MANAGEMENT-1]
Nina Satragno; Marcos Caceres. Credential Management Level 1。URL: https://w3c.github.io/webappsec-credential-management/
[DOM]
Anne van Kesteren. DOM 标准。Living Standard。URL: https://dom.spec.whatwg.org/
[ENCRYPTED-MEDIA-2]
Joey Parrish; Greg Freedman. 加密媒体扩展。URL: https://w3c.github.io/encrypted-media/
[FETCH]
Anne van Kesteren. Fetch 标准。Living Standard。URL: https://fetch.spec.whatwg.org/
[FILE-SYSTEM-ACCESS]
文件系统访问。社区组草案报告。URL: https://wicg.github.io/file-system-access/
[FULLSCREEN]
Philip Jägenstedt. 全屏 API 标准。Living Standard。URL: https://fullscreen.spec.whatwg.org/
[GAMEPAD]
Steve Agoston; Matthew Reynolds. 游戏手柄。URL: https://w3c.github.io/gamepad/
[GENERIC-SENSOR]
Rick Waldron. 通用传感器 API。URL: https://w3c.github.io/sensors/
[GEOLOCATION]
Marcos Caceres; Reilly Grant. 地理定位。URL: https://w3c.github.io/geolocation/
[HR-TIME-3]
Yoav Weiss. 高分辨率时间。URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML 标准。Living Standard。URL: https://html.spec.whatwg.org/multipage/
[IDLE-DETECTION]
Reilly Grant. 空闲检测 API。CG-DRAFT。URL: https://wicg.github.io/idle-detection/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准。Living Standard。URL: https://infra.spec.whatwg.org/
[KEYBOARD-LOCK]
键盘锁。社区组草案报告。URL: https://wicg.github.io/keyboard-lock/
[MEDIACAPTURE-STREAMS]
Cullen Jennings; et al. 媒体捕获与流。URL: https://w3c.github.io/mediacapture-main/
[NAVIGATION-TIMING-2]
Yoav Weiss; Noam Rosenthal. 导航时序 2 级。URL: https://w3c.github.io/navigation-timing/
No-Vary-Search HTTP 响应头字段。编辑者草案。URL: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-no-vary-search
[NOTIFICATIONS]
Anne van Kesteren. 通知 API 标准。Living Standard。URL: https://notifications.spec.whatwg.org/
[PAYMENT-REQUEST]
Marcos Caceres; Ian Jacobs; Stephen McGruer. 支付请求 API。URL: https://w3c.github.io/payment-request/
[PICTURE-IN-PICTURE]
Francois Beaufort. 画中画。URL: https://w3c.github.io/picture-in-picture/
[POINTERLOCK-2]
Mustaq Ahmed; Vincent Scheib. Pointer Lock 2.0。URL: https://w3c.github.io/pointerlock/
[PREFETCH]
预取。社区组草案报告。URL: https://wicg.github.io/nav-speculation/prefetch.html
[PRESENTATION-API]
Mark Foltz. 投屏 API。URL: https://w3c.github.io/presentation-api/
[PUSH-API]
Marcos Caceres; Kagami Rosylight. 推送 API。URL: https://w3c.github.io/push-api/
[RFC8941]
M. Nottingham; P-H. Kamp. HTTP 结构化字段值。2021年2月。提出为标准。URL: https://httpwg.org/specs/rfc8941.html
[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. 屏幕捕获。URL: https://w3c.github.io/mediacapture-screen-share/
[SCREEN-ORIENTATION]
Marcos Caceres. 屏幕方向。URL: https://w3c.github.io/screen-orientation/
[SCREEN-WAKE-LOCK]
Kenneth Christiansen; Marcos Caceres. 屏幕唤醒锁 API。URL: https://w3c.github.io/screen-wake-lock/
[SECURE-CONTEXTS]
Mike West. 安全上下文。URL: https://w3c.github.io/webappsec-secure-contexts/
[SERIAL]
Web 串口 API。编辑者草案。URL: https://wicg.github.io/serial/
[SERVICE-WORKERS]
Monica CHINTALA; Yoshisato Yanagisawa. Service Workers Nightly。URL: https://w3c.github.io/ServiceWorker/
[SPEECH-API]
Web 语音 API。社区组草案报告。URL: https://webaudio.github.io/web-speech-api/
[STORAGE]
Anne van Kesteren. 存储标准。Living Standard。URL: https://storage.spec.whatwg.org/
[URL]
Anne van Kesteren. URL 标准。Living Standard。 URL: https://url.spec.whatwg.org/
[WEB-BLUETOOTH]
Jeffrey Yasskin. Web 蓝牙。 URL: https://webbluetoothcg.github.io/web-bluetooth/
[WEB-LOCKS]
Kagami Rosylight. Web Locks API。URL: https://w3c.github.io/web-locks/
[WEB-NFC]
Web NFC API。社区组草案报告。URL: https://w3c.github.io/web-nfc/
[WEB-SHARE]
Marcos Caceres; Eric Willigers; Matt Giuca. Web 分享 API。URL: https://w3c.github.io/web-share/
[WEBAUDIO-1.0]
Paul Adenot; Hongchan Choi. Web 音频 API。URL: https://webaudio.github.io/web-audio-api/
[WEBCRYPTO-2]
Daniel Huigens. Web 加密 Level 2。URL: https://w3c.github.io/webcrypto/
[WEBDRIVER-BIDI]
James Graham; Alex Rudenko; Maksim Sadym. WebDriver BiDi。URL: https://w3c.github.io/webdriver-bidi/
[WEBHID]
WebHID API。社区组草案报告。URL: https://wicg.github.io/webhid/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准。Living Standard。URL: https://webidl.spec.whatwg.org/
[WEBMIDI]
Chris Wilson; Michael Wilson. Web MIDI API。URL: https://webaudio.github.io/web-midi-api/
[WEBUSB]
WebUSB API。社区组草案报告。URL: https://wicg.github.io/webusb/
[WEBXR]
Brandon Jones; Manish Goregaokar; Rik Cabanier. WebXR 设备 API。URL: https://immersive-web.github.io/webxr/

参考性引用

[POINTERLOCK]
Vincent Scheib. Pointer Lock。URL: https://w3c.github.io/pointerlock/

IDL 索引

partial interface Document {
    readonly attribute boolean prerendering;

    // Under "special event handler IDL attributes that only apply to Document objects"
    attribute EventHandler onprerenderingchange;
};

partial interface PerformanceNavigationTiming {
    readonly attribute DOMHighResTimeStamp activationStart;
};

问题索引

尚未定义 fenced frame navigable 的概念,需定义后补充链接。
需补充 prerendering 相关安全性考虑。见 issue #319
需补充 prerendering 相关隐私性考虑。见 issue #319。尤其要说明由于隐私复杂性,跨分区 prerendering 被直接禁止。