Skip to content

Commit 337e987

Browse files
authored
Merge pull request bugsnag#582 from bugsnag/remove-event-listener-bug
fix(plugin-inline-script-content): Ensure event handlers added before Bugsnag can be removed
2 parents 3a78486 + c5ff67f commit 337e987

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed

packages/plugin-inline-script-content/inline-script-content.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,23 @@ module.exports = {
102102
'Notification', 'SVGElementInstance', 'Screen', 'TextTrack', 'TextTrackCue', 'TextTrackList',
103103
'WebSocket', 'WebSocketWorker', 'Worker', 'XMLHttpRequest', 'XMLHttpRequestEventTarget', 'XMLHttpRequestUpload'
104104
], o => {
105-
if (!win[o] || !win[o].prototype || typeof win[o].prototype.addEventListener !== 'function') return
105+
if (!win[o] || !win[o].prototype || !win[o].prototype.hasOwnProperty || !win[o].prototype.hasOwnProperty('addEventListener')) return
106106
__proxy(win[o].prototype, 'addEventListener', original =>
107107
__traceOriginalScript(original, eventTargetCallbackAccessor)
108108
)
109109
__proxy(win[o].prototype, 'removeEventListener', original =>
110-
__traceOriginalScript(original, eventTargetCallbackAccessor)
110+
__traceOriginalScript(original, eventTargetCallbackAccessor, true)
111111
)
112112
})
113113

114-
function __traceOriginalScript (fn, callbackAccessor) {
114+
function __traceOriginalScript (fn, callbackAccessor, alsoCallOriginal = false) {
115115
return function () {
116-
var args = Array.prototype.slice.call(arguments)
117-
var cba = callbackAccessor(args)
118-
var cb = cba.get()
116+
// this is required for removeEventListener to remove anything added with
117+
// addEventListener before the functions started being wrapped by Bugsnag
118+
const args = Array.prototype.slice.call(arguments)
119+
const cba = callbackAccessor(args)
120+
const cb = cba.get()
121+
if (alsoCallOriginal) fn.apply(this, args)
119122
if (typeof cb !== 'function') return fn.apply(this, args)
120123
try {
121124
if (cb.__trace__) {

packages/plugin-inline-script-content/test/inline-script-content.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,45 @@ Lorem ipsum dolor sit amet.
190190
expect(payloads[0].events[0].stacktrace).toEqual([])
191191
expect(spy).toHaveBeenCalledTimes(0)
192192
})
193+
194+
it('calls removeEventListener with wrapped and unwrapped callback', () => {
195+
const scriptContent = `console.log("unwrapped")`
196+
const document = {
197+
scripts: [ { innerHTML: scriptContent } ],
198+
currentScript: { innerHTML: scriptContent },
199+
documentElement: {
200+
outerHTML: `<p>
201+
Lorem ipsum dolor sit amet.
202+
Lorem ipsum dolor sit amet.
203+
Lorem ipsum dolor sit amet.
204+
</p>
205+
<script>${scriptContent}
206+
</script>
207+
<p>more content</p>`
208+
}
209+
}
210+
function Window () {}
211+
Window.prototype = {
212+
addEventListener: function () {},
213+
removeEventListener: function () {}
214+
}
215+
const window = {
216+
location: { href: 'https://app.bugsnag.com/errors' }
217+
}
218+
219+
Object.setPrototypeOf(window, Window.prototype)
220+
window.Window = Window
221+
222+
function myfun () {}
223+
window.addEventListener('click', myfun)
224+
225+
const spy = spyOn(Window.prototype, 'removeEventListener')
226+
const client = new Client(VALID_NOTIFIER)
227+
client.setOptions({ apiKey: 'API_KEY_YEAH' })
228+
client.configure()
229+
client.use(plugin, document, window)
230+
231+
window.removeEventListener('click', myfun)
232+
expect(spy).toHaveBeenCalledTimes(2)
233+
})
193234
})

0 commit comments

Comments
 (0)