dom
dom copied to clipboard
Add a fully active check for EventTarget in event listener inner invoke
This adds a fully active check in https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke that skips running event listeners if the EventTarget's realm's global's document is not fully active. Blink, Gecko, and WebKit were all skipping these event listeners, but the spec does not currently reflect this behavior.
I put the check where Blink and WebKit have it — just after the event listener was removed if it is a one-off. My understanding of Gecko's implementation is that the listeners are cleared on frame detach, so this should be compatable. The position of the check could be web observable if the document becomes fully active again, i.e. with bfcache, but Chromium at least doesn't cache pages with opener references.
Closes #1084.
- [ ] At least two implementers are interested (and none opposed):
- Chromium
- …
- [ ] Tests are written and can be reviewed and commented upon at:
- https://chromium-review.googlesource.com/c/chromium/src/+/3691385
- [ ] Implementation bugs are filed:
- Chrome: …
- Firefox: …
- Safari: …
- Deno (only for aborting and events): …
- Node.js (only for aborting and events): …
(See WHATWG Working Mode: Changes for more details.)
I manually tested with browser-initiated events via AbortController/AbortSignal and with synthetic events, for both detaching a frame prior to and during dispatch. I observed the behavior to be consistent across browsers. I have WPT tests for synthetic events here (not yet landed), and happy to add/run more tests if it makes sense.
Firefox has checks in the webidl callbacks layer to prevent calling listener in the globals which are "gone".
That would suggest it needs to be added to https://webidl.spec.whatwg.org/#call-a-user-objects-operation although that would also affect other callers of that algorithm. (None come to mind at the moment.)
Firefox has checks in the webidl callbacks layer to prevent calling listener in the globals which are "gone".
The global of the EventTarget or the listener? In this case, the checks are about the global of the EventTarget, i.e. the listener's global can still be attached, but the target's is not.
When I filed the issue, I did some debugging in FF code to see if I could figure out where the check was happening. I observed the following:
- If the
EventTarget's global is detached when dispatching events, we hit an early out in EventTargetChainItem::HandleEvent() because theEventListenerManageris null. - If the
EventTarget's global is detached during dispatch, I found iteration duringHandleEventInternal()abruptly ends because the listeners got cleared. This happens, for example, if there are two event listeners and the first detaches the event target's global. I found code that "disconnects" the event targets and clears listeners which I think caused this, but regardless I definitely observed iteration ending after the first event listener detached the event target's global.
I don't know this code nearly as well as y'all, but this is what I observed with some hacky printf debugging; hopefully that's helpful.
In Gecko if the global of the WebIDL callback itself isn't the current one anymore, the bindings layer prevents the call. Certain EventTarget objects do indeed disconnect themselves too when closing the relevant global (the idea is roughly that things like XHR isn't really usable one the global is gone).