diff --git a/docs/index.bs b/docs/index.bs index 7ce82190..d34117f9 100644 --- a/docs/index.bs +++ b/docs/index.bs @@ -21,6 +21,19 @@ Abstract: The service worker is a generic entry point for event-driven backgroun Markup Shorthands: css no, markdown yes + +
spec: html; type: dfn; text: task queues; for: / @@ -2275,6 +2288,8 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe A job has a list of equivalent jobs (a list of jobs). It is initially the empty list. A [=job=] has a force bypass cache flag. It is initially unset. + + A [=job=] has an abort flag. It is initially unset. @@ -2295,6 +2310,71 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe Note: Keep this definition in sync with [=fetch a classic worker-imported script=]. ++ + + +Aborting algorithms
+ + Algorithms may throw abort, which acts like "throw" in that it will also make the caller rethrow. + + Algorithms may define abort steps. [=Abort steps=] blocks are not run as they are encountered, instead they are effectively appended to a list associated with the algorithm. + + If an algorithm [=throw aborts=], the steps in the associated list are run starting with the last set in the list. + + If any of the steps "return"s, all subsiquent [=abort steps=] in the associated list are skipped, and the "throw" is "caught". + + A set of steps may throw abort when a particular condition becomes true. This indicates that the specified steps must be evaluated, not as-written, but by additionally inserting a step before each of them that evaluates condition, and if condition evaluates to true, [=throw aborts=]. + + Note: This is based on the behaviour of [=abort when=]. + ++ Imagine the following algorithm: + + 1. Run these steps, but [=throw abort when=] the user clicks the "Cancel" button: + +++ [=Abort steps=]: + + 1. Reject promise with a {{TypeError}}. + 1. Return. ++ + 1. Let |a| be the result of starting machine A. + +[=Abort step=]: Stop |a|.
+ + 1. Let |b| be the result of starting machine B. + +[=Abort step=]: Stop |b|.
+ + 1. Increment global counter by 1. + +[=Abort step=]: Decrement global counter by 1.
+ + 1. Let |c| be the result of starting machine C. + +[=Abort step=]: Stop |c|.
+ + 1. Resolve promise. + + It may run like this: + + 1. Let |a| be the result of starting machine A. + 1. Let |b| be the result of starting machine B. + 1. Increment global counter by 1. + + And if, at this point, the user clicks the "Cancel" button, the previous [=abort steps=] are processed in reverse order: + + 1. Decrement global counter by 1. + 1. Stop |b|. + 1. Stop |a|. + 1. Reject promise with a {{TypeError}}. + 1. Return. + + Note that the blocks are processed in reverse order, but the steps within blocks are not. +@@ -2483,10 +2575,13 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe : Output :: none + Run these steps, but [=throw abort when=] |job|'s [=job/abort flag=] is set: + 1. Let |registration| be the result of running the Get Registration algorithm passing |job|'s [=job/scope url=] as the argument. 1. If |registration| is null, then: 1. Invoke [=Reject Job Promise=] with |job| and `TypeError`. 1. Invoke Finish Job with |job| and abort these steps. + 1. Let |scopeURL| be |registration|'s [=service worker registration/scope url=]. 1. Let |newestWorker| be the result of running Get Newest Worker algorithm passing |registration| as the argument. 1. If |job|'s job type is *update*, and |newestWorker| is not null and its [=service worker/script url=] does not [=url/equal=] |job|'s [=job/script url=], then: 1. Invoke [=Reject Job Promise=] with |job| and `TypeError`. @@ -2518,68 +2613,71 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Set |request|'s [=service-workers mode=] to "`none`". 1. If the [=fetching scripts/is top-level=] flag is unset, then return the result of [=/fetching=] |request|. 1. Set |request|'s [=request/redirect mode=] to " Create Job
@@ -2347,8 +2427,15 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe :: none 1. Assert: |jobQueue| [=queue/is not empty=]. - 1. [=Queue a task=] to run these steps: - 1. Let |job| be the first [=queue/item=] in |jobQueue|. + 1. Let |job| be the first [=queue/item=] in |jobQueue|. + 1. Run these steps, but [=throw abort when=] |job|'s [=job/abort flag=] is set: + ++ [=Abort steps=]: + 1. Invoke [=Reject Job Promise=] with |job| and "{{AbortError}}" {{DOMException}}. + 1. Invoke [=Finish Job=] with |job|. ++ 1. If |job|'s [=job type=] is *register*, run [=Register=] with |job| [=in parallel=]. 1. Else if |job|'s [=job type=] is *update*, run [=Update=] with |job| [=in parallel=]. @@ -2455,6 +2542,8 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe : Output :: none + Run these steps, but [=throw abort when=] |job|'s [=job/abort flag=] is set: + 1. If the result of running potentially trustworthy origin with the [=environment settings object/origin=] of |job|'s [=job/script url=] as the argument isNot Trusted
, then: 1. Invoke [=Reject Job Promise=] with |job| and "{{SecurityError}}" {{DOMException}}. 1. Invoke Finish Job with |job| and abort these steps. @@ -2472,6 +2561,9 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Invoke Finish Job with |job| and abort these steps. 1. Else: 1. Invoke Set Registration algorithm with |job|'s [=job/scope url=] and |job|'s [=job/update via cache mode=]. + +[=Abort step=]: [=map/Remove=] [=scope to registration map=][|job|'s [=job/scope url=], [=URL serializer|serialized=]].
+ 1. Invoke Update algorithm passing |job| as the argument.error
". - 1. [=/Fetch=] |request|, and asynchronously wait to run the remaining steps as part of fetch's process response for the [=/response=] |response|. - 1. [=Extract a MIME type=] from the |response|'s [=response/header list=]. If this MIME type (ignoring parameters) is not a [=JavaScript MIME type=], then: - 1. Invoke [=Reject Job Promise=] with |job| and "{{SecurityError}}" {{DOMException}}. - 1. Asynchronously complete these steps with a [=network error=]. - 1. Let |serviceWorkerAllowed| be the result of [=extracting header list values=] given \`Service-Worker-Allowed
\` and |response|'s [=response/header list=]. - - Note: See the definition of the [=Service-Worker-Allowed=] header in Appendix B: Extended HTTP headers. - - 1. Set |httpsState| to |response|'s [=response/HTTPS state=]. - 1. Set |referrerPolicy| to the result of parse a referrer policy from aReferrer-Policy
header of |response|. - 1. If |serviceWorkerAllowed| is failure, then: - 1. Asynchronously complete these steps with a network error. - 1. Let |scopeURL| be |registration|'s [=service worker registration/scope url=]. - 1. Let |maxScopeString| be null. - 1. If |serviceWorkerAllowed| is null, then: - 1. Let |resolvedScope| be the result of [=URL parser|parsing=] "`./`" using |job|'s [=job/script url=] as the [=base URL=]. - 1. Set |maxScopeString| to "`/`", followed by the strings in |resolvedScope|'s [=url/path=] (including empty strings), separated from each other by "`/`". - - Note: The final item in |resolvedScope|'s [=url/path=] will always be an empty string, so |maxScopeString| will have a trailing "`/`". - - 1. Else: - 1. Let |maxScope| be the result of [=URL parser|parsing=] |serviceWorkerAllowed| using |job|'s [=job/script url=] as the [=base URL=]. - 1. If |maxScope|'s [=url/origin=] is |job|'s [=job/script url=]'s [=url/origin=], then: - 1. Set |maxScopeString| to "`/`", followed by the strings in |maxScope|'s [=url/path=] (including empty strings), separated from each other by "`/`". - 1. Let |scopeString| be "`/`", followed by the strings in |scopeURL|'s [=url/path=] (including empty strings), separated from each other by "`/`". - 1. If |maxScopeString| is null or |scopeString| does not start with |maxScopeString|, then: - 1. Invoke [=Reject Job Promise=] with |job| and "{{SecurityError}}" {{DOMException}}. - 1. Asynchronously complete these steps with a network error. - 1. Let |url| be |request|'s [=request/url=]. - 1. Set |updatedResourceMap|[|url|] to |response|. - 1. If |response|'s [=response/cache state=] is not "`local`", set |registration|'s [=last update check time=] to the current time. - 1. Set |hasUpdatedResources| to true if any of the following are true: - * |newestWorker| is null. - * |newestWorker|'s [=service worker/script url=] is not |url| or |newestWorker|'s [=service worker/type=] is not |job|'s [=worker type=]. - * |newestWorker|'s [=script resource map=][|url|]'s [=response/body=] is not byte-for-byte identical with |response|'s [=response/body=]. - 1. If |hasUpdatedResources| is false and |newestWorker|'s [=classic scripts imported flag=] is set, then: - - Note: The following checks to see if an imported script has been updated, since the main script has not changed. - - 1. [=map/For each=] |importUrl| → |storedResponse| of |newestWorker|'s [=script resource map=]: - 1. If |importUrl| is |url|, then continue. - 1. Let |importRequest| be a new [=/request=] whose [=request/url=] is |importUrl|, [=request/client=] is |job|'s [=job/client=], [=request/destination=] is "`script`", [=request/parser metadata=] is "`not parser-inserted`", [=request/synchronous flag=] is set, and whose [=request/use-URL-credentials flag=] is set. - 1. Set |importRequest|'s [=request/cache mode=] to "`no-cache`" if any of the following are true: - * |registration|'s [=service worker registration/update via cache mode=] is "`none`". - * |job|'s [=force bypass cache flag=] is set. - * |registration| is [=stale=]. - 1. Let |fetchedResponse| be the result of [=fetch|fetching=] |importRequest|. - 1. Set |updatedResourceMap|[|importRequest|'s [=request/url=]] to |fetchedResponse|. - 1. Set |fetchedResponse| to |fetchedResponse|'s [=unsafe response=]. - 1. If |fetchedResponse|'s [=response/cache state=] is not "`local`", set |registration|’s [=last update check time=] to the current time. - 1. If |fetchedResponse| is a [=bad import script response=], continue. - - Note: Bad responses for importScripts() are ignored for the purpose of the byte-to-byte check. Only good responses for the incumbent worker and good responses for the potential update worker are considered. See issue #1374 for some rationale. - - 1. If |fetchedResponse|'s [=response/body=] is not byte-for-byte identical with |storedResponse|'s [=unsafe response=]'s [=response/body=], set |hasUpdatedResources| to true. - - Note: The control does not break the loop in this step to continue with all the imported scripts to populate the cache. - 1. Asynchronously complete these steps with |response|. - - If the algorithm asynchronously completes with null, then: - - 1. Invoke [=Reject Job Promise=] with |job| and `TypeError`. + 1. [=/Fetch=] |request|. If |job|'s [=job/abort flag=] becomes set during this step, [=fetch/terminate=] the [=fetch=] with the abort flag set. Run the these steps as part of fetch's process response for the [=/response=] |response|, but [=throw abort when=] |job|'s [=job/abort flag=] is set. + +[=Abort step=]: Asynchronously complete these steps with an [=aborted network error=].
+ + 1. [=Extract a MIME type=] from the |response|'s [=response/header list=]. If this MIME type (ignoring parameters) is not a [=JavaScript MIME type=], then: + 1. Invoke [=Reject Job Promise=] with |job| and "{{SecurityError}}" {{DOMException}}. + 1. Asynchronously complete these steps with a [=network error=]. + 1. Let |serviceWorkerAllowed| be the result of [=extracting header list values=] given \`Service-Worker-Allowed
\` and |response|'s [=response/header list=]. + + Note: See the definition of the [=Service-Worker-Allowed=] header in Appendix B: Extended HTTP headers. + + 1. Set |httpsState| to |response|'s [=response/HTTPS state=]. + 1. Set |referrerPolicy| to the result of parse a referrer policy from aReferrer-Policy
header of |response|. + 1. If |serviceWorkerAllowed| is failure, then: + 1. Asynchronously complete these steps with a network error. + 1. Let |maxScopeString| be null. + 1. If |serviceWorkerAllowed| is null, then: + 1. Let |resolvedScope| be the result of [=URL parser|parsing=] "`./`" using |job|'s [=job/script url=] as the [=base URL=]. + 1. Set |maxScopeString| to "`/`", followed by the strings in |resolvedScope|'s [=url/path=] (including empty strings), separated from each other by "`/`". + + Note: The final item in |resolvedScope|'s [=url/path=] will always be an empty string, so |maxScopeString| will have a trailing "`/`". + + 1. Else: + 1. Let |maxScope| be the result of [=URL parser|parsing=] |serviceWorkerAllowed| using |job|'s [=job/script url=] as the [=base URL=]. + 1. If |maxScope|'s [=url/origin=] is |job|'s [=job/script url=]'s [=url/origin=], then: + 1. Set |maxScopeString| to "`/`", followed by the strings in |maxScope|'s [=url/path=] (including empty strings), separated from each other by "`/`". + 1. Let |scopeString| be "`/`", followed by the strings in |scopeURL|'s [=url/path=] (including empty strings), separated from each other by "`/`". + 1. If |maxScopeString| is null or |scopeString| does not start with |maxScopeString|, then: + 1. Invoke [=Reject Job Promise=] with |job| and "{{SecurityError}}" {{DOMException}}. + 1. Asynchronously complete these steps with a network error. + 1. Let |url| be |request|'s [=request/url=]. + 1. Set |updatedResourceMap|[|url|] to |response|. + 1. If |response|'s [=response/cache state=] is not "`local`", set |registration|'s [=last update check time=] to the current time. + 1. Set |hasUpdatedResources| to true if any of the following are true: + * |newestWorker| is null. + * |newestWorker|'s [=service worker/script url=] is not |url| or |newestWorker|'s [=service worker/type=] is not |job|'s [=worker type=]. + * |newestWorker|'s [=script resource map=][|url|]'s [=response/body=] is not byte-for-byte identical with |response|'s [=response/body=]. + 1. If |hasUpdatedResources| is false and |newestWorker|'s [=classic scripts imported flag=] is set, then: + + Note: The following checks to see if an imported script has been updated, since the main script has not changed. + + 1. [=map/For each=] |importUrl| → |storedResponse| of |newestWorker|'s [=script resource map=]: + 1. If |importUrl| is |url|, then continue. + 1. Let |importRequest| be a new [=/request=] whose [=request/url=] is |importUrl|, [=request/client=] is |job|'s [=job/client=], [=request/destination=] is "`script`", [=request/parser metadata=] is "`not parser-inserted`", [=request/synchronous flag=] is set, and whose [=request/use-URL-credentials flag=] is set. + 1. Set |importRequest|'s [=request/cache mode=] to "`no-cache`" if any of the following are true: + * |registration|'s [=service worker registration/update via cache mode=] is "`none`". + * |job|'s [=force bypass cache flag=] is set. + * |registration| is [=stale=]. + 1. Let |fetchedResponse| be the result of [=fetch|fetching=] |importRequest|. If |job|'s [=job/abort flag=] becomes set during this step, [=fetch/terminate=] the [=fetch=] with the abort flag set. + 1. Set |updatedResourceMap|[|importRequest|'s [=request/url=]] to |fetchedResponse|. + 1. Set |fetchedResponse| to |fetchedResponse|'s [=unsafe response=]. + 1. If |fetchedResponse|'s [=response/cache state=] is not "`local`", set |registration|’s [=last update check time=] to the current time. + 1. If |fetchedResponse| is a [=bad import script response=], continue. + + Note: Bad responses for importScripts() are ignored for the purpose of the byte-to-byte check. Only good responses for the incumbent worker and good responses for the potential update worker are considered. See issue #1374 for some rationale. + + 1. If |fetchedResponse|'s [=response/body=] is not byte-for-byte identical with |storedResponse|'s [=unsafe response=]'s [=response/body=], set |hasUpdatedResources| to true. + + Note: The control does not break the loop in this step to continue with all the imported scripts to populate the cache. + 1. Asynchronously complete these steps with |response|. + + If the algorithm asynchronously completes with a [=network error=] (|response|), then: + + 1. If |response| is an [=abort network error=], then invoke [=Reject Job Promise=] with |job| and "{{AbortError}}" {{DOMException}}. + 1. Else, invoke [=Reject Job Promise=] with |job| and `TypeError`. Note: This will do nothing if [=Reject Job Promise=] was previously invoked with "{{SecurityError}}" {{DOMException}}. @@ -2587,20 +2685,21 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Invoke Finish Job with |job| and abort these steps. Else, continue the rest of these steps after the algorithm's asynchronous completion, with |script| being the asynchronous completion value. + 1. If |hasUpdatedResources| is false, then: 1. Set |registration|'s [=service worker registration/update via cache mode=] to |job|'s [=job/update via cache mode=]. 1. Invoke [=Resolve Job Promise=] with |job| and |registration|. 1. Invoke [=Finish Job=] with |job| and abort these steps. 1. Let |worker| be a new [=/service worker=]. 1. Set |worker|'s [=service worker/script url=] to |job|'s [=job/script url=], |worker|'s [=script resource=] to |script|, |worker|'s [=service worker/type=] to |job|'s [=worker type=], and |worker|'s [=script resource map=] to |updatedResourceMap|. - 1. Append |url| to |worker|'s [=set of used scripts=]. + 1. Append |job|'s [=job/script url=] to |worker|'s [=set of used scripts=]. 1. Set |worker|'s script resource's HTTPS state to |httpsState|. 1. Set |worker|'s script resource's [=script resource/referrer policy=] to |referrerPolicy|. 1. Let |forceBypassCache| be true if |job|'s [=job/force bypass cache flag=] is set, and false otherwise. 1. Let |runResult| be the result of running the [=Run Service Worker=] algorithm with |worker| and |forceBypassCache|. 1. If |runResult| is *failure* or an [=abrupt completion=], then: 1. Invoke [=Reject Job Promise=] with |job| and `TypeError`. - 1. If |newestWorker| is null, then [=map/remove=] [=scope to registration map=][|registration|'s [=service worker registration/scope url=], [[=URL serializer|serialized=]]. + 1. If |newestWorker| is null, then [=map/remove=] [=scope to registration map=][|scopeURL|, [=URL serializer|serialized=]]. 1. Invoke [=Finish Job=] with |job|. 1. Else, invoke [=Install=] algorithm with |job|, |worker|, and |registration| as its arguments. @@ -2637,6 +2736,10 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe : Output :: none + Run these steps, but [=throw abort when=] |job|'s [=job/abort flag=] is set: + + TODO: I need to make this abortable too. + 1. Let |installFailed| be false. 1. Let |newestWorker| be the result of running Get Newest Worker algorithm passing |registration| as its argument. 1. Set |registration|'s [=service worker registration/update via cache mode=] to |job|'s [=job/update via cache mode=].