From 39d3132c30e9f7003043cf3907b1eb9634caa647 Mon Sep 17 00:00:00 2001 From: Jungkee Song Date: Thu, 5 Jan 2017 20:09:31 +0900 Subject: [PATCH] Improve handling of extend lifetime promises (1) This changes the approach to unsetting the extendable events' extensions allowed flag. This change introduced a reference count based approach instead of the promise-copying-and-checking approach. (2) This also extends the opportunities of the lifetime extension by allowing calling waitUntil() within microtasks queued by the given promise's Promise.prototype.then callback. Related issues: - https://github.com/w3c/ServiceWorker/issues/931 (1) - https://github.com/w3c/ServiceWorker/issues/935 (2) - https://github.com/w3c/ServiceWorker/issues/1039 (2) --- docs/index.bs | 49 +++++++++++--- docs/index.html | 164 ++++++++++++++++++++++++++++++++------------- docs/v1/index.bs | 44 +++++++++--- docs/v1/index.html | 157 ++++++++++++++++++++++++++++++------------- 4 files changed, 302 insertions(+), 112 deletions(-) diff --git a/docs/index.bs b/docs/index.bs index 15ee725b..64c156d1 100644 --- a/docs/index.bs +++ b/docs/index.bs @@ -1289,7 +1289,9 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe An {{ExtendableEvent}} object has an associated extend lifetime promises (an array of promises). It is initially an empty array. - An {{ExtendableEvent}} object has an associated extensions allowed flag. It is initially set. + An {{ExtendableEvent}} object has an associated extensions allowed flag. It is initially set. + + An {{ExtendableEvent}} object has an associated pending promises count (the number of pending promises in the [=ExtendableEvent/extend lifetime promises=]). It is initially set to zero. [=/Service workers=] have two lifecycle events, {{install!!event}} and {{activate!!event}}. [=/Service workers=] use the {{ExtendableEvent}} interface for {{activate!!event}} event and {{install!!event}} event. @@ -1308,6 +1310,12 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Throw an "{{InvalidStateError}}" exception. 1. Abort these steps. 1. Add |f| to the [=ExtendableEvent/extend lifetime promises=]. + 1. Increase the [=ExtendableEvent/pending promises count=] by one. + + Note: The [=ExtendableEvent/pending promises count=] is increased even if the given promise has already been settled. The corresponding count decrease is done within [=Handle Extend Lifetime Promise=] algorithm. + + 1. Run [=Handle Extend Lifetime Promise=] with the [=context object=] and |f|. + 1. Return. [=/Service workers=] and extensions that define event handlers *may* define their own behaviors, allowing the [=ExtendableEvent/extend lifetime promises=] to suggest operation length, and the rejected state of any of the promise in [=ExtendableEvent/extend lifetime promises=] to suggest operation failure. @@ -1450,6 +1458,11 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Throw an "{{InvalidStateError}}" exception. 1. Abort these steps. 1. Add |r| to the extend lifetime promises. + 1. Increase the [=ExtendableEvent/pending promises count=] by one. + + Note: The [=ExtendableEvent/pending promises count=] is increased even if the given promise has already been settled. The corresponding count decrease is done within [=Handle Extend Lifetime Promise=] algorithm. + + 1. Run [=Handle Extend Lifetime Promise=] with the [=context object=] and |r|. Note: {{FetchEvent/respondWith(r)|event.respondWith(r)}} extends the lifetime of the event by default as if {{ExtendableEvent/waitUntil()|event.waitUntil(r)}} is called. @@ -1561,6 +1574,11 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Throw an "{{InvalidStateError}}" exception. 1. Abort these steps. 1. Add |r| to the extend lifetime promises. + 1. Increase the [=ExtendableEvent/pending promises count=] by one. + + Note: The [=ExtendableEvent/pending promises count=] is increased even if the given promise has already been settled. The corresponding count decrease is done within [=Handle Extend Lifetime Promise=] algorithm. + + 1. Run [=Handle Extend Lifetime Promise=] with the [=context object=] and |r|. 1. Set the stop propagation flag and stop immediate propagation flag. 1. Set the [=ForeignFetchEvent/respond-with entered flag=]. 1. Set the [=ForeignFetchEvent/wait to respond flag=]. @@ -2837,17 +2855,32 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe : Output :: None - 1. If |event|'s extend lifetime promises is empty, unset |event|'s extensions allowed flag and abort these steps. - 1. Let |extendLifetimePromises| be an empty array. - 1. Run the following substeps in parallel: - 1. *SetupPromiseArray*: Set |extendLifetimePromises| to a copy of |event|'s extend lifetime promises. - 1. Wait until all the promises in |extendLifetimePromises| settle. - 1. If the length of |extendLifetimePromises| does not equal the length of |event|'s extend lifetime promises, jump to the step labeled *SetupPromiseArray*. - 1. Unset |event|'s extensions allowed flag. + 1. If |event|'s [=ExtendableEvent/pending promises count=] is zero, unset |event|'s [=ExtendableEvent/extensions allowed flag=] and abort these steps. + Note: If no lifetime extension promise has been added up to this point (i.e., at the end of the task that called the event handlers), the [=ExtendableEvent/extensions allowed flag=] is immediately unset. Calling {{ExtendableEvent/waitUntil()}} in subsequent asynchronous tasks will throw. + The user agent *should not* terminate the [=/service worker=] associated with |event|'s relevant settings object's [=environment settings object/global object=] until |event|'s extensions allowed flag is unset. However, the user agent *may* impose a time limit to this lifetime extension. +
+

