Skip to content

Commit

Permalink
Wait for service worker registration to become active before any oper…
Browse files Browse the repository at this point in the history
…ations (#8661)
  • Loading branch information
hsubox76 authored Dec 4, 2024
1 parent ffbf5a6 commit 1294e64
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/neat-beans-rescue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@firebase/messaging': patch
---

Fix an issue where PushManager.subscribe() is called too soon after registering the default service worker.
48 changes: 47 additions & 1 deletion packages/messaging/src/helpers/registerDefaultSw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
* limitations under the License.
*/

import { DEFAULT_SW_PATH, DEFAULT_SW_SCOPE } from '../util/constants';
import {
DEFAULT_REGISTRATION_TIMEOUT,
DEFAULT_SW_PATH,
DEFAULT_SW_SCOPE
} from '../util/constants';
import { ERROR_FACTORY, ErrorCode } from '../util/errors';

import { MessagingService } from '../messaging-service';
Expand All @@ -39,9 +43,51 @@ export async function registerDefaultSw(
messaging.swRegistration.update().catch(() => {
/* it is non blocking and we don't care if it failed */
});
await waitForRegistrationActive(messaging.swRegistration);
} catch (e) {
throw ERROR_FACTORY.create(ErrorCode.FAILED_DEFAULT_REGISTRATION, {
browserErrorMessage: (e as Error)?.message
});
}
}

/**
* Waits for registration to become active. MDN documentation claims that
* a service worker registration should be ready to use after awaiting
* navigator.serviceWorker.register() but that doesn't seem to be the case in
* practice, causing the SDK to throw errors when calling
* swRegistration.pushManager.subscribe() too soon after register(). The only
* solution seems to be waiting for the service worker registration `state`
* to become "active".
*/
async function waitForRegistrationActive(
registration: ServiceWorkerRegistration
): Promise<void> {
return new Promise<void>((resolve, reject) => {
const rejectTimeout = setTimeout(
() =>
reject(
new Error(
`Service worker not registered after ${DEFAULT_REGISTRATION_TIMEOUT} ms`
)
),
DEFAULT_REGISTRATION_TIMEOUT
);
const incomingSw = registration.installing || registration.waiting;
if (registration.active) {
clearTimeout(rejectTimeout);
resolve();
} else if (incomingSw) {
incomingSw.onstatechange = ev => {
if ((ev.target as ServiceWorker)?.state === 'activated') {
incomingSw.onstatechange = null;
clearTimeout(rejectTimeout);
resolve();
}
};
} else {
clearTimeout(rejectTimeout);
reject(new Error('No incoming service worker found.'));
}
});
}
1 change: 1 addition & 0 deletions packages/messaging/src/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST = 1000;
export const MAX_RETRIES = 3;
export const LOG_INTERVAL_IN_MS = 86400000; //24 hour
export const DEFAULT_BACKOFF_TIME_MS = 5000;
export const DEFAULT_REGISTRATION_TIMEOUT = 10000;

// FCM log source name registered at Firelog: 'FCM_CLIENT_EVENT_LOGGING'. It uniquely identifies
// FCM's logging configuration.
Expand Down

0 comments on commit 1294e64

Please sign in to comment.