预取

社区组草案报告,

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

摘要

导航预取规范

本文档状态

本规范由Web平台孵化社区组发布。 这不是W3C标准,也不在W3C标准进程中。 请注意,依据 W3C社区贡献者许可协议(CLA) 有有限的选择退出权,并适用其他条件。 了解更多关于 W3C社区和业务组的信息。

1. 概念

鉴于存储分区,本规范定义了将在同一分区内发生导航的预取(例如,同一网站内的顶级导航)和将在不同分区内发生导航的预取(例如,前往不同网站的顶级导航)。

存在冲突的凭据,针对响应 response,给定可导航对象navigable网络分区键sourcePartitionKey,如以下步骤返回true:
  1. hypotheticalEnvironment创建保留客户端的结果,参数为navigableresponseURL和null。

  2. hypotheticalPartitionKey确定网络分区键的结果,参数为hypotheticalEnvironment

  3. 如果hypotheticalPartitionKey等于sourcePartitionKey,或没有与URLhypotheticalPartitionKey相关联的凭据,则返回false。

  4. 返回true。


交换记录是一个结构体,包含如下条目

这些记录可用于延后原本应在导航获取期间进行的检查,以及用于检查已修改的凭据

重定向链是一个列表,包含交换记录

更新重定向链redirectChain,给定一个请求 request响应response
  1. 断言redirectChain为空

  2. 断言redirectChain的最后一项请求request相同,其响应为null。

  3. redirectChain最后一项的请求设置为request克隆

    克隆可确保在后续重定向期间对request进行的任何修改不会影响存储的请求,后者在激活时(在根据预取记录创建导航参数)需要用于获取原始重定向链的全貌。目前已知唯一在请求字段发生变更且激活时也需要查询的情况是referrer字段,但为规范的健壮性,我们克隆整个请求。

    由于预取仅对`GET`请求进行,请求主体始终为null,因此克隆过程很简单。

  4. redirectChain最后一项的响应设置为response


每个Document都有预取记录,它是一个列表,包括预取记录

预取记录是一个结构体,包含如下条目

该结构体用于追踪预取从启动到最终被使用或丢弃的全过程。因此部分字段不可变,部分与进行中的操作有关(如获取控制器),部分(如过期时间)在预取完成时填充。

一个本应携带凭据但由于跨分区预取而无法发送凭据的请求将导致预取被放弃。

预取记录响应为其重定向链最后一个元素的响应,若该列表为空则为null。

即使没有过期,用户代理也可取消和丢弃预取记录,比如因资源受限。已完成记录且过期时间在过去的,永远不会成为匹配预取记录,可无影响地移除。

预取记录 prefetchRecord匹配URL,给定URLurl,以下算法返回true:
  1. 如果prefetchRecordURL等于url,返回true。

  2. 如果prefetchRecord响应不为null:

    1. searchVariance获取URL搜索变体的结果,参数为prefetchRecord重定向链[0]的响应

      需检查第0个响应而不是最后一个响应(即不是prefetchRecord响应),因为预取记录的URL属于第一个请求/响应对,因此只有第一个响应的No-Vary-Search头影响匹配。

    2. 如果prefetchRecordURLurl按照searchVariance进行搜索变体等价比较,返回true。

  3. 否则返回false。

预取记录 prefetchRecord预计匹配URL,给定URL url,以下算法返回true:
  1. 如果prefetchRecord匹配URL给定url,返回true。

  2. 如果prefetchRecord响应为null:

    1. searchVarianceprefetchRecordNo-Vary-Search提示

    2. 如果prefetchRecordURLurl按照searchVariance进行搜索变体等价比较,返回true。

  3. 否则返回false。

取消和丢弃预取记录prefetchRecord 给定Document document,执行以下步骤:
  1. 断言prefetchRecorddocument预取记录中。

  2. 断言prefetchRecord状态不为“已取消”。

  3. prefetchRecord状态设置为“已取消”。

  4. 中止prefetchRecord获取控制器这将导致任何正在进行的获取被取消并产生网络错误

  5. 如果prefetchRecord预渲染可遍历对象可遍历导航对象,则销毁它。

  6. 移除prefetchRecorddocument预取记录

  7. 触发预取状态更新事件,参数为document节点可导航对象prefetchRecord和“失败”状态。

即使是已完成的预取或预渲染也不会被激活。然而,预取或预渲染过程可能已修改HTTP缓存,从而加速后续导航。

完成预取记录prefetchRecord,给定Document document,执行以下步骤:
  1. 断言document完全活跃

  2. currentTime当前高分辨率时间,取自document相关全局对象

  3. expiryTime=currentTime+300000(即五分钟)。

  4. 移除所有document预取记录,其URLprefetchRecord相同且状态等于“已完成”。

  5. prefetchRecord状态设置为“已完成”,过期时间设置为expiryTime

  6. 触发预取状态更新事件,参数为document节点可导航对象prefetchRecord和“就绪”状态。

