demo · v140

Module Worker Chain

A chain of workers created from blob URLs — each spawning the next. Before Chrome 140 only the first-generation worker inherited the page's service worker controller; child blob workers bypassed the SW entirely. Chrome 140 fixes the full chain.

Service Worker note This demo works best on a page controlled by a SW. If the "page controlled" badge below shows "no", the workers will still launch but SW interception badges will reflect no SW present. Reload after a SW is installed to see the full chain effect.
SW: checking... controller: checking...
Page (window)
→ creates blob URL →
Worker A (blob:)
→ spawns blob URL →
Worker B (blob:)
↕ SW intercepts all fetches post-140
Service Worker

Chain log — each entry shows context + SW controller presence:

Press "Start chain" to launch the worker chain.

pre-140 — child blob workers uncontrolled

// Worker A (blob): inherits controller ✓
// Worker B (blob, child of A): NO controller ✗
// Fetches from B bypass the SW entirely

const code = `
  const child = new Worker(
    URL.createObjectURL(blob)
  );
  // child has no SW controller
`;
const urlA = URL.createObjectURL(
  new Blob([code], {type:'text/javascript'})
);
new Worker(urlA); // A ok, B broken

Chrome 140 — full chain inherits

// Worker A (blob): inherits controller ✓
// Worker B (blob, child of A): inherits ✓
// All fetches in the chain hit the SW

const code = `
  const child = new Worker(
    URL.createObjectURL(blob)
  );
  // child now inherits controller too
`;
const urlA = URL.createObjectURL(
  new Blob([code], {type:'text/javascript'})
);
new Worker(urlA); // entire chain controlled
// Chain: page → blob Worker A → blob Worker B
const workerBCode = `
  self.onmessage = async (e) => {
    const ctrl = !!navigator?.serviceWorker?.controller;
    self.postMessage({ ctx: 'Worker B (blob child)', ctrl });
  };
`;

const workerACode = `
  self.onmessage = async (e) => {
    const ctrl = !!navigator?.serviceWorker?.controller;
    self.postMessage({ ctx: 'Worker A (blob)', ctrl });

    // spawn child blob worker — pre-140: child had no controller
    const bBlob = new Blob([${JSON.stringify('...')}], { type: 'text/javascript' });
    const childWorker = new Worker(URL.createObjectURL(bBlob));
    childWorker.onmessage = (ce) => self.postMessage(ce.data);
    childWorker.postMessage('go');
  };
`;

const urlA = URL.createObjectURL(
  new Blob([workerACode], { type: 'text/javascript' })
);
const wA = new Worker(urlA);
wA.postMessage('go');

see also