diff --git a/docs/index.bs b/docs/index.bs
index 472b2cd4..d9309be6 100644
--- a/docs/index.bs
+++ b/docs/index.bs
@@ -1093,7 +1093,7 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
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=], "pending
", or null.
+ A race response is a [=struct=] used to contain the network response when {{RouterSourceEnum/"race-network-and-fetch-handler"}} or {{RouterSourceEnum/"race-network-and-cache"}} performs. It has a value, which is a [=/response=], "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=].
@@ -1591,6 +1591,7 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
dictionary RouterSourceDict {
DOMString cacheName;
+ DOMString raceNetworkAndCacheCacheName;
};
enum RunningStatus { "running", "not-running" };
@@ -1598,7 +1599,8 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
"cache",
"fetch-event",
"network",
- "race-network-and-fetch-handler"
+ "race-network-and-fetch-handler",
+ "race-network-and-cache"
};
@@ -3220,43 +3222,73 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
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. Let |response| be the result of running [=Query Static Router Cache for Response=] with |request|, |source|, "{{RouterSourceDict/cacheName}}", and |registration|.
+ 1. If |response| is null, return null.
+ 1. Return |response|.
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. Let |networkFetchCompleted| be false.
+ 1. Let |fetchHandlerCompleted| be false.
1. Run the following substeps [=in parallel=], but [=abort when=] |fetchController|'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. Set |networkFetchCompleted| to true.
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. Set |networkFetchCompleted| to true.
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. Set |fetchHandlerCompleted| to true.
+ 1. Wait until any of the following are true:
+ 1. |queue| is not empty.
+ 1. Both |networkFetchCompleted| and |fetchHandlerCompleted| are true.
+ 1. If |queue| is empty, then:
+ 1. If |raceResponse|'s [=race response/value=] is a [=/response=], return |raceResponse|'s [=race response/value=].
+ 1. If |raceResponse|'s [=race response/value=] is a [=network error=], return a [=network error=].
+ 1. Return null.
+ 1. Return the result of [=dequeue=] |queue|.
+ 1. If |source| is {{RouterSourceEnum/"race-network-and-cache"}}, or |source|["{{RouterSourceDict/raceNetworkAndCacheCacheName}}"] [=map/exists=], 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. Let |networkFetchCompleted| be false.
+ 1. Let |cacheLookupCompleted| be false.
+ 1. Run the following substeps [=in parallel=], but [=abort when=] |fetchController|'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. Set |networkFetchCompleted| to true.
+ 1. [=If aborted=] and |raceFetchController| is not null, then:
+ 1. If |raceFetchController|'s [=fetch controller/state=] is not "terminated
", [=fetch controller/abort=] |raceFetchController|.
+ 1. Set |raceResponse| to a [=race response=] whose [=race response/value=] is null.
+ 1. Set |networkFetchCompleted| to true.
+ 1. Resolve |preloadResponse| with undefined.
+ 1. Run the following substeps [=in parallel=]:
+ 1. Let |response| be the result of running [=Query Static Router Cache for Response=] with |request|, |source|, "{{RouterSourceDict/raceNetworkAndCacheCacheName}}", and |registration|.
+ 1. If |response| is not null:
+ 1. [=queue/Enqueue=] |response| to |queue|.
+ 1. If |raceFetchController| is not null, [=fetch controller/abort=] |raceFetchController|.
+ 1. Set |cacheLookupCompleted| to true.
+ 1. Wait until any of the following are true:
+ 1. |queue| is not empty.
+ 1. Both |networkFetchCompleted| and |cacheLookupCompleted| are true.
+ 1. If |queue| is empty, then:
+ 1. If |raceResponse|'s [=race response/value=] is a [=/response=], return |raceResponse|'s [=race response/value=].
+ 1. If |raceResponse|'s [=race response/value=] is a [=network error=], return a [=network error=].
+ 1. Return null.
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:
@@ -4097,6 +4129,36 @@ spec: storage; urlPrefix: https://storage.spec.whatwg.org/
1. If |entry|'s [=race response/value=] is [=/response=], return |entry|'s [=race response/value=].
1. Return null.
+
+
+ Query Static Router Cache for Response
+
+ : Input
+ :: |request|, a [=/request=]
+ :: |source|, a [=map=]
+ :: |cacheNameKey|, a [=string=]
+ :: |registration|, a [=/service worker registration=]
+ : Output
+ :: a [=/response=] or null
+
+ 1. [=map/For each=] |cacheName| → |cache| of the |registration|'s [=service worker registration/storage key=]'s [=name to cache map=].
+ 1. If |source|[|cacheNameKey|] [=map/exists=] and |source|[|cacheNameKey|] [=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.
+