I am trying to register a service worker, and it seems to be working. However, I get 3 warnings:
- Event handler of ‘push’ event must be added on the initial evaluation of worker script.
- Event handler of ‘pushsubscriptionchange’ event must be added on the initial evaluation of worker script.
- Event handler of ‘notificationclick’ event must be added on the initial evaluation of worker script.
I am developing a Flutter Web app, and wanted to secure my firebaseConfig. I noticed that you can view a service worker when in the developer options, so I didn’t want to place the config there. I opted for storing my firebaseConfig setup on the cloud, in the google cloud secret manager (for now, anyway).
When the user clicks to “allow” for notifications, I register the service worker, which is the famous firebase-messaging-sw.js. I do this from a async function:
firebase_messaging_manager.js
async function registerFCMServiceWorker() {
if ('serviceWorker' in navigator) {
await navigator.serviceWorker.register('firebase-messaging-sw.js');
}
}
I ended up removing some code from the function, so there may not need to be async/await functionality here.
firebase-messaging-sw.js
// Event handlers
self.addEventListener('push', (event) => {
console.log("push called");
});
self.addEventListener('pushsubscriptionchange', (event) => {
console.log("pushsubscriptionchange called");
});
self.addEventListener('notificationclick', (event) => {
console.log("notificationclick called");
});
// Contains the code to run getFirebaseConfig()
importScripts('assets/lib/firebase_options.js');
importScripts('https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.22.0/firebase-messaging-compat.js');
// Initialize Firebase
getFirebaseConfig().then((firebaseConfig) => {
firebase.initializeApp(firebaseConfig.result);
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
console.log('Received background message', payload);
});
});
As you can see, all of the mentioned events are at the top of the service worker, but it doesn’t seem to matter. I know that the ‘notificationclick’ event needs to be up there, but I don’t even use the other events. My only thought is that the warning messages must be firebase related. The reason I think this, is because I don’t initialize firebase until the promise is returned. In fact, if I hard-code the initialization for messaging, the warnings disappear.
I also noticed that the warnings do not point to firebase-messaging-sw.js, they point to a file called register.ts, which is a google file:

import {
Component,
ComponentContainer,
ComponentType,
InstanceFactory
} from '@firebase/component';
import {
onNotificationClick,
onPush,
onSubChange
} from '../listeners/sw-listeners';
import { GetTokenOptions } from '../interfaces/public-types';
import { MessagingInternal } from '@firebase/messaging-interop-types';
import { MessagingService } from '../messaging-service';
import { ServiceWorkerGlobalScope } from '../util/sw-types';
import { _registerComponent, registerVersion } from '@firebase/app';
import { getToken } from '../api/getToken';
import { messageEventListener } from '../listeners/window-listener';
import { name, version } from '../../package.json';
const WindowMessagingFactory: InstanceFactory<'messaging'> = (
container: ComponentContainer
) => {
const messaging = new MessagingService(
container.getProvider('app').getImmediate(),
container.getProvider('installations-internal').getImmediate(),
container.getProvider('analytics-internal')
);
navigator.serviceWorker.addEventListener('message', e =>
messageEventListener(messaging as MessagingService, e)
);
return messaging;
};
const WindowMessagingInternalFactory: InstanceFactory<'messaging-internal'> = (
container: ComponentContainer
) => {
const messaging = container
.getProvider('messaging')
.getImmediate() as MessagingService;
const messagingInternal: MessagingInternal = {
getToken: (options?: GetTokenOptions) => getToken(messaging, options)
};
return messagingInternal;
};
declare const self: ServiceWorkerGlobalScope;
const SwMessagingFactory: InstanceFactory<'messaging'> = (
container: ComponentContainer
) => {
const messaging = new MessagingService(
container.getProvider('app').getImmediate(),
container.getProvider('installations-internal').getImmediate(),
container.getProvider('analytics-internal')
);
self.addEventListener('push', e => {
e.waitUntil(onPush(e, messaging as MessagingService));
});
self.addEventListener('pushsubscriptionchange', e => {
e.waitUntil(onSubChange(e, messaging as MessagingService));
});
self.addEventListener('notificationclick', e => {
e.waitUntil(onNotificationClick(e));
});
return messaging;
};
export function registerMessagingInWindow(): void {
_registerComponent(
new Component('messaging', WindowMessagingFactory, ComponentType.PUBLIC)
);
_registerComponent(
new Component(
'messaging-internal',
WindowMessagingInternalFactory,
ComponentType.PRIVATE
)
);
registerVersion(name, version);
// BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation
registerVersion(name, version, '__BUILD_TARGET__');
}
/**
* The messaging instance registered in sw is named differently than that of in client. This is
* because both `registerMessagingInWindow` and `registerMessagingInSw` would be called in
* `messaging-compat` and component with the same name can only be registered once.
*/
export function registerMessagingInSw(): void {
_registerComponent(
new Component('messaging-sw', SwMessagingFactory, ComponentType.PUBLIC)
);
}
Here’s the TL/DR section of the code that the warnings point to:
declare const self: ServiceWorkerGlobalScope;
const SwMessagingFactory: InstanceFactory<'messaging'> = (
container: ComponentContainer
) => {
const messaging = new MessagingService(
container.getProvider('app').getImmediate(),
container.getProvider('installations-internal').getImmediate(),
container.getProvider('analytics-internal')
);
self.addEventListener('push', e => {
e.waitUntil(onPush(e, messaging as MessagingService));
});
self.addEventListener('pushsubscriptionchange', e => {
e.waitUntil(onSubChange(e, messaging as MessagingService));
});
self.addEventListener('notificationclick', e => {
e.waitUntil(onNotificationClick(e));
});
return messaging;
};
I haven’t tested this yet, but the most likely solution is to instantiate firebase messaging without my configuration, then update the messaging afterwards. However, i’m not sure how to do that. I’m sure it’s not as simple as updating the messaging object. That is, if I do this, it may not work with the event ‘notificationclick’ when I send messages.
Any thoughts would be extremely helpful. Thank you all!