<
8000
button class="Button Button--iconOnly Button--invisible ExpandableHunkHeaderDiffLine-module__expand-button-line--wZKjF ExpandableHunkHeaderDiffLine-module__expand-button-unified--Eae6C" aria-label="Expand file up from line 231" data-direction="up" aria-hidden="true" tabindex="-1">
@@ -231,6 +231,26 @@ export class DOMEvent implements Event {
231
231
this . propagationState = EventPropagationState . stop ;
232
232
}
233
233
234
+ // During handleEvent(), we want to work on a copy of the listeners array,
235
+ // as any callback could modify the original array during the loop.
236
+ //
237
+ // However, cloning arrays is expensive on this hot path, so we'll do it
238
+ // lazily - i.e. only take a clone if a mutation is about to happen.
239
+ // This optimisation is particularly worth doing as it's very rare that
240
+ // an event listener callback will end up modifying the listeners array.
241
+ private listenersLive : MutationSensitiveArray < ListenerEntry > = emptyArray ;
242
+ private listenersLazyCopy : ListenerEntry [ ] = emptyArray ;
243
+
244
+ // Creating this upon class construction as an arrow function rather than as
245
+ // an inline function bound afresh on each usage saves about 210 nanoseconds
246
+ // per run of handleEvent().
247
+ private onCurrentListenersMutation = ( ) => {
248
+ // Cloning the array via spread syntax is up to 180 nanoseconds
249
+ // faster per run than using Array.prototype.slice().
250
+ this . listenersLazyCopy = [ ...this . listenersLive ] ;
251
+ this . listenersLive . onMutation = null ;
252
+ } ;
253
+
234
254
/**
235
255
* Dispatches a synthetic event event to target and returns true if either
236
256
* event's cancelable attribute value is false or its preventDefault()
@@ -257,6 +277,8 @@ export class DOMEvent implements Event {
257
277
this . target = null ;
258
278
this . eventPhase = this . NONE ;
259
279
this . propagationState = EventPropagationState . resume ;
280
+ this . listenersLive = emptyArray ;
281
+ this . listenersLazyCopy = emptyArray ;
260
282
} ;
261
283
262
284
// `Observable.removeEventListener` would likely suffice, but grabbing
@@ -282,10 +304,10 @@ export class DOMEvent implements Event {
282
304
// event. This keeps behaviour as consistent with DOM Events as
283
305
// possible.
284
306
307
+ this . listenersLazyCopy = this . listenersLive = getGlobalEventHandlersPreHandling ?.( ) || emptyArray ;
285
308
this . handleEvent ( {
286
309
data,
287
310
isGlobal : true ,
288
- getListenersForType : ( ) => getGlobalEventHandlersPreHandling ?.( ) ?? emptyArray ,
289
311
removeEventListener : removeGlobalEventListener ,
290
312
phase : this . CAPTURING_PHASE ,
291
313
} ) ;
@@ -297,13 +319,14 @@ export class DOMEvent implements Event {
297
319
this . currentTarget = currentTarget ;
298
320
this . eventPhase = this . target === this . currentTarget ? this . AT_TARGET : this . CAPTURING_PHASE ;
299
321
322
+ this . listenersLazyCopy = this . listenersLive = currentTarget . getEventList ( this . type ) || emptyArray ;
300
323
this . handleEvent ( {
301
324
data,
302
325
isGlobal : false ,
303
- getListenersForType : ( ) => currentTarget . getEventList ( this . type ) ?? emptyArray ,
304
326
removeEventListener : currentTarget . removeEventListener . bind ( currentTarget ) as Observable [ 'removeEventListener' ] ,
305
327
phase : this . CAPTURING_PHASE ,
306
328
} ) ;
329
+
307
330
if ( this . propagationState !== EventPropagationState . resume ) {
308
331
reset ( ) ;
309
332
return this . returnValue ;
@@ -316,13 +339,14 @@ export class DOMEvent implements Event {
316
339
const currentTarget = eventPath [ i ] ;
317
340
this . eventPhase = this . target === this <
8000
span class=pl-kos>.currentTarget ? this . AT_TARGET : this . BUBBLING_PHASE ;
318
341
342
+ this . listenersLazyCopy = this . listenersLive = currentTarget . getEventList ( this . type ) || emptyArray ;
319
343
this . handleEvent ( {
320
344
data,
321
345
isGlobal : false ,
322
- getListenersForType : ( ) => currentTarget . getEventList ( this . type ) ?? emptyArray ,
323
346
removeEventListener : currentTarget . removeEventListener . bind ( currentTarget ) as Observable [ 'removeEventListener' ] ,
324
347
phase : this . BUBBLING_PHASE ,
325
348
} ) ;
349
+
326
350
if ( this . propagationState !== EventPropagationState . resume ) {
327
351
reset ( ) ;
328
352
return this . returnValue ;
@@ -341,10 +365,10 @@ export class DOMEvent implements Event {
341
365
this . eventPhase = this . BUBBLING_PHASE ;
342
366
}
343
367
368
+ this . listenersLazyCopy = this . listenersLive = getGlobalEventHandlersPostHandling ?.( ) || emptyArray ;
344
369
this . handleEvent ( {
345
370
data,
346
371
isGlobal : true ,
347
- getListenersForType : ( ) => getGlobalEventHandlersPostHandling ?.( ) ?? emptyArray ,
348
372
removeEventListener : removeGlobalEventListener ,
349
373
phase : this . BUBBLING_PHASE ,
350
374
} ) ;
@@ -353,32 +377,12 @@ export class DOMEvent implements Event {
353
377
return this . returnValue ;
354
378
}
355
379
356
- private handleEvent ( { data, isGlobal, getListenersForType, phase, removeEventListener } : { data : EventData ; isGlobal : boolean ; getListenersForType : ( ) => MutationSensitiveArray < ListenerEntry > ; phase : 0 | 1 | 2 | 3 ; removeEventListener : ( eventName : string , callback ?: any , thisArg ?: any , capture ?: boolean ) => void } ) {
357
- // We want to work on a copy of the array, as any callback could modify
358
- // the original array during the loop.
359
- //
360
- // However, cloning arrays is expensive on this hot path, so we'll do it
361
- // lazily - i.e. only take a clone if a mutation is about to happen.
362
- // This optimisation is particularly worth doing as it's very rare that
363
- // an event listener callback will end up modifying the listeners array.
364
- const listenersLive : MutationSensitiveArray < ListenerEntry > = getListenersForType ( ) ;
365
-
380
+ private handleEvent ( { data, isGlobal, phase, removeEventListener } : { data : EventData ; isGlobal : boolean ; phase : 0 | 1 | 2 | 3 ; removeEventListener : ( eventName : string , callback ?: any , thisArg ?: any , capture ?: boolean ) => void } ) {
366
381
// Set a listener to clone the array just before any mutations.
367
- let listenersLazyCopy : ListenerEntry [ ] = listenersLive ;
368
- listenersLive . onMutation = ( ) => ( mutation : string , payload ?: unknown ) => {
369
- console . log ( `handleEvent "${ data . eventName } ": doLazyCopy due to "${ mutation } "` , payload ) ;
370
- // Cloning the array via spread syntax is up to 180 nanoseconds
371
- // faster per run than using Array.prototype.slice().
372
- listenersLazyCopy = [ ...listenersLive ] ;
373
- listenersLive . onMutation = null ;
374
- } ;
382
+ this . listenersLive . onMutation = this . onCurrentListenersMutation ;
375
383
376
- // Make sure we clear the callback before we exit the function,
377
- // otherwise we may wastefully clone the array on future mutations.
378
- const cleanup = ( ) => ( listenersLive . onMutation = null ) ;
379
-
380
- for ( let i = listenersLazyCopy . length - 1 ; i >= 0 ; i -- ) {
381
- const listener = listenersLazyCopy [ i ] ;
384
+ for ( let i = this . listenersLazyCopy . length - 1 ; i >= 0 ; i -- ) {
385
+ const listener = this . listenersLazyCopy [ i ] ;
382
386
383
387
// Assigning variables this old-fashioned way is up to 50
384
388
// nanoseconds faster per run than ESM destructuring syntax.
@@ -394,7 +398,7 @@ export class DOMEvent implements Event {
394
398
// We simply use a strict equality check here because we trust that
395
399
// the listeners provider will never allow two deeply-equal
396 400
// listeners into the array.
397
- if ( ! listenersLive . includes ( listener ) ) {
401
+ if ( ! this . listenersLive . includes ( listener ) ) {
398
402
continue ;
399
403
}
400
404
@@ -424,12 +428,13 @@ export class DOMEvent implements Event {
424
428
}
425
429
426
430
if ( this . propagationState === EventPropagationState . stopImmediate ) {
427
- cleanup ( ) ;
428
- return ;
431
+ break ;
429
432
}
430
433
}
431
434
432
- cleanup ( ) ;
435
+ // Make sure we clear the callback before we exit the function,
436
+ // otherwise we may wastefully clone the array on future mutations.
437
+ this . listenersLive . onMutation = null ;
433
438
}
434
439
}
435
440
0 commit comments