From 1d263aabbdcc61bc1dd0d03cafc22ca85dcd033b Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Fri, 10 Jul 2026 16:47:42 +0200 Subject: [PATCH] webapi: keep explicit null as AbortSignal abort reason Fixes 1 failing test in WPT /dom/abort/event.any.html (and its .worker variant): "AbortController abort(null) should set signal.reason". abort() reason parameters were typed ?js.Value.Global, and the JS->Zig conversion collapses an explicit JS null into a missing argument, so controller.abort(null) fell back to the default "AbortError" DOMException. Per the DOM spec, the abort reason defaults to a new AbortError only when the reason is not given (undefined); an explicit null must be stored as-is. The parameters are now ?js.Value, which preserves the missing vs explicit-null distinction, and the new AbortSignal.reasonFromJs helper only falls back to the default for a missing or undefined reason. Coverage: /dom/abort/event.any.worker.html 15/16 -> 16/16, /dom/abort/event.any.html 15/16 -> 16/16. Co-Authored-By: Claude Fable 5 --- src/browser/webapi/AbortController.zig | 4 ++-- src/browser/webapi/AbortSignal.zig | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/browser/webapi/AbortController.zig b/src/browser/webapi/AbortController.zig index 3d6a30040..67d3d0453 100644 --- a/src/browser/webapi/AbortController.zig +++ b/src/browser/webapi/AbortController.zig @@ -37,8 +37,8 @@ pub fn getSignal(self: *const AbortController) *AbortSignal { return self._signal; } -pub fn abort(self: *AbortController, reason_: ?js.Value.Global, exec: *const Execution) !void { - try self._signal.abort(if (reason_) |r| .{ .js_val = r } else null, exec); +pub fn abort(self: *AbortController, reason_: ?js.Value, exec: *const Execution) !void { + try self._signal.abort(try AbortSignal.reasonFromJs(reason_), exec); } pub const JsApi = struct { diff --git a/src/browser/webapi/AbortSignal.zig b/src/browser/webapi/AbortSignal.zig index 75d8e69b3..64cbd2752 100644 --- a/src/browser/webapi/AbortSignal.zig +++ b/src/browser/webapi/AbortSignal.zig @@ -149,10 +149,21 @@ fn dispatchAbortEvent(self: *AbortSignal, exec: *const Execution) !void { } } +// Converts an abort(reason) JS argument to a Reason. Per spec, only a +// missing or undefined reason falls back to the default "AbortError" +// DOMException: an explicit null (or any other value) is kept as-is. +pub fn reasonFromJs(reason_: ?js.Value) !?Reason { + const reason = reason_ orelse return null; + if (reason.isUndefined()) { + return null; + } + return .{ .js_val = try reason.persist() }; +} + // Static method to create an already-aborted signal -pub fn createAborted(reason_: ?js.Value.Global, exec: *const Execution) !*AbortSignal { +pub fn createAborted(reason_: ?js.Value, exec: *const Execution) !*AbortSignal { const signal = try init(exec); - try signal.abort(if (reason_) |r| .{ .js_val = r } else null, exec); + try signal.abort(try reasonFromJs(reason_), exec); return signal; }