Skip to content

Commit

Permalink
Add async isSupported to Modular FM SDK (#4665)
Browse files Browse the repository at this point in the history
  • Loading branch information
zwu52 committed Apr 7, 2021
1 parent 5ae7365 commit 8e1ace4
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 38 deletions.
13 changes: 6 additions & 7 deletions packages-exp/messaging-exp/src/api.ts
Expand Up @@ -15,22 +15,21 @@
* limitations under the License.
*/

import { FirebaseApp, _getProvider, getApp } from '@firebase/app-exp';
import { FirebaseMessaging, MessagePayload } from './interfaces/public-types';
import {
FirebaseMessaging,
MessagePayload,
NextFn,
Observer,
Unsubscribe
} from './interfaces/public-types';
Unsubscribe,
getModularInstance
} from '@firebase/util';

import { MessagingService } from './messaging-service';
import { Provider } from '@firebase/component';
import { deleteToken as _deleteToken } from './api/deleteToken';
import { _getProvider, FirebaseApp, getApp } from '@firebase/app-exp';
import { getToken as _getToken } from './api/getToken';
import { onBackgroundMessage as _onBackgroundMessage } from './api/onBackgroundMessage';
import { onMessage as _onMessage } from './api/onMessage';
import { getModularInstance } from '@firebase/util';

