Skip to content

Commit 2a852b7

Browse files
authored
fix(loader): Avoid setting/resetting window.onerror (#47311)
This uses `addEventListener` instead of `window.onerror` to set the handler for the loader. This way, we avoid infinite recursion when users also do that, e.g. right after loading the loader: ```js const oldOnError = window.onerror; window.onerror = function () { console.log('custom error'); oldOnError && oldOnError.apply(this, arguments); }; window.doSomethingWrong(); ```
1 parent 5b9f294 commit 2a852b7

File tree

3 files changed

+42
-78
lines changed

3 files changed

+42
-78
lines changed

src/sentry/templates/sentry/js-sdk-loader.js.tmpl

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@
3232
queue.data.push(content);
3333
};
3434
queue.data = [];
35+
function onError() {
36+
// Use keys as "data type" to save some characters"
37+
queue({
38+
e: [].slice.call(arguments),
39+
});
40+
}
41+
function onUnhandledRejection(e) {
42+
queue({
43+
p: 'reason' in e
44+
? e.reason
45+
: 'detail' in e && 'reason' in e.detail
46+
? e.detail.reason
47+
: e,
48+
});
49+
}
3550
function injectSdk(callbacks) {
3651
if (injected) {
3752
return;
@@ -50,14 +65,8 @@
5065
// Once our SDK is loaded
5166
_newScriptTag.addEventListener('load', function () {
5267
try {
53-
// Restore onerror/onunhandledrejection handlers - only if not mutated in the meanwhile
54-
if (_window[_onerror] && _window[_onerror].__SENTRY_LOADER__) {
55-
_window[_onerror] = _oldOnerror;
56-
}
57-
if (_window[_onunhandledrejection] &&
58-
_window[_onunhandledrejection].__SENTRY_LOADER__) {
59-
_window[_onunhandledrejection] = _oldOnunhandledrejection;
60-
}
68+
_window.removeEventListener('error', onError);
69+
_window.removeEventListener('unhandledrejection', onUnhandledRejection);
6170
// Add loader as SDK source
6271
_window.SENTRY_SDK_SOURCE = 'loader';
6372
var SDK_1 = _window[_namespace];
@@ -186,34 +195,8 @@
186195
queue({ f: f, a: arguments });
187196
};
188197
});
189-
// Store reference to the old `onerror` handler and override it with our own function
190-
// that will just push exceptions to the queue and call through old handler if we found one
191-
var _oldOnerror = _window[_onerror];
192-
_window[_onerror] = function () {
193-
// Use keys as "data type" to save some characters"
194-
queue({
195-
e: [].slice.call(arguments),
196-
});
197-
if (_oldOnerror) {
198-
_oldOnerror.apply(_window, arguments);
199-
}
200-
};
201-
_window[_onerror].__SENTRY_LOADER__ = true;
202-
// Do the same store/queue/call operations for `onunhandledrejection` event
203-
var _oldOnunhandledrejection = _window[_onunhandledrejection];
204-
_window[_onunhandledrejection] = function (e) {
205-
queue({
206-
p: 'reason' in e
207-
? e.reason
208-
: 'detail' in e && 'reason' in e.detail
209-
? e.detail.reason
210-
: e,
211-
});
212-
if (_oldOnunhandledrejection) {
213-
_oldOnunhandledrejection.apply(_window, arguments);
214-
}
215-
};
216-
_window[_onunhandledrejection].__SENTRY_LOADER__ = true;
198+
_window.addEventListener('error', onError);
199+
_window.addEventListener('unhandledrejection', onUnhandledRejection);
217200
if (!lazy) {
218201
setTimeout(function () {
219202
injectSdk(onLoadCallbacks);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{% load sentry_helpers %}!function(n,e,r,t,a,i,o,c,_,f){for(var p=f,forceLoad=!1,s=0;s<document.scripts.length;s++)if(document.scripts[s].src.indexOf(o)>-1){p&&"no"===document.scripts[s].getAttribute("data-lazy")&&(p=!1);break}var u=!1,l=[],d=function(n){("e"in n||"p"in n||n.f&&n.f.indexOf("capture")>-1||n.f&&n.f.indexOf("showReportDialog")>-1)&&p&&R(l),d.data.push(n)};function R(o){if(!u){u=!0;var f=e.scripts[0],p=e.createElement(r);p.src=c,p.crossOrigin="anonymous",p.addEventListener("load",(function(){try{n[t]&&n[t].__SENTRY_LOADER__&&(n[t]=E),n[a]&&n[a].__SENTRY_LOADER__&&(n[a]=v),n.SENTRY_SDK_SOURCE="loader";var e=n[i],r=e.init;e.init=function(n){var t=_;for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(t[a]=n[a]);!function(n,e){var r=n.integrations||[];if(!Array.isArray(r))return;var t=r.map((function(n){return n.name}));n.tracesSampleRate&&-1===t.indexOf("BrowserTracing")&&r.push(new e.BrowserTracing);(n.replaysSessionSampleRate||n.replaysOnErrorSampleRate)&&-1===t.indexOf("Replay")&&r.push(new e.Replay);n.integrations=r}(t,e),r(t)},function(e,r){try{for(var i=0;i<e.length;i++)"function"==typeof e[i]&&e[i]();var o=d.data,c=!(void 0===(u=n.__SENTRY__)||!u.hub||!u.hub.getClient());o.sort((function(n){return"init"===n.f?-1:0}));var _=!1;for(i=0;i<o.length;i++)if(o[i].f){_=!0;var f=o[i];!1===c&&"init"!==f.f&&r.init(),c=!0,r[f.f].apply(r,f.a)}!1===c&&!1===_&&r.init();var p=n[t],s=n[a];for(i=0;i<o.length;i++)"e"in o[i]&&p?p.apply(n,o[i].e):"p"in o[i]&&s&&s.apply(n,[o[i].p])}catch(n){console.error(n)}var u}(o,e)}catch(n){console.error(n)}})),f.parentNode.insertBefore(p,f)}}d.data=[],n[i]=n[i]||{},n[i].onLoad=function(n){l.push(n),p&&!forceLoad||R(l)},n[i].forceLoad=function(){forceLoad=!0,p&&setTimeout((function(){R(l)}))},["init","addBreadcrumb","captureMessage","captureException","captureEvent","configureScope","withScope","showReportDialog"].forEach((function(e){n[i][e]=function(){d({f:e,a:arguments})}}));var E=n[t];n[t]=function(){d({e:[].slice.call(arguments)}),E&&E.apply(n,arguments)},n[t].__SENTRY_LOADER__=!0;var v=n[a];n[a]=function(e){d({p:"reason"in e?e.reason:"detail"in e&&"reason"in e.detail?e.detail.reason:e}),v&&v.apply(n,arguments)},n[a].__SENTRY_LOADER__=!0,p||setTimeout((function(){R(l)}))}(window,document,"script","onerror","onunhandledrejection","Sentry",'{{ publicKey|safe }}','{{ jsSdkUrl|safe }}',{{ config|to_json|safe }},{{ isLazy|safe|lower }});
1+
{% load sentry_helpers %}!function(e,n,r,t,i,o,a,c,s,f){for(var u=f,forceLoad=!1,p=0;p<document.scripts.length;p++)if(document.scripts[p].src.indexOf(a)>-1){u&&"no"===document.scripts[p].getAttribute("data-lazy")&&(u=!1);break}var d=!1,l=[],_=function(e){("e"in e||"p"in e||e.f&&e.f.indexOf("capture")>-1||e.f&&e.f.indexOf("showReportDialog")>-1)&&u&&E(l),_.data.push(e)};function v(){_({e:[].slice.call(arguments)})}function h(e){_({p:"reason"in e?e.reason:"detail"in e&&"reason"in e.detail?e.detail.reason:e})}function E(a){if(!d){d=!0;var f=n.scripts[0],u=n.createElement(r);u.src=c,u.crossOrigin="anonymous",u.addEventListener("load",(function(){try{e.removeEventListener("error",v),e.removeEventListener("unhandledrejection",h),e.SENTRY_SDK_SOURCE="loader";var n=e[o],r=n.init;n.init=function(e){var t=s;for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);!function(e,n){var r=e.integrations||[];if(!Array.isArray(r))return;var t=r.map((function(e){return e.name}));e.tracesSampleRate&&-1===t.indexOf("BrowserTracing")&&r.push(new n.BrowserTracing);(e.replaysSessionSampleRate||e.replaysOnErrorSampleRate)&&-1===t.indexOf("Replay")&&r.push(new n.Replay);e.integrations=r}(t,n),r(t)},function(n,r){try{for(var o=0;o<n.length;o++)"function"==typeof n[o]&&n[o]();var a=_.data,c=!(void 0===(d=e.__SENTRY__)||!d.hub||!d.hub.getClient());a.sort((function(e){return"init"===e.f?-1:0}));var s=!1;for(o=0;o<a.length;o++)if(a[o].f){s=!0;var f=a[o];!1===c&&"init"!==f.f&&r.init(),c=!0,r[f.f].apply(r,f.a)}!1===c&&!1===s&&r.init();var u=e[t],p=e[i];for(o=0;o<a.length;o++)"e"in a[o]&&u?u.apply(e,a[o].e):"p"in a[o]&&p&&p.apply(e,[a[o].p])}catch(e){console.error(e)}var d}(a,n)}catch(e){console.error(e)}})),f.parentNode.insertBefore(u,f)}}_.data=[],e[o]=e[o]||{},e[o].onLoad=function(e){l.push(e),u&&!forceLoad||E(l)},e[o].forceLoad=function(){forceLoad=!0,u&&setTimeout((function(){E(l)}))},["init","addBreadcrumb","captureMessage","captureException","captureEvent","configureScope","withScope","showReportDialog"].forEach((function(n){e[o][n]=function(){_({f:n,a:arguments})}})),e.addEventListener("error",v),e.addEventListener("unhandledrejection",h),u||setTimeout((function(){E(l)}))}(window,document,"script","onerror","onunhandledrejection","Sentry",'{{ publicKey|safe }}','{{ jsSdkUrl|safe }}',{{ config|to_json|safe }},{{ isLazy|safe|lower }});

src/sentry/templates/sentry/js-sdk-loader.ts

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ declare const __LOADER__IS_LAZY__: any;
5454
};
5555
queue.data = [];
5656

57+
function onError() {
58+
// Use keys as "data type" to save some characters"
59+
queue({
60+
e: [].slice.call(arguments),
61+
});
62+
}
63+
64+
function onUnhandledRejection(e) {
65+
queue({
66+
p:
67+
'reason' in e
68+
? e.reason
69+
: 'detail' in e && 'reason' in e.detail
70+
? e.detail.reason
71+
: e,
72+
});
73+
}
74+
5775
function injectSdk(callbacks) {
5876
if (injected) {
5977
return;
@@ -74,16 +92,8 @@ declare const __LOADER__IS_LAZY__: any;
7492
// Once our SDK is loaded
7593
_newScriptTag.addEventListener('load', function () {
7694
try {
77-
// Restore onerror/onunhandledrejection handlers - only if not mutated in the meanwhile
78-
if (_window[_onerror] && _window[_onerror].__SENTRY_LOADER__) {
79-
_window[_onerror] = _oldOnerror;
80-
}
81-
if (
82-
_window[_onunhandledrejection] &&
83-
_window[_onunhandledrejection].__SENTRY_LOADER__
84-
) {
85-
_window[_onunhandledrejection] = _oldOnunhandledrejection;
86-
}
95+
_window.removeEventListener('error', onError);
96+
_window.removeEventListener('unhandledrejection', onUnhandledRejection);
8797

8898
// Add loader as SDK source
8999
_window.SENTRY_SDK_SOURCE = 'loader';
@@ -239,37 +249,8 @@ declare const __LOADER__IS_LAZY__: any;
239249
};
240250
});
241251

242-
// Store reference to the old `onerror` handler and override it with our own function
243-
// that will just push exceptions to the queue and call through old handler if we found one
244-
const _oldOnerror = _window[_onerror];
245-
_window[_onerror] = function () {
246-
// Use keys as "data type" to save some characters"
247-
queue({
248-
e: [].slice.call(arguments),
249-
});
250-
251-
if (_oldOnerror) {
252-
_oldOnerror.apply(_window, arguments);
253-
}
254-
};
255-
_window[_onerror].__SENTRY_LOADER__ = true;
256-
257-
// Do the same store/queue/call operations for `onunhandledrejection` event
258-
const _oldOnunhandledrejection = _window[_onunhandledrejection];
259-
_window[_onunhandledrejection] = function (e) {
260-
queue({
261-
p:
262-
'reason' in e
263-
? e.reason
264-
: 'detail' in e && 'reason' in e.detail
265-
? e.detail.reason
266-
: e,
267-
});
268-
if (_oldOnunhandledrejection) {
269-
_oldOnunhandledrejection.apply(_window, arguments);
270-
}
271-
};
272-
_window[_onunhandledrejection].__SENTRY_LOADER__ = true;
252+
_window.addEventListener('error', onError);
253+
_window.addEventListener('unhandledrejection', onUnhandledRejection);
273254

274255
if (!lazy) {
275256
setTimeout(function () {

0 commit comments

Comments
 (0)