[w3c/ServiceWorker] The promises returned by skipWaiting() should have more consistent activation-waiting behavior and not call Activate() multiple times (#1015)

The current high-level intent for ServiceWorkerGlobalScope.skipWaiting() seems to be:
* If the SW is handling an "install" event (state = "installing"), set the skip waiting flag and resolve the promise after the flag is set.  But don't wait for activation because that would deadlock any install handler that does `event.waitUntil(self.skipWaiting())`.
* If the SW has been installed (state = "installed") and calls skipWaiting(), set the skip waiting flag and wait for activation to complete; the promise will not resolve until the SW has been made active.  This happens in cases like the WPT `skip-waiting-installed.https.html` test where skipWaiting() is invoked in response to a "message" event.

As [specified right now](https://w3c.github.io/ServiceWorker/#service-worker-global-scope-skipwaiting), in the latter case if you call skipWaiting() twice, the first call will work as expected (set skip waiting, invoke Activate() and not resolve until Activate() fully completes).  If you call it a second time, Activate() will be invoked again, aborting on step 1 ("If registration’s waiting worker is null, abort these steps.") because the previous Activate() call will have transitioned the worker to be the registration's "active worker".  The promise will then resolve immediately regardless of whether the SW has activated or not.

There is also a potential variant on the second skipWaiting() call case where "Handle Service Worker Client Unload" calling Activate() races against the SW calling skipWaiting() at the request of an uncontrolled client's postMessage.  If skipWaiting() wins, the promise waits for activation.  If it loses, it doesn't wait.

Per discussion with @wanderview we think the core of a fix to make skipWaiting()'s promise consistent would be to:
* Only invoke Activate() if the skip waiting flag was not already set.
* Keep a list of all of the skipWaiting promises handed out (skipWaiting is annotated with `[NewObject]`) that are waiting on activation and resolve them when the SW activates, even if it's via a different Activate() invocation.
* Ensure the promise will always be resolved even if the skipWaiting() call is made after the SW is already active.

One way to do accomplish this might be to:
* Add verbiage to service worker: "A service worker has an associated list of skip waiting promises whose element type is a promise.  It is initially empty."
* Rewrite skipWaiting() to be:
  * Let promise be a new promise.
  * Run the following substeps in parallel:
    * If service worker's state is not installed and not activating, then resolve promise with undefined and abort these steps.
    * Add promise to the service worker's list of pending skip waiting promises.
    * If service worker's skip waiting flag is set then abort these steps.
    * Set service worker's skip waiting flag.
    * Run Activate algorithm passing service worker's registration as the argument.
  * Return promise.
* Add the following step to the end of Activate.
  * Resolve the promises in active worker's skip waiting promise list with undefined and clear the list.


-- 
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/issues/1015

Received on Sunday, 27 November 2016 09:49:41 UTC