Skip to content
Draft
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 183 additions & 4 deletions fetch.bs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262

<pre class=link-defaults>
spec:dom; type:dfn; text:element
spec:dom; type:dfn; text:event;
spec:infra; type:dfn; text:implementation-defined
</pre>

Expand Down Expand Up @@ -8512,7 +8513,19 @@ otherwise false.

<pre class=idl>
partial interface mixin WindowOrWorkerGlobalScope {
[NewObject] Promise&lt;Response> fetch(RequestInfo input, optional RequestInit init = {});
[NewObject] Promise&lt;Response> fetch(RequestInfo input, optional FetchInit init = {});
};

dictionary FetchInit : RequestInit {
FetchMonitorCallback monitor;
};

callback FetchMonitorCallback = undefined (FetchMonitor monitor);

[Exposed=(Window,Worker)]
interface FetchMonitor : EventTarget {
attribute EventHandler onrequestprogress;
attribute EventHandler onresponseprogress;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhas we can have requestFinished and responseFinished promises as well?

</pre>

Expand Down Expand Up @@ -8584,10 +8597,56 @@ method steps are:
https://github.com/whatwg/dom/issues/1031#issuecomment-1233206400 -->
</ol>

<li><p>Let <var>monitor</var> be null.

<li>
<p>If <var>init</var>["{{FetchInit/monitor}}"] <a for=map>exists</a>, then:

<ol>
<li><p>Let <var>monitorCallback</var> be <var>init</var>["{{FetchInit/monitor}}"].

<li><p>Set <var>monitor</var> to a {{FetchMonitor}}.

<li><p>Let <var>args</var> be « <var>monitor</var> ».

<li><p>[=invoke|Invoke=] <var>monitorCallback</var> with <var>args</var>
and <code>"rethrow"</code>. If this throws an exception, <a for=/>reject</a> <var>p</var> with it
and return <var>p</var>.
</ol>

<li><p>Let <var>requestBodyTransmitted</var> be 0.

<li><p>Let <var>requestBodyLength</var> be <var>request</var>'s <a for=request>body</a>'s
<a for=body>length</a>, if <var>request</var>'s <a for=request>body</a> is non-null;
otherwise 0.

<li><p>Assert: <var>requestBodyLength</var> is an integer.

<li>
<p>Let <var>processRequestBodyChunkLength</var>, given a <var>bytesLength</var>, be these steps:

<ol>
<li><p>Increase <var>requestBodyTransmitted</var> by <var>bytesLength</var>.

<li><p>If not roughly 50ms has passed since these steps were last invoked, then return.

<li><p>If <var>monitor</var> is not null, then <a>fire a progress event</a> named
<a event><code>requestprogress</code></a> at <var>monitor</var> with <var>requestBodyTransmitted</var>
and <var>requestBodyLength</var>.
</ol>

<li>
<p><p>Set <var>controller</var> to the result of calling <a for=/>fetch</a> given
<var>request</var> and <a for=fetch><i>processResponse</i></a> given <var>response</var> being
these steps:
<p>Let <var>processRequestEndOfBody</var> be these steps:

<ol>
<li><p>If <var>monitor</var> is null, then return.

<li><p><a>Fire a progress event</a> named <a event><code>requestprogress</code></a> at <var>monitor</var>
with <var>requestBodyTransmitted</var> and <var>requestBodyLength</var>.
</ol>

<li>
<p>Let <var>processResponse</var> given a <var>response</var> be these steps:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to do progress tracking for response we'll need to do it differently to XHR, XHR consumes the body inside of this algorithm but fetch obviously can't.

One idea is to wrap response's body with a wrapper readable stream that allows use to fire progress events when the body actually gets read. This way we don't consume anything to report progress, (we can make it so we only wrapper if there's a responseObserver with a progress event listeners).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially because there's no access to it from within the JS fetch() definition.


<ol>
<li><p>If <var>locallyAborted</var> is true, then abort these steps.
Expand Down Expand Up @@ -8615,10 +8674,17 @@ method steps are:
<li><p><a for=/>Resolve</a> <var>p</var> with <var>responseObject</var>.
</ol>

<li><p>Set <var>controller</var> to the result of calling <a for=/>fetch</a> given
<var>request</var> with <a for=fetch><i>processResponse</i></a> set to <var>processResponse</var>,
<a for=fetch><i>processRequestBodyChunkLength</i></a> set to <var>processRequestBodyChunkLength</var>,
and <a for=fetch><i>processRequestEndOfBody</i></a> set to <var>processRequestEndOfBody</var>.

<li><p>Return <var>p</var>.
</ol>
</div>

TEMPORARY <dfn id=event-fetchmonitor-requestprogress event for=FetchMonitor><code>requestprogress</code></dfn>

<div algorithm>
<p>To <dfn lt="Abort the fetch() call" export id=abort-fetch>abort a <code>fetch()</code> call</dfn>
with a <var>promise</var>, <var>request</var>, <var>responseObject</var>, and an <var>error</var>:
Expand Down Expand Up @@ -9132,6 +9198,119 @@ done only by navigations). The <a>fetch controller</a> is also used to
<a for="fetch controller">process the next manual redirect</a> for <a for=/>requests</a> with
<a for=request>redirect mode</a> set to "<code>manual</code>".

<h2 id=interface-progressevent>Interface {{ProgressEvent}}</h2>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is taken from XHR and should be removed from XHR in a corresponding PR.


<pre class=idl>
[Exposed=(Window,Worker)]
interface ProgressEvent : Event {
constructor(DOMString type, optional ProgressEventInit eventInitDict = {});

readonly attribute boolean lengthComputable;
readonly attribute double loaded;
readonly attribute double total;
};

dictionary ProgressEventInit : EventInit {
boolean lengthComputable = false;
double loaded = 0;
double total = 0;
};
</pre>

<p><a>Events</a> using the {{ProgressEvent}} interface indicate some kind of progression.

<p>The
<dfn attribute for=ProgressEvent><code>lengthComputable</code></dfn>,
<dfn attribute for=ProgressEvent><code>loaded</code></dfn>, and
<dfn attribute for=ProgressEvent><code>total</code></dfn>
getter steps are to return the value they were initialized to.


<h3 id=firing-events-using-the-progressevent-interface>Firing events using the {{ProgressEvent}} interface</h3>

<p>To <dfn id=concept-event-fire-progress>fire a progress event</dfn> named <var>e</var> at
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fetch really fire sync events? It would match XHR but I wonder if these should be queued instead?

<var>target</var>, given <var>transmitted</var> and <var>length</var>, means to <a>fire an event</a>
named <var>e</var> at <var>target</var>, using {{ProgressEvent}}, with the {{ProgressEvent/loaded}}
attribute initialized to <var>transmitted</var>, and if <var>length</var> is not 0, with the
{{ProgressEvent/lengthComputable}} attribute initialized to true and the {{ProgressEvent/total}}
attribute initialized to <var>length</var>.


<h3 id=suggested-names-for-events-using-the-progressevent-interface>Suggested names for events using the {{ProgressEvent}} interface</h3>

<p><em>This section is non-normative.</em>

<p>The suggested {{Event/type}}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these events aren't used in Fetch so this table should probably be kept in the XHR spec.

attribute values for use with
<a>events</a> using the
{{ProgressEvent}} interface are summarized in the table below.
Specification editors are free to tune the details to their specific
scenarios, though are strongly encouraged to discuss their usage with the
WHATWG community to ensure input from people familiar with the subject.

<table>
<tbody>
<tr>
<th>{{Event/type}} attribute value
<th>Description
<th>Times
<th>When
<tr>
<th><code>loadstart</code>
<td>Progress has begun.
<td>Once.
<td>First.
<tr>
<th><a event><code>requestprogress</code></a>
<td>In progress request.
<td>Once or more.
<td>After <code>loadstart</code> has been
<a>dispatched</a>.
<tr>
<th><code>error</code>
<td>Progression failed.
<td rowspan=4>Zero or once (mutually exclusive).
<td rowspan=4>After the last <a event><code>progress</code></a> has
been
<a>dispatched</a>.
<tr>
<th><code>abort</code>
<td>Progression is terminated.
<tr>
<th><code>timeout</code>
<td>Progression is terminated due to preset time expiring.
<tr>
<th><code>load</code>
<td>Progression is successful.
<tr>
<th><code>loadend</code>
<td>Progress has stopped.
<td>Once.
<td>After one of <code>error</code>, <code>abort</code>,
<code>timeout</code> or <code>load</code> has been
<a>dispatched</a>.
</table>

<p>The <code>error</code>, <code>abort</code>, <code>timeout</code>, and
<code>load</code> event types are mutually exclusive.

<p>Throughout the web platform the <code>error</code>, <code>abort</code>,
<code>timeout</code> and <code>load</code> event types have
their {{Event/bubbles}} and {{Event/cancelable}}
attributes initialized to false, so it is suggested that for consistency all
<a>events</a> using the
{{ProgressEvent}} interface do the same.


<h3 id=security-considerations>Security considerations</h3>

<p>For cross-origin requests some kind of opt-in, e.g., the
<a>CORS protocol</a>, has to be used before <a>events</a> using the
{{ProgressEvent}} interface are
<a>dispatched</a>
as information (e.g., size) would be revealed that cannot be obtained
otherwise.


<h2 id=acknowledgments class=no-num>Acknowledgments</h2>

Expand Down
Loading