0

I am serving my service worker from /worker.js and want it to intercept fetches to /localzip/*, but the fetch event is never fired.

I register it like this:

navigator.serviceWorker.register(
    "worker.js",
    { scope: "/localzip/" }
);

And I claim all clients when it activates, so that I can start intercepting fetches from the current page immediately. I am sure that the service worker is activating and that clients.claim() is succeeding.

self.addEventListener("activate", (e) => {
    // Important! Start processing fetches for all clients immediately.
    //
    // MDN: "When a service worker is initially registered, pages won't use it
    // until they next load. The claim() method causes those pages to be
    // controlled immediately."
    e.waitUntil(clients.claim());
});

Chrome seems happy with it and the scope appears correct:

screenshot of Chrome Developer Tools showing active service worker

My fetch event handler is very simple:

self.addEventListener("fetch", (e) => {
    console.log("Trying to make fetch happen!");
});

From my application, after the worker is active, I try to make a request, e.g.,

const response = await fetch("/localzip/lol.jpg");

The fetch does not appear to trigger the above event handler, and the browser instead tries to make a request directly to the server and logs GET http://localhost:3000/localzip/lol.jpg 404 (Not Found).

I have tried:

  • Making sure the latest version of my worker code is running.

  • Disabling / clearing caches to make sure the fetch isn't being handled by the browser's cache.

  • Hosting from an HTTPS server. (Chrome is supposed to support service workers on plaintext localhost for development.)

What more does it want from me?

Live demo: https://rgov.github.io/service-worker-zip-experiment/
Note that the scope is slightly different, and the fetch is performed by creating an <img> tag.

rgov
  • 3,516
  • 1
  • 31
  • 51

2 Answers2

2

First, let's confirm you are not using hard-reload while testing your code. If you use hard-reload, all requests will not go through the service worker. See https://web.dev/service-worker-lifecycle/#shift-reload

I also checked chrome://serviceworker-internals/ in Chrome, and your service worker has fetch handler.

enter image description here

Then, let's check the codes in detail.

After trying your demo page, I found a network request is handled by the service worker after clicking "Display image from zip archive" button since I can see this log:

Service Worker: Serving archive/maeby.jpg from zip archive

Then, the error is thrown:

Failed to load ‘https://rgov.github.io/localzip/maeby.jpg’. A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with ‘TypeError: db is undefined’.

This is caused by db object is not initialized properly. It would be worth confirming whether you see the DB related issue as I see in your demo. If not, my following statement might be incorrect.

I try to explain some service worker mechanism alongside my understanding of your code:

Timing of install handler

Your DB open code happens in the install handler only. This means DB object will be assigned only when the install handler is executed. Please notice the install handler will be executed only when it's necessary. If a service worker exists already and does not need to update, the install handler won't be called. Hence, the db object in your code might not be always available.

Stop/Start Status

When the service worker does not handle events for a while (how long it would be is by browser's design), the service worker will go to stop/idle state. When the service worker is stopped/idle (you can check the state in the devtools) and started again, the global db object will be undefined. In my understanding, this is why I see the error TypeError: db is undefined’.

Whenever the service worker wakes up, the whole worker script will be executed. However, the execution of event handlers will depend on whether the events are coming.

How to prevent stop/idle for debugging?

Open your devtools for the page then the browser will keep it being alive. Once you close the devtool, the service worker might go to "stop" soon.

Why does the service worker stops?

The service worker is designed for handling requests. If no request/event should be handled by a service worker, the service worker thread is not necessary to run for saving resources.

Please notice both fetch and message events (but not limited to) will awake the service worker.

See Prevent Service Worker from automatically stopping

Solution for the demo page

If the error is from the DB, this means the getFromZip function should open the DB when db is unavailable.


By the way, even without any change, your demo code works well in the following steps:

  1. As a user, I open the demo page at the first time. (This is for ensuring that the install handler is called.)
  2. I open the devtools ASAP once I see the page content. (This is for preventing the service worker goes to "stop" state)
  3. I click "Download zip archive to IndexedDB" button.
  4. I click "Display image from zip archive" button.
  5. Then I can see the image is shown properly.
halfer
  • 19,824
  • 17
  • 99
  • 186
Sean Lee
  • 21
  • 2
0

Jake Archibald pointed out that I was confused about the meaning of the service worker's scope, explained here:

We call pages, workers, and shared workers clients. Your service worker can only control clients that are in-scope. Once a client is "controlled", its fetches go through the in-scope service worker.

In other words:

The scope affects which pages (clients) get placed under the service worker's control and not which fetched URLs get intercepted.

rgov
  • 3,516
  • 1
  • 31
  • 51