@@ -83,202 +83,203 @@ export class TryCatch implements Integration {
83
83
const global = getGlobalObject ( ) ;
84
84
85
85
if ( this . _options . setTimeout ) {
86
- fill ( global , 'setTimeout' , this . _wrapTimeFunction . bind ( this ) ) ;
86
+ fill ( global , 'setTimeout' , _wrapTimeFunction ) ;
87
87
}
88
88
89
89
if ( this . _options . setInterval ) {
90
- fill ( global , 'setInterval' , this . _wrapTimeFunction . bind ( this ) ) ;
90
+ fill ( global , 'setInterval' , _wrapTimeFunction ) ;
91
91
}
92
92
93
93
if ( this . _options . requestAnimationFrame ) {
94
- fill ( global , 'requestAnimationFrame' , this . _wrapRAF . bind ( this ) ) ;
94
+ fill ( global , 'requestAnimationFrame' , _wrapRAF ) ;
95
95
}
96
96
97
97
if ( this . _options . XMLHttpRequest && 'XMLHttpRequest' in global ) {
98
- fill ( XMLHttpRequest . prototype , 'send' , this . _wrapXHR . bind ( this ) ) ;
98
+ fill ( XMLHttpRequest . prototype , 'send' , _wrapXHR ) ;
99
99
}
100
100
101
- if ( this . _options . eventTarget ) {
102
- const eventTarget = Array . isArray ( this . _options . eventTarget ) ? this . _options . eventTarget : DEFAULT_EVENT_TARGET ;
103
- eventTarget . forEach ( this . _wrapEventTarget . bind ( this ) ) ;
101
+ const eventTargetOption = this . _options . eventTarget ;
102
+ if ( eventTargetOption ) {
103
+ const eventTarget = Array . isArray ( eventTargetOption ) ? eventTargetOption : DEFAULT_EVENT_TARGET ;
104
+ eventTarget . forEach ( _wrapEventTarget ) ;
104
105
}
105
106
}
107
+ }
106
108
107
- /** JSDoc */
108
- private _wrapTimeFunction ( original : ( ) => void ) : ( ) => number {
109
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
- return function ( this : any , ...args : any [ ] ) : number {
111
- const originalCallback = args [ 0 ] ;
112
- args [ 0 ] = wrap ( originalCallback , {
109
+ /** JSDoc */
110
+ function _wrapTimeFunction ( original : ( ) => void ) : ( ) => number {
111
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
+ return function ( this : any , ...args : any [ ] ) : number {
113
+ const originalCallback = args [ 0 ] ;
114
+ args [ 0 ] = wrap ( originalCallback , {
115
+ mechanism : {
116
+ data : { function : getFunctionName ( original ) } ,
117
+ handled : true ,
118
+ type : 'instrument' ,
119
+ } ,
120
+ } ) ;
121
+ return original . apply ( this , args ) ;
122
+ } ;
123
+ }
124
+
125
+ /** JSDoc */
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ function _wrapRAF ( original : any ) : ( callback : ( ) => void ) => any {
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ return function ( this : any , callback : ( ) => void ) : ( ) => void {
130
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
131
+ return original . call (
132
+ this ,
133
+ wrap ( callback , {
113
134
mechanism : {
114
- data : { function : getFunctionName ( original ) } ,
135
+ data : {
136
+ function : 'requestAnimationFrame' ,
137
+ handler : getFunctionName ( original ) ,
138
+ } ,
115
139
handled : true ,
116
140
type : 'instrument' ,
117
141
} ,
118
- } ) ;
119
- return original . apply ( this , args ) ;
120
- } ;
121
- }
142
+ } ) ,
143
+ ) ;
144
+ } ;
145
+ }
122
146
123
- /** JSDoc */
147
+ /** JSDoc */
148
+ function _wrapXHR ( originalSend : ( ) => void ) : ( ) => void {
124
149
// eslint-disable-next-line @typescript-eslint/no-explicit-any
125
- private _wrapRAF ( original : any ) : ( callback : ( ) => void ) => any {
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
- return function ( this : any , callback : ( ) => void ) : ( ) => void {
128
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
129
- return original . call (
130
- this ,
131
- wrap ( callback , {
132
- mechanism : {
133
- data : {
134
- function : 'requestAnimationFrame' ,
135
- handler : getFunctionName ( original ) ,
136
- } ,
137
- handled : true ,
138
- type : 'instrument' ,
139
- } ,
140
- } ) ,
141
- ) ;
142
- } ;
143
- }
144
-
145
- /** JSDoc */
146
- private _wrapEventTarget ( target : string ) : void {
147
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
- const global = getGlobalObject ( ) as { [ key : string ] : any } ;
149
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
150
- const proto = global [ target ] && global [ target ] . prototype ;
151
-
152
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
153
- if ( ! proto || ! proto . hasOwnProperty || ! proto . hasOwnProperty ( 'addEventListener' ) ) {
154
- return ;
155
- }
150
+ return function ( this : XMLHttpRequest , ...args : any [ ] ) : void {
151
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
152
+ const xhr = this ;
153
+ const xmlHttpRequestProps : XMLHttpRequestProp [ ] = [ 'onload' , 'onerror' , 'onprogress' , 'onreadystatechange' ] ;
156
154
157
- fill ( proto , 'addEventListener' , function (
158
- original : ( ) => void ,
159
- ) : ( eventName : string , fn : EventListenerObject , options ?: boolean | AddEventListenerOptions ) => void {
160
- return function (
155
+ xmlHttpRequestProps . forEach ( prop => {
156
+ if ( prop in xhr && typeof xhr [ prop ] === 'function' ) {
161
157
// eslint-disable-next-line @typescript-eslint/no-explicit-any
162
- this : any ,
163
- eventName : string ,
164
- fn : EventListenerObject ,
165
- options ?: boolean | AddEventListenerOptions ,
166
- ) : ( eventName : string , fn : EventListenerObject , capture ?: boolean , secure ?: boolean ) => void {
167
- try {
168
- if ( typeof fn . handleEvent === 'function' ) {
169
- fn . handleEvent = wrap ( fn . handleEvent . bind ( fn ) , {
170
- mechanism : {
171
- data : {
172
- function : 'handleEvent' ,
173
- handler : getFunctionName ( fn ) ,
174
- target,
175
- } ,
176
- handled : true ,
177
- type : 'instrument' ,
178
- } ,
179
- } ) ;
180
- }
181
- } catch ( err ) {
182
- // can sometimes get 'Permission denied to access property "handle Event'
183
- }
184
-
185
- return original . call (
186
- this ,
187
- eventName ,
188
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
189
- wrap ( ( fn as any ) as WrappedFunction , {
158
+ fill ( xhr , prop , function ( original : WrappedFunction ) : ( ) => any {
159
+ const wrapOptions = {
190
160
mechanism : {
191
161
data : {
192
- function : 'addEventListener' ,
193
- handler : getFunctionName ( fn ) ,
194
- target,
162
+ function : prop ,
163
+ handler : getFunctionName ( original ) ,
195
164
} ,
196
165
handled : true ,
197
166
type : 'instrument' ,
198
167
} ,
199
- } ) ,
200
- options ,
201
- ) ;
202
- } ;
203
- } ) ;
168
+ } ;
204
169
205
- fill ( proto , 'removeEventListener' , function (
206
- originalRemoveEventListener : ( ) => void ,
207
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
208
- ) : ( this : any , eventName : string , fn : EventListenerObject , options ?: boolean | EventListenerOptions ) => ( ) => void {
209
- return function (
210
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
211
- this : any ,
212
- eventName : string ,
213
- fn : EventListenerObject ,
214
- options ?: boolean | EventListenerOptions ,
215
- ) : ( ) => void {
216
- /**
217
- * There are 2 possible scenarios here:
218
- *
219
- * 1. Someone passes a callback, which was attached prior to Sentry initialization, or by using unmodified
220
- * method, eg. `document.addEventListener.call(el, name, handler). In this case, we treat this function
221
- * as a pass-through, and call original `removeEventListener` with it.
222
- *
223
- * 2. Someone passes a callback, which was attached after Sentry was initialized, which means that it was using
224
- * our wrapped version of `addEventListener`, which internally calls `wrap` helper.
225
- * This helper "wraps" whole callback inside a try/catch statement, and attached appropriate metadata to it,
226
- * in order for us to make a distinction between wrapped/non-wrapped functions possible.
227
- * If a function was wrapped, it has additional property of `__sentry_wrapped__`, holding the handler.
228
- *
229
- * When someone adds a handler prior to initialization, and then do it again, but after,
230
- * then we have to detach both of them. Otherwise, if we'd detach only wrapped one, it'd be impossible
231
- * to get rid of the initial handler and it'd stick there forever.
232
- */
233
- const wrappedEventHandler = ( fn as unknown ) as WrappedFunction ;
234
- try {
235
- const originalEventHandler = wrappedEventHandler && wrappedEventHandler . __sentry_wrapped__ ;
236
- if ( originalEventHandler ) {
237
- originalRemoveEventListener . call ( this , eventName , originalEventHandler , options ) ;
170
+ // If Instrument integration has been called before TryCatch, get the name of original function
171
+ if ( original . __sentry_original__ ) {
172
+ wrapOptions . mechanism . data . handler = getFunctionName ( original . __sentry_original__ ) ;
238
173
}
239
- } catch ( e ) {
240
- // ignore, accessing __sentry_wrapped__ will throw in some Selenium environments
241
- }
242
- return originalRemoveEventListener . call ( this , eventName , wrappedEventHandler , options ) ;
243
- } ;
174
+
175
+ // Otherwise wrap directly
176
+ return wrap ( original , wrapOptions ) ;
177
+ } ) ;
178
+ }
244
179
} ) ;
245
- }
246
180
247
- /** JSDoc */
248
- private _wrapXHR ( originalSend : ( ) => void ) : ( ) => void {
249
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
250
- return function ( this : XMLHttpRequest , ...args : any [ ] ) : void {
251
- // eslint-disable-next-line @typescript-eslint/no-this-alias
252
- const xhr = this ;
253
- const xmlHttpRequestProps : XMLHttpRequestProp [ ] = [ 'onload' , 'onerror' , 'onprogress' , 'onreadystatechange' ] ;
181
+ return originalSend . apply ( this , args ) ;
182
+ } ;
183
+ }
254
184
255
- xmlHttpRequestProps . forEach ( prop => {
256
- if ( prop in xhr && typeof xhr [ prop ] === 'function' ) {
257
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
258
- fill ( xhr , prop , function ( original : WrappedFunction ) : ( ) => any {
259
- const wrapOptions = {
260
- mechanism : {
261
- data : {
262
- function : prop ,
263
- handler : getFunctionName ( original ) ,
264
- } ,
265
- handled : true ,
266
- type : 'instrument' ,
267
- } ,
268
- } ;
185
+ /** JSDoc */
186
+ function _wrapEventTarget ( target : string ) : void {
187
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
188
+ const global = getGlobalObject ( ) as { [ key : string ] : any } ;
189
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
190
+ const proto = global [ target ] && global [ target ] . prototype ;
269
191
270
- // If Instrument integration has been called before TryCatch, get the name of original function
271
- if ( original . __sentry_original__ ) {
272
- wrapOptions . mechanism . data . handler = getFunctionName ( original . __sentry_original__ ) ;
273
- }
192
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
193
+ if ( ! proto || ! proto . hasOwnProperty || ! proto . hasOwnProperty ( 'addEventListener' ) ) {
194
+ return ;
195
+ }
274
196
275
- // Otherwise wrap directly
276
- return wrap ( original , wrapOptions ) ;
197
+ fill ( proto , 'addEventListener' , function (
198
+ original : ( ) => void ,
199
+ ) : ( eventName : string , fn : EventListenerObject , options ?: boolean | AddEventListenerOptions ) => void {
200
+ return function (
201
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
+ this : any ,
203
+ eventName : string ,
204
+ fn : EventListenerObject ,
205
+ options ?: boolean | AddEventListenerOptions ,
206
+ ) : ( eventName : string , fn : EventListenerObject , capture ?: boolean , secure ?: boolean ) => void {
207
+ try {
208
+ if ( typeof fn . handleEvent === 'function' ) {
209
+ fn . handleEvent = wrap ( fn . handleEvent . bind ( fn ) , {
210
+ mechanism : {
211
+ data : {
212
+ function : 'handleEvent' ,
213
+ handler : getFunctionName ( fn ) ,
214
+ target,
215
+ } ,
216
+ handled : true ,
217
+ type : 'instrument' ,
218
+ } ,
277
219
} ) ;
278
220
}
279
- } ) ;
221
+ } catch ( err ) {
222
+ // can sometimes get 'Permission denied to access property "handle Event'
223
+ }
224
+
225
+ return original . call (
226
+ this ,
227
+ eventName ,
228
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
229
+ wrap ( ( fn as any ) as WrappedFunction , {
230
+ mechanism : {
231
+ data : {
232
+ function : 'addEventListener' ,
233
+ handler : getFunctionName ( fn ) ,
234
+ target,
235
+ } ,
236
+ handled : true ,
237
+ type : 'instrument' ,
238
+ } ,
239
+ } ) ,
240
+ options ,
241
+ ) ;
242
+ } ;
243
+ } ) ;
280
244
281
- return originalSend . apply ( this , args ) ;
245
+ fill ( proto , 'removeEventListener' , function (
246
+ originalRemoveEventListener : ( ) => void ,
247
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
248
+ ) : ( this : any , eventName : string , fn : EventListenerObject , options ?: boolean | EventListenerOptions ) => ( ) => void {
249
+ return function (
250
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
+ this : any ,
252
+ eventName : string ,
253
+ fn : EventListenerObject ,
254
+ options ?: boolean | EventListenerOptions ,
255
+ ) : ( ) => void {
256
+ /**
257
+ * There are 2 possible scenarios here:
258
+ *
259
+ * 1. Someone passes a callback, which was attached prior to Sentry initialization, or by using unmodified
260
+ * method, eg. `document.addEventListener.call(el, name, handler). In this case, we treat this function
261
+ * as a pass-through, and call original `removeEventListener` with it.
262
+ *
263
+ * 2. Someone passes a callback, which was attached after Sentry was initialized, which means that it was using
264
+ * our wrapped version of `addEventListener`, which internally calls `wrap` helper.
265
+ * This helper "wraps" whole callback inside a try/catch statement, and attached appropriate metadata to it,
266
+ * in order for us to make a distinction between wrapped/non-wrapped functions possible.
267
+ * If a function was wrapped, it has additional property of `__sentry_wrapped__`, holding the handler.
268
+ *
269
+ * When someone adds a handler prior to initialization, and then do it again, but after,
270
+ * then we have to detach both of them. Otherwise, if we'd detach only wrapped one, it'd be impossible
271
+ * to get rid of the initial handler and it'd stick there forever.
272
+ */
273
+ const wrappedEventHandler = ( fn as unknown ) as WrappedFunction ;
274
+ try {
275
+ const originalEventHandler = wrappedEventHandler && wrappedEventHandler . __sentry_wrapped__ ;
276
+ if ( originalEventHandler ) {
277
+ originalRemoveEventListener . call ( this , eventName , originalEventHandler , options ) ;
278
+ }
279
+ } catch ( e ) {
280
+ // ignore, accessing __sentry_wrapped__ will throw in some Selenium environments
281
+ }
282
+ return originalRemoveEventListener . call ( this , eventName , wrappedEventHandler , options ) ;
282
283
} ;
283
- }
284
+ } ) ;
284
285
}
0 commit comments