diff --git a/docs/index.bs b/docs/index.bs index a5b3635f..baa047ed 100644 --- a/docs/index.bs +++ b/docs/index.bs @@ -51,6 +51,7 @@ spec: webidl; text: resolve; spec:csp-next; type:dfn; text:enforced +spec:urlpattern; type:dfn; text:match
@@ -65,6 +66,7 @@ spec: ecma-262; urlPrefix: http://tc39.github.io/ecma262/ text: IsCallable; url: sec-iscallable text: Get; url: sec-get-o-p type: dfn + text: agent; text: Assert; url: sec-algorithm-conventions text: \[[Call]]; url: sec-ecmascript-function-objects-call-thisargument-argumentslist text: promise; url: sec-promise-objects @@ -132,7 +134,6 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/ text: storage key; url: storage-key text: obtain a storage key; url: obtain-a-storage-key text: storage key/equals; url: storage-key-equals -
@@ -207,6 +208,8 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/ A [=/service worker=] has an associated all fetch listeners are empty flag. It is initially unset. + A [=/service worker=] has an associated list of router rules (a [=list=] of {{RouterRule}}s). It is initially an empty [=list=]. + A [=/service worker=] is said to be running if its [=event loop=] is running.- A {{ServiceWorkerGlobalScope}} object represents the global execution context of a [=/service worker=]. A {{ServiceWorkerGlobalScope}} object has an associated service worker (a [=/service worker=]). A {{ServiceWorkerGlobalScope}} object has an associated force bypass cache for import scripts flag. It is initially unset. + A {{ServiceWorkerGlobalScope}} object represents the global execution context of a [=/service worker=]. + + A {{ServiceWorkerGlobalScope}} object has an associated service worker (a [=/service worker=]). + + A {{ServiceWorkerGlobalScope}} object has an associated force bypass cache for import scripts flag. It is initially unset. + + A {{ServiceWorkerGlobalScope}} object has an associated race response map which is an [=ordered map=] where the [=map/keys=] are [=/requests=] and the [=map/values=] are [=race response=]. + + A race response is a [=struct=] used to contain the network response when {{RouterSourceEnum/"race-network-and-fetch-handler"}} performs. It has a value, which is a [=/response=], "@@ -1077,7 +1080,15 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/ };
pending
", or null.
Note: {{ServiceWorkerGlobalScope}} object provides generic, event-driven, time-limited script execution contexts that run at an origin. Once successfully registered, a [=/service worker=] is started, kept alive and killed by their relationship to events, not [=/service worker clients=]. Any type of synchronous requests must not be initiated inside of a [=/service worker=].
@@ -1545,6 +1556,65 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
Note: [=/Service workers=] delay treating the [=installing worker=] as "`installed`" until all the [=promises=] in the {{install!!event}} event's [=extend lifetime promises=] resolve successfully. (See the relevant [Install algorithm step](#install-settle-step).) If any of the promises rejects, the installation fails. This is primarily used to ensure that a [=/service worker=] is not considered "`installed`" until all of the core caches it depends on are populated. Likewise, [=/service workers=] delay treating the [=active worker=] as "`activated`" until all the [=promises=] in the {{activate!!event}} event's [=extend lifetime promises=] settle. (See the relevant [Activate algorithm step](#activate-settle-step).) This is primarily used to ensure that any [=functional events=] are not dispatched to the [=/service worker=] until it upgrades database schemas and deletes the outdated cache entries.
+ + [Exposed=ServiceWorker] + interface InstallEvent : ExtendableEvent { + Promise<undefined> addRoutes((RouterRule or sequence<RouterRule>) rules); + }; + + dictionary RouterRule { + required RouterCondition condition; + required RouterSource source; + }; + + dictionary RouterCondition { + URLPatternCompatible urlPattern; + ByteString requestMethod; + RequestMode requestMode; + RequestDestination requestDestination; + RunningStatus runningStatus; + + sequence<RouterCondition> _or; + }; + + typedef (RouterSourceDict or RouterSourceEnum) RouterSource; + + dictionary RouterSourceDict { + DOMString cacheName; + }; + + enum RunningStatus { "running", "not-running" }; + enum RouterSourceEnum { + "cache", + "fetch-event", + "network", + "race-network-and-fetch-handler" + }; ++ +
addRoutes(|rules|)
method steps are:
+
+ 1. Let |serviceWorker| be the [=current global object=]'s associated [=ServiceWorkerGlobalScope/service worker=].
+ 1. Let |routerRules| be a copy of |serviceWorker|'s [=list of router rules=].
+ 1. If |rules| is a {{RouterRule}} dictionary, set |rules| to « |rules| ».
+ 1. For each |rule| of |rules|:
+ 1. If running the [=Verify Router Condition=] algorithm with |rule|["{{RouterRule/condition}}"] and |serviceWorker| returns false, return [=a promise rejected with=] a {{TypeError}}.
+ 1. Append |rule| to |routerRules|.
+ 1. If |routerRules| [=list/contains=] a {{RouterRule}} whose {{RouterRule/source}} is "{{RouterSourceEnum/fetch-event}}" and |serviceWorker|'s [=set of event types to handle=] does not [=set/contain=] {{ServiceWorkerGlobalScope/fetch!!event}}, return [=a promise rejected with=] a {{TypeError}}.
+ 1. Set |serviceWorker|'s [=service worker/list of router rules=] to |routerRules|.
+ 1. Return [=a promise resolved with=] undefined.
+
+ Blocked
" when executed upon |workerGlobalScope|, set |startFailed| to true and abort these steps.
+ 1. If the [=run CSP initialization for a global object=] algorithm returns "Blocked
" when executed upon |workerGlobalScope|, set |setupFailed| to true and abort these steps.
+ 1. Set |globalObject| to |workerGlobalScope|.
+ 1. Wait for |globalObject| is not null, or for |setupFailed| to be true.
+ 1. If |setupFailed| is true, then return null.
+ 1. Return |globalObject|.
+ serviceworker
".
1. If |request|'s [=request/destination=] is either "embed
" or "object
", then:
@@ -3078,28 +3176,6 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
1. Set |registration| to the result of running Match Service Worker Registration given |storage key| and |request|'s [=request/url=].
1. If |registration| is null or |registration|'s active worker is null, return null.
1. If |request|'s [=request/destination=] is not {{RequestDestination/"report"}}, set |reservedClient|'s active service worker to |registration|'s active worker.
- 1. If |request| is a [=navigation request=], |registration|'s [=navigation preload enabled flag=] is set, |request|'s [=request/method=] is \`GET
\`, |registration|'s [=active worker=]'s [=set of event types to handle=] [=set/contains=] fetch
, and |registration|'s [=active worker=]'s [=all fetch listeners are empty flag=] is not set then:
-
- Note: If the above is true except |registration|'s [=active worker=]'s set of event types to handle **does not** contain fetch
, then the user agent may wish to show a console warning, as the developer's intent isn't clear.
-
- 1. Let |preloadRequest| be the result of [=request/cloning=] the request |request|.
- 1. Let |preloadRequestHeaders| be |preloadRequest|'s [=request/header list=].
- 1. Let |preloadResponseObject| be a new {{Response}} object associated with a new {{Headers}} object whose [=guard=] is "`immutable`".
- 1. [=header list/Append=] to |preloadRequestHeaders| a new [=header=] whose [=header/name=] is \`Service-Worker-Navigation-Preload
\` and [=header/value=] is |registration|'s [=navigation preload header value=].
- 1. Set |preloadRequest|'s [=service-workers mode=] to "`none`".
- 1. Let |preloadFetchController| be null.
- 1. Run the following substeps [=in parallel=], but [=abort when=] |controller|'s [=fetch controller/state=] is "terminated
" or "aborted
":
- 1. Set |preloadFetchController| to the result of [=Fetch|fetching=] |preloadRequest|.
-
- To [=fetch/processResponse=] for |navigationPreloadResponse|, run these substeps:
-
- 1. If |navigationPreloadResponse|'s [=response/type=] is "`error`", reject |preloadResponse| with a `TypeError` and terminate these substeps.
- 1. Associate |preloadResponseObject| with |navigationPreloadResponse|.
- 1. Resolve |preloadResponse| with |preloadResponseObject|.
- 1. [=If aborted=], then:
- 1. Let |deserializedError| be the result of [=deserialize a serialized abort reason=] given null and |workerRealm|.
- 1. [=fetch controller/Abort=] |preloadFetchController| with |deserializedError|.
- 1. Else, resolve |preloadResponse| with undefined.
Note: From this point, the [=/service worker client=] starts to use its active service worker's containing service worker registration.
@@ -3107,6 +3183,101 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
1. If |client|'s active service worker is non-null, set |registration| to |client|'s active service worker's containing service worker registration.
1. Else, return null.
1. Let |activeWorker| be |registration|'s active worker.
+ 1. Let |shouldSoftUpdate| be true if any of the following are true, and false otherwise:
+ * |request| is a [=non-subresource request=].
+ * |request| is a [=subresource request=] and |registration| is [=stale=].
+ 1. If |activeWorker|'s [=service worker/list of router rules=] [=list/is not empty=]:
+ 1. Let |source| be the result of running the [=Get Router Source=] algorithm with |registration|'s active worker and |request|.
+ 1. If |source| is {{RouterSourceEnum/"network"}}:
+ 1. If |shouldSoftUpdate| is true, then [=in parallel=] run the [=Soft Update=] algorithm with |registration|.
+ 1. Return null.
+ 1. Else if |source| is {{RouterSourceEnum/"cache"}}, or |source|["{{RouterSourceDict/cacheName}}"] [=map/exists=], then:
+ 1. If |shouldSoftUpdate| is true, then [=in parallel=] run the [=Soft Update=] algorithm with |registration|.
+ 1. [=map/For each=] |cacheName| → |cache| of the |registration|'s [=service worker registration/storage key=]'s [=name to cache map=].
+ 1. If |source|["{{RouterSourceDict/cacheName}}"] [=map/exists=] and |source|["{{RouterSourceDict/cacheName}}"] [=string/is=] not |cacheName|, [=continue=].
+ 1. Let |requestResponses| be the result of running [=Query Cache=] with |request|, a new {{CacheQueryOptions}}, and |cache|.
+ 1. If |requestResponses| is an empty [=list=], return null.
+ 1. Else:
+ 1. Let |requestResponse| be the first element of |requestResponses|.
+ 1. Let |response| be |requestResponse|'s response.
+ 1. Let |globalObject| be |activeWorker|'s [=service worker/global object=].
+ 1. If |globalObject| is null:
+ 1. Set |globalObject| to the result of running [=Setup ServiceWorkerGlobalScope=] with |activeWorker|.
+ 1. If |globalObject| is null, return null.
+
+ Note: This only creates a ServiceWorkerGlobalScope because CORS checks require that. It is not expected that implementations will actually create a ServiceWorkerGlobalScope here.
+
+ 1. If |response|'s [=response/type=] is "`opaque`", and [=cross-origin resource policy check=] with |globalObject|'s [=environment settings object/origin=], |globalObject|, "", and |response|'s [=filtered response/internal response=] returns blocked, then return null.
+ 1. Return |response|.
+ 1. Return null.
+ 1. Else if |source| is {{RouterSourceEnum/"race-network-and-fetch-handler"}}, and |request|'s [=request/method=] is \`GET
\` then:
+ 1. If |shouldSoftUpdate| is true, then [=in parallel=] run the [=Soft Update=] algorithm with |registration|.
+ 1. Let |queue| be an empty [=queue=] of [=/response=].
+ 1. Let |raceFetchController| be null.
+ 1. Let |raceResponse| be a [=race response=] whose [=race response/value=] is "pending
".
+ 1. Run the following substeps [=in parallel=], but [=abort when=] |controller|'s [=fetch controller/state=] is "terminated
" or "aborted
":
+ 1. Set |raceFetchController| to the result of calling [=fetch=] given |request|, with [=fetch/processResponse=] set to the following steps given a [=/response=] |raceNetworkRequestResponse|:
+ 1. If |raceNetworkRequestResponse|'s [=response/status=] is [=ok status=], then:
+ 1. Set |raceResponse|'s [=race response/value=] to |raceNetworkRequestResponse|.
+ 1. [=queue/Enqueue=] |raceNetworkRequestResponse| to |queue|.
+ 1. Otherwise, set |raceResponse|'s [=race response/value=] to a [=network error=].
+ 1. [=If aborted=] and |raceFetchController| is not null, then:
+ 1. [=fetch controller/Abort=] |raceFetchController|.
+ 1. Set |raceResponse| to a [=race response=] whose [=race response/value=] is null.
+ 1. Resolve |preloadResponse| with undefined.
+ 1. Run the following substeps [=in parallel=]:
+ 1. Let |fetchHandlerResponse| be the result of [=Create Fetch Event and Dispatch=] with |request|, |registration|, |useHighResPerformanceTimers|, |timingInfo|, |workerRealm|, |reservedClient|, |preloadResponse|, and |raceResponse|.
+ 1. If |fetchHandlerResponse| is not null and not a [=network error=], and |raceFetchController| is not null, [=fetch controller/abort=] |raceFetchController|.
+ 1. [=queue/Enqueue=] |fetchHandlerResponse| to |queue|.
+ 1. Wait until |queue| is not empty.
+ 1. Return the result of [=dequeue=] |queue|.
+ 1. Assert: |source| is "{{RouterSourceEnum/fetch-event}}"
+ 1. If |request| is a [=navigation request=], |registration|'s [=navigation preload enabled flag=] is set, |request|'s [=request/method=] is \`GET
\`, |registration|'s [=active worker=]'s [=set of event types to handle=] [=set/contains=] fetch
, and |registration|'s [=active worker=]'s [=all fetch listeners are empty flag=] is not set then:
+
+ Note: If the above is true except |registration|'s [=active worker=]'s set of event types to handle **does not** contain fetch
, then the user agent may wish to show a console warning, as the developer's intent isn't clear.
+
+ 1. Let |preloadRequest| be the result of [=request/cloning=] the request |request|.
+ 1. Let |preloadRequestHeaders| be |preloadRequest|'s [=request/header list=].
+ 1. Let |preloadResponseObject| be a new {{Response}} object associated with a new {{Headers}} object whose [=guard=] is "`immutable`".
+ 1. [=header list/Append=] to |preloadRequestHeaders| a new [=header=] whose [=header/name=] is \`Service-Worker-Navigation-Preload
\` and [=header/value=] is |registration|'s [=navigation preload header value=].
+ 1. Set |preloadRequest|'s [=service-workers mode=] to "`none`".
+ 1. Let |preloadFetchController| be null.
+ 1. Run the following substeps [=in parallel=], but [=abort when=] |controller|'s [=fetch controller/state=] is "terminated
" or "aborted
":
+ 1. Set |preloadFetchController| to the result of [=Fetch|fetching=] |preloadRequest|.
+
+ To [=fetch/processResponse=] for |navigationPreloadResponse|, run these substeps:
+
+ 1. If |navigationPreloadResponse|'s [=response/type=] is "`error`", reject |preloadResponse| with a `TypeError` and terminate these substeps.
+ 1. Associate |preloadResponseObject| with |navigationPreloadResponse|.
+ 1. Resolve |preloadResponse| with |preloadResponseObject|.
+ 1. [=If aborted=], then:
+ 1. Let |deserializedError| be the result of [=deserialize a serialized abort reason=] given null and |workerRealm|.
+ 1. [=fetch controller/Abort=] |preloadFetchController| with |deserializedError|.
+ 1. Else, resolve |preloadResponse| with undefined.
+ 1. Return the result of [=Create Fetch Event and Dispatch=] with |request|, |registration|, |useHighResPerformanceTimers|, |timingInfo|, |workerRealm|, |reservedClient|, |preloadResponse|, and null.
+ pending
".
+ 1. If |raceResponse|'s [=race response/value=] is a [=/response=], return |raceResponse|'s [=race response/value=].
1. Return null.
1. If |handleFetchFailed| is true, then:
1. If |eventHandled| is not null, then [=reject=] |eventHandled| with a "{{NetworkError}}" {{DOMException}} in |workerRealm|.
@@ -3175,6 +3351,109 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
1. Return |response|.
pending
"
+ 1. If |entry|'s [=race response/value=] is [=/response=], return |entry|'s [=race response/value=].
+ 1. Return null.
+