查找匹配的完成预取记录,给定顶级可遍历对象navigable源快照参数sourceSnapshotParamsURL url
  1. exactRecord为null。

  2. inexactRecord为null。

  3. 遍历sourceSnapshotParams预取记录

    1. 如果record状态不是“已完成”,则跳过

    2. 如果recordURL等于url

      1. 设置exactRecordrecord

      2. 跳出

    3. 如果inexactRecord为null,且record匹配URL给定url

      1. 设置inexactRecordrecord

  4. recordToUseexactRecord(若不为null),否则为inexactRecord

  5. 如果recordToUse不为null:

    1. currentTimenavigable活跃文档当前高分辨率时间

    2. 如果recordToUse过期时间小于currentTime

      1. 触发预取状态更新事件,参数为navigablerecordToUse和“失败”状态。

      2. 返回null。

    3. 遍历recordToUse重定向链的每个exchangeRecord

      1. 如果存在冲突的凭据针对exchangeRecord响应navigablerecordToUse来源分区键

        1. 触发预取状态更新事件,参数为navigablerecordToUse和“失败”状态。

        2. 返回null。

      这用于处理起初没有跨分区凭据,但后来出现这种情况。用户代理可以采用更粗略的算法如监控该URL的cookie是否变动,或存储哈希值、时间戳或版本号。
    4. 返回recordToUse

  6. 返回null。

虽然不明显,但该逻辑实际上不要求预取已接收完整主体,只需响应头即可。也就是说,导航到已预取的响应仍可能无法瞬间加载。

也许可用缓存响应头判断响应是否能多次使用,但由于预取缓冲区生存期极短,是否有必要尚不明确。

等待匹配的预取记录,给定顶级可遍历对象navigable源快照参数sourceSnapshotParamsURL url
  1. 断言:本操作在并行环境中运行。

  2. timeout为null。

  3. 可选地,将timeout设置为实现自定义的持续时间,代表实现愿意等待进行中预取响应的最长时间,超过即等待结束并重新发起导航。

    选择合适超时时间很难,且可能依赖具体导航或预取的发起者(如其推测规则积极性,或是否用于预渲染)。一般不建议用超时时间,因为很少重新导航比等预取更优。但部分实现发现某些情况下几秒的超时能取得更好体验。

  4. startTime不安全共享当前时间

  5. 执行如下步骤,timeout不为null且不安全共享当前时间startTime大于timeout时终止:

    1. 循环如下:

      1. completeRecord查找匹配的完成预取记录的结果,参数为navigablesourceSnapshotParamsurl

      2. 如果completeRecord不为null,则返回completeRecord

      3. potentialRecords为一个空列表

      4. 遍历sourceSnapshotParams预取记录

        1. 若如下全部成立:

          追加recordpotentialRecords

        循环每次都会重新计算potentialRecords,使得后续循环的potentialRecords是上次的子集。这因sourceSnapshotParams预取记录为快照,且上述条件不能由false变为true。

        等价做法是初次构建完整potentialRecords,随后逐步移除不再满足条件的项。

      5. 如果potentialRecords为空,返回null。

      6. 等待sourceSnapshotParams中任意预取记录状态发生变化。

  6. 返回null。

sourceSnapshotParams预取记录是快照,导航后启动的预取不会随该导航而被激活。

一个预取记录 prefetchRecord是否仍在推测中,给定一个推测加载候选集candidates,按如下步骤返回 true:
  1. 遍历candidates中的每个candidate

    1. 如果prefetchRecord未能匹配URL给定 candidateURL,则继续

    2. 如果candidate预取候选prefetchRecord匿名策略不等于candidate匿名策略,则继续

    3. 如果candidate预渲染候选prefetchRecord预渲染可遍历对象 为 null,则继续

    4. 返回 true。

  2. 返回 false。

一个Document document有匹配的预取记录,给定一个预取记录 recordUnderConsideration和一个可选布尔值checkPrerender(默认 false),如下算法返回 true:
  1. 遍历document预取记录中的每个record

    1. 如果record状态为"已取消",则继续

    2. 如果checkPrerender为 true:

      1. 如果record预渲染可遍历对象为 null,则继续

      2. 如果record预渲染目标可导航名称提示不等于 recordUnderConsideration预渲染目标可导航名称提示,则用户代理可继续

        对于依照预渲染可遍历对象预渲染目标可导航名称提示创建独立实例的用户代理,会继续该分支,认为此类记录是不同的。能激活已有预渲染可遍历对象且不区分预渲染目标可导航名称提示的用户代理则认为此类记录等价。

    3. recordNVS为 null。

    4. 如果record响应不为 null,则设置 recordNVS获取URL搜索变体的结果, 参数为record响应

    5. 否则,设置recordNVSrecordNo-Vary-Search提示

    6. 如果recordUnderConsiderationNo-Vary-Search提示不等于recordNVS,则继续

    7. 如果recordUnderConsiderationURLrecordURL根据recordUnderConsiderationNo-Vary-Search提示变体等价,返回true。

  2. 返回false。

参见 HTML 标准中No-Vary-Search比对相关讨论

