title: Can't get custom push notification event working in PWA (Firebase)
body: <p>I've been searching for a few hours on how to get my custom push notification working. Here is how I've set up my project: no front-end framework, a Node.js/Express.js back-end, Firebase Cloud Messaging (FCM) as push manager and a custom service worker. I am currently hosting the app on localhost and I have HTTPS set up and a manifest.json that contains the minimum amount of fields to get started. The manifest.json contains a start_url field that points to /index.html, the landing page for the app. The app is bundled with webpack v. 4.</p>
  
  <h1>Back-end</h1>
  
  <p>On the back-end, I have set up the Firebase Admin SDK in a specific router and I send a custom notification object a bit like the following to the FCM server: </p>
  
  <pre><code>let fcMessage = {
    data : {
      title : 'Foo',
      tag : 'url to view that opens bar.html'
    }
  };
  </code></pre>
  
  <p>When an interesting event occurs on the back-end, it retrieves a list of users that contains the FCM registration tokens and sends the notification to the FCM servers. This part works great.</p>
  
  <h1>Front-end</h1>
  
  <p>I have two service workers on the front-end. Inside my front-end index.js, I register a custom service worker named sw.js in the domain root and tell firebase to use that service worker like so:</p>
  
  <pre><code>if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('./sw.js')
               .then(registration =&gt; {
                    messaging.useServiceWorker(registration);
               })
               .catch(err =&gt; console.error(`OOps! ${err}`));
  }
  </code></pre>
  
  <p>FCM and its credentials are set up and the user can subscribe to push notifications. I won't show that code here since it works and I don't believe it is the issue.</p>
  
  <p>Now on to the service workers themselves. I have a firebase-messaging-sw.js file at the root of my domain. It contains the following code:</p>
  
  <pre><code>importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js');
  importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');
  
  firebase.initializeApp(configuration);
  const messaging = firebase.messaging();
  </code></pre>
  
  <p>Configuration is just a placeholder for all of the creds. Again that stuff works.</p>
  
  <p>What I want to do is to NOT use the FCM push notification and instead create my own push notification that contains a url to a view that the user can click on and go to that view. The following code almost works and is a <a href="https://github.com/firebase/quickstart-js/issues/71" rel="nofollow noreferrer">hack</a> I found on another site:</p>
  
  <pre><code>class CustomPushEvent extends Event {
    constructor(data) {
      super('push');
  
      Object.assign(this, data);
        this.custom = true;
      }
  }
  self.addEventListener('push', (e) =&gt; {
    console.log('[Service Worker] heard a push ', e);
    // Skip if event is our own custom event
    if (e.custom) return;
  
    // Keep old event data to override
    let oldData = e.data;
  
    // Create a new event to dispatch
    let newEvent = new CustomPushEvent({
      data: {
        json() {
          let newData = oldData.json();
          newData._notification = newData.notification;
          delete newData.notification;
          return newData;
        },
      },
  
      waitUntil: e.waitUntil.bind(e),
    })
  
      // Stop event propagation
      e.stopImmediatePropagation();
  
      // Dispatch the new wrapped event
      dispatchEvent(newEvent);
  });
  
  messaging.setBackgroundMessageHandler(function(payload) {
    if (payload.hasOwnProperty('_notification')) {
      return self.registration.showNotification(payload.data.title,
        {
          body : payload.data.text,
          actions : [
              { 
                  action : `${payload.data.tag}`,
                  title : 'Go to link'
              }
          ]
      });
    } else {
      return;
    }
  });
  
    self.addEventListener('notificationclick', function(e) {
      console.log('CLICK');
  
      e.notification.close();
  
      e.waitUntil(clients.matchAll({ type : 'window' })
       .then(function(clientList) {
         console.log('client List ', clientList);
         const cLng = clientList.length;
         if (clientList.length &gt; 0) {
            for (let i = 0; i &lt; cLng; i++) {
              const client = clientList[i];
  
              if (client.url === '/' &amp;&amp; 'focus' in client) {
                return client.focus();
              }
          }
      } else {
          console.log('no clients ', e.action);
          clients.openWindow(e.action)
              .then(client =&gt; {
                  console.log('client ', client);
                  return client.navigate(e.action);
              })
              .catch(err =&gt; console.error(`[Service Worker] cannot open client : ${err} `));
      }
    }))
  });
  </code></pre>
  
  <p>The hack is meant to capture a push event and the FCM default notification payload and instead serve that payload through a custom one made via the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification" rel="nofollow noreferrer">Notification API</a>.</p>
  
  <p>The code above works great but ONLY if I put it in the firebase-messaging-sw.js file. That's not what I really want to do: I want to put it in the sw.js file instead but when I do, the sw.js cannot hear any push events and instead I get the default FCM push notification. I've also tried importing the entire firebase-messaging-sw scripts into the custom service worker and it still won't hear the message events.</p>
  
  <p>Why do I want to use it in my service worker instead of the Firebase one? It's to be able to open the app on the view passed into the 'tag' field on the notification's body. If I use the Firebase service worker, it tells me that it's not the active registered service worker and though the app does open in a new window, it only opens on /index.html.</p>
  
  <p>Some minor observations I've made: the clients array is always empty when the last bit of code is added to the firebase-messaging-sw.js file. The custom service worker is installed properly because it handles the app shell cache and listens to all of the other events normally. The firebase-messaging-sw service worker is also installed properly.</p>