From 8b59c27aacc54a52a1655202f084aa4ca69b66f8 Mon Sep 17 00:00:00 2001 From: Erik Arvidsson Date: Tue, 10 Jun 2014 10:10:53 -0400 Subject: [PATCH] Fix issue with event reentrancy If an inner invoke removes a listener, the outer invoke now loops over the stale listener length. To solve this we only clean up the listener array when we are in the outermost invoke. --- src/wrappers/events.js | 9 ++++++++- test/js/events.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/wrappers/events.js b/src/wrappers/events.js index f61cf45..e1a1911 100644 --- a/src/wrappers/events.js +++ b/src/wrappers/events.js @@ -382,6 +382,10 @@ targetTable.set(event, target); currentTargetTable.set(event, currentTarget); + // Keep track of the invoke depth so that we only clean up the removed + // listeners if we are in the outermost invoke. + listeners.depth++; + for (var i = 0, len = listeners.length; i < len; i++) { var listener = listeners[i]; if (listener.removed) { @@ -410,7 +414,9 @@ } } - if (anyRemoved) { + listeners.depth--; + + if (anyRemoved && listeners.depth === 0) { var copy = listeners.slice(); listeners.length = 0; for (var i = 0; i < copy.length; i++) { @@ -707,6 +713,7 @@ var listeners = listenersTable.get(this); if (!listeners) { listeners = []; + listeners.depth = 0; listenersTable.set(this, listeners); } else { // Might have a duplicate. diff --git a/test/js/events.js b/test/js/events.js index 2e22645..431379a 100644 --- a/test/js/events.js +++ b/test/js/events.js @@ -1448,4 +1448,32 @@ test('retarget order (multiple shadow roots)', function() { assert.equal(gCount, 2); assert.equal(hCount, 1); }); + + test('Event reentrancy', function() { + div = document.createElement('div'); + document.body.appendChild(div); + var s = ''; + var depth = 0; + + function f() { + s += 'f' + depth; + if (depth === 0) { + depth++; + div.dispatchEvent(new Event('x')); + } else if (depth === 1) { + div.removeEventListener('x', g); + } + } + + function g() { + s += 'g' + depth; + } + + div.addEventListener('x', f); + div.addEventListener('x', g); + + div.dispatchEvent(new Event('x')); + + assert.equal(s, 'f0f1'); + }); });