diff --git a/src/dom/event_target.zig b/src/dom/event_target.zig index b8f32581..0f8fd4b4 100644 --- a/src/dom/event_target.zig +++ b/src/dom/event_target.zig @@ -37,6 +37,13 @@ pub const EventTarget = struct { // TODO: hanle EventListenerOptions // see #https://github.com/lightpanda-io/jsruntime-lib/issues/114 ) !void { + + // check if event target has already this listener + const lst = try parser.eventTargetHasListener(self, eventType, cbk.id()); + if (lst != null) { + return; + } + // TODO: when can we free this allocation? const cbk_ptr = try alloc.create(Callback); cbk_ptr.* = cbk; @@ -85,6 +92,14 @@ pub fn testExecFn( }; try checkCases(js_env, &basic); + var basic_twice = [_]Case{ + .{ .src = "nb = 0", .ex = "0" }, + .{ .src = "content.addEventListener('basic', cbk)", .ex = "undefined" }, + .{ .src = "content.dispatchEvent(new Event('basic'))", .ex = "true" }, + .{ .src = "nb", .ex = "1" }, + }; + try checkCases(js_env, &basic_twice); + var basic_child = [_]Case{ .{ .src = "nb = 0; evt = undefined; phase = undefined; cur = undefined", .ex = "undefined" }, .{ .src = "para.dispatchEvent(new Event('basic'))", .ex = "true" }, diff --git a/src/netsurf.zig b/src/netsurf.zig index b609768b..ddf0e833 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -458,6 +458,13 @@ const event_handler = struct { } }.handle; +// EventListener +pub const EventListener = c.dom_event_listener; + +pub fn eventListenerGetData(lst: *EventListener) ?*anyopaque { + return c.dom_event_listener_get_data(lst); +} + // EventTarget pub const EventTarget = c.dom_event_target; @@ -465,6 +472,44 @@ fn eventTargetVtable(et: *EventTarget) c.dom_event_target_vtable { return getVtable(c.dom_event_target_vtable, EventTarget, et); } +pub fn eventTargetHasListener(et: *EventTarget, typ: []const u8, cbk_id: usize) !?*EventListener { + const str = try strFromData(typ); + + const EventListenerEntry = c.listener_entry; + var current: ?*EventListenerEntry = null; + var next: ?*EventListenerEntry = undefined; + var lst: ?*EventListener = undefined; + + // iterate over the EventTarget's listeners + while (true) { + const err = eventTargetVtable(et).iter_event_listener.?(et, str, current, &next, &lst); + try DOMErr(err); + + if (lst) |listener| { + // the EventTarget has a listener for this event type, + // let's check if the callback is the same + defer c.dom_event_listener_unref(listener); + const data = eventListenerGetData(listener); + if (data) |d| { + const ptr: *align(@alignOf(*Callback)) anyopaque = @alignCast(d); + const cbk = @as(*Callback, @ptrCast(ptr)); + if (cbk_id == cbk.id()) + return lst; + } + } + + if (next == null) { + // no more listeners, end of the iteration + break; + } + + // next iteration + current = next; + } + + return null; +} + pub fn eventTargetAddEventListener( et: *EventTarget, typ: []const u8, @@ -473,7 +518,7 @@ pub fn eventTargetAddEventListener( ) !void { const s = try strFromData(typ); const ctx = @as(*anyopaque, @ptrCast(cbk_ptr)); - var listener: ?*c.dom_event_listener = undefined; + var listener: ?*EventListener = undefined; const errLst = c.dom_event_listener_create(event_handler, ctx, &listener); try DOMErr(errLst); const err = eventTargetVtable(et).add_event_listener.?(et, s, listener, capture); diff --git a/vendor/jsruntime-lib b/vendor/jsruntime-lib index 7de65ccd..637cd3a3 160000 --- a/vendor/jsruntime-lib +++ b/vendor/jsruntime-lib @@ -1 +1 @@ -Subproject commit 7de65ccd304b25a8db69c57787bf28cd3b188b90 +Subproject commit 637cd3a34471793767a15da2f9898900561bc117