fetchLater() 配额
延迟的 fetchLater() API 请求会被批量处理,并在标签页关闭时发送。此时,用户没有办法中止这些请求。为了避免文档滥用带宽发送无限量数据,该 API 对可延迟发送的数据量设置了配额。
这些配额可以通过 deferred-fetch 和 deferred-fetch-minimal 权限策略 指令来管理。
概述
fetchLater() 的总配额为每个文档 640KiB。默认情况下,这被分为 512KiB 的顶级配额和 128KiB 的共享配额。
- 默认情况下,512KiB 的顶级配额用于由顶级文档及其使用该源的直接子帧发起的任何
fetchLater()请求。 - 默认情况下,128KiB 的共享配额用于在跨源子帧(例如
<iframe>、<object>、<embed>和<frame>元素)中发起的任何fetchLater()请求。
fetchLater() 请求可以发送到任何 URL,不受限于文档或子帧的同源策略,因此区分在顶级文档内容中(无论是第一方还是第三方源)发起的请求和在子帧中发起的请求很重要。
例如,如果顶级文档 a.com 包含一个发出 fetchLater() 请求到 analytics.example.com 的 <script>,该请求将受制于 512KiB 的顶级配额。或者,如果顶级文档嵌入了一个源为 analytics.example.com 的 <iframe> 并发出 fetchLater() 请求,该请求将受制于 128KiB 的限制。
按报告源和子帧划分的配额限制
对于同一个报告源(请求 URL 的源),顶级 512KiB 配额中最多只能并发使用 64KiB。这可以防止第三方库在有数据要发送之前就机会性地占用配额。
默认情况下,每个跨源子帧从共享的 128KiB 配额中获得 8KiB 的配额,该配额在子帧添加到 DOM 时分配(无论该子帧是否会使用 fetchLater())。这意味着,总的来说,页面上只能使用前 16 个添加的跨源子帧,因为它们会用完 128KiB 的配额。
通过共享顶级配额来增加子帧配额
顶级源可以为选定的跨源子帧分配增加到 64KiB 的配额,这会从更大的顶级 512KiB 限制中扣除。通过在 deferred-fetch 权限策略指令中列出这些源来实现。这会在子帧添加到 DOM 时分配,从而为顶级文档和直接同源子帧留下更少的配额。多个同源子域可以各自获得 64KiB 的配额。
限制共享配额
顶级源还可以通过在 deferred-fetch-minimal 权限策略中列出特定跨源子帧来限制 128KiB 的共享配额。它还可以通过将 deferred-fetch-minimal 权限策略设置为 () 来撤销整个 128KiB 的默认子帧配额,转而将全部 640KiB 配额保留给自己以及任何命名的 deferred-fetch 跨源。
将配额委派给子帧的子帧
默认情况下,子帧的子帧没有被分配配额,因此无法使用 fetchLater()。被分配了增加到 64KiB 配额的子帧可以通过设置自己的 deferred-fetch 权限策略,将全部 64KiB 配额委派给更深层的子帧,并允许它们使用 fetchLater()。它们只能将全部配额委派给更深的子帧,而不能委派部分配额,也不能指定新的配额。使用最小 8KiB 配额的子帧无法将配额委派给子帧。要获得委派的配额,子子帧必须同时包含在顶级和子帧的 deferred-fetch Permissions-Policy 指令中。
超出配额时
当配额被超出时,调用 fetchLater() 方法来发起延迟请求会抛出 QuotaExceededError。
权限策略检查与配额检查无法区分。调用 fetchLater() 会抛出 QuotaExceededError,无论是实际超出配额,还是通过权限策略限制了该源的配额。
fetchLater() 的调用者应保持防御性,几乎在所有情况下都应捕获 QuotaExceededError 错误,尤其是在嵌入第三方 JavaScript 时。
示例
使用最小配额
Permissions-Policy: deferred-fetch=(self "https://b.com")
<iframe src="https://b.com/page">在添加到顶级文档时,从顶级文档的 512KiB 限制中获得 64KiB 配额。<iframe src="https://c.com/page">未被列出,因此从 128KiB 的共享限制中获得 8KiB 配额。- 另外 15 个跨源 iframe 在添加到顶级文档时,每个都会获得 8KiB 的配额(类似于
c.com)。 - 下一个跨源 iframe 将不会获得任何配额。
- 如果其中一个跨源 iframe 被移除,其延迟的获取请求将被发送。
- 下一个跨源 iframe 将 获得 8KiB 的配额,因为又有可用配额了。
撤销将最小配额限制为命名源
Permissions-Policy: deferred-fetch-minimal=("https://b.com")
<iframe src="https://b.com/page">在添加到顶级文档时获得 8KiB 配额。<iframe src="https://c.com/page">在添加到顶级文档时未获得任何配额。- 顶级文档及其同源后代最多可以使用 512KiB。
完全撤销最小配额,但保留顶级例外
Permissions-Policy: deferred-fetch=(self "https://b.com")
Permissions-Policy: deferred-fetch-minimal=()
<iframe src="https://b.com/page">在添加到顶级文档时获得 64KiB 配额。<iframe src="https://c.com/page">在添加到顶级文档时未获得任何配额。- 顶级文档及其同源后代最多可以使用全部 640KiB,但如果创建了一个
b.com子帧,则会减少到 574KiB(如果创建了多个b.com子帧,每个子帧都会分配 64KiB 配额,则会更少)。
完全撤销最小配额,且没有任何例外
Permissions-Policy: deferred-fetch-minimal=()
- 顶级文档及其同源后代可以使用全部 640KiB。
- 子帧未被分配任何配额,无法使用
fetchLater()。
同源子帧与顶级共享配额,并可以委派给子帧
假设顶级文档位于 a.com,嵌入了一个 a.com 的子帧,该子帧又嵌入了一个 b.com 的子帧,并且没有显式的权限策略。
a.com的顶级文档拥有默认的 512KiB 配额。<iframe src="https://a.com/embed">在添加到顶级文档时共享 512KiB 配额。<iframe src="https://b.com/embed">在添加到顶级文档时获得 8KiB 配额。
当被跨源子帧分隔时,同源子帧无法与顶级共享配额
假设顶级文档位于 a.com,嵌入了一个 <iframe src="https://b.com/">,该 iframe 又嵌入了一个 <iframe src="https://a.com/embed">,并且没有显式的权限策略。
a.com的顶级文档拥有默认的 512KiB 配额。<iframe src="https://b.com/">共享 8KiB 配额。<iframe src="https://a.com/embed">未获得任何配额;尽管它与顶级源同源,但它被一个跨源分隔开。
子帧的二级子帧默认情况下未获得配额
假设顶级文档位于 a.com,嵌入了一个 <iframe src="https://b.com/">,该 iframe 又嵌入了一个 <iframe src="https://c.com/">,并且没有显式的权限策略。
a.com的顶级框架拥有默认的 512KiB 配额。<iframe src="https://b.com/">从默认共享配额中获得 8KiB。<iframe src="https://c.com/">未获得任何配额。
授予更深层子帧全部配额
假设顶级文档位于 a.com,嵌入了一个 <iframe src="https://b.com/">,该 iframe 又嵌入了一个 <iframe src="https://c.com/">。
假设 a.com 具有以下权限策略
Permissions-Policy: deferred-fetch=("https://c.com" "https://c.com")
并且,假设 b.com 具有以下权限策略
Permissions-Policy: deferred-fetch=("https://c.com")
a.com的顶级框架拥有默认的 512KiB 配额。<iframe src="https://b.com/">从默认配额中获得 64KiB。<iframe src="https://b.com/">将其全部 8KiB 配额委派给c.com。b.com无法使用fetchLater()。<iframe src="https://c.com/">获得 8KiB 配额。
重定向不会转移配额
假设顶级文档位于 a.com,嵌入了一个 <iframe src="https://b.com/">,该 iframe 重定向到 c.com,并且没有显式的顶级权限策略。
a.com的顶级框架拥有默认的 512KiB 配额。<iframe src="https://b.com/">从默认共享配额中获得 8KiB。- 当
<iframe src="https://b.com/">重定向到c.com时,8KiB 不会转移到c.com,但 8KiB 也不会被释放。
沙盒化同源 iframe 有效地被视为不同的源
例如,如果在 https://www.example.com 上嵌入了以下 <iframe>
<iframe src="https://www.example.com/iframe" sandbox="allow-scripts"></iframe>
尽管此 iframe 与顶级文档托管在同一源上,但由于它位于沙盒环境中,因此不会被视为“同源”。因此,默认情况下,它应该从总共 128KiB 的共享配额中获得 8KiB 配额。
禁止 iframe 使用 fetchLater()
您可以使用 <iframe> 的 allow 属性来阻止为 <iframe> 分配 fetchLater() 配额。
<iframe
src="https://www.example.com/iframe"
allow="deferred-fetch;deferred-fetch-minimal;"></iframe>
需要 allow="deferred-fetch" 指令来防止同源 iframe 耗尽 512KiB 配额,需要 allow="deferred-fetch-minimal" 指令来防止跨源 iframe 耗尽 128KiB 配额。包含这两个指令将阻止两个配额的使用,无论 src 值如何。
导致 QuotaExceededError 的示例
// Maximum of 64KiB per origin
fetchLater(a_72_kb_url);
// Maximum of 64KiB per origin including headers
fetchLater("https://origin.example.com", { headers: headers_exceeding_64kb });
// Maximum of 64KiB per origin including body and headers
fetchLater(a_32_kib_url, { headers: headers_exceeding_32kib });
// Maximum of 64KiB per origin including body
fetchLater("https://origin.example.com", {
method: "POST",
body: body_exceeding_64_kib,
});
// Maximum of 64KiB per origin including body and automatically added headers
fetchLater(a_62_kib_url /* with a 3kb referrer */);
最终导致 QuotaExceededError 的示例
在以下位于顶级文档中的序列中,前两个请求将成功,但第三个请求会失败。这是因为,即使总共 640KiB 的配额未被超出,第三个请求也超出了 https://a.example.com 的报告源配额,并将导致错误。
fetchLater("https://a.example.com", { method: "POST", body: a_40kb_body });
fetchLater("https://b.example.com", { method: "POST", body: a_40kb_body });
fetchLater("https://a.example.com", { method: "POST", body: a_40kb_body });
将子帧重定向回顶级源允许使用顶级配额
假设顶级文档位于 a.com,嵌入了 <iframe src="https://b.com/">,该 iframe 重定向到 a.com,并且没有显式的顶级权限策略。
a.com的顶级框架拥有默认的 512KiB 配额。<iframe src="https://b.com/">从 128KiB 的默认共享配额中获得 8KiB。- 当
<iframe src="https://b.com/">重定向到a.com时,8KiB 不会转移到a.com,但它可以再次共享全部顶级配额,并且之前分配的 8KiB 配额将被释放。
规范
| 规范 |
|---|
| Fetch # available-deferred-fetch-quota |
浏览器兼容性
http.headers.Permissions-Policy.deferred-fetch
加载中…
http.headers.Permissions-Policy.deferred-fetch-minimal
加载中…