@@ -27,6 +27,7 @@ const Page = @import("../page.zig").Page;
2727const DOMException = @import ("../dom/exceptions.zig" ).DOMException ;
2828const EventTarget = @import ("../dom/event_target.zig" ).EventTarget ;
2929const EventTargetUnion = @import ("../dom/event_target.zig" ).Union ;
30+ const AbortSignal = @import ("../html/AbortController.zig" ).AbortSignal ;
3031
3132const CustomEvent = @import ("custom_event.zig" ).CustomEvent ;
3233const ProgressEvent = @import ("../xhr/progress_event.zig" ).ProgressEvent ;
@@ -175,7 +176,7 @@ pub const EventHandler = struct {
175176 // that the listener won't call preventDefault() and thus can safely
176177 // run the default as needed).
177178 passive : ? bool ,
178- signal : ? bool , // currently does nothing
179+ signal : ? * AbortSignal , // currently does nothing
179180 };
180181 };
181182
@@ -188,25 +189,44 @@ pub const EventHandler = struct {
188189 ) ! ? * EventHandler {
189190 var once = false ;
190191 var capture = false ;
192+ var signal : ? * AbortSignal = null ;
193+
191194 if (opts_ ) | opts | {
192195 switch (opts ) {
193196 .capture = > | c | capture = c ,
194197 .flags = > | f | {
195- // Done this way so that, for common cases that _only_ set
196- // capture, i.e. {captrue: true}, it works.
197- // But for any case that sets any of the other flags, we
198- // error. If we don't error, this function call would succeed
199- // but the behavior might be wrong. At this point, it's
200- // better to be explicit and error.
201- if (f .signal orelse false ) return error .NotImplemented ;
202198 once = f .once orelse false ;
199+ signal = f .signal orelse null ;
203200 capture = f .capture orelse false ;
204201 },
205202 }
206203 }
207204
208205 const callback = (try listener .callback (target )) orelse return null ;
209206
207+ if (signal ) | s | {
208+ std .debug .print ("add signal\n " , .{});
209+ const signal_target = parser .toEventTarget (AbortSignal , s );
210+
211+ const scb = try allocator .create (SignalCallback );
212+ scb .* = .{
213+ .target = target ,
214+ .capture = capture ,
215+ .callback_id = callback .id ,
216+ .typ = try allocator .dupe (u8 , typ ),
217+ .signal_target = signal_target ,
218+ .signal_listener = undefined ,
219+ .node = .{.func = SignalCallback .handle },
220+ };
221+
222+ scb .signal_listener = try parser .eventTargetAddEventListener (
223+ signal_target ,
224+ "abort" ,
225+ & scb .node ,
226+ false ,
227+ );
228+ }
229+
210230 // check if event target has already this listener
211231 if (try parser .eventTargetHasListener (target , typ , capture , callback .id ) != null ) {
212232 return null ;
@@ -262,6 +282,50 @@ pub const EventHandler = struct {
262282 }
263283};
264284
285+ const SignalCallback = struct {
286+ typ : []const u8 ,
287+ capture : bool ,
288+ callback_id : usize ,
289+ node : parser.EventNode ,
290+ target : * parser.EventTarget ,
291+ signal_target : * parser.EventTarget ,
292+ signal_listener : * parser.EventListener ,
293+
294+ fn handle (node : * parser.EventNode , _ : * parser.Event ) void {
295+ const self : * SignalCallback = @fieldParentPtr ("node" , node );
296+ self ._handle () catch | err | {
297+ log .err (.app , "event signal handler" , .{ .err = err });
298+ };
299+ }
300+
301+ fn _handle (self : * SignalCallback ) ! void {
302+ const lst = try parser .eventTargetHasListener (
303+ self .target ,
304+ self .typ ,
305+ self .capture ,
306+ self .callback_id ,
307+ );
308+ if (lst == null ) {
309+ return ;
310+ }
311+
312+ try parser .eventTargetRemoveEventListener (
313+ self .target ,
314+ self .typ ,
315+ lst .? ,
316+ self .capture ,
317+ );
318+
319+ // remove the abort signal listener itself
320+ try parser .eventTargetRemoveEventListener (
321+ self .signal_target ,
322+ "abort" ,
323+ self .signal_listener ,
324+ false ,
325+ );
326+ }
327+ };
328+
265329const testing = @import ("../../testing.zig" );
266330test "Browser.Event" {
267331 var runner = try testing .jsRunner (testing .tracking_allocator , .{});
@@ -367,5 +431,18 @@ test "Browser.Event" {
367431 .{ "document.dispatchEvent(new Event('count'))" , "true" },
368432 .{ "document.dispatchEvent(new Event('count'))" , "true" },
369433 .{ "nb" , "1" },
434+ .{ "document.removeEventListener('count', cbk)" , "undefined" },
435+ }, .{});
436+
437+ try runner .testCases (&.{
438+ .{ "nb = 0; function cbk(event) { nb ++; }" , null },
439+ .{ "let ac = new AbortController()" , null },
440+ .{ "document.addEventListener('count', cbk, {signal: ac.signal})" , null },
441+ .{ "document.dispatchEvent(new Event('count'))" , "true" },
442+ .{ "document.dispatchEvent(new Event('count'))" , "true" },
443+ .{ "ac.abort()" , null },
444+ .{ "document.dispatchEvent(new Event('count'))" , "true" },
445+ .{ "nb" , "2" },
446+ .{ "document.removeEventListener('count', cbk)" , "undefined" },
370447 }, .{});
371448}
0 commit comments