/**
* Retrieves a firebase messaging instance.
Expand Down Expand Up @@ -131,7 +130,7 @@ export function onMessage(
*
* @returns To stop listening for messages execute this returned function
*
* make it internal to hide it from the browser entrypoint
* make it internal to hide it from the browser entry point.
* @internal
*/
export function onBackgroundMessage(
Expand Down
@@ -1,6 +1,6 @@
/**
* @license
* Copyright 2019 Google LLC
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,21 +15,14 @@
* limitations under the License.
*/

export function isSupported(): boolean {
if (self && 'ServiceWorkerGlobalScope' in self) {
// Running in ServiceWorker context
return isSWControllerSupported();
} else {
// Assume we are in the window context.
return isWindowControllerSupported();
}
}
import { validateIndexedDBOpenable } from '@firebase/util';

/**
* Checks to see if the required APIs exist.
*/
function isWindowControllerSupported(): boolean {
export async function isWindowSupported(): Promise<boolean> {
// firebase-js-sdk/issues/2393 reveals that idb#open in Safari iframe and Firefox private browsing
// might be prohibited to run. In these contexts, an error would be thrown during the messaging
// instantiating phase, informing the developers to import/call isSupported for special handling.
return (
(await validateIndexedDBOpenable()) &&
'indexedDB' in window &&
indexedDB !== null &&
navigator.cookieEnabled &&
Expand All @@ -45,8 +38,12 @@ function isWindowControllerSupported(): boolean {
/**
* Checks to see if the required APIs exist within SW Context.
*/
function isSWControllerSupported(): boolean {
export async function isSwSupported(): Promise<boolean> {
// firebase-js-sdk/issues/2393 reveals that idb#open in Safari iframe and Firefox private browsing
// might be prohibited to run. In these contexts, an error would be thrown during the messaging
// instantiating phase, informing the developers to import/call isSupported for special handling.
return (
(await validateIndexedDBOpenable()) &&
'indexedDB' in self &&
indexedDB !== null &&
'PushManager' in self &&
Expand Down
54 changes: 47 additions & 7 deletions packages-exp/messaging-exp/src/helpers/register.ts
Expand Up @@ -22,17 +22,27 @@ import {
InstanceFactory
} from '@firebase/component';
import { ERROR_FACTORY, ErrorCode } from '../util/errors';
import { isSwSupported, isWindowSupported } from '../api/isSupported';

import { MessagingService } from '../messaging-service';
import { _registerComponent } from '@firebase/app-exp';
import { isSupported } from './isSupported';

const messagingFactory: InstanceFactory<'messaging-exp'> = (
const WindowMessagingFactory: InstanceFactory<'messaging-exp'> = (
container: ComponentContainer
) => {
if (!isSupported()) {
throw ERROR_FACTORY.create(ErrorCode.UNSUPPORTED_BROWSER);
}
// Conscious decision to make this async check non-blocking during the messaging instance
// initialization phase for performance consideration. An error would be thrown latter for
// developer's information. Developers can then choose to import and call `isSupported` for
// special handling.
isWindowSupported()
.then(isSupported => {
if (!isSupported) {
throw ERROR_FACTORY.create(ErrorCode.UNSUPPORTED_BROWSER);
}
})
.catch(_ => {
throw ERROR_FACTORY.create(ErrorCode.INDEXED_DB_UNSUPPORTED);
});

return new MessagingService(
container.getProvider('app-exp').getImmediate(),
Expand All @@ -41,8 +51,38 @@ const messagingFactory: InstanceFactory<'messaging-exp'> = (
);
};

export function registerMessaging(): void {
const SwMessagingFactory: InstanceFactory<'messaging-exp'> = (
container: ComponentContainer
) => {
// Conscious decision to make this async check non-blocking during the messaging instance
// initialization phase for performance consideration. An error would be thrown latter for
// developer's information. Developers can then choose to import and call `isSupported` for
// special handling.
isSwSupported()
.then(isSupported => {
if (!isSupported) {
throw ERROR_FACTORY.create(ErrorCode.UNSUPPORTED_BROWSER);
}
})
.catch(_ => {
throw ERROR_FACTORY.create(ErrorCode.INDEXED_DB_UNSUPPORTED);
});

return new MessagingService(
container.getProvider('app-exp').getImmediate(),
container.getProvider('installations-exp-internal').getImmediate(),
container.getProvider('analytics-internal')
);
};

export function registerMessagingInWindow(): void {
_registerComponent(
new Component('messaging-exp', WindowMessagingFactory, ComponentType.PUBLIC)
);
}

export function registerMessagingInSw(): void {
_registerComponent(
new Component('messaging-exp', messagingFactory, ComponentType.PUBLIC)
new Component('messaging-exp', SwMessagingFactory, ComponentType.PUBLIC)
);
}
8 changes: 5 additions & 3 deletions packages-exp/messaging-exp/src/index.sw.ts
Expand Up @@ -15,16 +15,18 @@
* limitations under the License.
*/

import { FirebaseMessaging } from './interfaces/public-types';
import { registerMessaging } from './helpers/register';
import '@firebase/installations-exp';

import { FirebaseMessaging } from './interfaces/public-types';
import { registerMessagingInSw } from './helpers/register';

export { onBackgroundMessage, getMessaging } from './api';
export { isSwSupported as isSupported } from './api/isSupported';

declare module '@firebase/component' {
interface NameServiceMapping {
'messaging-exp': FirebaseMessaging;
}
}

registerMessaging();
registerMessagingInSw();
8 changes: 5 additions & 3 deletions packages-exp/messaging-exp/src/index.ts
Expand Up @@ -15,17 +15,19 @@
* limitations under the License.
*/

import { FirebaseMessaging } from './interfaces/public-types';
import { registerMessaging } from './helpers/register';
import '@firebase/installations-exp';

import { FirebaseMessaging } from './interfaces/public-types';
import { registerMessagingInWindow } from './helpers/register';

export {
getToken,
deleteToken,
onMessage,
getMessaging,
onBackgroundMessage
} from './api';
export { isWindowSupported as isSupported } from './api/isSupported';
export * from './interfaces/public-types';

declare module '@firebase/component' {
Expand All @@ -34,4 +36,4 @@ declare module '@firebase/component' {
}
}

registerMessaging();
registerMessagingInWindow();
3 changes: 3 additions & 0 deletions packages-exp/messaging-exp/src/util/errors.ts
Expand Up @@ -24,6 +24,7 @@ export const enum ErrorCode {
PERMISSION_DEFAULT = 'permission-default',
PERMISSION_BLOCKED = 'permission-blocked',
UNSUPPORTED_BROWSER = 'unsupported-browser',
INDEXED_DB_UNSUPPORTED = 'indexed-db-unsupported',
FAILED_DEFAULT_REGISTRATION = 'failed-service-worker-registration',
TOKEN_SUBSCRIBE_FAILED = 'token-subscribe-failed',
TOKEN_SUBSCRIBE_NO_TOKEN = 'token-subscribe-no-token',
Expand All @@ -50,6 +51,8 @@ export const ERROR_MAP: ErrorMap<ErrorCode> = {
'The notification permission was not granted and blocked instead.',
[ErrorCode.UNSUPPORTED_BROWSER]:
"This browser doesn't support the API's required to use the firebase SDK.",
[ErrorCode.INDEXED_DB_UNSUPPORTED]:
"This browser doesn't support indexedDb.open() (ex. Safari iFrame, Firefox Private Browsing, etc)",
[ErrorCode.FAILED_DEFAULT_REGISTRATION]:
'We are unable to register the default service worker. {$browserErrorMessage}',
[ErrorCode.TOKEN_SUBSCRIBE_FAILED]:
Expand Down
9 changes: 6 additions & 3 deletions packages/util/src/environment.ts
Expand Up @@ -144,21 +144,24 @@ export function isIndexedDBAvailable(): boolean {
}

/**
* This method validates browser context for indexedDB by opening a dummy indexedDB database and reject
* This method validates browser/sw context for indexedDB by opening a dummy indexedDB database and reject
* if errors occur during the database open operation.
*
* @throws exception if current browser/sw context can't run idb.open (ex: Safari iframe, Firefox
* private browsing)
*/
export function validateIndexedDBOpenable(): Promise<boolean> {
return new Promise((resolve, reject) => {
try {
let preExist: boolean = true;
const DB_CHECK_NAME =
'validate-browser-context-for-indexeddb-analytics-module';
const request = window.indexedDB.open(DB_CHECK_NAME);
const request = self.indexedDB.open(DB_CHECK_NAME);
request.onsuccess = () => {
request.result.close();
// delete database only when it doesn't pre-exist
if (!preExist) {
window.indexedDB.deleteDatabase(DB_CHECK_NAME);
self.indexedDB.deleteDatabase(DB_CHECK_NAME);
}
resolve(true);
};
Expand Down

0 comments on commit 8e1ace4

Please sign in to comment.