-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bikeshed errors in fetch callbacks #164
Open
jakearchibald
wants to merge
3
commits into
main
Choose a base branch
from
new-fetch-args
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,9 +13,7 @@ Indent: 2 | |
</pre> | ||
|
||
<pre class="link-defaults"> | ||
spec:html; type:dfn; for:agent; text:event loop | ||
spec:infra; type:dfn; text:list | ||
spec:service-workers; type:dfn; for:/; text:service worker | ||
spec:webidl; type:dfn; text:resolve | ||
</pre> | ||
|
||
|
@@ -49,27 +47,27 @@ spec: image-resource; urlPrefix: https://w3c.github.io/image-resource/ | |
</pre> | ||
|
||
<style> | ||
.algorithm dl { | ||
dl.compact { | ||
overflow: hidden; | ||
} | ||
.algorithm dt { | ||
dl.compact dt { | ||
font-weight: normal; | ||
float: left; | ||
clear: left; | ||
line-height: 1.5; | ||
margin-right: 0.3em; | ||
} | ||
.algorithm dt::after { | ||
dl.compact dt::after { | ||
content: '- '; | ||
} | ||
.algorithm dd { | ||
dl.compact dd { | ||
margin-left: 0em; | ||
} | ||
</style> | ||
|
||
# Introduction # {#intro} | ||
|
||
A [=service worker=] is capable of fetching and caching assets, the size of which is restricted only by [origin storage](https://storage.spec.whatwg.org/#usage-and-quota). However, if the user navigates away from the site or closes the browser, the service worker is [[service-workers#service-worker-lifetime|likely to be killed]]. This can happen even if there's a pending promise passed to {{ExtendableEvent/waitUntil()}} - if it hasn't resolved within a few minutes the browser may consider it an abuse of [=service worker=] and kill the process. | ||
A [=/service worker=] is capable of fetching and caching assets, the size of which is restricted only by [origin storage](https://storage.spec.whatwg.org/#usage-and-quota). However, if the user navigates away from the site or closes the browser, the service worker is [[service-workers#service-worker-lifetime|likely to be killed]]. This can happen even if there's a pending promise passed to {{ExtendableEvent/waitUntil()}} - if it hasn't resolved within a few minutes the browser may consider it an abuse of [=/service worker=] and kill the process. | ||
|
||
This is excellent for battery and privacy, but it makes it difficult to download and cache large assets such as podcasts and movies, and upload video and images. | ||
|
||
|
@@ -100,7 +98,7 @@ A resource is considered <dfn>temporarily unavailable</dfn> if the user agent be | |
The <dfn>background fetch task source</dfn> is a [=task source=]. | ||
|
||
<div algorithm> | ||
To <dfn>queue a bgfetch task</dfn> on an optional |eventLoop| (an [=event loop=], defaulting to the caller's [=this=]'s [=relevant settings object=]'s [=responsible event loop=]) with |steps| (steps), [=queue a task=] on |eventLoop| using the [=background fetch task source=] to run |steps|. | ||
To <dfn>queue a bgfetch task</dfn> on an optional |eventLoop| (an [=/event loop=], defaulting to the caller's [=this=]'s [=relevant settings object=]'s [=responsible event loop=]) with |steps| (steps), [=queue a task=] on |eventLoop| using the [=background fetch task source=] to run |steps|. | ||
</div> | ||
|
||
## Extensions to service worker registration ## {#service-worker-registration-concept-extensions} | ||
|
@@ -151,20 +149,22 @@ A <dfn>background fetch</dfn> consists of: | |
* The UI cannot be dismissed without aborting (e.g. swiped away) until |bgFetch|'s [=background fetch/result=] is not the empty string. | ||
* The UI may display |bgFetch|'s [=background fetch/title=]. | ||
* The UI may select an [=image resource=] (|icon|) for display from |bgFetch|'s [=background fetch/icons=], after successfully [=processing=] it, and [=/fetch=] it using a new [=/request=] with the following properties: | ||
: [=request/URL=] | ||
:: |icon|'s [=image resource/src=]. | ||
: [=request/Client=] | ||
:: |environment|. | ||
: [=request/Keepalive=] | ||
:: True. | ||
: [=request/Service-workers mode=] | ||
:: "`none`". | ||
: [=request/Destination=] | ||
:: "`image`". | ||
: [=request/Mode=] | ||
:: "`no-cors`". | ||
: [=request/Credentials mode=] | ||
:: "`include`". | ||
<dl class="compact"> | ||
: [=request/URL=] | ||
:: |icon|'s [=image resource/src=]. | ||
: [=request/Client=] | ||
:: |environment|. | ||
: [=request/Keepalive=] | ||
:: True. | ||
: [=request/Service-workers mode=] | ||
:: "`none`". | ||
: [=request/Destination=] | ||
:: "`image`". | ||
: [=request/Mode=] | ||
:: "`no-cors`". | ||
: [=request/Credentials mode=] | ||
:: "`include`". | ||
</dl> | ||
|
||
Issue: [manifest/pull/710](https://github.com/w3c/manifest/pull/710). | ||
|
||
|
@@ -291,60 +291,60 @@ Note: This algorithm manages the fetching of a [=/background fetch record=]. One | |
Note: If the |rangeStart| is 0, a normal request is made. This allows the initial request to make use of content encoding, since `Accept-Encoding: identity` is added to requests with a range header. | ||
|
||
1. Let |fetchAttemptComplete| be false. | ||
1. Let |lastTransmittedSize| be 0. | ||
1. [=/Fetch=] |request|. | ||
|
||
Issue: The remainder of this step uses fetch "callbacks" which currently queue tasks. This isn't desirable or possible here, so let's pretend that tasks aren't queued. ([issue](https://github.com/whatwg/fetch/issues/536#issuecomment-330184276)) | ||
|
||
To [=process request body=] for |request|, run these steps: | ||
1. Let |transmittedSize| be |request|'s [=request/body=]'s [=body/transmitted bytes=]. | ||
1. Increment |bgFetch|'s [=background fetch/uploaded=] by |transmittedSize| minus |lastTransmittedSize|. | ||
1. Set |lastTransmittedSize| to |transmittedSize|. | ||
1. [=Update background fetch instances=] for |bgFetch|. | ||
|
||
To [=process response=] for |response|, run these steps: | ||
1. If |response| is a [=network error=], then: | ||
1. If the resource is [=temporarily unavailable=] and |request|'s [=request/method=] is \``GET`\`, then wait until the resource is not [=temporarily unavailable=], then set |fetchAttemptComplete| to true and abort these steps. | ||
|
||
Note: If |request|'s [=request/method=] is not \``GET`\`, reissuing the request may have unwanted side effects. If a standard method to resume requests becomes available, it'll be adopted here. | ||
|
||
1. If |response| is an [=aborted network error=], then set |responseData|'s [=background fetch response/result=] to `"aborted"`, otherwise `"fetch-error"`. | ||
1. Set |fetchAttemptComplete| to true, and abort these steps. | ||
1. If |response|'s [=response/status=] is `206`, then: | ||
1. If [=validate a partial response=] for |rangeStart|, |response|, and |responseData|'s [=background fetch response/response=] returns invalid, then: | ||
1. Set |responseData|'s [=background fetch response/result=] to `"fetch-error"`. | ||
1. Set |fetchAttemptComplete| to true. | ||
1. [=fetch/Terminate=] the ongoing fetch, and abort these steps. | ||
1. Otherwise: | ||
1. Set |responseData|'s [=background fetch response/result=] to `"redundant"`. | ||
1. Set |responseData| to a new [=background fetch response=]. | ||
1. Set |record|'s [=background fetch record/response data=] to |responseData|. | ||
1. [=/Fetch=] |request| with the following arguments: | ||
<dl class="compact"> | ||
: [=fetch/useParallelQueue=] | ||
:: True. | ||
: [=fetch/processRequestBodyChunkLength=] | ||
:: The following steps given |chunkLength| (a number): | ||
1. Increment |bgFetch|'s [=background fetch/uploaded=] by |chunkLength|. | ||
1. [=Update background fetch instances=] for |bgFetch|. | ||
: [=fetch/processResponse=] | ||
:: The following steps given |response| (a [=/response=]): | ||
Comment on lines
+299
to
+303
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are pretty much just re-indentings of the previous algorithm. |
||
1. If |response| is a [=network error=], then: | ||
1. If the resource is [=temporarily unavailable=] and |request|'s [=request/method=] is \``GET`\`, then wait until the resource is not [=temporarily unavailable=], then set |fetchAttemptComplete| to true and abort these steps. | ||
|
||
Note: The [=create record objects=] algorithm may hold a reference to the previous [=background fetch response=]. | ||
Note: If |request|'s [=request/method=] is not \``GET`\`, reissuing the request may have unwanted side effects. If a standard method to resume requests becomes available, it'll be adopted here. | ||
|
||
1. [=Update background fetch instances=] for |bgFetch|. | ||
1. If |rangeStart| is 0 or |response|'s [=response/status=] is not `206`, then set |responseData|'s [=background fetch response/response=] to a copy of |response| except for its [=response/body=]. | ||
1. Let |stream| be the |response| [=response/body=]'s [=body/stream=]. | ||
1. Whenever one or more bytes are transmitted from |stream|, let |bytes| be the transmitted bytes and run these steps: | ||
1. If |bgFetch|'s [=background fetch/stored body bytes total=] plus the size of |bytes| is greater than |downloadTotal|, then: | ||
1. [=ReadableStream/Cancel=] |stream|. | ||
1. Set |responseData|'s [=background fetch response/result=] to `"download-total-exceeded"`, |fetchAttemptComplete| to true, and abort these steps. | ||
1. Append |bytes| to |responseData|'s [=background fetch response/bytes=]. | ||
1. If the previous step fails due to exceeding a quota limit, set |responseData|'s [=background fetch response/result=] to `"quota-exceeded"`, |fetchAttemptComplete| to true, and abort these steps. | ||
1. [=Update background fetch instances=] for |bgFetch|. | ||
1. If at any point the bytes transmission for |stream| is done normally, then: | ||
1. If |response| is an [=aborted network error=], then set |responseData|'s [=background fetch response/result=] to `"aborted"`, otherwise `"fetch-error"`. | ||
1. Set |fetchAttemptComplete| to true, and abort these steps. | ||
1. If |response|'s [=response/status=] is `206`, then: | ||
1. Let <var ignore>firstBytePos</var>, <var ignore>lastBytePos</var>, and |completeLength| be the result of [=extracting content-range values=] from |response|. | ||
1. If |completeLength| is not null, and equal to the [=byte sequence/length=] of |responseData|'s [=background fetch response/bytes=], set |responseData|'s [=background fetch response/result=] to `"success"`. | ||
|
||
Note: Although we ask for the whole resource, or the remainder of the resource, the server may not have returned the remainder, in which case we need to make an additional request. | ||
1. If [=validate a partial response=] for |rangeStart|, |response|, and |responseData|'s [=background fetch response/response=] returns invalid, then: | ||
1. Set |responseData|'s [=background fetch response/result=] to `"fetch-error"`. | ||
1. Set |fetchAttemptComplete| to true. | ||
1. [=fetch/Terminate=] the ongoing fetch, and abort these steps. | ||
1. Otherwise: | ||
1. Set |responseData|'s [=background fetch response/result=] to `"redundant"`. | ||
1. Set |responseData| to a new [=background fetch response=]. | ||
1. Set |record|'s [=background fetch record/response data=] to |responseData|. | ||
|
||
Note: The [=create record objects=] algorithm may hold a reference to the previous [=background fetch response=]. | ||
|
||
1. [=Update background fetch instances=] for |bgFetch|. | ||
1. If |rangeStart| is 0 or |response|'s [=response/status=] is not `206`, then set |responseData|'s [=background fetch response/response=] to a copy of |response| except for its [=response/body=]. | ||
1. Let |stream| be the |response| [=response/body=]'s [=body/stream=]. | ||
1. Whenever one or more bytes are transmitted from |stream|, let |bytes| be the transmitted bytes and run these steps: | ||
1. If |bgFetch|'s [=background fetch/stored body bytes total=] plus the size of |bytes| is greater than |downloadTotal|, then: | ||
1. [=ReadableStream/Cancel=] |stream|. | ||
1. Set |responseData|'s [=background fetch response/result=] to `"download-total-exceeded"`, |fetchAttemptComplete| to true, and abort these steps. | ||
1. Append |bytes| to |responseData|'s [=background fetch response/bytes=]. | ||
1. If the previous step fails due to exceeding a quota limit, set |responseData|'s [=background fetch response/result=] to `"quota-exceeded"`, |fetchAttemptComplete| to true, and abort these steps. | ||
1. [=Update background fetch instances=] for |bgFetch|. | ||
1. If at any point the bytes transmission for |stream| is done normally, then: | ||
1. If |response|'s [=response/status=] is `206`, then: | ||
1. Let <var ignore>firstBytePos</var>, <var ignore>lastBytePos</var>, and |completeLength| be the result of [=extracting content-range values=] from |response|. | ||
1. If |completeLength| is not null, and equal to the [=byte sequence/length=] of |responseData|'s [=background fetch response/bytes=], set |responseData|'s [=background fetch response/result=] to `"success"`. | ||
|
||
Note: Although we ask for the whole resource, or the remainder of the resource, the server may not have returned the remainder, in which case we need to make an additional request. | ||
|
||
1. Otherwise, if |response|'s [=response/status=] is not an [=ok status=], set |responseData|'s [=background fetch response/result=] to `"bad-status"`. | ||
1. Otherwise, set |responseData|'s [=background fetch response/result=] to `"success"`. | ||
1. Set |fetchAttemptComplete| to true. | ||
1. If at any point |stream| becomes [=ReadableStream/errored=], then: | ||
1. If the resource is [=temporarily unavailable=] and |request|'s [=request/method=] is \``GET`\`, then wait until the resource is not [=temporarily unavailable=], then set |fetchAttemptComplete| to true. | ||
1. Otherwise, set |responseData|'s [=background fetch response/result=] to `"fetch-error"` and |fetchAttemptComplete| to true. | ||
</dl> | ||
|
||
1. Otherwise, if |response|'s [=response/status=] is not an [=ok status=], set |responseData|'s [=background fetch response/result=] to `"bad-status"`. | ||
1. Otherwise, set |responseData|'s [=background fetch response/result=] to `"success"`. | ||
1. Set |fetchAttemptComplete| to true. | ||
1. If at any point |stream| becomes [=ReadableStream/errored=], then: | ||
1. If the resource is [=temporarily unavailable=] and |request|'s [=request/method=] is \``GET`\`, then wait until the resource is not [=temporarily unavailable=], then set |fetchAttemptComplete| to true. | ||
1. Otherwise, set |responseData|'s [=background fetch response/result=] to `"fetch-error"` and |fetchAttemptComplete| to true. | ||
1. Let |result| be the empty string. | ||
1. Run these steps, but [=abort when=] |bgFetch|'s [=background fetch/paused flag=] or [=background fetch/abort all flag=] is set: | ||
1. Wait for |fetchAttemptComplete| to be true. | ||
|
@@ -401,8 +401,11 @@ Note: This algorithm manages the fetching of a [=/background fetch record=]. One | |
|
||
<div algorithm> | ||
To <dfn>fire a background fetch click event</dfn> for a |bgFetch| (a [=/background fetch=]), [=fire a functional event=] named "`backgroundfetchclick`" using {{BackgroundFetchEvent}} on |bgFetch|'s [=background fetch/service worker registration=] with the following properties: | ||
: {{BackgroundFetchEvent/registration}} | ||
:: The result of [=getting a BackgroundFetchRegistration instance=] for |bgFetch| in the event object's [=relevant Realm=]. | ||
|
||
<dl class="compact"> | ||
: {{BackgroundFetchEvent/registration}} | ||
:: The result of [=getting a BackgroundFetchRegistration instance=] for |bgFetch| in the event object's [=relevant Realm=]. | ||
</dl> | ||
</div> | ||
|
||
## [=Get a BackgroundFetchRegistration instance=] ## {#get-a-backgroundfetchregistration-instance-algorithm} | ||
|
@@ -472,10 +475,12 @@ Note: This algorithm creates platform objects for [=background fetch records=]. | |
1. Let |recordObject| be a new {{BackgroundFetchRecord}}. | ||
1. Set |recordObject|'s {{BackgroundFetchRecord/responseReady}} to [=a new promise=]. | ||
1. Let |requestObject| be a new {{Request}} object with the following set: | ||
: [=Request/Request=] | ||
:: A copy of |record|'s [=background fetch record/request=], including its [=request/body=]. | ||
: [=Request/Headers=] | ||
:: A new {{Headers}} object associated with this {{Request}}'s [=Request/request=]'s [=request/header list=]. | ||
<dl class="compact"> | ||
: [=Request/Request=] | ||
:: A copy of |record|'s [=background fetch record/request=], including its [=request/body=]. | ||
: [=Request/Headers=] | ||
:: A new {{Headers}} object associated with this {{Request}}'s [=Request/request=]'s [=request/header list=]. | ||
</dl> | ||
1. Set |recordObject|'s {{BackgroundFetchRecord/request}} to |requestObject|. | ||
1. Let |transmittedBytes| be 0. | ||
1. Let |stream| be a [=new=] {{ReadableStream}}. | ||
|
@@ -506,10 +511,12 @@ Note: This algorithm creates platform objects for [=background fetch records=]. | |
1. Set |response|'s [=response/body=] to |body|. | ||
1. [=Queue a task=] in |recordObject|'s [=relevant settings object=]'s [=responsible event loop=] using the [=networking task source=] to run these steps: | ||
1. Let |responseObject| be a new {{Response}} object with the following set: | ||
: [=Response/Response=] | ||
:: |response|. | ||
: [=Response/Headers=] | ||
:: A new {{Headers}} object associated with this {{Response}}'s [=Response/response=]'s [=response/header list=]. | ||
<dl class="compact"> | ||
: [=Response/Response=] | ||
:: |response|. | ||
: [=Response/Headers=] | ||
:: A new {{Headers}} object associated with this {{Response}}'s [=Response/response=]'s [=response/header list=]. | ||
</dl> | ||
1. [=Resolve=] |recordObject|'s {{BackgroundFetchRecord/responseReady}} with |responseObject|. | ||
1. Otherwise, if |responseData|'s [=background fetch response/result=] is `"aborted"`, then [=reject=] |recordObject|'s {{BackgroundFetchRecord/responseReady}} with an {{AbortError}} {{DOMException}}. | ||
1. Otherwise, [=reject=] |recordObject|'s {{BackgroundFetchRecord/responseReady}} with a {{TypeError}}. | ||
|
@@ -713,20 +720,22 @@ dictionary BackgroundFetchOptions : BackgroundFetchUIOptions { | |
1. Wait for |requestBodiesRemaining| to be 0, or |requestReadFailed| to be true. | ||
1. If |requestReadFailed| is true, then [=reject=] |promise| with a {{TypeError}} and abort these steps. | ||
1. Let |bgFetch| be a new [=/background fetch=] with: | ||
: [=background fetch/id=] | ||
:: |id|. | ||
: [=background fetch/records=] | ||
:: |records|. | ||
: [=background fetch/download total=] | ||
:: |options|' `downloadTotal` member. | ||
: [=background fetch/upload total=] | ||
:: |uploadTotal|. | ||
: [=background fetch/icons=] | ||
:: |options|' `icons` member if present, otherwise an empty [=/list=]. | ||
: [=background fetch/title=] | ||
:: |options|' `title` member if present, otherwise the empty string. | ||
: [=background fetch/service worker registration=] | ||
:: |registration|. | ||
<dl class="compact"> | ||
: [=background fetch/id=] | ||
:: |id|. | ||
: [=background fetch/records=] | ||
:: |records|. | ||
: [=background fetch/download total=] | ||
:: |options|' `downloadTotal` member. | ||
: [=background fetch/upload total=] | ||
:: |uploadTotal|. | ||
: [=background fetch/icons=] | ||
:: |options|' `icons` member if present, otherwise an empty [=/list=]. | ||
: [=background fetch/title=] | ||
:: |options|' `title` member if present, otherwise the empty string. | ||
: [=background fetch/service worker registration=] | ||
:: |registration|. | ||
</dl> | ||
1. Set |bgFetchMap|[|id|] to |bgFetch|. | ||
1. [=Queue a bgfetch task=] to run these steps: | ||
1. [=Resolve=] |promise| with the result of [=getting a BackgroundFetchRegistration instance=] for |bgFetch| in [=this=]'s [=relevant Realm=]. | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@annevk I know the fetch spec says I shouldn't need this, but my intent is to be able to do this processing without any page open.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tricky aspect is how we enforce any of the things that Fetch requires a client for, such as referrer and CSP. It seems you would have to obtain these ahead of time and pass them in somehow, but there's no infrastructure for that as of yet.