@@ -2,6 +2,7 @@ const std = @import("std");
22
33const jsruntime = @import ("jsruntime" );
44const Callback = jsruntime .Callback ;
5+ const JSObjectID = jsruntime .JSObjectID ;
56const Case = jsruntime .test_utils .Case ;
67const checkCases = jsruntime .test_utils .checkCases ;
78
@@ -39,22 +40,71 @@ pub const EventTarget = struct {
3940 ) ! void {
4041
4142 // check if event target has already this listener
42- const lst = try parser .eventTargetHasListener (self , eventType , cbk .id ());
43+ const lst = try parser .eventTargetHasListener (
44+ self ,
45+ eventType ,
46+ capture orelse false ,
47+ cbk .id (),
48+ );
4349 if (lst != null ) {
4450 return ;
4551 }
4652
47- // TODO: when can we free this allocation?
53+ // NOTE: this allocation will be removed either if removeEventListener
54+ // or at EventTarget deinit
4855 const cbk_ptr = try alloc .create (Callback );
4956 cbk_ptr .* = cbk ;
50- try parser .eventTargetAddEventListener (self , eventType , cbk_ptr , capture orelse false );
57+ try parser .eventTargetAddEventListener (
58+ self ,
59+ eventType ,
60+ cbk_ptr ,
61+ capture orelse false ,
62+ );
63+ }
64+
65+ pub fn _removeEventListener (
66+ self : * parser.EventTarget ,
67+ alloc : std.mem.Allocator ,
68+ eventType : []const u8 ,
69+ cbk_id : JSObjectID ,
70+ capture : ? bool ,
71+ // TODO: hanle EventListenerOptions
72+ // see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
73+ ) ! void {
74+
75+ // check if event target has already this listener
76+ const lst = try parser .eventTargetHasListener (
77+ self ,
78+ eventType ,
79+ capture orelse false ,
80+ cbk_id .get (),
81+ );
82+ if (lst == null ) {
83+ return ;
84+ }
85+
86+ // remove listener
87+ const cbk_handler = try parser .eventTargetRemoveEventListener (
88+ self ,
89+ eventType ,
90+ lst .? ,
91+ capture orelse false ,
92+ );
93+ if (cbk_handler ) | cbk_ptr | {
94+ cbk_ptr .deinit (alloc );
95+ alloc .destroy (cbk_ptr );
96+ }
5197 }
5298
5399 pub fn _dispatchEvent (self : * parser.EventTarget , event : * parser.Event ) ! bool {
54100 return try parser .eventTargetDispatchEvent (self , event );
55101 }
56102
57- pub fn deinit (_ : * parser.EventTarget , _ : std.mem.Allocator ) void {}
103+ pub fn deinit (_ : * parser.EventTarget , _ : std.mem.Allocator ) void {
104+ // TODO:
105+ // - deinit and destroy all cbk_handler
106+ // - remove all listeners
107+ }
58108};
59109
60110// Tests
@@ -92,6 +142,14 @@ pub fn testExecFn(
92142 };
93143 try checkCases (js_env , & basic );
94144
145+ var basic_child = [_ ]Case {
146+ .{ .src = "nb = 0; evt = undefined; phase = undefined; cur = undefined" , .ex = "undefined" },
147+ .{ .src = "para.dispatchEvent(new Event('basic'))" , .ex = "true" },
148+ .{ .src = "nb" , .ex = "0" }, // handler is not called, no capture, not the target, no bubbling
149+ .{ .src = "evt === undefined" , .ex = "true" },
150+ };
151+ try checkCases (js_env , & basic_child );
152+
95153 var basic_twice = [_ ]Case {
96154 .{ .src = "nb = 0" , .ex = "0" },
97155 .{ .src = "content.addEventListener('basic', cbk)" , .ex = "undefined" },
@@ -100,13 +158,29 @@ pub fn testExecFn(
100158 };
101159 try checkCases (js_env , & basic_twice );
102160
103- var basic_child = [_ ]Case {
104- .{ .src = "nb = 0; evt = undefined; phase = undefined; cur = undefined " , .ex = "undefined " },
105- .{ .src = "para.dispatchEvent(new Event( 'basic')) " , .ex = "true " },
106- .{ .src = "nb " , .ex = "0 " }, // handler is not called, no capture, not the target, no bubbling
107- .{ .src = "evt === undefined " , .ex = "true " },
161+ var basic_twice_capture = [_ ]Case {
162+ .{ .src = "nb = 0 " , .ex = "0 " },
163+ .{ .src = "content.addEventListener( 'basic', cbk, true) " , .ex = "undefined " },
164+ .{ .src = "content.dispatchEvent(new Event('basic')) " , .ex = "true " },
165+ .{ .src = "nb " , .ex = "2 " },
108166 };
109- try checkCases (js_env , & basic_child );
167+ try checkCases (js_env , & basic_twice_capture );
168+
169+ var basic_remove = [_ ]Case {
170+ .{ .src = "nb = 0" , .ex = "0" },
171+ .{ .src = "content.removeEventListener('basic', cbk)" , .ex = "undefined" },
172+ .{ .src = "content.dispatchEvent(new Event('basic'))" , .ex = "true" },
173+ .{ .src = "nb" , .ex = "1" },
174+ };
175+ try checkCases (js_env , & basic_remove );
176+
177+ var basic_capture_remove = [_ ]Case {
178+ .{ .src = "nb = 0" , .ex = "0" },
179+ .{ .src = "content.removeEventListener('basic', cbk, true)" , .ex = "undefined" },
180+ .{ .src = "content.dispatchEvent(new Event('basic'))" , .ex = "true" },
181+ .{ .src = "nb" , .ex = "0" },
182+ };
183+ try checkCases (js_env , & basic_capture_remove );
110184
111185 var capture = [_ ]Case {
112186 .{ .src = "nb = 0; evt = undefined; phase = undefined; cur = undefined" , .ex = "undefined" },
0 commit comments