使用 Web Bundle 的子资源加载

社区组草案报告,

该版本:
https://wicg.github.io/webpackage/subresource-loading.html
问题追踪:
GitHub
规范内
编辑:
(Google公司)
(Google)

摘要

UA 如何从 Web Bundle 加载子资源。

本文档状态

本规范由 Web Platform Incubator Community Group 发布。 这不是 W3C 标准,也未被纳入 W3C 标准路线。 请注意,根据 W3C Community Contributor License Agreement (CLA),存在有限的选择退出权及其他适用条件。 了解更多 W3C Community and Business Groups 信息。

1. 简介

本节为非规范性内容。

“使用 Web Bundle 的子资源加载”规范描述了一种利用支持多资源打包的格式 —— Web Bundles —— 高效加载大量资源的方法。该规范说明了 Web 浏览器如何加载这些资源。本规范作为对 [HTML]、[FETCH] 和 [CSP] 规范的补丁,调用这里定义的算法。

注意: 本规范正在建设中。参见 #708。

2. 结构

已获取的 web bundle 指的是在 [draft-ietf-wpack-bundled-responses-latest] 中定义的 web bundle 格式的表示。

web bundle 获取条目 是一个包含以下 条目的 结构体:

有更好的 web bundle 获取条目 名称吗?

web bundle 获取条目 entry 在 文档 document 中 被注册条目使用,如果 document 的 web bundle 注册列表 包含一个 web bundle 注册,其 fetch entry 为 entry。

bundle 规则 是一个包含以下 条目的 结构体:

web bundle 注册 是一个包含以下 条目的 结构体:

web bundle 解析结果 是一个包含以下 条目的 结构体:

每个 环境设置对象 都会获得一个 web bundle 注册列表 算法,返回 web bundle 注册 的 列表。

Document 具有 web bundle 注册列表, 是 web bundle 注册 的 列表。初始为空。

在设置 window 环境设置对象时,settings object 的 web bundle 注册列表返回 window 所关联 web bundle 注册列表。

在设置 worker 环境设置对象时,settings object 的 web bundle 注册列表返回一个空 列表。

Document 具有 web bundle 获取条目列表, 是 web bundle 获取条目 的 列表。初始为空。

虽然 列表 用于web bundle 获取条目列表,但顺序其实不重要。

尚不支持 workers。

3. HTML 补丁

为使 web bundles 在 prepare the script element 算法中能与现有脚本类型(即 classic 或 module)一致处理, 我们作如下更改:

注意: 因为没有让 web bundle 结果 成为 script 的子类, 其它与脚本执行相关的规范不会受影响。

3.1. 准备 script 元素

在 prepare the script element 算法中作如下更改:

注意: CSP 会在 prepare the script element 第15步应用于内联 web bundle,正如在 classic/module 脚本上一样。

要准备 web bundle,给定 HTMLScriptElement element、字符串 sourceText 和 URL baseURL 时:

  1. 令 parse result 为 解析 web bundle 字符串,给定 sourceText 和 baseURL 的结果。

  2. 如果此操作抛出异常:

    1. 将 脚本结果设为新的 web bundle 结果,其 registration 为 null,等待抛出的错误为该异常。

    2. 标记为就绪。

    3. 返回。

  3. 令 document 为 element 的 节点文档。

  4. 令 fetch entry 为 null。

  5. 对 document 的 web bundle 获取条目列表 中的每个 r 执行:

    1. 如果 r 的 source 等于 parse result 的 source 且 r 的 credentials 等于 parse result 的 credentials,那么:

      1. 如果 r 未被 注册条目使用于 document 中,令 fetch entry 为 r。

        注意: 这意味着另一个 script 元素,其 脚本结果 的 registration 的 fetch entry 为 r 已被移除。 这样可确保在具有相同 web bundle source 和 credentials 的 HTMLScriptElement 被删除和添加时,web bundle 获取条目不会被销毁和重新获取。

  6. 如果 fetch entry 为 null:

    1. 将 fetch entry 设为新建的 web bundle 获取条目,其 source 为 parse result 的 source,credentials 为 parse result 的 credentials,state 为 "fetching",fetched bundle 为 null。

    2. 将 fetch entry 添加到 document 的 web bundle 获取条目列表。

    3. 并行地,获取 web bundle fetch entry。

  7. 令 registration 为新建的 web bundle 注册,其 fetch entry 为 fetch entry,rule 为 parse result 的 rule。

  8. 将 registration 添加到 document 的 web bundle 注册列表。

  9. 将 脚本结果设为新的 web bundle 结果,其 registration 为 registration,等待抛出的错误为 null。

  10. 标记为就绪。

3.2. 触发事件

在执行 script 元素时,在步骤 6 添加如下分支:

要处理 web bundle 事件,给定 HTMLScriptElement element:

  1. 令 result 为 element 的脚本结果。

  2. 断言:element 的脚本类型为 "webbundle"。

  3. 断言:result 是web bundle 结果。

  4. 异步等待直到发生以下任一情况:

    注意: 不同于其他脚本类型,这里会异步等待获取 web bundle完成后再在 HTMLScriptElement 上触发 load 事件。 这里不会延迟 load 事件,因为会在标记为就绪时同步进行。 这是有意为之,因为获取 web bundle更类似于预加载(preload)。

  5. 如果 element 的脚本结果为 null,则返回。

    注意: 当 element 在上一步骤中被移出文档时,可能会发生这种情况。

    注意: 该处理方式与 whatwg/html#2673 保持一致。 目前该情况下不会触发 error 事件。如果后续对于 whatwg/html#2673 的决策变为需要触发 error,此处也应相应更改。

  6. 断言:element 的节点文档等于 element 的准备时的文档。

  7. 如果 result 的等待抛出的错误非 null,则:

    1. 报告异常,参数为 result 的等待抛出的错误。

      这里没有相关的脚本, 因为web bundle 结果不是脚本。 这一点需要等 whatwg/html#958,才能修正。

    2. 返回。

  8. 断言:result 的registration不为 null。

  9. 如果 result 的registration的fetch entry的state为 "failed":

    1. 在 element 上触发 error 事件。

    2. 返回。

  10. 断言:result 的registration的fetch entry的state为 "fetched"。

  11. 在 element 上触发 load 事件。

3.3. 移除

如果 script 元素被移出文档,用户代理必须执行以下算法:

  1. 如果脚本类型不是 "webbundle",则返回。

  2. 如果脚本结果为 null,则返回。

  3. 断言:脚本结果为web bundle 结果。

  4. 令 registration 为脚本结果的registration。

  5. 将脚本结果设为 null。

  6. 如果 registration 为 null,则返回。

  7. 令 document 为节点文档。

  8. 断言:document 的web bundle 注册列表包含 registration。

  9. 从 document 的web bundle 注册列表中移除 registration。

  10. 入队微任务以执行以下步骤:

    1. 令 fetch entry 为 registration 的fetch entry。

    2. 如果 fetch entry被注册条目使用于 document,则返回。

    3. 从 document 的web bundle 获取条目列表中移除 fetch entry。

      注意: 如果 fetch entry 被多个 script 元素的web bundle 注册使用,并且这些 script 元素被依次移除,那么在这一步之前, document 的web bundle 获取条目列表可能已经不包含 fetch entry。

      注意: 此时,fetch entry 已不能再被后续的 子资源请求或准备 web bundle调用使用,但它的已获取的 web bundle仍可能被进行中的 fetch 使用。

4. Fetch 补丁

4.1. 补丁 fetch

在fetch中,插入如下步骤到

  1. 令 taskDestination 为 null。

之前:

  1. 如果给定 request,调用查找匹配的 web bundle 注册的结果为 null,则将 request 的service-workers mode 设置为 "none"。

注意: 这意味着对于从 webbundle 加载的子资源,不会有 service worker 获得事件。

4.2. 补丁 fetch scheme

将 "uuid-in-package" 添加到fetch scheme 的支持列表中。

注意: 这确保导航算法会对 uuid-in-package: URL 使用 处理导航 fetch 算法。

注意: origin 为 "uuid-in-package" scheme 的 URL 是不透明 origin。

4.3. 补丁 HTTP-network-or-cache fetch

在HTTP-network-or-cache fetch中,插入如下步骤到

8.22. 使用 httpRequest 计算 httpCache 分区。

之前:

  1. 将 response 设为给定 httpRequest,调用从 web bundle 获取子资源的结果。

    1. 如果 response 为网络错误,返回网络错误。

    2. 如果 response 非 null,则跳过 8.22-8.24 步骤,转到第 9 步。

      注意: 也就是说,从 webbundle 获取的子资源不会与 HttpCache 交互。未来计划作为特性增强支持 HttpCache。

5. CSP 补丁

5.1. 补丁“请求是否匹配源列表?”

重写 请求是否匹配源列表?,执行如下步骤:

  1. 令 url 为 request 的当前 url。

  2. 如果 url 的scheme 为 "uuid-in-package",则:

    1. 令 registration 为调用查找匹配的 web bundle 注册给定 request 的结果。

    2. 如果 registration 非 null,则将 url 设为 registration 的fetch entry的source。

  3. 返回调用url 是否与源列表和 origin、重定向次数匹配,参数为 url、source list、policy 的self-origin、request 的redirect count的结果。

注意: 这意味着 CSP 限制将作用于 bundle 的 URL,而非 uuid-in-package: URL。具体原因见 #651。

5.2. 补丁“响应请求是否匹配源列表?”

重写 响应请求是否匹配源列表?,执行如下步骤:

  1. 令 url 为 response 的url。

  2. 如果 url 的scheme 为 "uuid-in-package",则:

    1. 令 registration 为调用查找匹配的 web bundle 注册给定 request 的结果。

    2. 如果 registration 非 null,则将 url 设为 registration 的fetch entry的source。

  3. 返回调用url 是否与源列表和 origin、重定向次数匹配,参数为 url、source list、policy 的self-origin、request 的redirect count的结果。

注意: 这意味着 CSP 限制将应用于 bundle 的 URL,而非 uuid-in-package: URL。详细原因见 #651。

6. 算法

6.1. 解析

要解析 web bundle 字符串,给定 字符串 sourceText 和 URL baseURL:

  1. 令 parsed 为 将 JSON 解析为 Infra 值,参数为 sourceText。

  2. 如果 parsed 不是 map,那么抛出 TypeError ,表示顶层值需要是一个 JSON 对象。

  3. 如果 parsed["source"] 不存在,那么抛出 TypeError。

  4. 如果 parsed["source"] 不是 字符串,那么抛出 TypeError。

  5. 令 source 为 解析 parsed["source"],以 baseURL 为基准。

  6. 如果 source 为 null,则抛出 TypeError。

  7. 令 credentials 为 "same-origin"。

  8. 如果 parsed["credentials"] 存在,则:

    1. 如果 parsed["credentials"] 是 "omit",那么将 credentials 设为 "omit"。

    2. 否则,如果 parsed["credentials"] 是 "include",那么将 credentials 设为 "include"。

  9. 令 resources 为一个空的 列表。

  10. 如果 parsed["resources"] 存在,则:

    1. 如果 parsed["resources"] 不是 列表,那么抛出 TypeError。

    2. 将 resources 设为 解析 URL 列表 的结果,参数为 parsed["resources"] 和 source。

  11. 令 scopes 为一个空的 列表。

  12. 如果 parsed["scopes"] 存在,则:

    1. 如果 parsed["scopes"] 不是 列表,那么抛出 TypeError。

    2. 将 scopes 设为 解析 URL 列表 的结果,参数为 parsed["scopes"] 和 source。

  13. 如果 parsed 的 keys 中包含除 "source"、"credentials"、"resources" 或 "scopes" 以外的任何项,在控制台报告警告,说明 web bundle 字符串中有无效的顶级键。

    注意:这有助于检测拼写错误。这不是错误,否则将妨碍将来向后兼容地扩展。

  14. 令 rule 为bundle 规则,其 resources 为 resources,scopes 为 scopes。

  15. 返回web bundle 解析结果,其source 为 source,credentials 为 credentials,rule 为 rule 。

要解析 URL 列表,给定 列表 originalList 和 URL baseURL:

  1. 令 parsed URL list 为一个空的 列表。

  2. 遍历 originalList 的每个 item,

    1. 如果 item 是 字符串,则

      1. 令 URL 为 解析 item,以 baseURL 为基准得到的结果。

      2. 如果 URL 不为 null,将 URL 添加到 parsed URL list。

  3. 返回 parsed URL list。

6.2. 获取 web bundle

要获取 web bundle,给定 web bundle 获取条目 fetch entry 和 fetch params fetch params:

  1. 断言:fetch entry 的 state 是 "fetching"。

  2. 令 request 为 fetch params 的 request。

  3. 将 request 的 url 设为 fetch entry 的 source。

    注意: source URL 根据文档 base URL 解析。

  4. 将 request 的 destination 设为 "webbundle",

  5. 将 request 的 mode 设为 "cors",

  6. 将 request 的 credentials mode 设为 fetch entry 的 credentials。

  7. 将 request 的 service-workers mode 设为 "none"。

  8. 向 request 的 header list 添加 header (“Accept”, “application/webbundle;v=b2”) 元组。

    注意: 最终 [draft-ietf-wpack-bundled-responses-latest] 使用版本 1,本规范追踪浏览器实际实现,仍用 draft 版本。

  9. fetch request,其 processResponse 算法设为 处理 web bundle 响应,该算法以 fetch entry 部分应用。

    注意: Chromium 当前实现不允许嵌套 bundle,即不会从其他 bundle 再取 web bundle。

6.3. 处理 web bundle 响应

