1. 简介
本节为非规范性内容。
“使用 Web Bundle 的子资源加载”规范描述了一种利用支持多资源打包的格式 —— Web Bundles —— 高效加载大量资源的方法。该规范说明了 Web 浏览器如何加载这些资源。本规范作为对 [HTML]、[FETCH] 和 [CSP] 规范的补丁,调用这里定义的算法。
注意: 本规范正在建设中。参见 #708。
2. 结构
已获取的 web bundle 指的是在 [draft-ietf-wpack-bundled-responses-latest] 中定义的 web bundle 格式的表示。
web bundle 获取条目 是一个包含以下 条目的 结构体:
-
source:web bundle 的 URL。
-
credentials:凭据模式。
-
state:内部状态,可为 "fetching"、"fetched" 或 "failed"。初始为 "fetching"。
-
fetched bundle:已获取的 web bundle,或 null。
有更好的 web bundle 获取条目 名称吗?
web bundle 获取条目 entry 在 文档 document 中 被注册条目使用,如果 document 的 web bundle 注册列表 包含一个 web bundle 注册,其 fetch entry 为 entry。
web bundle 注册 是一个包含以下 条目的 结构体:
-
fetch entry:web bundle 获取条目。
-
rule: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 获取条目列表,但顺序其实不重要。
3. HTML 补丁
为使 web bundles 在 prepare the script element 算法中能与现有脚本类型(即 classic 或 module)一致处理, 我们作如下更改:
-
脚本类型应为 "classic"、"module" 或 "webbundle"
-
引入 web bundle 结果,它是有两个 条目的结构体:
-
一个 registration,即 web bundle 注册;
-
一个 等待抛出的错误,即非 null 时表示解析出错的 JS 值。
-
-
脚本结果 可为 "uninitiated"、null、script 或 web bundle 结果。
注意: 因为没有让 web bundle 结果 成为 script 的子类, 其它与脚本执行相关的规范不会受影响。
3.1. 准备 script 元素
在 prepare the script element 算法中作如下更改:
-
在 prepare the script element 步骤10插入:
-
如果 script 块的类型字符串与 "
webbundle" ASCII 不区分大小写匹配,则将 el 的类型设为 "webbundle"。
-
-
在 prepare the script element 步骤26.7插入:
-
"
webbundle":-
入队任务,向该元素 触发名为
error的事件, 并返回。注意:
<script type="webbundle" src=...>不支持。目前这里没有额外的出错处理要求,因此与src属性为空的情况一样触发error事件。
-
-
-
在 prepare the script element 步骤27.2插入:
-
"
webbundle":-
准备 web bundle,给定 element、source text 和 base URL。
-
-
-
在 prepare the script element 步骤28插入:
-
如果 脚本类型 为 "
webbundle":-
断言:脚本的 ready to be parser-executed 为 true。
-
并行地,处理 web bundle 事件,给定 element。
-
-
注意: CSP 会在 prepare the script element 第15步应用于内联 web bundle,正如在 classic/module 脚本上一样。
要准备 web bundle,给定
HTMLScriptElement
element、字符串 sourceText 和 URL baseURL 时:
-
令 parse result 为 解析 web bundle 字符串,给定 sourceText 和 baseURL 的结果。
-
如果此操作抛出异常:
-
将 脚本结果设为新的 web bundle 结果,其 registration 为 null,等待抛出的错误为该异常。
-
返回。
-
-
令 document 为 element 的 节点文档。
-
令 fetch entry 为 null。
-
对 document 的 web bundle 获取条目列表 中的每个 r 执行:
-
如果 r 的 source 等于 parse result 的 source 且 r 的 credentials 等于 parse result 的 credentials,那么:
-
如果 r 未被 注册条目使用于 document 中,令 fetch entry 为 r。
注意: 这意味着另一个 script 元素,其 脚本结果 的 registration 的 fetch entry 为 r 已被移除。 这样可确保在具有相同 web bundle source 和 credentials 的
HTMLScriptElement被删除和添加时,web bundle 获取条目不会被销毁和重新获取。
-
-
-
如果 fetch entry 为 null:
-
将 fetch entry 设为新建的 web bundle 获取条目,其 source 为 parse result 的 source,credentials 为 parse result 的 credentials,state 为 "fetching",fetched bundle 为 null。
-
将 fetch entry 添加到 document 的 web bundle 获取条目列表。
-
并行地,获取 web bundle fetch entry。
-
-
令 registration 为新建的 web bundle 注册,其 fetch entry 为 fetch entry,rule 为 parse result 的 rule。
-
将 registration 添加到 document 的 web bundle 注册列表。
-
将 脚本结果设为新的 web bundle 结果,其 registration 为 registration,等待抛出的错误为 null。
3.2. 触发事件
在执行 script 元素时,在步骤 6 添加如下分支:
-
"
webbundle":-
断言:永远不会到达。
注意: Web bundle 的处理由处理 web bundle 事件 负责,而不是执行 script 元素。
-
要处理 web
bundle 事件,给定 HTMLScriptElement
element:
-
令 result 为 element 的脚本结果。
-
断言:element 的脚本类型为 "
webbundle"。 -
断言:result 是web bundle 结果。
-
异步等待直到发生以下任一情况:
-
result 的等待抛出的错误非 null,或
-
result 的registration不为 null 且 result 的registration的fetch entry的state变为 "fetched" 或 "failed"。
注意: 不同于其他脚本类型,这里会异步等待获取 web bundle完成后再在
HTMLScriptElement上触发load事件。 这里不会延迟 load 事件,因为会在标记为就绪时同步进行。 这是有意为之,因为获取 web bundle更类似于预加载(preload)。 -
-
如果 element 的脚本结果为 null,则返回。
注意: 当 element 在上一步骤中被移出文档时,可能会发生这种情况。
注意: 该处理方式与 whatwg/html#2673 保持一致。 目前该情况下不会触发
error事件。如果后续对于 whatwg/html#2673 的决策变为需要触发error,此处也应相应更改。 -
如果 result 的等待抛出的错误非 null,则:
-
这里没有相关的脚本, 因为web bundle 结果不是脚本。 这一点需要等 whatwg/html#958,才能修正。
-
返回。
-
-
断言:result 的registration不为 null。
-
如果 result 的registration的fetch entry的state为 "failed":
-
断言:result 的registration的fetch entry的state为 "fetched"。
3.3. 移除
如果 script 元素被移出文档,用户代理必须执行以下算法:
-
如果脚本类型不是 "
webbundle",则返回。 -
如果脚本结果为 null,则返回。
-
断言:脚本结果为web bundle 结果。
-
令 registration 为脚本结果的registration。
-
将脚本结果设为 null。
-
如果 registration 为 null,则返回。
-
令 document 为节点文档。
-
断言:document 的web bundle 注册列表包含 registration。
-
从 document 的web bundle 注册列表中移除 registration。
-
入队微任务以执行以下步骤:
-
令 fetch entry 为 registration 的fetch entry。
-
如果 fetch entry被注册条目使用于 document,则返回。
-
从 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中,插入如下步骤到
令 taskDestination 为 null。
之前:
-
如果给定 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 分区。
之前:
-
将 response 设为给定 httpRequest,调用从 web bundle 获取子资源的结果。
5. CSP 补丁
5.1. 补丁“请求是否匹配源列表?”
重写 请求是否匹配源列表?,执行如下步骤:
-
令 url 为 request 的当前 url。
-
如果 url 的scheme 为 "
uuid-in-package",则:-
令 registration 为调用查找匹配的 web bundle 注册给定 request 的结果。
-
如果 registration 非 null,则将 url 设为 registration 的fetch entry的source。
-
-
返回调用url 是否与源列表和 origin、重定向次数匹配,参数为 url、source list、policy 的self-origin、request 的redirect count的结果。
注意: 这意味着 CSP 限制将作用于 bundle 的 URL,而非
uuid-in-package: URL。具体原因见 #651。
5.2. 补丁“响应请求是否匹配源列表?”
重写 响应请求是否匹配源列表?,执行如下步骤:
-
令 url 为 response 的url。
-
如果 url 的scheme 为 "
uuid-in-package",则:-
令 registration 为调用查找匹配的 web bundle 注册给定 request 的结果。
-
如果 registration 非 null,则将 url 设为 registration 的fetch entry的source。
-
-
返回调用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:
-
令 parsed 为 将 JSON 解析为 Infra 值,参数为 sourceText。
-
令 source 为 解析 parsed["
source"],以 baseURL 为基准。 -
如果 source 为 null,则抛出
TypeError。 -
令 credentials 为 "
same-origin"。 -
如果 parsed["
credentials"] 存在,则:-
如果 parsed["
credentials"] 是 "omit",那么将 credentials 设为 "omit"。 -
否则,如果 parsed["
credentials"] 是 "include",那么将 credentials 设为 "include"。
-
-
令 resources 为一个空的 列表。
-
如果 parsed["
resources"] 存在,则: -
令 scopes 为一个空的 列表。
-
如果 parsed["
scopes"] 存在,则: -
如果 parsed 的 keys 中包含除 "
source"、"credentials"、"resources" 或 "scopes" 以外的任何项,在控制台报告警告,说明 web bundle 字符串中有无效的顶级键。注意:这有助于检测拼写错误。这不是错误,否则将妨碍将来向后兼容地扩展。
-
返回web bundle 解析结果,其source 为 source,credentials 为 credentials,rule 为 rule 。
要解析 URL 列表,给定 列表 originalList 和 URL baseURL:
6.2. 获取 web bundle
要获取 web bundle,给定 web bundle 获取条目 fetch entry 和 fetch params fetch params:
-
断言:fetch entry 的 state 是 "fetching"。
-
令 request 为 fetch params 的 request。
-
将 request 的 url 设为 fetch entry 的 source。
注意: source URL 根据文档 base URL 解析。
-
将 request 的 destination 设为 "webbundle",
-
将 request 的 mode 设为 "cors",
-
将 request 的 credentials mode 设为 fetch entry 的 credentials。
-
将 request 的 service-workers mode 设为 "
none"。 -
向 request 的 header list 添加 header (“Accept”, “application/webbundle;v=b2”) 元组。
注意: 最终 [draft-ietf-wpack-bundled-responses-latest] 使用版本 1,本规范追踪浏览器实际实现,仍用 draft 版本。
-
fetch request,其 processResponse 算法设为 处理 web bundle 响应,该算法以 fetch entry 部分应用。
注意: Chromium 当前实现不允许嵌套 bundle,即不会从其他 bundle 再取 web bundle。
6.3. 处理 web bundle 响应
要处理 web bundle 响应,给定 web bundle 获取条目 fetch entry 和 response response:
-
如果 response 的 status 是 ok status,
-
将 response 的 body 作为 Web Bundle 解析 ([draft-ietf-wpack-bundled-responses-latest])。
注意: 此时 response 的 body 可能还未完全可用。UA 可以异步增量读取 body,以尽快提供子资源。
注意: 解析时,Chromium 实验性实现只接受 "b2" 作为 bundle 格式版本号。
-
当解析算法异步完成时,将 fetch entry 的 fetched bundle 设为解析结果,state 设为 "fetched"。若解析失败或不符合规范,则 fetched bundle 设为 null,state 设为 "failed"。
-
-
否则,将 fetch entry 的 state 设为 "failed"。
6.4. 从 web bundle 获取子资源
要从 web bundle 获取子资源,给定 request httpRequest:
-
令 registration 为执行 查找匹配的 web bundle 注册(参数为 httpRequest)的结果。
-
如果 registration 不为 null:
-
令 response 为 从 web bundle 获取条目获取响应 的结果,参数为 httpRequest 的 url 和 registration 的 fetch entry。
-
如果 response 为 null,则返回网络错误。
注意: 这意味着浏览器不会回退到从网络获取子资源。
-
否则,返回 response。
-
-
返回 null。
注意: 返回 null 时会回退到 HTTP 缓存和常规网络请求;而上面返回网络错误则不会。
要从 web bundle 获取条目获取响应,给定 url url 和 web bundle 获取条目 fetch entry:
-
如果 fetch entry 的 state 为 "fetching",则异步等待 直到 state 变为 "fetched" 或 "failed"。
-
如果 fetch entry 的 state 为 "failed",则返回 null。
-
断言:fetch entry 的 fetched bundle 非 null。
-
返回 fetch entry 的 fetched bundle 根据 url 返回的 response([draft-ietf-wpack-bundled-responses-latest])。如果 fetched bundle 未找到 url,则返回 null。
6.5. 查找匹配的注册
要查找匹配的 web bundle 注册,给定 request httpRequest: