diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index 77cb47b0..cd44a7e8 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -388,16 +388,31 @@ fn dispatchPhase(self: *EventManager, list: *std.DoublyLinkedList, current_targe event._target = getAdjustedTarget(original_target, current_target); } + // 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. Mirrors the + // catch on the inline-handler invocation in dispatchDirect. switch (listener.function) { - .value => |value| try local.toLocal(value).callWithThis(void, current_target, .{event}), + .value => |value| local.toLocal(value).callWithThis(void, current_target, .{event}) catch |err| { + log.warn(.event, "listener", .{ .err = err }); + }, .string => |string| { const str = try frame.call_arena.dupeZ(u8, string.str()); - try local.eval(str, null); + local.eval(str, null) catch |err| { + log.warn(.event, "listener string", .{ .err = err }); + }; }, .object => |obj_global| { const obj = local.toLocal(obj_global); - if (try obj.getFunction("handleEvent")) |handleEvent| { - try handleEvent.callWithThis(void, obj, .{event}); + const handle_event = obj.getFunction("handleEvent") catch |err| blk: { + log.warn(.event, "listener handleEvent", .{ .err = err }); + break :blk null; + }; + if (handle_event) |handleEvent| { + handleEvent.callWithThis(void, obj, .{event}) catch |err| { + log.warn(.event, "listener object", .{ .err = err }); + }; } }, } diff --git a/src/browser/EventManagerBase.zig b/src/browser/EventManagerBase.zig index 8e13ecd5..7ac622ae 100644 --- a/src/browser/EventManagerBase.zig +++ b/src/browser/EventManagerBase.zig @@ -305,16 +305,30 @@ pub fn dispatchDirect( event._current_target = target; + // Per DOM §2.9 step 4 substep 8 ("Inner invoke"), a listener callback that + // throws must have its exception *reported*, not propagated to the dispatch + // caller — subsequent listeners must still run. Same shape as the catch on + // the property-handler invocation above and on EventManager.dispatchPhase. switch (listener.function) { - .value => |value| try ls.local.toLocal(value).callWithThis(void, target, .{event}), + .value => |value| ls.local.toLocal(value).callWithThis(void, target, .{event}) catch |err| { + log.warn(.event, opts.context, .{ .err = err }); + }, .string => |string| { const str = try arena.dupeZ(u8, string.str()); - try ls.local.eval(str, null); + ls.local.eval(str, null) catch |err| { + log.warn(.event, opts.context, .{ .err = err }); + }; }, .object => |obj_global| { const obj = ls.local.toLocal(obj_global); - if (try obj.getFunction("handleEvent")) |handleEvent| { - try handleEvent.callWithThis(void, obj, .{event}); + const handle_event = obj.getFunction("handleEvent") catch |err| blk: { + log.warn(.event, opts.context, .{ .err = err }); + break :blk null; + }; + if (handle_event) |handleEvent| { + handleEvent.callWithThis(void, obj, .{event}) catch |err| { + log.warn(.event, opts.context, .{ .err = err }); + }; } }, } 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); } + +
+ + +
+ + +