Skip to content

Commit ed638f2

Browse files
committed
Add examples
1 parent 043abc7 commit ed638f2

File tree

1 file changed

+140
-9
lines changed

1 file changed

+140
-9
lines changed

fetch.bs

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6846,11 +6846,73 @@ i.e., when a <a>fetch group</a> is <a for="fetch group">terminated</a>, or after
68466846

68476847
<h4 id=deferred-fetch-quota>Deferred fetching quota</h4>
68486848

6849-
<p class=note>The quota asserts that this deferred fetch doesn't exceed two quotas: one for the
6850-
document and it's same-origin same-tree relatives, and one for the reporting origin (64 kibibytes).
6851-
The larger quota ensures that the top-level {{Document}} and its subresources don't continue using
6852-
an unlimited amount of bandwidth after being destroyed. The per-origin quota ensures that a single
6853-
reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.
6849+
<!-- non-normative -->
6850+
<p>The deferred-fetch quota is allocated to a <a for=/>top-level traversable</a> (a "tab"),
6851+
amounting to 640 kibibytes. The top-level {{Document}} and its same-origin same-agent subframes can
6852+
use this quota to queue deferred fetches, or delegate some of it to cross-origin or cross-agent
6853+
subframes, using permissions policy.
6854+
6855+
<p>By default, 128 kibibytes out of these 640 kibibytes are allocated to delegating the quota to
6856+
cross-origin or cross-agent subframes, each reserving 8 kibibytes.
6857+
6858+
<p>The top-level {{Document}}, and subsequently its subframes, can control how much of their quota
6859+
is delegates to cross-origin/cross-agent subframes, by using {{PermissionsPolicy}}.
6860+
By default, {{PermissionPolicy/"deferred-fetch-minimal"}} is enabled for any origin, while
6861+
{{PermissionPolicy/"deferred-fetch"}} is enabled for the top-level document's origin only.
6862+
By relaxing the {{PermissionPolicy/"deferred-fetch"}} policy for particular origins and subframes,
6863+
the top-level document can allocate 64 kibibytes to those subframes. Similarly, by restricting the
6864+
{{PermissionPolicy/"deferred-fetch-minimal"}} policy for a particular origin or subframe, the
6865+
document can prevent the iframe from reserving the 8 kibibytes it would receive by default. By
6866+
disabling {{PermissionPolicy/"deferred-fetch-minimal"}} for the top-level document itself, the
6867+
entire 128 kibibytes delegated quota is collected back into the main pool of 640 kibibytes.
6868+
6869+
<p>Out of the allocated quota for a {{Document}}, only 64 kibibytes can be used concurrently for the
6870+
same reporting origin (the <a for=/>request</a>'s <a for=request>URL</a>'s <a for=url>origin</a>).
6871+
This prevents a situation where particular 3rd party libraries would reserve quota
6872+
opportunistically, before they have data to send.
6873+
6874+
<div class=example id=deferred-fetch-quota-examples>
6875+
<p>Any of the following calls to <a method><code>fetchLater()</code></a> would throw due to
6876+
the request itself exceeding the 64 kibibytes quota allocated to a reporting origin. Note that the
6877+
size of the request includes the <a for=request>URL</a> itself, the <a for=request>body</a>, and the
6878+
<a for=request>header list</a>.
6879+
<pre><code class=lang-javascript>
6880+
fetchLater(a_64_kb_url);
6881+
fetchLater("https://origin.example.com", {headers: headers_exceeding_64kb});
6882+
fetchLater(a_32_kb_url, {headers: headers_exceeding_32kb});
6883+
fetchLater("https://origin.example.com", {method: "POST", body: body_exceeding_64_kb});
6884+
</code></pre>
6885+
6886+
<p>In the following sequence, the first two requests would succeed, but the third one would throw.
6887+
That's because al overall 640 kibibytes quota was not exceeded in the first to call, however the 3rd
6888+
request exceeds the reporting-origin quota for <code>https://a.example.com</code>, and would throw.
6889+
<pre><code class=lang-javascript>
6890+
fetchLater("https://a.example.com", {method: "POST", body: a_64kb_body});
6891+
fetchLater("https://b.example.com", {method: "POST", body: a_64kb_body});
6892+
fetchLater("https://a.example.com");
6893+
</code></pre>
6894+
6895+
<p>Same-origin same-agent subframes share the quota of their parent. However, cross-origin or
6896+
cross-agent iframes only receive 8kb of quota by default. So in the following example, the first 3
6897+
calls would succeed and the last one would throw.
6898+
<pre><code class=lang-javascript>
6899+
// In main page
6900+
fetchLater("https://a.example.com", {method: "POST", body: a_64kb_body});
6901+
6902+
// In same-origin iframe
6903+
fetchLater("https://b.example.com", {method: "POST", body: a_64kb_body});
6904+
6905+
// In cross-origin iframe at https://frame.example.com
6906+
fetchLater("https://a.example.com", {body: a_5kb_body});
6907+
fetchLater("https://a.example.com", {body: a_12kb_body});
6908+
</code></pre>
6909+
6910+
<p>To make the previous example not throw, the top-level {{Document}} needs to delegate some of its
6911+
quota to <code>https://frame.example.com</code>, for example by serving the following header:
6912+
<pre><code>Permissions-Policy: deferred-fetch=(self "https://frame.example.com")</code></pre>
6913+
6914+
</div>
6915+
68546916

