@@ -522,12 +522,6 @@ pub const EventType = enum(u8) {
522
522
progress_event = 1 ,
523
523
};
524
524
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
-
531
525
// EventListener
532
526
pub const EventListener = c .dom_event_listener ;
533
527
const EventListenerEntry = c .listener_entry ;
@@ -587,10 +581,9 @@ pub fn eventTargetHasListener(
587
581
// and capture property,
588
582
// let's check if the callback handler is the same
589
583
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 ()) {
594
587
return lst ;
595
588
}
596
589
}
@@ -608,29 +601,99 @@ pub fn eventTargetHasListener(
608
601
return null ;
609
602
}
610
603
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
+ };
612
675
613
676
pub fn eventTargetAddEventListener (
614
677
et : * EventTarget ,
615
678
alloc : std.mem.Allocator ,
616
679
typ : []const u8 ,
617
- cbk : Callback ,
680
+ handlerFunc : EventHandlerFunc ,
681
+ data : EventHandlerData ,
618
682
capture : bool ,
619
- handler : EventHandler ,
620
683
) ! void {
621
684
// this allocation will be removed either on
622
685
// 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 ) ;
625
688
626
689
// When a function is used as an event handler, its this parameter is bound
627
690
// to the DOM element on which the listener is placed.
628
691
// 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 );
630
693
631
- const ctx = @as (* anyopaque , @ptrCast (cbk_ptr ));
694
+ const ctx = @as (* anyopaque , @ptrCast (ehd ));
632
695
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 );
634
697
try DOMErr (errLst );
635
698
defer c .dom_event_listener_unref (listener );
636
699
@@ -646,13 +709,9 @@ pub fn eventTargetRemoveEventListener(
646
709
lst : * EventListener ,
647
710
capture : bool ,
648
711
) ! 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 );
656
715
657
716
const s = try strFromData (typ );
658
717
const err = eventTargetVtable (et ).remove_event_listener .? (et , s , lst , capture );
@@ -680,13 +739,10 @@ pub fn eventTargetRemoveAllEventListeners(
680
739
681
740
if (lst ) | listener | {
682
741
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
+
690
746
const err = eventTargetVtable (et ).remove_event_listener .? (
691
747
et ,
692
748
null ,
0 commit comments