Handle Extend Lifetime Promise

+ + : Input + :: |event|, an {{ExtendableEvent}} object + :: |promise|, a [=promise=] + : Output + :: None + + 1. Wait until |promise| is settled [=in parallel=]. + 2. [=Queue a microtask=] to run the following substeps: + 1. Decrease |event|'s [=ExtendableEvent/pending promises count=] by one. + 1. For each |reaction| in |promise|.\[[PromiseFulfillReactions]]: + 1. Append |reaction|.\[[Capability]].\[[Promise]] to |event|'s [=ExtendableEvent/extend lifetime promises=]. + 1. Increase |event|'s [=ExtendableEvent/pending promises count=] by one. + 1. Invoke [=Handle Extend Lifetime Promise=] with |event| and |reaction|.\[[Capability]].\[[Promise]]. + 1. If |event|'s [=ExtendableEvent/pending promises count=] is zero, unset |event|'s [=ExtendableEvent/extensions allowed flag=]. +
+

Handle Fetch

diff --git a/docs/index.html b/docs/index.html index 8dde2e70..0304e494 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1690,6 +1690,7 @@

Table of Contents

  • Run Service Worker
  • Terminate Service Worker
  • Extend Service Worker Lifetime +
  • Handle Extend Lifetime Promise
  • Handle Fetch
  • Handle Foreign Fetch
  • Handle Functional Event @@ -3037,10 +3038,11 @@

    An ExtendableEvent object has an associated extend lifetime promises (an array of promises). It is initially an empty array.

    -

    An ExtendableEvent object has an associated extensions allowed flag. It is initially set.

    -

    Service workers have two lifecycle events, install and activate. Service workers use the ExtendableEvent interface for activate event and install event.

    -

    Service worker extensions that define event handlers may also use or extend the ExtendableEvent interface.

    -

    Note: To extend the lifetime of a service worker, algorithms that dispatch events using the ExtendableEvent interface run Extend Service Worker Lifetime algorithm after dispatching the event. See Handle Fetch, Handle Foreign Fetch, and Handle Functional Event.

    +

    An ExtendableEvent object has an associated extensions allowed flag. It is initially set.

    +

    An ExtendableEvent object has an associated pending promises count (the number of pending promises in the extend lifetime promises). It is initially set to zero.

    +

    Service workers have two lifecycle events, install and activate. Service workers use the ExtendableEvent interface for activate event and install event.

    +

    Service worker extensions that define event handlers may also use or extend the ExtendableEvent interface.

    +

    Note: To extend the lifetime of a service worker, algorithms that dispatch events using the ExtendableEvent interface run Extend Service Worker Lifetime algorithm after dispatching the event. See Handle Fetch, Handle Foreign Fetch, and Handle Functional Event.

    4.4.1. event.waitUntil(f)

    waitUntil() method extends the lifetime of the event.

    @@ -3055,7 +3057,14 @@

    Abort these steps.

  • -

    Add f to the extend lifetime promises.

    +

    Add f to the extend lifetime promises.

    +
  • +

    Increase the pending promises count by one.

    +

    Note: The pending promises count is increased even if the given promise has already been settled. The corresponding count decrease is done within Handle Extend Lifetime Promise algorithm.

    +
  • +

    Run Handle Extend Lifetime Promise with the context object and f.

    +
  • +

    Return.

  • Service workers and extensions that define event handlers may define their own behaviors, allowing the extend lifetime promises to suggest operation length, and the rejected state of any of the promise in extend lifetime promises to suggest operation failure.

    @@ -3070,7 +3079,7 @@

    4.5. InstallEvent

    [Constructor(DOMString type, optional ExtendableEventInit eventInitDict), Exposed=ServiceWorker]
    -interface InstallEvent : ExtendableEvent {
    +interface InstallEvent : ExtendableEvent {
       void registerForeignFetch(ForeignFetchOptions options);
     };
     
    @@ -3138,7 +3147,7 @@ 

    4.6. FetchEvent

    [Constructor(DOMString type, FetchEventInit eventInitDict), Exposed=ServiceWorker]
    -interface FetchEvent : ExtendableEvent {
    +interface FetchEvent : ExtendableEvent {
       [SameObject] readonly attribute Request request;
       readonly attribute Promise<any> preloadResponse;
       readonly attribute DOMString clientId;
    @@ -3158,7 +3167,7 @@ 

    boolean isReload = false; };

    -

    Service workers have an essential functional event fetch. For fetch event, service workers use the FetchEvent interface which extends the ExtendableEvent interface.

    +

    Service workers have an essential functional event fetch. For fetch event, service workers use the FetchEvent interface which extends the ExtendableEvent interface.

    Each event using FetchEvent interface has an associated potential response (a response), initially set to null, and the following associated flags that are initially unset:

  • origins, in §4.5 +
  • pending promises count, in §4.4
  • ports + + +
  • [=/Service workers=] and extensions that define event handlers *may* define their own behaviors, allowing the [=ExtendableEvent/extend lifetime promises=] to suggest operation length, and the rejected state of any of the promise in [=ExtendableEvent/extend lifetime promises=] to suggest operation failure. @@ -1316,6 +1324,11 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe 1. Throw an "{{InvalidStateError}}" exception. 1. Abort these steps. 1. Add |r| to the extend lifetime promises. + 1. Increase the [=ExtendableEvent/pending promises count=] by one. + + Note: The [=ExtendableEvent/pending promises count=] is increased even if the given promise has already been settled. The corresponding count decrease is done within [=Handle Extend Lifetime Promise=] algorithm. + + 1. Run [=Handle Extend Lifetime Promise=] with the [=context object=] and |r|. Note: {{FetchEvent/respondWith(r)|event.respondWith(r)}} extends the lifetime of the event by default as if {{ExtendableEvent/waitUntil()|event.waitUntil(r)}} is called. @@ -2471,17 +2484,32 @@ spec: webappsec-referrer-policy; urlPrefix: https://w3c.github.io/webappsec-refe : Output :: None - 1. If |event|'s extend lifetime promises is empty, unset |event|'s extensions allowed flag and abort these steps. - 1. Let |extendLifetimePromises| be an empty array. - 1. Run the following substeps in parallel: - 1. *SetupPromiseArray*: Set |extendLifetimePromises| to a copy of |event|'s extend lifetime promises. - 1. Wait until all the promises in |extendLifetimePromises| settle. - 1. If the length of |extendLifetimePromises| does not equal the length of |event|'s extend lifetime promises, jump to the step labeled *SetupPromiseArray*. - 1. Unset |event|'s extensions allowed flag. + 1. If |event|'s [=ExtendableEvent/pending promises count=] is zero, unset |event|'s [=ExtendableEvent/extensions allowed flag=] and abort these steps. + Note: If no lifetime extension promise has been added up to this point (i.e., at the end of the task that called the event handlers), the [=ExtendableEvent/extensions allowed flag=] is immediately unset. Calling {{ExtendableEvent/waitUntil()}} in subsequent asynchronous tasks will throw. + The user agent *should not* terminate the [=/service worker=] associated with |event|'s relevant settings object's [=environment settings object/global object=] until |event|'s extensions allowed flag is unset. However, the user agent *may* impose a time limit to this lifetime extension. +
    +

    Handle Extend Lifetime Promise

    + + : Input + :: |event|, an {{ExtendableEvent}} object + :: |promise|, a [=promise=] + : Output + :: None + + 1. Wait until |promise| is settled [=in parallel=]. + 2. [=Queue a microtask=] to run the following substeps: + 1. Decrease |event|'s [=ExtendableEvent/pending promises count=] by one. + 1. For each |reaction| in |promise|.\[[PromiseFulfillReactions]]: + 1. Append |reaction|.\[[Capability]].\[[Promise]] to |event|'s [=ExtendableEvent/extend lifetime promises=]. + 1. Increase |event|'s [=ExtendableEvent/pending promises count=] by one. + 1. Invoke [=Handle Extend Lifetime Promise=] with |event| and |reaction|.\[[Capability]].\[[Promise]]. + 1. If |event|'s [=ExtendableEvent/pending promises count=] is zero, unset |event|'s [=ExtendableEvent/extensions allowed flag=]. +
    +

    Handle Fetch

    diff --git a/docs/v1/index.html b/docs/v1/index.html index 1df42064..f1d277bb 100644 --- a/docs/v1/index.html +++ b/docs/v1/index.html @@ -1177,7 +1177,7 @@ } } - +