8
8
* @flow strict
9
9
*/
10
10
11
+ import type {
12
+ RawPerformanceEntry ,
13
+ RawPerformanceEntryList ,
14
+ RawPerformanceEntryType ,
15
+ } from '../NativeModules/specs/NativePerformanceObserverCxx' ;
16
+
17
+ import NativePerformanceObserver from '../NativeModules/specs/NativePerformanceObserverCxx' ;
18
+ import warnOnce from '../Utilities/warnOnce' ;
19
+
11
20
export type HighResTimeStamp = number ;
12
- // TODO: Extend once new types (such as event) are supported
13
- export type PerformanceEntryType = empty ;
21
+ // TODO: Extend once new types (such as event) are supported.
22
+ // TODO: Get rid of the "undefined" once there is at least one type supported.
23
+ export type PerformanceEntryType = 'undefined' ;
14
24
15
25
export class PerformanceEntry {
16
26
name : string ;
17
27
entryType: PerformanceEntryType ;
18
28
startTime: HighResTimeStamp ;
19
29
duration: number ;
20
30
31
+ constructor ( init : {
32
+ name : string ,
33
+ entryType : PerformanceEntryType ,
34
+ startTime : HighResTimeStamp ,
35
+ duration : number ,
36
+ } ) {
37
+ this . name = init . name ;
38
+ this . entryType = init . entryType ;
39
+ this . startTime = init . startTime ;
40
+ this . duration = init . duration ;
41
+ }
42
+
21
43
// $FlowIgnore: Flow(unclear-type)
22
44
toJSON ( ) : Object {
23
45
return {
@@ -29,6 +51,21 @@ export class PerformanceEntry {
29
51
}
30
52
}
31
53
54
+ function rawToPerformanceEntryType (
55
+ type : RawPerformanceEntryType ,
56
+ ) : PerformanceEntryType {
57
+ return 'undefined' ;
58
+ }
59
+
60
+ function rawToPerformanceEntry ( entry : RawPerformanceEntry ) : PerformanceEntry {
61
+ return new PerformanceEntry ( {
62
+ name : entry . name ,
63
+ entryType : rawToPerformanceEntryType ( entry . entryType ) ,
64
+ startTime : entry . startTime ,
65
+ duration : entry . duration ,
66
+ } ) ;
67
+ }
68
+
32
69
export type PerformanceEntryList = $ReadOnlyArray < PerformanceEntry > ;
33
70
34
71
export class PerformanceObserverEntryList {
@@ -73,6 +110,19 @@ export type PerformanceObserverInit =
73
110
type : PerformanceEntryType ,
74
111
} ;
75
112
113
+ let _observedEntryTypeRefCount : Map < PerformanceEntryType , number > = new Map ( ) ;
114
+
115
+ let _observers : Set < PerformanceObserver > = new Set ( ) ;
116
+
117
+ let _onPerformanceEntryCallbackIsSet : boolean = false ;
118
+
119
+ function warnNoNativePerformanceObserver ( ) {
120
+ warnOnce (
121
+ 'missing-native-performance-observer' ,
122
+ 'Missing native implementation of PerformanceObserver' ,
123
+ ) ;
124
+ }
125
+
76
126
/**
77
127
* Implementation of the PerformanceObserver interface for RN,
78
128
* corresponding to the standard in https://www.w3.org/TR/performance-timeline/
@@ -95,24 +145,81 @@ export type PerformanceObserverInit =
95
145
*/
96
146
export default class PerformanceObserver {
97
147
_callback : PerformanceObserverCallback ;
148
+ _entryTypes : $ReadOnlySet < PerformanceEntryType > ;
98
149
99
150
constructor ( callback : PerformanceObserverCallback ) {
100
151
this . _callback = callback ;
101
152
}
102
153
103
154
observe ( options : PerformanceObserverInit ) {
104
- console . log ( 'PerformanceObserver: started observing' ) ;
155
+ if ( ! NativePerformanceObserver ) {
156
+ warnNoNativePerformanceObserver ( ) ;
157
+ return ;
158
+ }
159
+ if ( ! _onPerformanceEntryCallbackIsSet ) {
160
+ NativePerformanceObserver . setOnPerformanceEntryCallback (
161
+ onPerformanceEntry ,
162
+ ) ;
163
+ _onPerformanceEntryCallbackIsSet = true ;
164
+ }
165
+ if ( options . entryTypes ) {
166
+ this . _entryTypes = new Set ( options . entryTypes ) ;
167
+ } else {
168
+ this . _entryTypes = new Set ( [ options . type ] ) ;
169
+ }
170
+ this . _entryTypes . forEach ( type => {
171
+ if ( ! _observedEntryTypeRefCount . has ( type ) ) {
172
+ NativePerformanceObserver . startReporting ( type ) ;
173
+ }
174
+ _observedEntryTypeRefCount . set (
175
+ type ,
176
+ ( _observedEntryTypeRefCount . get ( type ) ?? 0 ) + 1 ,
177
+ ) ;
178
+ } ) ;
179
+ _observers . add ( this ) ;
105
180
}
106
181
107
182
disconnect ( ) : void {
108
- console . log ( 'PerformanceObserver: stopped observing' ) ;
109
- }
110
-
111
- takeRecords ( ) : PerformanceEntryList {
112
- return [ ] ;
183
+ if ( ! NativePerformanceObserver ) {
184
+ warnNoNativePerformanceObserver ( ) ;
185
+ return ;
186
+ }
187
+ this . _entryTypes . forEach ( type => {
188
+ const entryTypeRefCount = _observedEntryTypeRefCount . get ( type ) ?? 0 ;
189
+ if ( entryTypeRefCount === 1 ) {
190
+ _observedEntryTypeRefCount . delete ( type ) ;
191
+ NativePerformanceObserver . stopReporting ( type ) ;
192
+ } else if ( entryTypeRefCount !== 0 ) {
193
+ _observedEntryTypeRefCount . set ( type , entryTypeRefCount - 1 ) ;
194
+ }
195
+ } ) ;
196
+ _observers . delete ( this ) ;
197
+ if ( _observers . size === 0 ) {
198
+ NativePerformanceObserver . setOnPerformanceEntryCallback ( ) ;
199
+ _onPerformanceEntryCallbackIsSet = false ;
200
+ }
113
201
}
114
202
115
203
static supportedEntryTypes : $ReadOnlyArray < PerformanceEntryType > =
116
204
// TODO: add types once they are fully supported
117
205
Object . freeze ( [ ] ) ;
118
206
}
207
+
208
+ // This is a callback that gets scheduled and periodically called from the native side
209
+ function onPerformanceEntry ( ) {
210
+ if ( ! NativePerformanceObserver ) {
211
+ return ;
212
+ }
213
+ const rawEntries : RawPerformanceEntryList =
214
+ NativePerformanceObserver . getPendingEntries ( ) ;
215
+ const entries = rawEntries . map ( rawToPerformanceEntry ) ;
216
+ _observers . forEach ( observer => {
217
+ const entriesForObserver : PerformanceEntryList = entries . filter ( entry =>
218
+ observer . _entryTypes . has ( entry . entryType ) ,
219
+ ) ;
220
+ observer . _callback (
221
+ new PerformanceObserverEntryList ( entriesForObserver ) ,
222
+ observer ,
223
+ ) ;
224
+ } ) ;
225
+ }
0 commit comments