Re: [w3c/ServiceWorker] Provide a way to skipWaiting when the last tab refreshes (#993)

I finally found time to do more research on this. You're quite right that I didn't know that AppCache assigns caches to documents, and so allows each tab to run its own cache.

re: assigning caches to particular clients (as AppCache does), I can see how the current API would allow for that, but, as you may have guessed, I really _don't_ want to do that; I was wrong to think AppCache did it correctly.

I want all tabs to share the same cache, until the last tab is refreshed, at which point I want the new cache to immediately set in before the refresh loads.

Since I posted this issue last year, there's been other discussion around a "navigation" event, to which @wanderview [replied](https://github.com/w3c/ServiceWorker/issues/1028#issuecomment-267136593):

> How is this different than a FetchEvent with a "navigate" RequestMode?
> 
> ```
> addEventListener('fetch', evt => {
>   if (evt.request.mode === 'navigate') {
>     // navigation event stuff here
>   }
> });
> ```

I, a fool, didn't yet know that `FetchEvent.request.mode` existed. But I think that alone may give me the power to do what I want.

The idea is: on `fetch` in `navigate` mode, if there's just one client, post a message to the new SW, asking it to skipWaiting. It's too late for the new SW to directly handle the navigation that already started, so in that case, we can just coordinate with the new SW to do what the new SW would have done.

In my case, I know that all my SWs ever do is to check the cache and fallback to the network, so I can just open the newest cache (instead of the oldest, which is the default behavior for `caches.match`) and return cached results from there, falling back to the network.

I've demonstrated this in https://github.com/dfabulich/service-worker-refresh-bug/tree/master/possible-workaround

Here's the key section:

```js
self.addEventListener('fetch', function(event) {
  console.log("[ServiceWorker] fetch", CACHE_NAME, event.request.url.replace(/^.*\//, ""));
  event.respondWith(clients.matchAll().then(function(clients) {
    if (clients.length < 2 && event.request.mode === "navigate" && registration.waiting) {
      console.log("[ServiceWorker]", CACHE_NAME, 'sending skipWaiting');
      registration.waiting.postMessage("skipWaiting");
      var lastKey;
      return caches.keys().then(function(keyList) {
        lastKey = keyList[keyList.length-1];
        return caches.open(lastKey);
      }).then(function(cache) {
        return cache.match(event.request);
      }).then(function(cached) {
        var response = cached || fetch(event.request);
        console.log("[ServiceWorker] response", CACHE_NAME, "from", lastKey, event.request.url.replace(/^.*\//, ""), response);
        return response;
      })
    } else {
      return caches.match(event.request).then(function(cached) {
        var response = cached || fetch(event.request);
        console.log("[ServiceWorker] response", CACHE_NAME, event.request.url.replace(/^.*\//, ""), response);
        return response;
      });
    }
  }));
});
```

If there's just one client, the request is a navigation, and there's a SW waiting, tell it to skipWaiting, and return index.html from the newest cache (the last cache key, instead of the first); otherwise, do the normal thing.

I tested this in Chrome 62 and it seemed to do what I want: with just one open tab, refreshing the tab twice (once to pick up the new SW and then again to activate it) updated the page, but with two open tabs, refreshing any number of times continued to use the old cache.

Unfortunately, the code didn't work at all in Firefox, because `registration.waiting` is always null. (In fact, `registration.active` is null, too, in the SW in Firefox.)

Can y'all confirm that my code is actually correct? Is there a cleverer or more standard way to handle this case? Do I just need to file a bug on Firefox?

-- 
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/993#issuecomment-338769960

Received on Monday, 23 October 2017 19:28:23 UTC