@@ -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
532526pub const EventListener = c .dom_event_listener ;
533527const 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
613676pub 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 ,
0 commit comments