New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
removeEventListener doesn't handle currently dispatching events #84
Comments
Hi, some times ago I noticed the same thing, although its correct in IE: |
Somewhat more readable: <!DOCTYPE html>
<script>
var es = [];
var ls = [
function(event) { _handleEvent(event); },
function(event) { _handleEvent(event); },
];
var _handleEvent = function(event) {
es.push(event);
ls.forEach(function(l) {
document.removeEventListener("foo", l);
})
};
ls.forEach(function(l) {
document.addEventListener("foo", l);
});
var event = new Event("foo");
document.dispatchEvent(event);
w(es.length); // returns 1 in Chrome + FF
</script> |
Servo returns 2. |
I'm pretty sure this was tested. That's why the whole copy clause is there. I wonder what's up. |
@annevk I thought the copy was to avoid invoking newly added listeners? The behavior in Gecko is certainly that listeners added to the current target during dispatch to one of its listeners are NOT invoked, but listeners removed from it will also not be invoked if they haven't been already. We don't make a list copy internally; we use an iterator along a live list that stops at what used to be the list end when the iterator started. |
Ah that makes sense. Not entirely sure how to describe iterator-like behavior in prose though. I guess I could "If any event listener is removed, also remove it from listeners" but that seems like a cop-out. |
The best I can think of is that rather than a copy we create a list of pointers. And then when a listener is removed some kind of "removed flag" gets set that is checked by the invoke algorithm. The concept of iteration over a live list isn't really defined (ideally IDL would define some constructs for us). Would that work @bzbarsky? |
That's starting to sound like the setup ES uses for There is a list of things, each of which is either an entry or a removed-entry marker (e.g. in the case of It's not quite exactly what we want here, because we want the event dispatch iteration to not consider newly added stuff. But we could do that by grabbing the index to stop at up front in the dispatch and then stopping there, I think. |
The way to not consider new stuff was to iterate once and only store pointers for the stuff currently in the list. So you wouldn't have any pointers to newly added listeners. |
Sure, that seems equivalent as long as removal marks the thing the pointer points to as "not a listener anymore" or something. Which specification device is clearer/simpler, I dunno. |
Thank you @Sebmaster for reporting this. I forgot to add your name in this commit, but that'll be sorted in a future one. Hope that's okay. |
Sure, no big deal. |
It's been sorted by the way, have a look: https://dom.spec.whatwg.org/#acks. |
Sweet 👍 Now let's have a look at this HTML spec... 😄 |
It seems like browsers remove an event listener even from the currently dispatched event listener list. Explaining it is kinda complicated, but here's a test which fails with an implementation that is (I hope) according to spec:
So, both listeners remove themselves + the other listener, which seems to cause the currently dispatched event to only get to 1 listener since the other one is removed.
In event listener invoke the spec says to copy the event listener list, which should cause it to not be impacted by changes from removeEventListener (I'd assume).
The text was updated successfully, but these errors were encountered: