Service Worker Popup Capability Delegation

Demonstrates how a Service Worker handling a notification click can delegate transient user activation to an existing window client via client.postMessage(message, {delegate: 'popup'}), enabling the client to open auxiliary popup windows without being blocked by the popup blocker.

1. Setup & Prerequisites / Enablement

Requirements: Chrome 157+ with chrome://flags/#enable-experimental-web-platform-features (or --enable-features=CapabilityDelegationPopup).


2. Primary Demo


3. Boundary / Error Test Cases

4. Activity Log

Ready.

Example Code

Service Worker (service-worker.js)

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  event.waitUntil((async () => {
    const clientList = await self.clients.matchAll({type: 'window'});
    if (clientList.length > 0) {
      // 1. Focus existing window (optional; AllowWindowInteraction is decoupled from popup token)
      await clientList[0].focus();

      // 2. Delegate popup capability token via postMessage
      try {
        clientList[0].postMessage(
          {type: 'open-popup', payload: event.notification.data},
          {delegate: 'popup'}
        );
      } catch (err) {
        // Fallback if popup capability delegation is unsupported in the browser
        if (self.clients.openWindow) {
          await self.clients.openWindow('index.html');
        }
      }
    } else if (self.clients.openWindow) {
      // Fallback when no client window is currently open
      await self.clients.openWindow('index.html?fallback=openWindow');
    }
  })());
});

Window Client (script.js)

navigator.serviceWorker.addEventListener('message', (event) => {
  if (event.data?.type === 'open-popup') {
    // 1. Share state directly with auxiliary window
    window.lastDelegatedMessage = event.data.payload;

    // 2. Open auxiliary popup without popup blocker obstruction
    const popup = window.open('popup.html', '_blank', 'width=540,height=620');
  }
});