Fix problems in PerformanceObserver notifications (#43847)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/43847

Changelog: [internal]

(This is an internal change because the API hasn't been released in OSS yet)

This fixes 2 problems in how we dispatch `PerformanceObserver` notifications:
1. If an observer callback throws an error, the remaining observers don't receive notifications.
2. We're notifying observers with an empty list of events when they don't match the filters.

Reviewed By: javache

Differential Revision: D55646390

fbshipit-source-id: 6511c7babd45517baf42076308268ea89afe1265
This commit is contained in:
Rubén Norte
2024-04-09 07:51:03 -07:00
committed by Facebook GitHub Bot
parent 1ad3593167
commit af27d29553
2 changed files with 65 additions and 5 deletions
@@ -100,11 +100,17 @@ const onPerformanceEntry = () => {
const durationThreshold = observerConfig.entryTypes.get(entry.entryType);
return entry.duration >= (durationThreshold ?? 0);
});
observerConfig.callback(
new PerformanceObserverEntryList(entriesForObserver),
observer,
droppedEntriesCount,
);
if (entriesForObserver.length !== 0) {
try {
observerConfig.callback(
new PerformanceObserverEntryList(entriesForObserver),
observer,
droppedEntriesCount,
);
} catch (error) {
console.error(error);
}
}
}
};
@@ -205,4 +205,58 @@ describe('PerformanceObserver', () => {
'mark7',
]);
});
it('should guard against errors in observer callbacks', () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
const observer1Callback = jest.fn(() => {
throw new Error('observer 1 callback');
});
const observer1 = new PerformanceObserver(observer1Callback);
const observer2Callback = jest.fn();
const observer2 = new PerformanceObserver(observer2Callback);
observer1.observe({type: 'mark'});
observer2.observe({type: 'mark'});
NativePerformanceObserver.logRawEntry({
name: 'mark1',
entryType: RawPerformanceEntryTypeValues.MARK,
startTime: 0,
duration: 200,
});
jest.runAllTicks();
expect(observer1Callback).toHaveBeenCalled();
expect(observer2Callback).toHaveBeenCalled();
expect(console.error).toHaveBeenCalledWith(
new Error('observer 1 callback'),
);
});
it('should not invoke observers with non-matching entries', () => {
const observer1Callback = jest.fn();
const observer1 = new PerformanceObserver(observer1Callback);
const observer2Callback = jest.fn();
const observer2 = new PerformanceObserver(observer2Callback);
observer1.observe({type: 'mark'});
observer2.observe({type: 'measure'});
NativePerformanceObserver.logRawEntry({
name: 'mark1',
entryType: RawPerformanceEntryTypeValues.MARK,
startTime: 0,
duration: 200,
});
jest.runAllTicks();
expect(observer1Callback).toHaveBeenCalled();
expect(observer2Callback).not.toHaveBeenCalled();
});
});