Bug 234730

Summary: load and beforeunload window event listeners callback order different in Safari than Chrome and Firefox
Product: WebKit Reporter: Jeff Johnson <opendarwin>
Component: DOMAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: cdumez, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: Safari 15   
Hardware: All   
OS: All   
Attachments:
Description Flags
Sample html demonstrating the inconsistency none

Description Jeff Johnson 2021-12-28 14:18:08 PST
Created attachment 448055 [details]
Sample html demonstrating the inconsistency

The EventTarget.addEventListener() method has a boolean useCapture parameter:
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters

This mostly works the same in all browsers. However, it works differently in Safari for some window events such as "load" and "beforeunload":
https://developer.mozilla.org/en-US/docs/Web/API/Window#events

The target of these events is the window object itself, so technically you could say there are no capturing or bubbling event phases.

Consider the following code:

window.addEventListener("load", () => { console.log("load false"); }, false);
window.addEventListener("load", () => { console.log("load true"); }, true);
window.addEventListener("beforeunload", () => { console.log("beforeunload false"); }, false);
window.addEventListener("beforeunload", () => { console.log("beforeunload true"); }, true);

In Safari, the above useCapture true listeners are called prior to the above useCapture false listeners. However, in Chrome and Firefox, these listeners are called in the order registered, as if the useCapture parameter were irrelevant.

I won't argue which behavior is "correct". I'm just noting that Safari is inconsistent with the other browsers.
Comment 1 Jeff Johnson 2021-12-28 15:02:39 PST
Tested on macOS 11.6.2 with Safari 15.2, Safari Technology Preview 137, Chrome 96, and Firefox 95. Also tested with Mobile Safari on iOS 15.2.
Comment 2 Radar WebKit Bug Importer 2022-01-04 14:19:46 PST
<rdar://problem/87114687>
Comment 3 Sam Sneddon [:gsnedders] 2022-01-05 12:55:10 PST
useCapture shouldn't be affecting event dispatch order; dom/events/Event-dispatch-order.html in WPT is meant to test this but clearly doesn't suffice.
Comment 4 Jeff Johnson 2022-01-05 13:22:11 PST
(In reply to Sam Sneddon [:gsnedders] from comment #3)
> useCapture shouldn't be affecting event dispatch order;
> dom/events/Event-dispatch-order.html in WPT is meant to test this but
> clearly doesn't suffice.

I'm happy to accept Safari's behavior as correct, and would file bugs against Chromium and Firefox, though perhaps someone more important than me would be more persuasive to them.
Comment 5 Chris Dumez 2022-01-06 09:04:08 PST
(In reply to Sam Sneddon [:gsnedders] from comment #3)
> useCapture shouldn't be affecting event dispatch order;
> dom/events/Event-dispatch-order.html in WPT is meant to test this but
> clearly doesn't suffice.

@Sam: Could you clarify why the event dispatch order should not be impacted by useCapture? I am looking at https://dom.spec.whatwg.org/#concept-event-dispatch and our implementation seems to match the spec:
1. First we Invoke with struct, event, "capturing", and legacyOutputDidListenersThrowFlag if given.
2. Then we Invoke with struct, event, "bubbling", and legacyOutputDidListenersThrowFlag if given.

From https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke, some listeners are skipped during each phase based on listener's "capture".

So it makes sense to me that we're calling the ones with useCapture=true first and then the ones with useCapture=false.