@@ -18,9 +18,9 @@ type RemoveEventListener = (
1818type InstrumentedElement = Element & {
1919 __sentry_instrumentation_handlers__ ?: {
2020 [ key in 'click' | 'keypress' ] ?: {
21- handler ?: unknown ;
22- /** The number of custom listeners attached to this element */
23- refCount : number ;
21+ handler ?: EventListenerOrEventListenerObject ;
22+ capture ?: boolean ;
23+ listeners : Map < EventListenerOrEventListenerObject , Set < boolean > > ;
2424 } ;
2525 } ;
2626} ;
@@ -31,6 +31,48 @@ let debounceTimerID: number | undefined;
3131let lastCapturedEventType : string | undefined ;
3232let lastCapturedEventTargetId : string | undefined ;
3333
34+ function getCapture ( options : boolean | EventListenerOptions | AddEventListenerOptions | undefined ) : boolean {
35+ return typeof options === 'boolean' ? options : ! ! options ?. capture ;
36+ }
37+
38+ function hasListener (
39+ handler : { listeners : Map < EventListenerOrEventListenerObject , Set < boolean > > } ,
40+ listener : EventListenerOrEventListenerObject ,
41+ capture : boolean ,
42+ ) : boolean {
43+ return handler . listeners . get ( listener ) ?. has ( capture ) ?? false ;
44+ }
45+
46+ function trackListener (
47+ handler : { listeners : Map < EventListenerOrEventListenerObject , Set < boolean > > } ,
48+ listener : EventListenerOrEventListenerObject ,
49+ capture : boolean ,
50+ ) : void {
51+ const captures = handler . listeners . get ( listener ) ;
52+ if ( captures ) {
53+ captures . add ( capture ) ;
54+ } else {
55+ handler . listeners . set ( listener , new Set ( [ capture ] ) ) ;
56+ }
57+ }
58+
59+ function untrackListener (
60+ handler : { listeners : Map < EventListenerOrEventListenerObject , Set < boolean > > } ,
61+ listener : EventListenerOrEventListenerObject ,
62+ capture : boolean ,
63+ ) : boolean {
64+ const captures = handler . listeners . get ( listener ) ;
65+ if ( ! captures ?. delete ( capture ) ) {
66+ return false ;
67+ }
68+
69+ if ( captures . size === 0 ) {
70+ handler . listeners . delete ( listener ) ;
71+ }
72+
73+ return true ;
74+ }
75+
3476/**
3577 * Add an instrumentation handler for when a click or a keypress happens.
3678 *
@@ -77,15 +119,21 @@ export function instrumentDOM(): void {
77119 try {
78120 const handlers = ( this . __sentry_instrumentation_handlers__ =
79121 this . __sentry_instrumentation_handlers__ || { } ) ;
80- const handlerForType = ( handlers [ type ] = handlers [ type ] || { refCount : 0 } ) ;
122+ const handlerForType = ( handlers [ type ] = handlers [ type ] || {
123+ listeners : new Map < EventListenerOrEventListenerObject , Set < boolean > > ( ) ,
124+ } ) ;
125+ const capture = getCapture ( options ) ;
126+
127+ if ( ! hasListener ( handlerForType , listener , capture ) ) {
128+ if ( ! handlerForType . handler ) {
129+ const handler = makeDOMEventHandler ( triggerDOMHandler ) ;
130+ originalAddEventListener . call ( this , type , handler , capture ) ;
131+ handlerForType . handler = handler ;
132+ handlerForType . capture = capture ;
133+ }
81134
82- if ( ! handlerForType . handler ) {
83- const handler = makeDOMEventHandler ( triggerDOMHandler ) ;
84- handlerForType . handler = handler ;
85- originalAddEventListener . call ( this , type , handler , options ) ;
135+ trackListener ( handlerForType , listener , capture ) ;
86136 }
87-
88- handlerForType . refCount ++ ;
89137 } catch {
90138 // Accessing dom properties is always fragile.
91139 // Also allows us to skip `addEventListeners` calls with no proper `this` context.
@@ -106,11 +154,10 @@ export function instrumentDOM(): void {
106154 const handlers = this . __sentry_instrumentation_handlers__ || { } ;
107155 const handlerForType = handlers [ type ] ;
108156
109- if ( handlerForType ) {
110- handlerForType . refCount -- ;
157+ if ( handlerForType && untrackListener ( handlerForType , listener , getCapture ( options ) ) ) {
111158 // If there are no longer any custom handlers of the current type on this element, we can remove ours, too.
112- if ( handlerForType . refCount < = 0 ) {
113- originalRemoveEventListener . call ( this , type , handlerForType . handler , options ) ;
159+ if ( handlerForType . listeners . size == = 0 ) {
160+ originalRemoveEventListener . call ( this , type , handlerForType . handler , handlerForType . capture ) ;
114161 handlerForType . handler = undefined ;
115162 delete handlers [ type ] ; // eslint-disable-line @typescript-eslint/no-dynamic-delete
116163 }
0 commit comments