Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Wait for service worker registration to become active before any operations #8661

Merged
merged 4 commits into from
Dec 4, 2024

Conversation

hsubox76
Copy link
Contributor

@hsubox76 hsubox76 commented Dec 2, 2024

Examples on MDN seem to claim that serviceWorkerRegistration.pushManager.subscribe() can be called right after awaiting navigator.serviceWorker.register, but that doesn't actually seem to be the case in practice. See issues #7784 and #7693.

When the user calls getToken() (and is using the default service worker), we await register() in registerDefaultSw() and we go ahead and call pushManager.subscribe() soon after. Unfortunately it's often not actually ready. Not sure if this is a bug - spec seems to indicate it should be ready?

One option is to await navigator.serviceWorker.ready but this can be tricky depending on the scope of the service worker, where it may not consider your service worker the page's default service worker. It seems to hang in my test apps. It also may get mixed up with other service workers from other apps on the same page.

Instead, we can listen for events on the specific service worker we just registered and check when the state becomes "active". Borrowed some working code for this from various contributors in #7693 (comment) (Thank you!)

Fixes #7784
Fixes #7693

@hsubox76 hsubox76 requested review from a team and zwu52 as code owners December 2, 2024 23:37
Copy link

changeset-bot bot commented Dec 2, 2024

🦋 Changeset detected

Latest commit: 2a81f96

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@firebase/messaging Patch
firebase Patch
@firebase/messaging-compat Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Dec 2, 2024

Size Report 1

Affected Products

  • @firebase/messaging

    TypeBase (ffbf5a6)Merge (d282893)Diff
    browser21.3 kB22.0 kB+765 B (+3.6%)
    main21.7 kB22.4 kB+765 B (+3.5%)
    module21.3 kB22.0 kB+765 B (+3.6%)
  • bundle

    TypeBase (ffbf5a6)Merge (d282893)Diff
    messaging (send + receive)46.9 kB47.4 kB+466 B (+1.0%)
  • firebase

    TypeBase (ffbf5a6)Merge (d282893)Diff
    firebase-compat.js794 kB795 kB+408 B (+0.1%)
    firebase-messaging-compat.js38.3 kB38.7 kB+408 B (+1.1%)
    firebase-messaging.js28.7 kB29.1 kB+452 B (+1.6%)

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/948OMRX0wa.html

Copy link
Member

@zwu52 zwu52 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. thanks for the change!

