From bd0649c317e95fc881a73fb3b772c8ba6e0abe3c Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Fri, 14 Feb 2020 11:46:31 +0100 Subject: [PATCH] fix(integrations): Treat specific `CustomEvents` as promise rejections (#2429) --- CHANGELOG.md | 2 ++ .../browser/src/integrations/globalhandlers.ts | 16 +++++++++++++++- packages/browser/src/loader.js | 4 ++-- packages/integrations/src/dedupe.ts | 6 +++--- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f9e48beb9dd..3a6ba4bad3a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- [browser] fix: Handle PromiseRejectionEvent-like CustomEvents + ## 5.12.3 - [apm] fix: Remove undefined keys from trace.context diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 30c1e079a9e8..769ace766496 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -138,8 +138,22 @@ export class GlobalHandlers implements Integration { this._global.onunhandledrejection = function(e: any): boolean { let error = e; + + // dig the object of the rejection out of known event types try { - error = e && 'reason' in e ? e.reason : e; + // PromiseRejectionEvents store the object of the rejection under 'reason' + // see https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent + if ('reason' in e) { + error = e.reason; + } + // something, somewhere, (likely a browser extension) effectively casts PromiseRejectionEvents + // to CustomEvents, moving the `promise` and `reason` attributes of the PRE into + // the CustomEvent's `detail` attribute, since they're not part of CustomEvent's spec + // see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent and + // https://github.com/getsentry/sentry-javascript/issues/2380 + else if ('detail' in e && 'reason' in e.detail) { + error = e.detail.reason; + } } catch (_oO) { // no-empty } diff --git a/packages/browser/src/loader.js b/packages/browser/src/loader.js index 17edc9c76075..16742a26e20c 100644 --- a/packages/browser/src/loader.js +++ b/packages/browser/src/loader.js @@ -200,9 +200,9 @@ // Do the same store/queue/call operations for `onunhandledrejection` event var _oldOnunhandledrejection = _window[_onunhandledrejection]; - _window[_onunhandledrejection] = function(exception) { + _window[_onunhandledrejection] = function(e) { queue({ - p: exception.reason + p: 'reason' in e ? e.reason : 'detail' in e && 'reason' in e.detail ? e.detail.reason : e }); if (_oldOnunhandledrejection) _oldOnunhandledrejection.apply(_window, arguments); }; diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index effa8396bd11..ed8cea670cb5 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -61,7 +61,7 @@ export class Dedupe implements Integration { const currentMessage = currentEvent.message; const previousMessage = previousEvent.message; - // If no event has a message, they were both exceptions, so bail out + // If neither event has a message property, they were both exceptions, so bail out if (!currentMessage && !previousMessage) { return false; } @@ -108,7 +108,7 @@ export class Dedupe implements Integration { let currentFrames = this._getFramesFromEvent(currentEvent); let previousFrames = this._getFramesFromEvent(previousEvent); - // If no event has a fingerprint, they are assumed to be the same + // If neither event has a stacktrace, they are assumed to be the same if (!currentFrames && !previousFrames) { return true; } @@ -178,7 +178,7 @@ export class Dedupe implements Integration { let currentFingerprint = currentEvent.fingerprint; let previousFingerprint = previousEvent.fingerprint; - // If no event has a fingerprint, they are assumed to be the same + // If neither event has a fingerprint, they are assumed to be the same if (!currentFingerprint && !previousFingerprint) { return true; }