根据预取记录创建导航参数,给定一个顶级可遍历对象navigable文档状态documentState导航idnavigationIdNavigationTimingType navTimingType请求request预取记录record目标快照参数targetSnapshotParams源快照参数sourceSnapshotParams,按如下步骤:
  1. responseOrigin为null。

  2. responseCOOP为null。

  3. coopEnforcementResult为导航创建跨域窗口打开策略执行结果,参数为navigable活跃文档documentState发起者源

  4. finalSandboxFlags为一个空沙箱标志集合

  5. responsePolicyContainer为null。

  6. urlList为一个空列表

  7. responserecord响应

  8. 遍历record重定向链中的每个exchangeRecord

    1. redirectChainRequestexchangeRecord请求

    2. redirectChainResponseexchangeRecord响应

    3. 追加redirectChainRequestURLurlList

    4. responsePolicyContainer根据获取响应创建策略容器的结果,参数为redirectChainResponseredirectChainRequest保留客户端

    5. finalSandboxFlagstargetSnapshotParams沙箱标志responsePolicyContainerCSP列表CSP派生沙箱标志的并集。

    6. responseOrigin确定源的结果,参数为redirectChainResponseURLfinalSandboxFlagsdocumentState发起者源与null。

    7. responseCOOP获取跨域窗口打开策略(参数为redirectChainResponseredirectChainRequest保留客户端)。

    8. coopEnforcementResult执行响应的跨域窗口打开策略的结果,参数为navigable活跃浏览环境redirectChainResponseURLresponseOriginresponseCOOPcoopEnforcementResult以及redirectChainRequestreferrer

    9. 如果finalSandboxFlags非空且responseCOOP为"unsafe-none",则将response设为适当的网络错误跳出

  9. 如果requestURL不等于urlList[0],则将requestURL插入urlList第0项之后。

    此时导航的是requestURL,但响应是来自urlList[0],因 No-Vary-Search。 视为第0个响应到URL的重定向。插入后若urlList长度为2,则生成的Document使用导航到的URL,否则长度更大时无效。

    假设我们预取了/page?param=1,服务器设置了No-Vary-Search: params=("param")且状态码为200。

    之后导航到/page?param=2即可复用预取。此步骤让urlList变为« /page?param=1, /page?param=2 »,于是生成的DocumentURL/page?param=2

    假设我们预取/page?param=1,服务器设置No-Vary-Search: params=("param"),并301跳转,Location: https://other.example/

    之后导航到/page?param=2即可复用预取。此步骤让urlList变为« /page?param=1, /page?param=2, https://other.example/ »,于是生成的DocumentURLhttps://other.example/

  10. requestURL列表设为urlList

  11. request保留客户端设为创建保留客户端的结果,参数为navigablerequestURL

    这将用作生成Document环境设置对象的环境。每次消耗一个预取记录时需独立环境,不能用record重定向链最后项的请求保留客户端。否则复用同一预取记录会导致创建的环境设置对象拥有相同id。会被如clients.get()API暴露,且极为混乱。

  12. request保留客户端活跃 Service Worker设为record重定向链最后项的请求保留客户端活跃 Service Worker

    此时理论上有两种 service worker 控制生成的Document:一种是在预取过程中拦截最终响应的,一种是与导航到的 URL(即requestURL)匹配的注册。这两者大部分情况相同,但涉及 No-Vary-Search 且开发者为/page?param=1/page?param=2分别建了不同 service worker 时可能不同。

    我们选择用预取阶段拦截最终响应的 service worker。这样实现更简单(无需额外查找),且与预渲染行为一致,“激活时 URL 更新”不会导致切换 service worker。

  13. resultPolicyContainer确定导航参数策略容器的结果,参数为record响应URLdocumentState历史策略容器sourceSnapshotParams源策略容器、null和responsePolicyContainer

  14. 如果response不是网络错误,则:

    1. 可选,设置response克隆版本。

      实现方可选此操作若预取响应可能重复消费。例如未来被预渲染消耗后,预渲染对象可因各种原因被销毁。正常应丢弃响应,但如进行本步,则预取响应仍可服务后续导航。参见 [PRERENDERING-REVAMPED]

    2. 如未执行上述可选步骤,则必须移除recordnavigable活跃文档预取记录中。

  15. 返回一个新的导航参数,内容如下:

    id

    navigationId

    请求

    request

    响应

    response

    responseOrigin

    策略容器

    resultPolicyContainer

    最终沙箱标志集

    finalSandboxFlags

    跨域窗口打开策略

    responseCOOP

    COOP执行结果

    coopEnforcementResult

    保留环境

    request保留客户端

    可导航对象

    navigable

    导航时序类型

    navTimingType

    获取控制器

    record获取控制器

    如上述可选步骤被采用,record可多次生成导航参数,均共享同一个获取控制器。因导航参数的唯一用途是统计导航时序,复用无影响。

    提前提示提交

    null

    意味着预取文档的提前提示不会被处理。若未来资源提示无法复用主响应头则可修订。因预取只在响应头到达时生效,提前提示不会早于主响应头。后续规范可考虑利用预取阶段获得的提前提示。

    交付类型

    "导航预取"

一个预取IP匿名策略policy 需要匿名,针对请求request,按如下步骤返回 true:
  1. 如果policy跨域预取IP匿名策略

    1. 如果requestURLpolicy相同,则返回 false。

    2. 返回 true。

  2. 断言policy为null。

  3. 返回 false。

2. HTML补丁

本节包含对[HTML]的补丁内容。

按如下方式为环境增加一项:

是否为导航预取客户端

一个布尔值(默认false)

按如下方式为导航参数增加一项:

交付类型

