diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index 5f169514..441f32e3 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -329,13 +329,36 @@ fn dispatchNode(self: *EventManager, target: *Node, event: *Event, was_handled: // Phase 2: At target event._event_phase = .at_target; const target_et = target.asEventTarget(); - if (self.lookup.get(.{ - .type_string = event._type_string, - .event_target = @intFromPtr(target_et), - })) |list| { - try self.dispatchPhase(list, target_et, event, was_handled, null); - if (event._stop_propagation) { - return; + + blk: { + // Get inline handler (e.g., onclick property) for this target + if (self.getInlineHandler(target_et, event)) |inline_handler| { + was_handled.* = true; + event._current_target = target_et; + + var ls: js.Local.Scope = undefined; + self.page.js.localScope(&ls); + defer ls.deinit(); + + try ls.toLocal(inline_handler).callWithThis(void, target_et, .{event}); + + if (event._stop_propagation) { + return; + } + + if (event._stop_immediate_propagation) { + break :blk; + } + } + + if (self.lookup.get(.{ + .type_string = event._type_string, + .event_target = @intFromPtr(target_et), + })) |list| { + try self.dispatchPhase(list, target_et, event, was_handled, null); + if (event._stop_propagation) { + return; + } } } @@ -460,6 +483,20 @@ fn dispatchAll(self: *EventManager, list: *std.DoublyLinkedList, current_target: return self.dispatchPhase(list, current_target, event, was_handled, null); } +fn getInlineHandler(self: *EventManager, target: *EventTarget, event: *Event) ?js.Function.Global { + const global_event_handlers = @import("webapi/global_event_handlers.zig"); + const handler_type = global_event_handlers.fromEventType(event._type_string.str()) orelse return null; + + // Look up the inline handler for this target + const Element = @import("webapi/Element.zig"); + const element = switch (target._type) { + .node => |n| n.is(Element) orelse return null, + else => return null, + }; + + return self.page.getAttrListener(element, handler_type); +} + fn removeListener(self: *EventManager, list: *std.DoublyLinkedList, listener: *Listener) void { // If we're in a dispatch, defer removal to avoid invalidating iteration if (self.dispatch_depth > 0) { diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 175cd3a9..e10b76e1 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -710,18 +710,6 @@ fn _documentIsComplete(self: *Page) !void { for (self._to_load.items) |element| { const event = try Event.initTrusted(comptime .wrap("load"), .{}, self); defer if (!event._v8_handoff) event.deinit(false); - - // Dispatch inline event. - blk: { - const html_element = element.is(HtmlElement) orelse break :blk; - - const listener = (try html_element.getOnLoad(self)) orelse break :blk; - ls.toLocal(listener).call(void, .{}) catch |err| { - log.warn(.event, "inline load event", .{ .element = element, .err = err }); - }; - } - - // Dispatch events registered to event manager. try self._event_manager.dispatch(element.asEventTarget(), event); } diff --git a/src/browser/tests/events.html b/src/browser/tests/events.html index 08319569..e463300e 100644 --- a/src/browser/tests/events.html +++ b/src/browser/tests/events.html @@ -635,3 +635,130 @@ // https://github.com/lightpanda-io/browser/pull/1316 testing.expectError('TypeError', () => MessageEvent('')); + +