@@ -12,7 +12,7 @@ import type {
12
12
import { isString } from './is' ;
13
13
import type { ConsoleLevel } from './logger' ;
14
14
import { CONSOLE_LEVELS , logger , originalConsoleMethods } from './logger' ;
15
- import { fill } from './object' ;
15
+ import { addNonEnumerableProperty , fill } from './object' ;
16
16
import { getFunctionName } from './stacktrace' ;
17
17
import { supportsHistory , supportsNativeFetch } from './supports' ;
18
18
import { getGlobalObject , GLOBAL_OBJ } from './worldwide' ;
@@ -400,31 +400,24 @@ function instrumentHistory(): void {
400
400
fill ( WINDOW . history , 'replaceState' , historyReplacementFunction ) ;
401
401
}
402
402
403
- const debounceDuration = 1000 ;
403
+ const DEBOUNCE_DURATION = 1000 ;
404
404
let debounceTimerID : number | undefined ;
405
405
let lastCapturedEvent : Event | undefined ;
406
406
407
407
/**
408
- * Decide whether the current event should finish the debounce of previously captured one.
409
- * @param previous previously captured event
410
- * @param current event to be captured
408
+ * Check whether two DOM events are similar to eachother. For example, two click events on the same button.
411
409
*/
412
- function shouldShortcircuitPreviousDebounce ( previous : Event | undefined , current : Event ) : boolean {
413
- // If there was no previous event, it should always be swapped for the new one.
414
- if ( ! previous ) {
415
- return true ;
416
- }
417
-
410
+ function areSimilarDomEvents ( a : Event , b : Event ) : boolean {
418
411
// If both events have different type, then user definitely performed two separate actions. e.g. click + keypress.
419
- if ( previous . type !== current . type ) {
420
- return true ;
412
+ if ( a . type !== b . type ) {
413
+ return false ;
421
414
}
422
415
423
416
try {
424
417
// If both events have the same type, it's still possible that actions were performed on different targets.
425
418
// e.g. 2 clicks on different buttons.
426
- if ( previous . target !== current . target ) {
427
- return true ;
419
+ if ( a . target !== b . target ) {
420
+ return false ;
428
421
}
429
422
} catch ( e ) {
430
423
// just accessing `target` property can throw an exception in some rare circumstances
@@ -434,7 +427,7 @@ function shouldShortcircuitPreviousDebounce(previous: Event | undefined, current
434
427
// If both events have the same type _and_ same `target` (an element which triggered an event, _not necessarily_
435
428
// to which an event listener was attached), we treat them as the same action, as we want to capture
436
429
// only one breadcrumb. e.g. multiple clicks on the same button, or typing inside a user input box.
437
- return false ;
430
+ return true ;
438
431
}
439
432
440
433
/**
@@ -475,11 +468,11 @@ function shouldSkipDOMEvent(event: Event): boolean {
475
468
* @hidden
476
469
*/
477
470
function makeDOMEventHandler ( handler : Function , globalListener : boolean = false ) : ( event : Event ) => void {
478
- return ( event : Event ) : void => {
471
+ return ( event : Event & { _sentryCaptured ?: true } ) : void => {
479
472
// It's possible this handler might trigger multiple times for the same
480
473
// event (e.g. event propagation through node ancestors).
481
474
// Ignore if we've already captured that event.
482
- if ( ! event || lastCapturedEvent === event ) {
475
+ if ( ! event || event [ '_sentryCaptured' ] ) {
483
476
return ;
484
477
}
485
478
@@ -488,20 +481,15 @@ function makeDOMEventHandler(handler: Function, globalListener: boolean = false)
488
481
return ;
489
482
}
490
483
484
+ // Mark event as "seen"
485
+ addNonEnumerableProperty ( event , '_sentryCaptured' , true ) ;
486
+
491
487
const name = event . type === 'keypress' ? 'input' : event . type ;
492
488
493
- // If there is no debounce timer, it means that we can safely capture the new event and store it for future comparisons.
494
- if ( debounceTimerID === undefined ) {
495
- handler ( {
496
- event : event ,
497
- name,
498
- global : globalListener ,
499
- } ) ;
500
- lastCapturedEvent = event ;
501
- }
502
- // If there is a debounce awaiting, see if the new event is different enough to treat it as a unique one.
489
+ // If there is no last captured event, it means that we can safely capture the new event and store it for future comparisons.
490
+ // If there is a last captured event, see if the new event is different enough to treat it as a unique one.
503
491
// If that's the case, emit the previous event and store locally the newly-captured DOM event.
504
- else if ( shouldShortcircuitPreviousDebounce ( lastCapturedEvent , event ) ) {
492
+ if ( lastCapturedEvent === undefined || ! areSimilarDomEvents ( lastCapturedEvent , event ) ) {
505
493
handler ( {
506
494
event : event ,
507
495
name,
@@ -513,8 +501,8 @@ function makeDOMEventHandler(handler: Function, globalListener: boolean = false)
513
501
// Start a new debounce timer that will prevent us from capturing multiple events that should be grouped together.
514
502
clearTimeout ( debounceTimerID ) ;
515
503
debounceTimerID = WINDOW . setTimeout ( ( ) => {
516
- debounceTimerID = undefined ;
517
- } , debounceDuration ) ;
504
+ lastCapturedEvent = undefined ;
505
+ } , DEBOUNCE_DURATION ) ;
518
506
} ;
519
507
}
520
508
0 commit comments