要处理 web bundle 响应,给定 web bundle 获取条目 fetch entry 和 response response:

  1. 如果 response 的 status 是 ok status,

    1. 将 response 的 body 作为 Web Bundle 解析 ([draft-ietf-wpack-bundled-responses-latest])。

      注意: 此时 response 的 body 可能还未完全可用。UA 可以异步增量读取 body,以尽快提供子资源。

      注意: 解析时,Chromium 实验性实现只接受 "b2" 作为 bundle 格式版本号。

    2. 当解析算法异步完成时,将 fetch entry 的 fetched bundle 设为解析结果,state 设为 "fetched"。若解析失败或不符合规范,则 fetched bundle 设为 null,state 设为 "failed"。

  2. 否则,将 fetch entry 的 state 设为 "failed"。

6.4. 从 web bundle 获取子资源

要从 web bundle 获取子资源,给定 request httpRequest:

  1. 令 registration 为执行 查找匹配的 web bundle 注册(参数为 httpRequest)的结果。

  2. 如果 registration 不为 null:

    1. 令 response 为 从 web bundle 获取条目获取响应 的结果,参数为 httpRequest 的 url 和 registration 的 fetch entry。

    2. 如果 response 为 null,则返回网络错误。

      注意: 这意味着浏览器不会回退到从网络获取子资源。

    3. 否则,返回 response。

  3. 返回 null。

注意: 返回 null 时会回退到 HTTP 缓存和常规网络请求;而上面返回网络错误则不会。

要从 web bundle 获取条目获取响应,给定 url url 和 web bundle 获取条目 fetch entry:

  1. 如果 fetch entry 的 state 为 "fetching",则异步等待 直到 state 变为 "fetched" 或 "failed"。

  2. 如果 fetch entry 的 state 为 "failed",则返回 null。

  3. 断言:fetch entry 的 fetched bundle 非 null。

  4. 返回 fetch entry 的 fetched bundle 根据 url 返回的 response([draft-ietf-wpack-bundled-responses-latest])。如果 fetched bundle 未找到 url,则返回 null。

6.5. 查找匹配的注册

要查找匹配的 web bundle 注册,给定 request httpRequest:

  1. 令 url 为 httpRequest 的 url。

  2. 对于 httpRequest 的 client 的 web bundle 注册列表 中的每个 registration:

    1. 令 rule 为 registration 的 rule。

    2. 如果 url 的 scheme 不是 "uuid-in-package",则

      1. 如果 url 的 origin 与 registration 的 fetch entry 的 source 的 origin 不是 同源,则 continue。

      2. 令 allowed path 为 缩短 registration 的 fetch entry 的 source 的 path 的结果。

      3. 如果 url 的 path 没有以 allowed path 开头,则 continue。

    3. 如果 rule 的 resources 包含 url,则返回 registration。

    4. 如果 url 以 rule 的任意 scopes 开头,则返回 registration。

  3. 返回 null。

一致性

文档惯例

一致性要求通过描述性断言与 RFC 2119 术语的组合来表达。 本文档规范性部分中的关键词 “MUST”、 “MUST NOT”、 “REQUIRED”、 “SHALL”、 “SHALL NOT”、 “SHOULD”、 “SHOULD NOT”、 “RECOMMENDED”、 “MAY” 和 “OPTIONAL” 应按 RFC 2119 中的描述来解释。 不过为便于阅读,本规范中这些词并未全部使用大写字母。

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

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

这是一个说明性示例。

说明性注释以 “Note”(注意)开头,并通过 class="note" 与规范性文本区分, 如下所示:

注意:这是一个说明性注释。

索引

本规范定义的术语

引用文档定义的术语

参考文献

规范性引用

[CONSOLE]
Dominic Farolino; Robert Kowalski; Terin Stock. Console 标准. Living Standard. URL: https://console.spec.whatwg.org/
[CSP]
Mike West; Antonio Sartori. 内容安全策略 第3级. URL: https://w3c.github.io/webappsec-csp/
[DOM]
Anne van Kesteren. DOM 标准. Living Standard. URL: https://dom.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch 标准. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 标准. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于 RFC 中表示需求级别的关键字. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[URL]
Anne van Kesteren. URL 标准. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. Living Standard. URL: https://webidl.spec.whatwg.org/

参考性引用

[DRAFT-IETF-WPACK-BUNDLED-RESPONSES-LATEST]
Web Bundles. URL: https://wpack-wg.github.io/bundled-responses/draft-ietf-wpack-bundled-responses.html

问题索引

是否有更好的 web bundle 获取条目 名称? ↵
虽然 列表 用于 web bundle 获取条目 列表,但顺序并不重要。 ↵
尚不支持 worker。 ↵
这里没有相关的 script, 因为 web bundle 结果 并不是 script。 这一点需要等待 whatwg/html#958 才能修复。 ↵