webapi: resolve the element's frame in hasNonPassiveListener

Review pattern from #2944: an injected `frame` parameter is the frame of
the calling realm, not the frame owning the element. The wheel/touch
cancelability check consulted the caller's event manager and window; for
an element living in another frame (e.g. inside an iframe) its listeners
are registered in its own frame's event manager and its propagation path
ends at that frame's window. Resolve the frame through Element.ownerFrame
like the other cross-realm-safe paths.

Co-Authored-By: Karl Seguin <karlseguin@users.noreply.github.com>
This commit is contained in:
Francis Bouvier
2026-07-14 15:35:41 +02:00
committed by Karl Seguin
parent 65e4b3c612
commit 2d52bd4f8e

View File

@@ -417,14 +417,18 @@ fn dispatch(target: *EventTarget, event: *Event, frame: *Frame, typ: []const u8)
}
fn hasNonPassiveListener(el: *Element, typ: []const u8, frame: *Frame) bool {
const base = &frame._event_manager.base;
// Listeners live in the event manager of the element's own frame (and the
// propagation path ends at that frame's window), which is not the caller's
// frame when the element belongs to e.g. an iframe's document.
const owner = el.ownerFrame(frame);
const base = &owner._event_manager.base;
var current: ?*@import("Node.zig") = el.asNode();
while (current) |node| : (current = node.parentNode()) {
if (anyNonPassive(base.getListeners(node.asEventTarget(), .wrap(typ)))) {
return true;
}
}
return anyNonPassive(base.getListeners(frame.window.asEventTarget(), .wrap(typ)));
return anyNonPassive(base.getListeners(owner.window.asEventTarget(), .wrap(typ)));
}
fn anyNonPassive(list_: ?*std.DoublyLinkedList) bool {