diff --git a/Libraries/WebPerformance/NativePerformanceObserver.cpp b/Libraries/WebPerformance/NativePerformanceObserver.cpp index 8a7aac5c06a1..cf98a4f4620a 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.cpp +++ b/Libraries/WebPerformance/NativePerformanceObserver.cpp @@ -58,4 +58,10 @@ void NativePerformanceObserver::setOnPerformanceEntryCallback( PerformanceEntryReporter::getInstance().setReportingCallback(callback); } +void NativePerformanceObserver::logRawEntry( + jsi::Runtime &rt, + RawPerformanceEntry entry) { + PerformanceEntryReporter::getInstance().logEntry(entry); +} + } // namespace facebook::react diff --git a/Libraries/WebPerformance/NativePerformanceObserver.h b/Libraries/WebPerformance/NativePerformanceObserver.h index 7eb5349c0eed..2b4fdc2f156b 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.h +++ b/Libraries/WebPerformance/NativePerformanceObserver.h @@ -69,6 +69,8 @@ class NativePerformanceObserver jsi::Runtime &rt, std::optional> callback); + void logRawEntry(jsi::Runtime &rt, RawPerformanceEntry entry); + private: }; diff --git a/Libraries/WebPerformance/NativePerformanceObserver.js b/Libraries/WebPerformance/NativePerformanceObserver.js index ae87d47151fb..e407652d8715 100644 --- a/Libraries/WebPerformance/NativePerformanceObserver.js +++ b/Libraries/WebPerformance/NativePerformanceObserver.js @@ -11,6 +11,7 @@ import type {TurboModule} from '../TurboModule/RCTExport'; import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; +import NativePerformanceObserverRef from './NativePerformanceObserverRef'; export const RawPerformanceEntryTypeValues = { UNDEFINED: 0, @@ -42,8 +43,8 @@ export interface Spec extends TurboModule { +stopReporting: (entryType: string) => void; +popPendingEntries: () => GetPendingEntriesResult; +setOnPerformanceEntryCallback: (callback?: () => void) => void; + +logRawEntry: (entry: RawPerformanceEntry) => void; } -export default (TurboModuleRegistry.get( - 'NativePerformanceObserverCxx', -): ?Spec); +export default (TurboModuleRegistry.get('NativePerformanceObserverCxx') ?? + NativePerformanceObserverRef: ?Spec); diff --git a/Libraries/WebPerformance/NativePerformanceObserverRef.js b/Libraries/WebPerformance/NativePerformanceObserverRef.js new file mode 100644 index 000000000000..5f85eec7c779 --- /dev/null +++ b/Libraries/WebPerformance/NativePerformanceObserverRef.js @@ -0,0 +1,71 @@ +/** + * 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. + * + * @flow strict + * @format + */ + +import type { + GetPendingEntriesResult, + RawPerformanceEntry, + RawPerformanceEntryType, + Spec as NativePerformanceObserver, +} from './NativePerformanceObserver'; + +import {RawPerformanceEntryTypeValues} from './NativePerformanceObserver'; + +function performanceEntryTypeToRaw(type: string): RawPerformanceEntryType { + switch (type) { + case 'mark': + return RawPerformanceEntryTypeValues.MARK; + case 'measure': + return RawPerformanceEntryTypeValues.MEASURE; + case 'event': + return RawPerformanceEntryTypeValues.EVENT; + default: + throw new TypeError( + `performanceEntryTypeToRaw: unexpected performance entry type received: ${type}`, + ); + } +} + +const NativePerformanceObserverRef: NativePerformanceObserver = (function () { + const _reportingType: Set = new Set(); + let _entries: Array = []; + let _onPerformanceEntryCallback: ?() => void; + + return { + startReporting: (entryType: string) => { + _reportingType.add(performanceEntryTypeToRaw(entryType)); + }, + + stopReporting: (entryType: string) => { + _reportingType.delete(performanceEntryTypeToRaw(entryType)); + }, + + popPendingEntries: (): GetPendingEntriesResult => { + const res = _entries; + _entries = []; + return { + droppedEntriesCount: 0, + entries: res, + }; + }, + + setOnPerformanceEntryCallback: (callback?: () => void) => { + _onPerformanceEntryCallback = callback; + }, + + logRawEntry: (entry: RawPerformanceEntry) => { + if (_reportingType.has(entry.entryType)) { + _entries.push(entry); + _onPerformanceEntryCallback?.(); + } + }, + }; +})(); + +export default NativePerformanceObserverRef; diff --git a/Libraries/WebPerformance/__tests__/NativePerformanceObserverRef-test.js b/Libraries/WebPerformance/__tests__/NativePerformanceObserverRef-test.js new file mode 100644 index 000000000000..6e60ebbd75d7 --- /dev/null +++ b/Libraries/WebPerformance/__tests__/NativePerformanceObserverRef-test.js @@ -0,0 +1,54 @@ +/** + * 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 + * @oncall react_native + */ + +import {RawPerformanceEntryTypeValues} from '../NativePerformanceObserver'; +import NativePerformanceObserverRef from '../NativePerformanceObserverRef'; + +describe('NativePerformanceObserver', () => { + it('correctly starts and stops listening to entries in a nominal scenario', async () => { + NativePerformanceObserverRef.startReporting('mark'); + + NativePerformanceObserverRef.logRawEntry({ + name: 'mark1', + entryType: RawPerformanceEntryTypeValues.MARK, + startTime: 0, + duration: 10, + }); + + NativePerformanceObserverRef.logRawEntry({ + name: 'mark2', + entryType: RawPerformanceEntryTypeValues.MARK, + startTime: 0, + duration: 20, + }); + + NativePerformanceObserverRef.logRawEntry({ + name: 'event1', + entryType: RawPerformanceEntryTypeValues.EVENT, + startTime: 0, + duration: 20, + }); + + const entriesResult = NativePerformanceObserverRef.popPendingEntries(); + expect(entriesResult).not.toBe(undefined); + const entries = entriesResult.entries; + + expect(entries.length).toBe(2); + expect(entries[0].name).toBe('mark1'); + expect(entries[1].name).toBe('mark2'); + + const entriesResult1 = NativePerformanceObserverRef.popPendingEntries(); + expect(entriesResult1).not.toBe(undefined); + const entries1 = entriesResult1.entries; + expect(entries1.length).toBe(0); + + NativePerformanceObserverRef.stopReporting('mark'); + }); +}); diff --git a/Libraries/WebPerformance/__tests__/PerformanceObserver-test.js b/Libraries/WebPerformance/__tests__/PerformanceObserver-test.js new file mode 100644 index 000000000000..58edcc24c8cd --- /dev/null +++ b/Libraries/WebPerformance/__tests__/PerformanceObserver-test.js @@ -0,0 +1,22 @@ +/** + * 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 + * @oncall react_native + */ + +import NativePerformanceObserver from '../NativePerformanceObserver'; +import PerformanceObserver from '../PerformanceObserver'; + +describe('PerformanceObserver', () => { + it('is backed by an existing NativePerformanceObserver implementation', async () => { + expect(NativePerformanceObserver).not.toBe(undefined); + + const observer = new PerformanceObserver((list, _observer) => {}); + expect(() => observer.observe({entryTypes: []})).not.toThrow(); + observer.disconnect(); + }); +});