@@ -616,10 +616,12 @@ pub fn eventTargetHasListener(
616616 // and capture property,
617617 // let's check if the callback handler is the same
618618 defer c .dom_event_listener_unref (listener );
619- const ehd = EventHandlerDataInternal .fromListener (listener );
620- if (ehd ) | d | {
621- if (cbk_id == d .data .cbk .id ) {
622- return lst ;
619+ if (EventHandlerData .fromListener (listener )) | ehd | {
620+ switch (ehd .* ) {
621+ .js = > | js | if (cbk_id == js .data .cbk .id ) {
622+ return lst ;
623+ },
624+ .zig = > {},
623625 }
624626 }
625627 }
@@ -636,100 +638,115 @@ pub fn eventTargetHasListener(
636638 return null ;
637639}
638640
639- // EventHandlerFunc is a zig function called when the event is dispatched to a
640- // listener .
641- // The EventHandlerFunc is responsible to call the callback included into the
642- // EventHandlerData.
643- pub const EventHandlerFunc = * const fn ( event : ? * Event , data : EventHandlerData ) void ;
641+ // The *anyopque that get stored in the libdom listener, which we'll retrieve
642+ // when then event is dispatched so that we can execute the JS or Zig callback .
643+ const EventHandlerData = union ( enum ) {
644+ js : JS ,
645+ zig : Zig ,
644646
645- // EventHandler implements the function exposed in C and called by libdom.
646- // It retrieves the EventHandlerInternalData and call the EventHandlerFunc with
647- // the EventHandlerData in parameter.
648- const EventHandler = struct {
649- fn handle (event : ? * Event , data : ? * anyopaque ) callconv (.C ) void {
650- if (data ) | d | {
651- const ehd = EventHandlerDataInternal .get (d );
652- ehd .handler (event , ehd .data );
653-
654- // NOTE: we can not call func.deinit here
655- // b/c the handler can be called several times
656- // either on this dispatch event or in anoter one
657- }
658- }
659- }.handle ;
660-
661- // EventHandlerData contains a JS callback and the data associated to the
662- // handler.
663- // If given, deinitFunc is called with the data pointer to allow the creator to
664- // clean memory.
665- // The callback is deinit by EventHandlerDataInternal. It must NOT be deinit
666- // into deinitFunc.
667- pub const EventHandlerData = struct {
668- cbk : Callback ,
669- data : ? * anyopaque = null ,
670- // deinitFunc implements the data deinitialization.
671- deinitFunc : ? DeinitFunc = null ,
672-
673- pub const DeinitFunc = * const fn (data : ? * anyopaque , allocator : std.mem.Allocator ) void ;
674- };
647+ const JS = struct {
648+ data : JSEventHandlerData ,
649+ func : JSEventHandlerFunc ,
650+ };
675651
676- // EventHandlerDataInternal groups the EventHandlerFunc and the EventHandlerData.
677- const EventHandlerDataInternal = struct {
678- data : EventHandlerData ,
679- handler : EventHandlerFunc ,
652+ const Zig = struct {
653+ ctx : * anyopaque ,
654+ func : ZigEventHandlerFunc ,
655+ };
680656
681- fn init (alloc : std.mem.Allocator , handler : EventHandlerFunc , data : EventHandlerData ) ! * EventHandlerDataInternal {
682- const ptr = try alloc .create (EventHandlerDataInternal );
683- ptr .* = .{
684- .data = data ,
685- .handler = handler ,
686- };
687- return ptr ;
657+ // retrieve a EventHandlerDataInternal from a listener.
658+ fn fromListener (lst : * EventListener ) ? * EventHandlerData {
659+ const ctx = eventListenerGetData (lst ) orelse return null ;
660+ const ehd : * EventHandlerData = @alignCast (@ptrCast (ctx ));
661+ return ehd ;
688662 }
689663
690- fn deinit (self : * EventHandlerDataInternal , alloc : std.mem.Allocator ) void {
691- if (self .data .deinitFunc ) | d | {
692- d (self .data .data , alloc );
664+ pub fn deinit (self : * EventHandlerData , alloc : std.mem.Allocator ) void {
665+ switch (self .* ) {
666+ .js = > | * js | {
667+ const js_data = & js .data ;
668+ if (js_data .deinitFunc ) | df | {
669+ df (js_data .ctx , alloc );
670+ }
671+ },
672+ .zig = > {},
693673 }
694674 alloc .destroy (self );
695675 }
696676
697- fn get (data : * anyopaque ) * EventHandlerDataInternal {
698- const ptr : * align (@alignOf (* EventHandlerDataInternal )) anyopaque = @alignCast (data );
699- return @as (* EventHandlerDataInternal , @ptrCast (ptr ));
677+ pub fn handle (self : * EventHandlerData , event : ? * Event ) void {
678+ switch (self .* ) {
679+ .js = > | * js | js .func (event , & js .data ),
680+ .zig = > | zig | zig .func (zig .ctx , event .? ),
681+ }
700682 }
683+ };
701684
702- // retrieve a EventHandlerDataInternal from a listener.
703- fn fromListener ( lst : * EventListener ) ? * EventHandlerDataInternal {
704- const data = eventListenerGetData ( lst );
705- // free cbk allocation made on eventTargetAddEventListener
706- if ( data == null ) return null ;
685+ pub const JSEventHandlerData = struct {
686+ cbk : Callback ,
687+ ctx : ? * anyopaque = null ,
688+ // deinitFunc implements the data deinitialization.
689+ deinitFunc : ? DeinitFunc = null ,
707690
708- return get (data .? );
709- }
691+ pub const DeinitFunc = * const fn (data : ? * anyopaque , alloc : std.mem.Allocator ) void ;
710692};
711693
694+ const JSEventHandlerFunc = * const fn (event : ? * Event , data : * JSEventHandlerData ) void ;
695+ const ZigEventHandlerFunc = * const fn (ctx : * anyopaque , event : * Event ) void ;
696+
712697pub fn eventTargetAddEventListener (
713698 et : * EventTarget ,
714699 alloc : std.mem.Allocator ,
715700 typ : []const u8 ,
716- handlerFunc : EventHandlerFunc ,
717- data : EventHandlerData ,
701+ func : JSEventHandlerFunc ,
702+ data : JSEventHandlerData ,
718703 capture : bool ,
719704) ! void {
720705 // this allocation will be removed either on
721706 // eventTargetRemoveEventListener or eventTargetRemoveAllEventListeners
722- const ehd = try EventHandlerDataInternal .init (alloc , handlerFunc , data );
707+ const ehd = try alloc .create (EventHandlerData );
708+ errdefer alloc .destroy (ehd );
709+ ehd .* = .{ .js = .{ .data = data , .func = func } };
723710 errdefer ehd .deinit (alloc );
724711
725712 // When a function is used as an event handler, its this parameter is bound
726713 // to the DOM element on which the listener is placed.
727714 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#this_in_dom_event_handlers
728- try ehd .data .cbk .setThis (et );
715+ try ehd .js .data .cbk .setThis (et );
716+
717+ return addEventTargetListener (et , typ , ehd , capture );
718+ }
719+
720+ pub fn eventTargetAddZigListener (
721+ et : * EventTarget ,
722+ alloc : std.mem.Allocator ,
723+ typ : []const u8 ,
724+ func : ZigEventHandlerFunc ,
725+ ctx : * anyopaque ,
726+ capture : bool ,
727+ ) ! void {
728+ const ehd = try alloc .create (EventHandlerData );
729+ errdefer alloc .destroy (ehd );
730+ ehd .* = .{ .zig = .{ .ctx = ctx , .func = func } };
731+ return addEventTargetListener (et , typ , ehd , capture );
732+ }
733+
734+ fn addEventTargetListener (et : * EventTarget , typ : []const u8 , data : * anyopaque , capture : bool ) ! void {
735+ // event_handler implements the function exposed in C and called by libdom.
736+ // It retrieves the EventHandler and calls the appropriate (JS or Zig)
737+ // handler function with the corresponding data.
738+ const event_handler = struct {
739+ fn handle (event : ? * Event , ptr_ : ? * anyopaque ) callconv (.C ) void {
740+ const ptr = ptr_ orelse return ;
741+ @as (* EventHandlerData , @alignCast (@ptrCast (ptr ))).handle (event );
742+ // NOTE: we can not call func.deinit here
743+ // b/c the handler can be called several times
744+ // either on this dispatch event or in anoter one
745+ }
746+ }.handle ;
729747
730- const ctx = @as (* anyopaque , @ptrCast (ehd ));
731748 var listener : ? * EventListener = undefined ;
732- const errLst = c .dom_event_listener_create (EventHandler , ctx , & listener );
749+ const errLst = c .dom_event_listener_create (event_handler , data , & listener );
733750 try DOMErr (errLst );
734751 defer c .dom_event_listener_unref (listener );
735752
@@ -746,8 +763,9 @@ pub fn eventTargetRemoveEventListener(
746763 capture : bool ,
747764) ! void {
748765 // free data allocation made on eventTargetAddEventListener
749- const ehd = EventHandlerDataInternal .fromListener (lst );
750- if (ehd ) | d | d .deinit (alloc );
766+ if (EventHandlerData .fromListener (lst )) | ehd | {
767+ ehd .deinit (alloc );
768+ }
751769
752770 const s = try strFromData (typ );
753771 const err = eventTargetVtable (et ).remove_event_listener .? (et , s , lst , capture );
@@ -776,16 +794,21 @@ pub fn eventTargetRemoveAllEventListeners(
776794 if (lst ) | listener | {
777795 defer c .dom_event_listener_unref (listener );
778796
779- const ehd = EventHandlerDataInternal .fromListener (listener );
780- if (ehd ) | d | d .deinit (alloc );
797+ if (EventHandlerData .fromListener (listener )) | ehd | {
798+ if (ehd .* == .zig ) {
799+ // we don't remove Zig listeners
800+ continue ;
801+ }
781802
782- const err = eventTargetVtable (et ).remove_event_listener .? (
783- et ,
784- null ,
785- lst ,
786- false ,
787- );
788- try DOMErr (err );
803+ ehd .deinit (alloc );
804+ const err = eventTargetVtable (et ).remove_event_listener .? (
805+ et ,
806+ null ,
807+ lst ,
808+ false ,
809+ );
810+ try DOMErr (err );
811+ }
789812 }
790813
791814 if (next == null ) {
0 commit comments