Skip to content

Commit fb87d34

Browse files
authored
ref(browser): Extract trycatch private methods into functions (#4305)
1 parent fc938c9 commit fb87d34

File tree

1 file changed

+160
-159
lines changed

1 file changed

+160
-159
lines changed

packages/browser/src/integrations/trycatch.ts

+160-159
Original file line numberDiff line numberDiff line change
@@ -83,202 +83,203 @@ export class TryCatch implements Integration {
8383
const global = getGlobalObject();
8484

8585
if (this._options.setTimeout) {
86-
fill(global, 'setTimeout', this._wrapTimeFunction.bind(this));
86+
fill(global, 'setTimeout', _wrapTimeFunction);
8787
}
8888

8989
if (this._options.setInterval) {
90-
fill(global, 'setInterval', this._wrapTimeFunction.bind(this));
90+
fill(global, 'setInterval', _wrapTimeFunction);
9191
}
9292

9393
if (this._options.requestAnimationFrame) {
94-
fill(global, 'requestAnimationFrame', this._wrapRAF.bind(this));
94+
fill(global, 'requestAnimationFrame', _wrapRAF);
9595
}
9696

9797
if (this._options.XMLHttpRequest && 'XMLHttpRequest' in global) {
98-
fill(XMLHttpRequest.prototype, 'send', this._wrapXHR.bind(this));
98+
fill(XMLHttpRequest.prototype, 'send', _wrapXHR);
9999
}
100100

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);
104105
}
105106
}
107+
}
106108

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, {
113134
mechanism: {
114-
data: { function: getFunctionName(original) },
135+
data: {
136+
function: 'requestAnimationFrame',
137+
handler: getFunctionName(original),
138+
},
115139
handled: true,
116140
type: 'instrument',
117141
},
118-
});
119-
return original.apply(this, args);
120-
};
121-
}
142+
}),
143+
);
144+
};
145+
}
122146

123-
/** JSDoc */
147+
/** JSDoc */
148+
function _wrapXHR(originalSend: () => void): () => void {
124149
// 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'];
156154

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') {
161157
// 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 = {
190160
mechanism: {
191161
data: {
192-
function: 'addEventListener',
193-
handler: getFunctionName(fn),
194-
target,
162+
function: prop,
163+
handler: getFunctionName(original),
195164
},
196165
handled: true,
197166
type: 'instrument',
198167
},
199-
}),
200-
options,
201-
);
202-
};
203-
});
168+
};
204169

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__);
238173
}
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+
}
244179
});
245-
}
246180

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+
}
254184

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;
269191

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+
}
274196

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+
},
277219
});
278220
}
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+
});
280244

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);
282283
};
283-
}
284+
});
284285
}

0 commit comments

Comments
 (0)