voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
164 lines (146 loc) • 4.71 kB
text/typescript
/**
* @license
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ERROR_FACTORY, ErrorCode } from '../utils/errors';
import { isIndexedDBAvailable } from '@firebase/util';
import { consoleLogger } from '../utils/console_logger';
declare global {
interface Window {
PerformanceObserver: typeof PerformanceObserver;
// eslint-disable-next-line @typescript-eslint/ban-types
perfMetrics?: { onFirstInputDelay: Function };
}
}
let apiInstance: Api | undefined;
let windowInstance: Window | undefined;
export type EntryType =
| 'mark'
| 'measure'
| 'paint'
| 'resource'
| 'frame'
| 'navigation';
/**
* This class holds a reference to various browser related objects injected by
* set methods.
*/
export class Api {
private readonly performance: Performance;
/** PreformanceObserver constructor function. */
private readonly PerformanceObserver: typeof PerformanceObserver;
private readonly windowLocation: Location;
// eslint-disable-next-line @typescript-eslint/ban-types
readonly onFirstInputDelay?: Function;
readonly localStorage?: Storage;
readonly document: Document;
readonly navigator: Navigator;
constructor(readonly window?: Window) {
if (!window) {
throw ERROR_FACTORY.create(ErrorCode.NO_WINDOW);
}
this.performance = window.performance;
this.PerformanceObserver = window.PerformanceObserver;
this.windowLocation = window.location;
this.navigator = window.navigator;
this.document = window.document;
if (this.navigator && this.navigator.cookieEnabled) {
// If user blocks cookies on the browser, accessing localStorage will
// throw an exception.
this.localStorage = window.localStorage;
}
if (window.perfMetrics && window.perfMetrics.onFirstInputDelay) {
this.onFirstInputDelay = window.perfMetrics.onFirstInputDelay;
}
}
getUrl(): string {
// Do not capture the string query part of url.
return this.windowLocation.href.split('?')[0];
}
mark(name: string): void {
if (!this.performance || !this.performance.mark) {
return;
}
this.performance.mark(name);
}
measure(measureName: string, mark1: string, mark2: string): void {
if (!this.performance || !this.performance.measure) {
return;
}
this.performance.measure(measureName, mark1, mark2);
}
getEntriesByType(type: EntryType): PerformanceEntry[] {
if (!this.performance || !this.performance.getEntriesByType) {
return [];
}
return this.performance.getEntriesByType(type);
}
getEntriesByName(name: string): PerformanceEntry[] {
if (!this.performance || !this.performance.getEntriesByName) {
return [];
}
return this.performance.getEntriesByName(name);
}
getTimeOrigin(): number {
// Polyfill the time origin with performance.timing.navigationStart.
return (
this.performance &&
(this.performance.timeOrigin || this.performance.timing.navigationStart)
);
}
requiredApisAvailable(): boolean {
if (
!fetch ||
!Promise ||
!this.navigator ||
!this.navigator.cookieEnabled
) {
consoleLogger.info(
'Firebase Performance cannot start if browser does not support fetch and Promise or cookie is disabled.'
);
return false;
}
if (!isIndexedDBAvailable()) {
consoleLogger.info('IndexedDB is not supported by current browswer');
return false;
}
return true;
}
setupObserver(
entryType: EntryType,
callback: (entry: PerformanceEntry) => void
): void {
if (!this.PerformanceObserver) {
return;
}
const observer = new this.PerformanceObserver(list => {
for (const entry of list.getEntries()) {
// `entry` is a PerformanceEntry instance.
callback(entry);
}
});
// Start observing the entry types you care about.
observer.observe({ entryTypes: [entryType] });
}
static getInstance(): Api {
if (apiInstance === undefined) {
apiInstance = new Api(windowInstance);
}
return apiInstance;
}
}
export function setupApi(window: Window): void {
windowInstance = window;
}