Comment on lines 62 to 76
return new Promise<void>((resolve, reject) => {
if (registration.active) {
resolve();
}
const incomingSw = registration.installing || registration.waiting;
if (incomingSw) {
incomingSw.onstatechange = ev => {
if ((ev.target as ServiceWorker)?.state === 'activated') {
incomingSw.onstatechange = null;
resolve();
}
};
} else {
reject(new Error('No incoming service worker found.'));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be extremely unlikely, but if the registration transitions to the active state after if(registration.active) and before const incomingSw = registration.installing || registration.waiting, I think we would reject and the registration would fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to an if/else but not sure if this affects the timing at all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is a tricky timing issue. I think now the SW could become active before we add the event listener, causing us to miss the activated event. For example:

  1. registration is waiting
  2. registration is not yet active, so we don't resolve immediately on line 79.
  3. registration becomes active, and the 'activated' event fires
  4. attach registration state event listener
  5. event listener never fires because we missed the 'activated' event and we reject the promise after 10s

This is just me speculating, I'm not sure if this order of events is possible.

packages/messaging/src/helpers/registerDefaultSw.ts Outdated Show resolved Hide resolved
@hsubox76 hsubox76 requested a review from a team as a code owner December 3, 2024 19:33
Copy link
Contributor

github-actions bot commented Dec 3, 2024

Changeset File Check ✅

  • No modified packages are missing from the changeset file.
  • No changeset formatting errors detected.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Dec 3, 2024

Size Analysis Report 1

Affected Products

  • @firebase/messaging

    • deleteToken

      Size

      TypeBase (ffbf5a6)Merge (d282893)Diff
      size12.7 kB13.1 kB+465 B (+3.7%)
      size-with-ext-deps39.4 kB39.9 kB+466 B (+1.2%)

      Dependency

      TypeBase (ffbf5a6)Merge (d282893)Diff
      functions

      39 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbRemove
      dbSet
      deleteToken
      deleteToken$1
      deleteTokenInternal
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey

      40 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbRemove
      dbSet
      deleteToken
      deleteToken$1
      deleteTokenInternal
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey
      waitForRegistrationActive

      + waitForRegistrationActive

      variables

      24 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      25 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_REGISTRATION_TIMEOUT
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      + DEFAULT_REGISTRATION_TIMEOUT

    • getMessaging

      Size

      TypeBase (ffbf5a6)Merge (d282893)Diff
      size12.7 kB13.2 kB+465 B (+3.6%)
      size-with-ext-deps46.6 kB47.0 kB+466 B (+1.0%)

      Dependency

      TypeBase (ffbf5a6)Merge (d282893)Diff
      functions

      37 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMessagingInWindow
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      isWindowSupported
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey

      38 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMessagingInWindow
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      isWindowSupported
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey
      waitForRegistrationActive

      + waitForRegistrationActive

      variables

      24 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      25 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_REGISTRATION_TIMEOUT
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      + DEFAULT_REGISTRATION_TIMEOUT

    • getToken

      Size

      TypeBase (ffbf5a6)Merge (d282893)Diff
      size12.2 kB12.6 kB+467 B (+3.8%)
      size-with-ext-deps38.9 kB39.3 kB+466 B (+1.2%)

      Dependency

      TypeBase (ffbf5a6)Merge (d282893)Diff
      functions

      36 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey

      37 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey
      waitForRegistrationActive

      + waitForRegistrationActive

      variables

      24 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      25 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_REGISTRATION_TIMEOUT
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      + DEFAULT_REGISTRATION_TIMEOUT

    • isSupported

      Size

      TypeBase (ffbf5a6)Merge (d282893)Diff
      size12.5 kB13.0 kB+465 B (+3.7%)
      size-with-ext-deps39.2 kB39.7 kB+466 B (+1.2%)

      Dependency

      TypeBase (ffbf5a6)Merge (d282893)Diff
      functions

      36 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      isWindowSupported
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey

      37 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      isWindowSupported
      logToScion
      messageEventListener
      migrateOldDatabase
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey
      waitForRegistrationActive

      + waitForRegistrationActive

      variables

      24 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      25 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_REGISTRATION_TIMEOUT
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      + DEFAULT_REGISTRATION_TIMEOUT

    • onMessage

      Size

      TypeBase (ffbf5a6)Merge (d282893)Diff
      size12.3 kB12.8 kB+467 B (+3.8%)
      size-with-ext-deps39.0 kB39.5 kB+466 B (+1.2%)

      Dependency

      TypeBase (ffbf5a6)Merge (d282893)Diff
      functions

      37 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      logToScion
      messageEventListener
      migrateOldDatabase
      onMessage
      onMessage$1
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey

      38 dependencies

      _mergeStrings
      arrayToBase64
      base64ToArray
      checkTokenDetails
      dbGet
      dbSet
      externalizePayload
      extractAppConfig
      getBody
      getDbPromise
      getEndpoint
      getEventType
      getHeaders
      getKey
      getMissingValueError
      getNewToken
      getPushSubscription
      getToken$1
      getTokenInternal
      isConsoleMessage
      isTokenValid
      logToScion
      messageEventListener
      migrateOldDatabase
      onMessage
      onMessage$1
      propagateDataPayload
      propagateFcmOptions
      propagateNotificationPayload
      registerDefaultSw
      registerMessagingInWindow
      requestDeleteToken
      requestGetToken
      requestUpdateToken
      updateSwReg
      updateToken
      updateVapidKey
      waitForRegistrationActive

      + waitForRegistrationActive

      variables

      24 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      25 dependencies

      CONSOLE_CAMPAIGN_ANALYTICS_ENABLED
      CONSOLE_CAMPAIGN_ID
      CONSOLE_CAMPAIGN_NAME
      CONSOLE_CAMPAIGN_TIME
      DATABASE_NAME
      DATABASE_VERSION
      DEFAULT_REGISTRATION_TIMEOUT
      DEFAULT_SW_PATH
      DEFAULT_SW_SCOPE
      DEFAULT_VAPID_KEY
      ENDPOINT
      ERROR_FACTORY
      ERROR_MAP
      MessageType
      MessageType$1
      OBJECT_STORE_NAME
      OLD_DB_NAME
      OLD_DB_VERSION
      OLD_OBJECT_STORE_NAME
      TOKEN_EXPIRATION_MS
      WindowMessagingFactory
      WindowMessagingInternalFactory
      dbPromise
      name
      version

      + DEFAULT_REGISTRATION_TIMEOUT

Test Logs

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/I1KZTeqPZv.html

@hsubox76 hsubox76 merged commit 1294e64 into main Dec 4, 2024
38 of 39 checks passed
@hsubox76 hsubox76 deleted the ch-fix-sw branch December 4, 2024 17:26
@google-oss-bot google-oss-bot mentioned this pull request Dec 10, 2024
@firebase firebase locked and limited conversation to collaborators Jan 4, 2025
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
4 participants