Re: [w3c/ServiceWorker] Remove incumbent/fetching record from Cache behavior (#1190)

jakearchibald requested changes on this pull request.

This is coming along nicely!

>  
-    A <a>fetching record</a> has an associated <dfn id="dfn-incumbent-record">incumbent record</dfn> (a <a>fetching record</a>). It is initially set to null.
+    When a [=request response list=] is referenced from within an algorithm, an attribute getter, an attribute setter, or a method, it designates the instance that the [=context object=] represents, unless specified otherwise.

Feels like this would be better as a new definition. As in:

The **relevant request response list** is the instance that the context object represents.

Then it can be linked to when used.

>  
-    Each [=/origin=] has an associated <a>name to cache map</a>.
+    When a [=name to cache map=] is referenced from within an algorithm, an attribute getter, an attribute setter, or a method, it designates the instance of the [=context object=]'s associated [=CacheStorage/global object=]'s [=environment settings object=]'s [=environment settings object/origin=], unless specified otherwise.

As above.

>              1. Else if |request| is a string, then:
-                1. Set |r| to the associated [=Request/request=] of the result of invoking the initial value of {{Request}} as constructor with |request| as its argument. If this <a>throws</a> an exception, return a <a>promise</a> rejected with that exception.
-        1. Let |promise| be a new <a>promise</a>.
-        1. Run these substeps <a>in parallel</a>:
-            1. Let |responseArray| be an empty array.
+                1. Set |r| to the associated [=Request/request=] of the result of invoking the initial value of {{Request}} as constructor with |request| as its argument. If this [=throws=] an exception, return [=a promise rejected with=] that exception.
+        1. Let |realm| be the [=current Realm Record=].

Should this be https://html.spec.whatwg.org/multipage/webappapis.html#concept-relevant-realm for the context object?

> @@ -1861,15 +1862,15 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe
   <section>
     <h3 id="cache-constructs">Constructs</h3>
 
-    A <dfn id="dfn-fetching-record">fetching record</dfn> is a <a>Record</a> {\[[key]], \[[value]]} where \[[key]] is a {{Request}} and \[[value]] is a {{Response}}.
+    A <dfn id="dfn-request-response-list">request response list</dfn> is a [=list=] of [=pairs=] consisting of a request (a [=/request=]) and a response (a [=/response=]).

Should request and response here be defined as for="request response list"? Then they can be linked to when used.

>              1. Else:
-                1. Let |entries| be the result of running <a>Query Cache</a> algorithm passing a {{Request}} object associated with |r| and |options| as the arguments.
-                1. For each |entry| of |entries|:
-                    1. Let |response| be null.
-                    1. If the <a>incumbent record</a> |incumbentRecord| of the corresponding <a>fetching record</a> in <a>request to response map</a> is not null, set |response| to a copy of |incumbentRecord|.\[[value]].
-                    1. Else, set |response| to a copy of |entry|[1].
-                    1. Add |response| to |responseArray|.
+                1. Let |requestResponses| be the result of running [=Query Cache=] with |r| and |options|.
+                1. [=list/For each=] |requestResponse| of |requestResponses|:
+                    1. Add a copy of |requestResponse|'s response to |responses|.
+            1. [=Queue a task=], on |promise|'s [=relevant settings object=]'s [=responsible event loop=] using the [=DOM manipulation task source=], to perform the following steps:
+                1. Let |responseArray| be an empty JavaScript array, in |realm|.

I think we could create a sequence, then use https://heycam.github.io/webidl/#dfn-create-frozen-array to turn it into an array.

>              1. Else:
-                1. Let |entries| be the result of running <a>Query Cache</a> algorithm passing a {{Request}} object associated with |r| and |options| as the arguments.
-                1. For each |entry| of |entries|:
-                    1. Let |response| be null.
-                    1. If the <a>incumbent record</a> |incumbentRecord| of the corresponding <a>fetching record</a> in <a>request to response map</a> is not null, set |response| to a copy of |incumbentRecord|.\[[value]].
-                    1. Else, set |response| to a copy of |entry|[1].
-                    1. Add |response| to |responseArray|.
+                1. Let |requestResponses| be the result of running [=Query Cache=] with |r| and |options|.
+                1. [=list/For each=] |requestResponse| of |requestResponses|:
+                    1. Add a copy of |requestResponse|'s response to |responses|.
+            1. [=Queue a task=], on |promise|'s [=relevant settings object=]'s [=responsible event loop=] using the [=DOM manipulation task source=], to perform the following steps:
+                1. Let |responseArray| be an empty JavaScript array, in |realm|.

We need to update the IDL to return a FrozenArray rather than a sequence too.

>          1. For each |request| whose type is {{Request}} in |requests|:
-            1. Let |r| be |request|'s [=Request/request=].
+            1. Set |r| to |request|'s [=Request/request=].

"Let" is [block-scoped](https://infra.spec.whatwg.org/#variables), so I think this should have remained "let".

>              1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "<code>http</code>" and "<code>https</code>", or |r|'s [=request/method=] is not \`<code>GET</code>\`, return a <a>promise</a> rejected with a <code>TypeError</code>.
         1. For each |request| in |requests|:
-            1. Let |r| be the associated [=Request/request=] of the result of invoking the initial value of {{Request}} as constructor with |request| as its argument. If this <a>throws</a> an exception, return a <a>promise</a> rejected with that exception.
+            1. Set |r| to the associated [=Request/request=] of the result of invoking the initial value of {{Request}} as constructor with |request| as its argument. If this <a>throws</a> an exception, return [=a promise rejected with=] that exception.

As above, should have remained "let"

>      </section>
 
     <section algorithm="cache-addAll">
       <h4 id="cache-addAll">{{Cache/addAll(requests)}}</h4>
 
       <dfn method for="Cache"><code>addAll(|requests|)</code></dfn> method *must* run these steps:
 
-        1. Let |responsePromiseArray| be an empty array.
-        1. Let |requestArray| be an empty array.
+        1. Let |r| be null.

I don't think we need this, see below.

>      </section>
 
     <section algorithm="cache-addAll">
       <h4 id="cache-addAll">{{Cache/addAll(requests)}}</h4>
 
       <dfn method for="Cache"><code>addAll(|requests|)</code></dfn> method *must* run these steps:
 
-        1. Let |responsePromiseArray| be an empty array.
-        1. Let |requestArray| be an empty array.
+        1. Let |r| be null.
+        1. Let |responsePromises| be an empty [=list=].
+        1. Let |requestList| be an empty [=list=].
         1. For each |request| whose type is {{Request}} in |requests|:

Is this so we reject without consuming the body of the request? Might be worth adding a note about that.

>              1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "<code>http</code>" and "<code>https</code>", then:
-                1. [=fetch/Terminate=] all the ongoing <a>fetches</a> initiated by |requests| with reason *fatal*.
+                1. [=fetch/Terminate=] all the ongoing [=fetches=] initiated by |requests| with reason *fatal*.

We've since dropped the "reason". There's an aborted flag which should probably be set in this case. https://fetch.spec.whatwg.org/#concept-fetch-terminate

>              1. If |r|'s [=request/url=]'s [=url/scheme=] is not one of "<code>http</code>" and "<code>https</code>", then:
-                1. [=fetch/Terminate=] all the ongoing <a>fetches</a> initiated by |requests| with reason *fatal*.
+                1. [=fetch/Terminate=] all the ongoing [=fetches=] initiated by |requests| with reason *fatal*.
                 1. Break the loop.

Should we just return at this point?

> -                            1. Else:
-                                1. Delete |fetchingRecord| from <a>request to response map</a>.
-                            1. Reject |responseBodyPromise| with a <code>TypeError</code>.
-                        1. Else:
-                            1. Set the <a>incumbent record</a> of the corresponding <a>fetching record</a> |fetchingRecord| in <a>request to response map</a> to the copy of |fetchingRecord|.
-                            1. Let |invalidRecords| be the result of running <a>Query Cache</a> algorithm passing |fetchingRecord|.\[[key]] as the argument.
-                            1. For each |invalidRecord| in |invalidRecords|:
-                                1. If |invalidRecord| is not |fetchingRecord|, delete it from <a>request to response map</a>.
-                            1. Resolve |responseBodyPromise| with |response|.
-                    1. Add |responseBodyPromise| to |responseBodyPromiseArray|.
-                1. Let |q| be <a>waiting for all</a> of |responseBodyPromiseArray|.
-                1. Return the result of <a>transforming</a> |q|  with a fulfillment handler that returns undefined.
+                        1. [=list/For each=] |fieldValue| of |fieldValues|:
+                            1. If |fieldValue| matches "`*`", then:
+                                1. Set |matchAsterisk| to true.
+                                1. [=Break=].

Could we just reject and abort steps here? Means we don't need *matchAsterisk*.

> -                            1. Set the <a>incumbent record</a> of the corresponding <a>fetching record</a> |fetchingRecord| in <a>request to response map</a> to the copy of |fetchingRecord|.
-                            1. Let |invalidRecords| be the result of running <a>Query Cache</a> algorithm passing |fetchingRecord|.\[[key]] as the argument.
-                            1. For each |invalidRecord| in |invalidRecords|:
-                                1. If |invalidRecord| is not |fetchingRecord|, delete it from <a>request to response map</a>.
-                            1. Resolve |responseBodyPromise| with |response|.
-                    1. Add |responseBodyPromise| to |responseBodyPromiseArray|.
-                1. Let |q| be <a>waiting for all</a> of |responseBodyPromiseArray|.
-                1. Return the result of <a>transforming</a> |q|  with a fulfillment handler that returns undefined.
+                        1. [=list/For each=] |fieldValue| of |fieldValues|:
+                            1. If |fieldValue| matches "`*`", then:
+                                1. Set |matchAsterisk| to true.
+                                1. [=Break=].
+                        1. If |matchAsterisk| is true, reject |responsePromise| with a `TypeError`.
+
+                * To [=process response end-of-body=] for |response|, run these substeps:
+                    1. If |response|'s [=response/aborted flag=] is set, reject |responsePromise| with a `TypeError` and abort these steps.

Feels like this should be an AbortError.

> -                    1. Add |responseBodyPromise| to |responseBodyPromiseArray|.
-                1. Let |q| be <a>waiting for all</a> of |responseBodyPromiseArray|.
-                1. Return the result of <a>transforming</a> |q|  with a fulfillment handler that returns undefined.
+                        1. [=list/For each=] |fieldValue| of |fieldValues|:
+                            1. If |fieldValue| matches "`*`", then:
+                                1. Set |matchAsterisk| to true.
+                                1. [=Break=].
+                        1. If |matchAsterisk| is true, reject |responsePromise| with a `TypeError`.
+
+                * To [=process response end-of-body=] for |response|, run these substeps:
+                    1. If |response|'s [=response/aborted flag=] is set, reject |responsePromise| with a `TypeError` and abort these steps.
+                    1. Resolve |responsePromise| with |response|.
+
+                    Note: The cache commit is allowed when the response's body is fully received.
+
+                * To [=process response done=] for |response|, do nothing.

I don't think we need this line.

> @@ -2134,23 +2104,29 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe
 
       <dfn method for="Cache"><code>keys(|request|, |options|)</code></dfn> method *must* run these steps:
 
-        1. Let |promise| be a new <a>promise</a>.
-        1. Run these substeps <a>in parallel</a>:
-            1. Let |resultArray| be an empty array.
+        1. Let |r| be null.
+        1. If the optional argument |request| is not omitted, then:
+            1. If |request| is a {{Request}} object, then:
+                1. Set |r| to |request|'s [=Request/request=].
+                1. If |r|'s [=request/method=] is not \`<code>GET</code>\` and |options|.ignoreMethod is false, return [=a promise resolved with=] an empty array.
+            1. Else if |request| is a string, then:
+                1. Set |r| to the associated [=Request/request=] of the result of invoking the initial value of {{Request}} as constructor with |request| as its argument. If this [=throws=] an exception, return [=a promise rejected with=] that exception.
+        1. Let |realm| be the [=current Realm Record=].

As above, we might not need this if we use frozenarray.

>          1. Else:
-            1. Let |p| be a <a>promise</a> resolved with undefined.
-            1. [=map/For each=] <var ignore>cacheName</var> → |cache| of <a>name to cache map</a>:
-                1. Set |p| to the result of <a>transforming</a> itself with a fulfillment handler that, when called with argument |v|, performs the following substeps <a>in parallel</a>:
-                    1. If |v| is not undefined, return |v|.
-                    1. Return the result of running the algorithm specified in {{Cache/match(request, options)}} method of {{Cache}} interface with |request| and |options| as the arguments (providing |cache|] as thisArgument to the <a>\[[Call]]</a> internal method of {{Cache/match(request, options)}}.)
-            1. Return |p|.
+            1. Let |promise| be [=a promise resolved with=] undefined.
+            1. [=map/For each=] <var ignore>cacheName</var> → |cache| of the [=name to cache map=]:
+                1. Set |promise| to the result of [=transforming=] itself with a fulfillment handler that, when called with argument |response|, performs the following substeps [=in parallel=]:
+                    1. If |response| is not undefined, return |response|.
+                    1. Return the result of running the algorithm specified in {{Cache/match(request, options)}} method of {{Cache}} interface with |request| and |options| as the arguments (providing |cache| as thisArgument to the `\[[Call]]` internal method of {{Cache/match(request, options)}}.)

I don't think you need the slash in \[[Call]]

>                  1. If |cacheName| matches |key|, then:
-                    1. Return true.
-            1. Return false.
+                    1. Resolve |promise| with true.
+                    1. Abort these steps.

Could probably roll this into the line above. "Resolve promise with true and abort these steps".

>              1. If |cacheExists| is true, then:
-                1. Delete a <a>Record</a> {\[[key]], \[[value]]} <var ignore>entry</var> from its <a>name to cache map</a> where |cacheName| matches entry.\[[key]].
+                1. [=map/Remove=] the [=name to cache map=][|cacheName|].
                 1. Return true.

I'm not sure we can "return" from in parallel steps.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/ServiceWorker/pull/1190#pullrequestreview-66099101

Received on Friday, 29 September 2017 10:31:08 UTC