Skip to content

Commit 1924f13

Browse files
committed
events: create an EventHandlerData struct
It simplifies the EventHandlerFunc creation and allows to insert user's data.
1 parent 522b293 commit 1924f13

File tree

4 files changed

+109
-61
lines changed

4 files changed

+109
-61
lines changed

src/dom/event_target.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ pub const EventTarget = struct {
7373
self,
7474
alloc,
7575
eventType,
76-
cbk,
77-
capture orelse false,
7876
EventHandler,
77+
.{ .cbk = cbk },
78+
capture orelse false,
7979
);
8080
}
8181

src/events/event.zig

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -241,31 +241,23 @@ pub fn testExecFn(
241241
}
242242

243243
pub const EventHandler = struct {
244-
fn handle(event: ?*parser.Event, data: ?*anyopaque) callconv(.C) void {
245-
if (data) |d| {
246-
const func = parser.event_handler_cbk(d);
247-
248-
// TODO get the allocator by another way?
249-
var res = CallbackResult.init(func.nat_ctx.alloc);
250-
defer res.deinit();
251-
252-
if (event) |evt| {
253-
func.trycall(.{
254-
Event.toInterface(evt) catch unreachable,
255-
}, &res) catch |e| log.err("event handler error: {any}", .{e});
256-
} else {
257-
func.trycall(.{event}, &res) catch |e| log.err("event handler error: {any}", .{e});
258-
}
259-
260-
// in case of function error, we log the result and the trace.
261-
if (!res.success) {
262-
log.info("event handler error: {s}", .{res.result orelse "unknown"});
263-
log.debug("{s}", .{res.stack orelse "no stack trace"});
264-
}
265-
266-
// NOTE: we can not call func.deinit here
267-
// b/c the handler can be called several times
268-
// either on this dispatch event or in anoter one
244+
fn handle(event: ?*parser.Event, data: parser.EventHandlerData) void {
245+
// TODO get the allocator by another way?
246+
var res = CallbackResult.init(data.cbk.nat_ctx.alloc);
247+
defer res.deinit();
248+
249+
if (event) |evt| {
250+
data.cbk.trycall(.{
251+
Event.toInterface(evt) catch unreachable,
252+
}, &res) catch |e| log.err("event handler error: {any}", .{e});
253+
} else {
254+
data.cbk.trycall(.{event}, &res) catch |e| log.err("event handler error: {any}", .{e});
255+
}
256+
257+
// in case of function error, we log the result and the trace.
258+
if (!res.success) {
259+
log.info("event handler error: {s}", .{res.result orelse "unknown"});
260+
log.debug("{s}", .{res.stack orelse "no stack trace"});
269261
}
270262
}
271263
}.handle;

src/netsurf/netsurf.zig

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -522,12 +522,6 @@ pub const EventType = enum(u8) {
522522
progress_event = 1,
523523
};
524524

525-
// EventHandler
526-
pub fn event_handler_cbk(data: *anyopaque) *Callback {
527-
const ptr: *align(@alignOf(*Callback)) anyopaque = @alignCast(data);
528-
return @as(*Callback, @ptrCast(ptr));
529-
}
530-
531525
// EventListener
532526
pub const EventListener = c.dom_event_listener;
533527
const EventListenerEntry = c.listener_entry;
@@ -587,10 +581,9 @@ pub fn eventTargetHasListener(
587581
// and capture property,
588582
// let's check if the callback handler is the same
589583
defer c.dom_event_listener_unref(listener);
590-
const data = eventListenerGetData(listener);
591-
if (data) |d| {
592-
const cbk = event_handler_cbk(d);
593-
if (cbk_id == cbk.id()) {
584+
const ehd = EventHandlerDataInternal.fromListener(listener);
585+
if (ehd) |d| {
586+
if (cbk_id == d.data.cbk.id()) {
594587
return lst;
595588
}
596589
}
@@ -608,29 +601,99 @@ pub fn eventTargetHasListener(
608601
return null;
609602
}
610603

611-
const EventHandler = fn (event: ?*Event, data: ?*anyopaque) callconv(.C) void;
604+
// EventHandlerFunc is a zig function called when the event is dispatched to a
605+
// listener.
606+
// The EventHandlerFunc is responsible to call the callback included into the
607+
// EventHandlerData.
608+
pub const EventHandlerFunc = *const fn (event: ?*Event, data: EventHandlerData) void;
609+
610+
// EventHandler implements the function exposed in C and called by libdom.
611+
// It retrieves the EventHandlerInternalData and call the EventHandlerFunc with
612+
// the EventHandlerData in parameter.
613+
const EventHandler = struct {
614+
fn handle(event: ?*Event, data: ?*anyopaque) callconv(.C) void {
615+
if (data) |d| {
616+
const ehd = EventHandlerDataInternal.get(d);
617+
ehd.handler(event, ehd.data);
618+
619+
// NOTE: we can not call func.deinit here
620+
// b/c the handler can be called several times
621+
// either on this dispatch event or in anoter one
622+
}
623+
}
624+
}.handle;
625+
626+
// EventHandlerData contains a JS callback and the data associated to the
627+
// handler.
628+
// If given, deinitFunc is called with the data pointer to allow the creator to
629+
// clean memory.
630+
// The callback is deinit by EventHandlerDataInternal. It must NOT be deinit
631+
// into deinitFunc.
632+
pub const EventHandlerData = struct {
633+
cbk: Callback,
634+
data: ?*anyopaque = null,
635+
// deinitFunc implements the data deinitialization.
636+
deinitFunc: ?DeinitFunc = null,
637+
638+
pub const DeinitFunc = *const fn (data: ?*anyopaque, alloc: std.mem.Allocator) void;
639+
};
640+
641+
// EventHandlerDataInternal groups the EventHandlerFunc and the EventHandlerData.
642+
const EventHandlerDataInternal = struct {
643+
data: EventHandlerData,
644+
handler: EventHandlerFunc,
645+
646+
fn init(alloc: std.mem.Allocator, handler: EventHandlerFunc, data: EventHandlerData) !*EventHandlerDataInternal {
647+
const ptr = try alloc.create(EventHandlerDataInternal);
648+
ptr.* = .{
649+
.data = data,
650+
.handler = handler,
651+
};
652+
return ptr;
653+
}
654+
655+
fn deinit(self: *EventHandlerDataInternal, alloc: std.mem.Allocator) void {
656+
if (self.data.deinitFunc) |d| d(self.data.data, alloc);
657+
self.data.cbk.deinit(alloc);
658+
alloc.destroy(self);
659+
}
660+
661+
fn get(data: *anyopaque) *EventHandlerDataInternal {
662+
const ptr: *align(@alignOf(*EventHandlerDataInternal)) anyopaque = @alignCast(data);
663+
return @as(*EventHandlerDataInternal, @ptrCast(ptr));
664+
}
665+
666+
// retrieve a EventHandlerDataInternal from a listener.
667+
fn fromListener(lst: *EventListener) ?*EventHandlerDataInternal {
668+
const data = eventListenerGetData(lst);
669+
// free cbk allocation made on eventTargetAddEventListener
670+
if (data == null) return null;
671+
672+
return get(data.?);
673+
}
674+
};
612675

613676
pub fn eventTargetAddEventListener(
614677
et: *EventTarget,
615678
alloc: std.mem.Allocator,
616679
typ: []const u8,
617-
cbk: Callback,
680+
handlerFunc: EventHandlerFunc,
681+
data: EventHandlerData,
618682
capture: bool,
619-
handler: EventHandler,
620683
) !void {
621684
// this allocation will be removed either on
622685
// eventTargetRemoveEventListener or eventTargetRemoveAllEventListeners
623-
const cbk_ptr = try alloc.create(Callback);
624-
cbk_ptr.* = cbk;
686+
const ehd = try EventHandlerDataInternal.init(alloc, handlerFunc, data);
687+
errdefer ehd.deinit(alloc);
625688

626689
// When a function is used as an event handler, its this parameter is bound
627690
// to the DOM element on which the listener is placed.
628691
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#this_in_dom_event_handlers
629-
try cbk_ptr.setThisArg(et);
692+
try ehd.data.cbk.setThisArg(et);
630693

631-
const ctx = @as(*anyopaque, @ptrCast(cbk_ptr));
694+
const ctx = @as(*anyopaque, @ptrCast(ehd));
632695
var listener: ?*EventListener = undefined;
633-
const errLst = c.dom_event_listener_create(handler, ctx, &listener);
696+
const errLst = c.dom_event_listener_create(EventHandler, ctx, &listener);
634697
try DOMErr(errLst);
635698
defer c.dom_event_listener_unref(listener);
636699

@@ -646,13 +709,9 @@ pub fn eventTargetRemoveEventListener(
646709
lst: *EventListener,
647710
capture: bool,
648711
) !void {
649-
const data = eventListenerGetData(lst);
650-
// free cbk allocation made on eventTargetAddEventListener
651-
if (data) |d| {
652-
const cbk_ptr = event_handler_cbk(d);
653-
cbk_ptr.deinit(alloc);
654-
alloc.destroy(cbk_ptr);
655-
}
712+
// free data allocation made on eventTargetAddEventListener
713+
const ehd = EventHandlerDataInternal.fromListener(lst);
714+
if (ehd) |d| d.deinit(alloc);
656715

657716
const s = try strFromData(typ);
658717
const err = eventTargetVtable(et).remove_event_listener.?(et, s, lst, capture);
@@ -680,13 +739,10 @@ pub fn eventTargetRemoveAllEventListeners(
680739

681740
if (lst) |listener| {
682741
defer c.dom_event_listener_unref(listener);
683-
const data = eventListenerGetData(listener);
684-
if (data) |d| {
685-
// free cbk allocation made on eventTargetAddEventListener
686-
const cbk = event_handler_cbk(d);
687-
cbk.deinit(alloc);
688-
alloc.destroy(cbk);
689-
}
742+
743+
const ehd = EventHandlerDataInternal.fromListener(listener);
744+
if (ehd) |d| d.deinit(alloc);
745+
690746
const err = eventTargetVtable(et).remove_event_listener.?(
691747
et,
692748
null,

src/xhr/event_target.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ pub const XMLHttpRequestEventTarget = struct {
5252
@as(*parser.EventTarget, @ptrCast(self)),
5353
alloc,
5454
typ,
55-
cbk,
56-
false,
5755
EventHandler,
56+
.{ .cbk = cbk },
57+
false,
5858
);
5959
}
6060
fn unregister(self: *XMLHttpRequestEventTarget, alloc: std.mem.Allocator, typ: []const u8, cbk: Callback) !void {

0 commit comments

Comments
 (0)