UNPKG

react-native

Version:

A framework for building native apps using React

233 lines (197 loc) 6.96 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format * @flow strict */ import type { DOMHighResTimeStamp, PerformanceEntryList, PerformanceEntryType, } from './PerformanceEntry'; import type {OpaqueNativeObserverHandle} from './specs/NativePerformance'; import {PerformanceEventTiming} from './EventTiming'; import { performanceEntryTypeToRaw, rawToPerformanceEntry, rawToPerformanceEntryType, } from './internals/RawPerformanceEntry'; import {warnNoNativePerformance} from './internals/Utilities'; import NativePerformance from './specs/NativePerformance'; export {PerformanceEntry} from './PerformanceEntry'; export class PerformanceObserverEntryList { #entries: PerformanceEntryList; constructor(entries: PerformanceEntryList) { this.#entries = entries; } getEntries(): PerformanceEntryList { return this.#entries; } getEntriesByType(type: PerformanceEntryType): PerformanceEntryList { return this.#entries.filter(entry => entry.entryType === type); } getEntriesByName( name: string, type?: PerformanceEntryType, ): PerformanceEntryList { if (type === undefined) { return this.#entries.filter(entry => entry.name === name); } else { return this.#entries.filter( entry => entry.name === name && entry.entryType === type, ); } } } export type PerformanceObserverCallbackOptions = { droppedEntriesCount: number, }; export type PerformanceObserverCallback = ( list: PerformanceObserverEntryList, observer: PerformanceObserver, // The number of buffered entries which got dropped from the buffer due to the buffer being full: options?: PerformanceObserverCallbackOptions, ) => void; export interface PerformanceObserverInit { +entryTypes?: Array<PerformanceEntryType>; +type?: PerformanceEntryType; +buffered?: boolean; +durationThreshold?: DOMHighResTimeStamp; } function getSupportedPerformanceEntryTypes(): $ReadOnlyArray<PerformanceEntryType> { if (!NativePerformance) { return Object.freeze([]); } if (!NativePerformance.getSupportedPerformanceEntryTypes) { // fallback if getSupportedPerformanceEntryTypes is not defined on native side return Object.freeze(['mark', 'measure', 'event']); } return Object.freeze( NativePerformance.getSupportedPerformanceEntryTypes().map( rawToPerformanceEntryType, ), ); } /** * Implementation of the PerformanceObserver interface for RN, * corresponding to the standard in https://www.w3.org/TR/performance-timeline/ * * @example * const observer = new PerformanceObserver((list, _observer) => { * const entries = list.getEntries(); * entries.forEach(entry => { * reportEvent({ * eventName: entry.name, * startTime: entry.startTime, * endTime: entry.startTime + entry.duration, * processingStart: entry.processingStart, * processingEnd: entry.processingEnd, * interactionId: entry.interactionId, * }); * }); * }); * observer.observe({ type: "event" }); */ export class PerformanceObserver { #nativeObserverHandle: OpaqueNativeObserverHandle | null = null; #callback: PerformanceObserverCallback; #type: 'single' | 'multiple' | void; #calledAtLeastOnce = false; constructor(callback: PerformanceObserverCallback) { this.#callback = callback; } observe(options: PerformanceObserverInit): void { if (!NativePerformance || NativePerformance.observe == null) { warnNoNativePerformance(); return; } this.#validateObserveOptions(options); if (this.#nativeObserverHandle == null) { this.#nativeObserverHandle = this.#createNativeObserver(); } if (options.entryTypes) { this.#type = 'multiple'; NativePerformance.observe?.(this.#nativeObserverHandle, { entryTypes: options.entryTypes.map(performanceEntryTypeToRaw), }); } else if (options.type) { this.#type = 'single'; NativePerformance.observe?.(this.#nativeObserverHandle, { type: performanceEntryTypeToRaw(options.type), buffered: options.buffered, durationThreshold: options.durationThreshold, }); } } disconnect(): void { if (!NativePerformance) { warnNoNativePerformance(); return; } if (this.#nativeObserverHandle == null || !NativePerformance.disconnect) { return; } NativePerformance.disconnect(this.#nativeObserverHandle); } #createNativeObserver(): OpaqueNativeObserverHandle { if (!NativePerformance || !NativePerformance.createObserver) { warnNoNativePerformance(); return; } this.#calledAtLeastOnce = false; return NativePerformance.createObserver(() => { const rawEntries = NativePerformance.takeRecords?.( this.#nativeObserverHandle, true, // sort records ); if (!rawEntries) { return; } const entries = rawEntries.map(rawToPerformanceEntry); const entryList = new PerformanceObserverEntryList(entries); let droppedEntriesCount = 0; if (!this.#calledAtLeastOnce) { droppedEntriesCount = NativePerformance.getDroppedEntriesCount?.( this.#nativeObserverHandle, ) ?? 0; this.#calledAtLeastOnce = true; } this.#callback(entryList, this, {droppedEntriesCount}); }); } #validateObserveOptions(options: PerformanceObserverInit): void { const {type, entryTypes, durationThreshold} = options; if (!type && !entryTypes) { throw new TypeError( "Failed to execute 'observe' on 'PerformanceObserver': An observe() call must not include both entryTypes and type arguments.", ); } if (entryTypes && type) { throw new TypeError( "Failed to execute 'observe' on 'PerformanceObserver': An observe() call must include either entryTypes or type arguments.", ); } if (this.#type === 'multiple' && type) { throw new Error( "Failed to execute 'observe' on 'PerformanceObserver': This observer has performed observe({entryTypes:...}, therefore it cannot perform observe({type:...})", ); } if (this.#type === 'single' && entryTypes) { throw new Error( "Failed to execute 'observe' on 'PerformanceObserver': This PerformanceObserver has performed observe({type:...}, therefore it cannot perform observe({entryTypes:...})", ); } if (entryTypes && durationThreshold != null) { throw new TypeError( "Failed to execute 'observe' on 'PerformanceObserver': An observe() call must not include both entryTypes and durationThreshold arguments.", ); } } static supportedEntryTypes: $ReadOnlyArray<PerformanceEntryType> = getSupportedPerformanceEntryTypes(); } export {PerformanceEventTiming};