68556917
<p>This specification defined a <a>policy-controlled feature</a> identified by the string
68566918
<dfn for=PermissionPolicy enum-value>"deferred-fetch"</dfn>. Its
@@ -7045,7 +7107,6 @@ shared.
70457107
</ol>
70467108

70477109
<li><p>Otherwise, set <var>container</var>'s <a>reserved deferred-fetch quota</a> to zero.
7048-
70497110
</div>
70507111

70517112

@@ -8957,9 +9018,12 @@ method steps are:
89579018
<li><p>If <var>request</var>'s <a for=request>URL</a>'s <a for=url>scheme</a> is not an
89589019
<a>HTTP(S) scheme</a>, then throw a {{TypeError}}.
89599020

8960-
<li><p><li><p>If <var>request</var>'s
8961-
<a for=request>body</a> is not null, and <var>request</var>'s
8962-
<a for=request>body</a> <a for=body>source</a> is null, then throw a {{TypeError}}.
9021+
<li>
9022+
<p>If <var>request</var>'s
9023+
<a for=request>body</a> is not null, and <var>request</var>'s <a for=request>body</a>
9024+
<a for=body>length</a> is null, then throw a {{TypeError}}.
9025+
9026+
<p class=note>Requests whose <a for=request>body</a> is a {{ReadableStream}} cannot be deferred.
89639027

89649028
<li><p>If <var>request</var>'s <a for=request>client</a> is not a <a>fully active</a>
89659029
{{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}.
@@ -8992,6 +9056,73 @@ method steps are:
89929056
<var>deferredRecord</var>.
89939057
</ol>
89949058

9059+
<div class=example id=fetch-later-examples>
9060+
<p>The following call would queue a request to be fetched when the document is terminated:
9061+
<pre><code class=lang-javascript>fetchLater("https://report.example.com", { method: "POST",
9062+
body: JSON.stringify(myReport) })</code></pre>
9063+
9064+
<p>The following call would also queue this request after 5 seconds, and the returned value would
9065+
allow callers to observe if it was indeed activated. Note that the request is guaranteed to be
9066+
invoked, even in cases where the user agent throttles timers.
9067+
9068+
<pre><code class=lang-javascript>
9069+
const result = fetchLater("https://report.example.com", {
9070+
method: "POST",
9071+
body: JSON.stringify(myReport),
9072+
activateAfter: 5000
9073+
});
9074+
9075+
function check_if_fetched() {
9076+
return result.activated;
9077+
}
9078+
</code></pre>
9079+
9080+
<p>The {{FetchLaterResult}} object can be used together with an {{AbortSignal}}. For example:
9081+
<pre><code class=lang-javascript>
9082+
let accumulated_events = [];
9083+
let previous_result = null;
9084+
const abort_signal = new AbortSignal();
9085+
function accumulate_event(event) {
9086+
if (previous_result) {
9087+
if (previous_result.activated) {
9088+
// The request is already activated, we can start from scratch.
9089+
accumulated_events = [];
9090+
} else {
9091+
// Abort this request, and start a new one with all the events.
9092+
signal.abort();
9093+
}
9094+
}
9095+
9096+
accumulated_events.push(event);
9097+
fetchLater("https://report.example.com", {
9098+
method: "POST",
9099+
body: JSON.stringify(accumulated_events),
9100+
activateAfter: 5000,
9101+
abort_signal
9102+
});
9103+
9104+
return result.activated;
9105+
}
9106+
</code></pre>
9107+
9108+
9109+
<p>Any of the following calls to <a method><code>fetchLater()</code></a> would throw:
9110+
<pre><code class=lang-javascript>
9111+
// Only <a>potentially trustworthy url</a>s are supported.
9112+
fetchLater("http://untrusted.example.com");
9113+
9114+
// The length of the deferred request has to be known when.
9115+
fetchLater("https://origin.example.com", {body: someDynamicStream});
9116+
9117+
// Deferred fetching only works on active windows.
9118+
const detachedWindow = iframe.contentWindow;
9119+
iframe.remove();
9120+
detachedWindow.fetchLater("https://origin.example.com");
9121+
</code></pre>
9122+
9123+
See <a href="#deferred-fetch-quota-examples">deferred fetch quota examples</a> for examples
9124+
portraying how the deferred-fetch quota works.
9125+
</div>
89959126

89969127
<h3 id=garbage-collection>Garbage collection</h3>
89979128

0 commit comments

Comments
 (0)