diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index 77cb47b0..fa698403 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -266,7 +266,11 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event, comptime opts was_handled = true; event._current_target = target_et; - try ls.toLocal(inline_handler).callWithThis(void, target_et, .{event}); + // Inline handlers (e.g. onclick property) follow the same "report, + // don't propagate" rule as addEventListener listeners — see Listener.run. + ls.toLocal(inline_handler).callWithThis(void, target_et, .{event}) catch |err| { + log.warn(.event, "inline handler", .{ .err = err }); + }; if (event._stop_propagation) { return; @@ -388,19 +392,7 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe event._target = getAdjustedTarget(original_target, current_target); } - switch (listener.function) { - .value => |value| try local.toLocal(value).callWithThis(void, current_target, .{event}), - .string => |string| { - const str = try frame.call_arena.dupeZ(u8, string.str()); - try local.eval(str, null); - }, - .object => |obj_global| { - const obj = local.toLocal(obj_global); - if (try obj.getFunction("handleEvent")) |handleEvent| { - try handleEvent.callWithThis(void, obj, .{event}); - } - }, - } + try listener.run(frame.call_arena, local, event, "listener"); // Restore original target (only if we changed it) if (event._needs_retargeting) { diff --git a/src/browser/EventManagerBase.zig b/src/browser/EventManagerBase.zig index 8e13ecd5..03bb6d15 100644 --- a/src/browser/EventManagerBase.zig +++ b/src/browser/EventManagerBase.zig @@ -305,19 +305,7 @@ pub fn dispatchDirect( event._current_target = target; - switch (listener.function) { - .value => |value| try ls.local.toLocal(value).callWithThis(void, target, .{event}), - .string => |string| { - const str = try arena.dupeZ(u8, string.str()); - try ls.local.eval(str, null); - }, - .object => |obj_global| { - const obj = ls.local.toLocal(obj_global); - if (try obj.getFunction("handleEvent")) |handleEvent| { - try handleEvent.callWithThis(void, obj, .{event}); - } - }, - } + try listener.run(arena, &ls.local, event, opts.context); if (event._stop_immediate_propagation) { return; @@ -392,6 +380,46 @@ pub const Listener = struct { signal: ?*@import("webapi/AbortSignal.zig") = null, node: std.DoublyLinkedList.Node, removed: bool = false, + + // Per DOM §2.9 step 4 substep 8 ("Inner invoke"), a listener callback that + // throws must have its exception *reported* to the global error handler, + // not propagated to the dispatch caller — subsequent listeners on the same + // target and the rest of the propagation path must still run. + // + // Caller must set `event._current_target` before invoking — the function- + // listener variant uses it as `this`, matching the spec contract that a + // listener sees its current target via both `event.currentTarget` and `this`. + pub fn run( + self: *const Listener, + arena: Allocator, + local: *const js.Local, + event: *Event, + comptime context: []const u8, + ) error{OutOfMemory}!void { + switch (self.function) { + .value => |value| local.toLocal(value).callWithThis(void, event._current_target.?, .{event}) catch |err| { + log.warn(.event, context, .{ .err = err }); + }, + .string => |string| { + const str = try arena.dupeZ(u8, string.str()); + local.eval(str, null) catch |err| { + log.warn(.event, context, .{ .err = err }); + }; + }, + .object => |obj_global| { + const obj = local.toLocal(obj_global); + const handle_event = obj.getFunction("handleEvent") catch |err| blk: { + log.warn(.event, context, .{ .err = err }); + break :blk null; + }; + if (handle_event) |handleEvent| { + handleEvent.callWithThis(void, obj, .{event}) catch |err| { + log.warn(.event, context, .{ .err = err }); + }; + } + }, + } + } }; pub const Function = union(enum) { diff --git a/src/browser/tests/events.html b/src/browser/tests/events.html index 80d41707..eae7f883 100644 --- a/src/browser/tests/events.html +++ b/src/browser/tests/events.html @@ -762,3 +762,95 @@ testing.expectEqual(2, calls.length); } + +
+ + +
+ + +