一个字符串(对应PerformanceResourceTiming delivery type

更新所有创建点设为""(空字符串),除非本规范文档内明示为其他值(见根据预取记录创建导航参数)。


源快照参数增加一项:

预取记录

一个列表, 包含预取记录

修改快照源快照参数算法,将返回值的预取记录设为sourceDocument预取记录克隆


本内容从根据获取创建导航参数提取。

创建保留客户端, 给定可导航对象navigable,一个URLurl与 一个不透明源或nullisolationOrigin

  1. topLevelCreationURLurl

  2. topLevelOrigin为null。

  3. 如果isolationOrigin非null,则:

    1. topLevelCreationURLabout:blank

    2. topLevelOriginisolationOrigin

  4. 否则,若navigable不是顶级可遍历对象,则:

    1. parentEnvironmentnavigable父对象活跃文档相关设置对象

    2. topLevelCreationURLparentEnvironment顶级创建URL

    3. topLevelOriginparentEnvironment顶级源

  5. 返回一个环境对象,其id为唯一不透明字符串,目标浏览环境navigable活跃浏览环境创建URLurl顶级创建URLtopLevelCreationURL顶级源topLevelOrigin


本内容从根据获取创建导航参数提取。

为导航创建跨域窗口打开策略执行结果 ,给定一个Document activeDocument与一个initiatorOrigin,返回一个新的跨域窗口打开策略执行结果对象,内容如下:

url

activeDocumentURL

origin

activeDocument

跨域窗口打开策略

activeDocument跨域窗口打开策略

当前环境为导航源

activeDocumentinitiatorOrigin同源则为true,否则为false


本内容从根据获取创建导航参数提取。

创建导航请求, 给定一个会话历史条目entry, 一个环境设置对象fetchClient, 一个可导航容器或nullcontainer, 以及一个布尔值hasTransientActivation,进行如下步骤。

  1. documentResourceentry文档状态资源

  2. 新建一个请求对象request,内容:

    url

    entryURL

    策略容器

    entry文档状态历史策略容器

    客户端

    fetchClient

    目标

    "document"

    凭据模式

    "include"

    使用URL凭据标志

    已设置

    重定向模式

    "manual"

    模式

    "navigate"

    referrer

    entry文档状态请求referrer

    referrer策略

    entry文档状态请求referrer策略

  3. 如果documentResourcePOST资源,则:

    1. request方法`POST`

    2. request主体documentResource请求主体

    3. 设置`Content-Type`documentResource请求内容类型,加入 request标头列表

  4. 如果entry文档状态待重载为true,则 设request重载导航标志

  5. 否则,若entry文档状态曾被加载为true,则 设request历史导航标志

  6. 如果hasTransientActivation为true,则设request用户激活为true。

  7. container非null:

    1. container浏览环境范围源,则 设request为该浏览环境范围源

    2. request目标发起者类型container本地名称

  8. 返回request


填充历史条目的文档算法中, 用如下步骤替换调用根据获取创建导航参数的那一步:
  1. 否则,若下列所有条件为真:

    则:

    1. request创建导航请求的结果,参数为 entrysourceSnapshotParams取用客户端navigable容器以及sourceSnapshotParams是否瞬时激活

    2. request替换客户端idnavigable活跃文档相关设置对象id

    3. prefetched为false。

    4. 如果documentResource为null且navigable顶级可遍历对象

      1. prefetchRecord等待匹配的预取记录, 参数为navigablesourceSnapshotParamsentryURL

      2. prefetchRecord非null:

        1. navigationParams根据预取记录创建导航参数, 参数为navigableentry文档状态navigationIdnavTimingTyperequestprefetchRecordtargetSnapshotParamssourceSnapshotParams

        2. 复制预取Cookie,参数为prefetchRecord隔离分区键navigationParams保留环境

          此复制在继续前完成, 即子资源获取、document.cookie 等能感知Cookie。若预取未访问跨站点URL,则无可复制的Cookie。
        3. prefetched为true。

        4. 触发预取状态更新事件, 参数为navigableprefetchRecord、"成功"状态。

        上述步骤保证预取仅用于满足`GET`请求, 且仅用于激活顶级可遍历对象

    5. prefetched为false,则设navigationParams根据获取创建导航参数结果, 参数为requestentrynavigablesourceSnapshotParamstargetSnapshotParamscspNavigationTypenavigationIdnavTimingType


这是对现有 通过 fetch 创建导航参数 算法的更新。

通过 fetch 创建导航参数给定一个 请求 request,一个 会话历史条目 entry,一个可导航对象 navigable,一个 源快照参数 sourceSnapshotParams,一个 目标快照参数 targetSnapshotParams,一个 字符串 cspNavigationType,一个 导航ID或null navigationId,一个 NavigationTimingType navTimingType,以及可选的 预取记录 prefetchRecord,执行以下步骤。

  1. 断言:此操作在并行环境中运行。

  2. 断言requestURL等于entryURL

  3. 断言requestmode为"navigate"。

  4. 断言requestredirect mode为"manual"。

  5. 断言request保留客户端为null。

  6. response为null。

  7. responseOrigin为null。

  8. fetchController为null。

  9. coopEnforcementResult为导航创建跨域窗口打开策略执行结果, 参数为navigable活跃文档entry文档状态发起者源

  10. finalSandboxFlags为空的沙箱标志集合

  11. responsePolicyContainer为null。

  12. responseCOOP为新的跨域窗口打开策略

  13. locationURL为null。

  14. currentURLrequestcurrent URL

  15. commitEarlyHints为null。

  16. isolationOrigin为null。

  17. 如给定prefetchRecord

    1. isolationSiteprefetchRecord隔离分区键[0]。

    2. 断言isolationSite不透明源

    3. isolationOriginisolationSite

  18. 循环如下:

    1. 如果request保留客户端非null且currentURLrequest保留客户端创建URL不同,则:

      1. request保留客户端 执行环境丢弃步骤

      2. request保留客户端为null。

      3. commitEarlyHints为null。

    2. 如果request保留客户端为null,则:

      1. request保留客户端创建保留客户端的结果,参数为 navigablecurrentURLisolationOrigin

      2. 如果prefetchRecord给定,则设request是否为导航预取客户端 为 true。

    3. 如果检查是否 CSP 应阻止此导航请求类型 的结果为"Blocked",则设response网络错误,并跳出

    4. 如给定prefetchRecord

      1. purpose为包含prefetch列表

      2. 如果prefetchRecord匿名策略 需要匿名 ,针对request,则:

        1. prefetch令牌中添加键为"anonymous-client-ip"值为true的参数。

        2. 用户代理必须在获取request时使用匿名化客户端IP连接(如代理),否则设response网络错误跳出

          目前IP匿名化尚无细致标准,实际会通过某些代理/中继实现。 理想情况应层层传递到获取连接处,未来或可进一步标准化。

      3. 如果prefetchRecord预渲染可遍历对象 非null,则向prefetch令牌中添加键为"prerender"值为true的参数。

      4. 写入结构化字段值, 参数为 (`Sec-Purpose`, purpose),至request标头列表

        实现可能兼容性发送厂商定制头,如 Chromium 的 `Purpose`/`prefetch`、 Mozilla 的 `X-moz`/`prefetch`、 WebKit 的 `X-Purpose`/`preview` 等。 期望业界最终采用统一头。
      5. 如果request当前URLprefetchRecordURL同站,则:

        1. tags为由prefetchRecord标签中的每一项映射得到的 列表:字符串转为 String,null转为 Token null

        2. 写入结构化字段值, 参数为 (`Sec-Speculation-Tags`, tags),至request标头列表。

      6. 如果request当前URL不是潜在可信URL,则设response网络错误跳出

        旨在降低预取流量被链路攻击者看到概率,并鼓励公网使用加密方案。
      7. proposedPartitionKey确定网络分区键, 参数为request保留客户端

      8. 如果proposedPartitionKey不等于prefetchRecord来源分区键requestreferrer策略 不在足够严格的推测导航referrer策略列表 中,则设response网络错误跳出

        实际上,跨站预取会放弃而不暴露比来源更多的referrer信息。
      9. 如因实现自定义原因无法按prefetchRecord匿名策略获取request,则设response网络错误跳出

        此处允许实现进一步限制,比如匿名流量无法访问部分主机、或主机拒绝私有预取流量。
      10. 追加交换记录, 其请求request响应为null, 加入prefetchRecord重定向链

    5. response为null。

    6. fetchController为null,则将fetchController设为request执行fetch的结果, processEarlyHintsResponse设为下述算法, processResponse设为下述算法, useParallelQueue设为true。

      processEarlyHintsResponse为如下算法,输入response earlyResponse

      1. 若给定prefetchRecordcommitEarlyHints为null, 设其为处理 early hint 头, 参数为earlyResponserequest保留客户端

      processResponse为如下算法,输入response fetchedResponse

      1. responsefetchedResponse

    7. 否则,对fetchController执行处理下一个手动重定向

    8. 等待直到response非null,或navigable进行中导航不等于navigationId

      如后者发生,则中止 fetchController,并返回。否则继续。 预取在导航开始时立刻中止,可能不是理想行为。

    9. request主体为null,设entry文档状态资源为null。

    10. responsePolicyContainer根据响应创建策略容器的结果,参数为responserequest保留客户端

    11. finalSandboxFlagstargetSnapshotParams沙箱标志responsePolicyContainerCSP列表CSP派生沙箱标志的并集。

    12. responseOrigin确定源的结果,参数为responseURLfinalSandboxFlagsentry文档状态发起者源、null。

    13. 如果navigable顶级可遍历对象,且prefetchRecord未给定:

      1. responseCOOP获取跨域窗口打开策略的结果,参数为responserequest保留客户端

      2. coopEnforcementResult执行响应的跨域窗口打开策略的结果,参数为navigable活跃浏览环境requestURLresponseOriginresponseCOOPcoopEnforcementResultrequestreferrer

      3. 如果finalSandboxFlags非空且responseCOOP不是"unsafe-none",则设response为相应网络错误跳出

      COOP检查在预取激活时进行,可知目标浏览环境时执行。

    14. 如果response不是网络错误navigable子可导航对象,且对 navigable容器文档相关设置对象request目标response、true 进行 跨域资源策略检查的结果是阻止,则设response网络错误跳出

    15. 如给定prefetchRecord

      1. 更新 prefetchRecord重定向链, 参数为requestresponse

      2. 存在冲突的凭据, 针对responsenavigableprefetchRecord来源分区键, 则设prefetchRecord曾有冲突凭据为true。

        不会立即中止预取或跳过后续重定向,因为这样会暴露用户是否有跨分区状态。预取继续,但不能被使用。 可建议用户代理向控制台等报告警告。

    16. locationURLresponselocation URL, 参数为currentURL片段

    17. locationURL为failure或null,跳出

    18. 断言locationURLURL

    19. entry序列化状态StructuredSerializeForStorage(null)。

    20. oldDocStateentry文档状态

    21. entry文档状态为新文档状态,内容如下:

      历史策略容器

      oldDocState历史策略容器克隆

      请求referrer

      oldDocState请求referrer

      请求referrer策略

      oldDocState请求referrer策略

      oldDocState

      资源

      oldDocState资源

      曾加载

      oldDocState曾加载

      导航目标名称

      oldDocState导航目标名称

    22. locationURL方案不是HTTP(S) 方案,则:

      1. entry文档状态资源为null。

      2. 跳出

    23. currentURLlocationURL

    24. entryURLcurrentURL

  19. 如果locationURLURL且其方案不是fetch 方案,则返回新的非fetch方案导航参数,内容:

    发起源

    request当前URL

  20. 如下任意为真:

    则返回null。
  21. resultPolicyContainer确定导航参数策略容器的结果, 参数为responseURLentry文档状态历史策略容器sourceSnapshotParams源策略容器、null、responsePolicyContainer

  22. 返回新的导航参数,内容如下:

    id

    navigationId

    请求

    request

    响应

    response

    responseOrigin

    策略容器

    resultPolicyContainer

    最终沙箱标志集合

    finalSandboxFlags

    跨域窗口打开策略

    responseCOOP

    COOP执行结果

    coopEnforcementResult

    保留环境

    request保留客户端

    可导航对象

    navigable

    导航时序类型

    navTimingType

    获取控制器

    fetchController

    提前提示提交

    commitEarlyHints


更新创建并初始化 Document 对象的步骤,给创建导航时序条目增加一个参数,具体为: navigationParams交付类型

修改为导航响应获取浏览环境的步骤,检查sourceOrigin是否与destinationOrigin同站,插入如下子步骤:
  1. 如果navigationParams交付类型为"prefetch",则可选设swapGroup为true。

    这样让引用站点更难通过加载时序判断预取是否被使用,有助于防止关于目标站点的信息泄露到引用站点。

本节包含对[NAVIGATION-TIMING]的补丁内容。

创建导航时序条目增加一个参数,为字符串 deliveryType,并作为额外参数传递给设置资源时序条目

4. Service Workers 补丁

本节包含对[SERVICE-WORKERS]的补丁内容。

修改创建 Fetch 事件并分发设置resultingClientId 的步骤如下:
如果request非子资源请求request目标不是"report" reservedClient是否为导航预取客户端为false ,则将eresultingClientId 属性初始化为reservedClientid,否则初始化为空字符串。

在预取场景下,虽然此时request保留客户端,仅为规范层帮助请求通过, 并不代表实际客户端,不会被如clients.matchAll() 等观察到。实际客户端创建将在激活时,即FetchEvent 触发后。

5. 清除站点数据补丁

清除站点数据 § 3.1 Clear-Site-Data HTTP 响应头字段中添加一条新的头部值说明:

"prefetchCache"

"prefetchCache" 类型表示服务器希望清除由特定发起的任意响应对应URL触发的预取内容。

该类型属于"cache"类型的子集。

具体实现细节如下。

修改解析响应的 Clear-Site-Data 头的解析步骤:

修改为响应清除站点数据的 switch 结构,新增处理 "prefetchCache" 的分支,调用清除预取缓存, 参数为origin

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

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

    2. 遍历navigables中的每个navigable

      1. activeDocumentnavigable活跃文档

      2. 如果activeDocument不是与origin同源,则跳过

      3. 遍历activeDocument预取记录中的每个prefetchRecord

        1. 如果prefetchRecord预渲染可遍历对象 非null,则跳过

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

6. 预取算法

推测导航足够严格的 referrer policy 列表包括如下内容:"", "strict-origin-when-cross-origin", "strict-origin", "same-origin", "no-referrer"。

启动 referrer 发起的导航预取,给定 Document document预取记录 prefetchRecord,按如下步骤:
  1. 断言document节点可导航对象顶级可遍历对象

    子可导航对象支持预取存在一些复杂性,当前尚未定义。将来有可能定义。

  2. 如果document已有匹配的预取记录给定 prefetchRecord,则返回。

  3. sourceSnapshotParams快照源快照参数,参数为document

  4. targetSnapshotParams快照目标快照参数,参数为document节点可导航对象

  5. prefetchRecord来源分区键确定网络分区键的结果,参数为document相关设置对象

  6. 断言prefetchRecordURL方案HTTP(S) 方案

  7. 追加prefetchRecorddocument预取记录

  8. prefetchRecord开始时间document当前高分辨率时间,对象为document相关全局对象

  9. 触发预取状态更新事件,参数为document节点可导航对象prefetchRecord和"待定" 状态。

  10. referrerPolicyprefetchRecordreferrer 策略 (若非空字符串),否则为document策略容器referrer 策略

  11. documentState为新文档状态,包括:

    请求referrer策略

    referrerPolicy

    发起者源

    document

  12. entry为新会话历史条目,包括:

    URL

    prefetchRecordURL

    文档状态

    documentState

  13. request创建导航请求的结果, 参数为entrydocument相关设置对象document节点可导航对象容器以及 false。

  14. globaldocument相关全局对象

  15. 并行执行:

    1. navigationParams通过 fetch 创建导航参数的结果, 参数为requestentrydocument节点可导航对象sourceSnapshotParamstargetSnapshotParams、"other"、null(navigationId)、 "navigate"及prefetchRecord prefetchRecord

      预取时用到的可导航对象,即document节点可导航,实际激活时可不同于后续传递给navigate算法的对象(最终会调 根据预取记录创建导航参数)。

      其实这没问题。最终激活导航时的目标 navigable 会参与大部分重要检查,而这些检查发生在填充历史条目的文档之前。 而且,查看 navigateParams 受 navigable 的影响方式:

      最后,需要注意本算法内 navigationParams 仅是预取响应的包裹,不会长期储存,参数中的 navigable 不会泄露到系统其他部分。激活时由根据预取记录创建导航参数重新生成,内含正确目标导航对象。

    2. 如果navigationParams响应支持预取,则设 navigationParams为null。

    3. 如果prefetchRecord曾有冲突凭据为 true,则设 navigationParams为 null。

      即重定向链中的任何跨分区 origin 存在凭据时,将丢弃预取。可减少用户看到登出页面的概率。
    4. 在全局任务队列中排队 网络任务源,参数为global,内容:

      1. navigationParams不是导航参数,则取消并丢弃prefetchRecord,参数为document,并中止步骤。

      2. 断言navigationParams响应prefetchRecord重定向链最后一个元素的响应

      3. 完成prefetchRecord给定document

响应response支持预取,如下步骤返回 true:
  1. statusresponse状态

  2. 断言status不是重定向状态

  3. 如果status不是ok状态,则返回 false。

    即错误响应不会缓存,发生导航时会重试。可提升导航成功的概率,如错误是临时或因预取导致,也便于服务器拒绝含Sec-Purpose头请求。
  4. 返回true。

虽然 103 不是ok状态,但 Early Hint 不影响预取。支持预取谓词仅用于实际响应,不会是 1xx。

其它注释,目前预取阶段会忽略早期提示。

7. Cookies

[COOKIES] 定义了“cookie”,可以使用 `Set-Cookie` 响应头字段进行设置。由于 "uncredentialed" 分区方案强制使用单独的网络分区键,因此需要将这些 cookie 复制到普通分区,就像它们是在导航时接收的一样。

缓存,虽然不是正确性所必需,也有用复制。
认证条目可在跨站点时获取,尽管这种情况似乎罕见。(例如,某网站可能会重定向到包含URL凭据的链接。)复制这些可能也是合理的,尽管目前尚不清楚是否真的希望鼓励这么做。
To 复制预取cookie,给定网络分区键 isolatedPartitionKey环境 environment,进行如下步骤。
标准上只有一个cookie存储,但有些浏览器会对cookie进行分区,例如同一域名在不同顶级站点下的cookie存储会分开。参见 客户端存储分区(Privacy CG)
  1. isolatedCookieStore为与isolatedPartitionKey关联的cookie存储。

  2. 遍历isolatedCookieStore中的每个cookie cookie

    1. isolatedCookieStore移除cookie

    2. 用户代理可以完全忽略某个cookie。如果是这种情况,则跳过。

      这与[COOKIES] 明确允许接收cookie时这种行为一致。
    3. topLevelSite为 null。

    4. 如果environment目标浏览环境顶级浏览环境

      1. topLevelSite获取站点的结果,参数为tuple origin ("https", cookie的domain, null, null)。

        这里使用"https"协议和null端口,是因为cookie可跨scheme和端口,不同于同源策略。用户代理分配cookie存储时因此不应区分。
      顶级浏览环境进行预取时,请求(包括重定向)是为顶级导航做准备。 environment的顶级站点会因重定向改变,且重定向可能跨站点,因此收到cookie时顶级站点可能已变。不过由于是顶级导航,下发cookie的origin即为当时的顶级站点。由于cookie的domain必须与下发origin同站,所以domain可用于判定顶级站点。
    5. 否则:

      1. topLevelOriginenvironment顶级源

      2. topLevelOrigin为null,则设为environment顶级创建URLorigin

      3. 断言topLevelOriginorigin

      4. topLevelSite获取站点的结果,参数为topLevelOrigin

      子导航对象预取时,顶级站点由包含它的顶级可遍历对象决定,且不会因重定向改变,因此可用environment判定。
    6. secondKey为null或实现自定义的值。

      secondKey应该与普通导航下该响应应有的值相同。
    7. destinationPartitionKey为(topLevelSite, secondKey)。

    8. cookieStore为与destinationPartitionKey关联的cookie存储。

    9. newCookiecookie的副本。

    10. 设置newCookie的creation-time和last-access-time为当前日期和时间。

    11. 如果cookieStore中已有与newCookie同名、同domain、同path的existingCookie

      1. newCookie的creation-time为existingCookie的creation-time。

      2. cookieStore移除existingCookie

    12. 插入newCookiecookieStore

      这种先移除后插入的处理和接收cookie时一致。

8. `Sec-Purpose` HTTP请求头

本节重定义了 `Sec-Purpose` HTTP请求头在[FETCH]中的定义。

`Sec-Purpose` HTTP请求头用于声明该请求目的不只是为了用户即时使用资源。

该头部字段为[RFC9651]结构化头,其值必须为List

其成员可能包含一个Item,即Token prefetch。若包含该项,即表示请求目的是预先下载即将被获取的资源。

下列参数用于prefetch令牌:

9. 自动化测试

为用户代理自动化和应用测试目的,本文档定义了对[WebDriver-BiDi] 规范的扩展。

CDDL代码片段使用"text"类型而不是"browsingContext.BrowsingContext",以便对CDDL片段进行独立编程处理。目前其它模块无法被引用。

9.1. speculation模块

speculation模块包含用于管理远端预取、预渲染和推测规则行为的命令。

9.1.1. 定义

本地端定义

speculation.PreloadingStatus = "pending" / "ready" / "success" / "failure"

speculation.PreloadingStatus类型表示不同预加载状态。此状态为预取和预渲染共用。

"pending"
预取记录 prefetchRecord开始启动referrer发起的导航预取
"ready"
预取记录 prefetchRecord完成
"success"
预取记录 prefetchRecord被激活(如用户代理导航时触发)。
"failure"
预取记录 prefetchRecord失败(如过期时)。

9.1.2. 事件

SpeculationEvent = (
  speculation.PrefetchStatusUpdated
)
9.1.2.1. speculation.prefetchStatusUpdated事件
speculation.PrefetchStatusUpdated = (
   method: "speculation.prefetchStatusUpdated",
   params: speculation.PrefetchStatusUpdatedParameters
)

speculation.PrefetchStatusUpdatedParameters = {
   context: text,
   url: text,
   status: speculation.PreloadingStatus
}
To 触发 prefetch 状态更新事件,给定可导航对象navigable预取记录prefetchRecord, 以及speculation.PreloadingStatus preloadingStatus
  1. params为一个有序映射,对应 speculation.PrefetchStatusUpdatedParameters定义,其 context字段为navigable顶级可遍历对象navigable idurl字段为prefetchRecordURLstatus字段为preloadingStatus

  2. body为一个有序映射,同 speculation.PrefetchStatusUpdatedParameters定义,其 params字段为params

  3. relatedNavigables为一个有序集合,包含navigable

  4. 对于"speculation.prefetchStatusUpdated"事件和relatedNavigables,对已启用事件的会话集合中的每个session

    1. 发送事件,参数为sessionbody

10. 安全注意事项

参见 HTML § 7.6.5 安全注意事项

11. 隐私注意事项

参见 HTML § 7.6.6 隐私注意事项

索引

本规范定义的术语

引用文献中定义的术语

参考文献

规范性引用

[CLEAR-SITE-DATA]
Mike West. 清除站点数据. URL: https://w3c.github.io/webappsec-clear-site-data/
[COOKIES]
A. Barth. HTTP状态管理机制. 2011年4月. 提案标准. URL: https://httpwg.org/specs/rfc6265.html
[CSP]
Mike West; Antonio Sartori. 内容安全策略 第3级. URL: https://w3c.github.io/webappsec-csp/
[DOM]
Anne van Kesteren. DOM 标准. 实时标准. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch 标准. 实时 标准. URL: https://fetch.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. 高分辨率时间. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML 标准. 实时标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 实时标准. URL: https://infra.spec.whatwg.org/
[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
[PRERENDERING-REVAMPED]
预渲染重构. 社区组报告草案. URL: https://wicg.github.io/nav-speculation/prerendering.html
[REFERRER-POLICY]
Jochen Eisinger; Emily Stark. 引用策略. URL: https://w3c.github.io/webappsec-referrer-policy/
[RESOURCE-TIMING]
Yoav Weiss; Noam Rosenthal. 资源时序. URL: https://w3c.github.io/resource-timing/
[RFC8610]
H. Birkholz; C. Vigano; C. Bormann. 简明数据定义语言(CDDL):用于表达 CBOR 与 JSON 数据结构的记号约定. 2019年6月. 提案标准. URL: https://www.rfc-editor.org/rfc/rfc8610
[RFC9651]
M. Nottingham; P-H. Kamp. HTTP结构化字段值. 2024年9月. 提案标准. URL: https://www.rfc-editor.org/rfc/rfc9651
[SECURE-CONTEXTS]
Mike West. 安全上下文. URL: https://w3c.github.io/webappsec-secure-contexts/
[SERVICE-WORKERS]
Monica CHINTALA; Yoshisato Yanagisawa. Service Workers Nightly. URL: https://w3c.github.io/ServiceWorker/
[URL]
Anne van Kesteren. URL 标准. 实时标准. URL: https://url.spec.whatwg.org/
[WebDriver-BiDi]
James Graham; Alex Rudenko; Maksim Sadym. WebDriver BiDi. URL: https://w3c.github.io/webdriver-bidi/

参考性引用

[CONSOLE]
Dominic Farolino; Robert Kowalski; Terin Stock. 控制台标准. 实时标准. URL: https://console.spec.whatwg.org/
[NAVIGATION-TIMING]
Zhiheng Wang. 导航时序. 2012年12月17日. REC. URL: https://www.w3.org/TR/navigation-timing/

CDDL 索引

问题索引

或许可以根据缓存响应头判断某个响应是否适合多次复用,但考虑到预取缓冲区的寿命很短,目前还不确定这样做是否有价值。
目前IP匿名化的实现方式尚不明确。可能会用某种代理或中继来完成(实现自定义)。理想情况下,该方案应深入到底层的获取连接,甚至可能进一步标准化。
预取会在导航开始的瞬间中止,这可能不是理想的行为。
缓存,虽然不是正确性所必需,也有用复制。
认证信息也可能在跨站点时获得,不过这似乎较少。(例如,某站点可能跳转到含有URL凭据的链接。)可能也应该复制这些内容,但是否真的鼓励尚不确定。
CDDL 片段使用 "text" 类型而不是 "browsingContext.BrowsingContext",以便对 CDDL 片段进行独立的程序化处理。目前不能引用其他模块。