From 74354d2027f1d541546f9a0d59aebc2787ada0cb Mon Sep 17 00:00:00 2001
From: Alexis Bouchez <209880115+alexisbouchez@users.noreply.github.com>
Date: Thu, 22 Jan 2026 10:12:06 +0100
Subject: [PATCH] invoke window.onerror callback in reportError
---
src/browser/tests/window/onerror.html | 211 ++++++++++++++++++++++++++
src/browser/webapi/Window.zig | 30 +++-
2 files changed, 239 insertions(+), 2 deletions(-)
create mode 100644 src/browser/tests/window/onerror.html
diff --git a/src/browser/tests/window/onerror.html b/src/browser/tests/window/onerror.html
new file mode 100644
index 00000000..d07ba190
--- /dev/null
+++ b/src/browser/tests/window/onerror.html
@@ -0,0 +1,211 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig
index 781081c4..7434ab75 100644
--- a/src/browser/webapi/Window.zig
+++ b/src/browser/webapi/Window.zig
@@ -58,7 +58,7 @@ _storage_bucket: *storage.Bucket,
_on_load: ?js.Function.Global = null,
_on_pageshow: ?js.Function.Global = null,
_on_popstate: ?js.Function.Global = null,
-_on_error: ?js.Function.Global = null, // TODO: invoke on error?
+_on_error: ?js.Function.Global = null,
_on_unhandled_rejection: ?js.Function.Global = null, // TODO: invoke on error
_location: *Location,
_timer_id: u30 = 0,
@@ -283,6 +283,32 @@ pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
}, page);
const event = error_event.asEvent();
+
+ // Invoke window.onerror callback if set (per WHATWG spec, this is called
+ // with 5 arguments: message, source, lineno, colno, error)
+ // If it returns true, the event is cancelled.
+ if (self._on_error) |on_error| {
+ var ls: js.Local.Scope = undefined;
+ page.js.localScope(&ls);
+ defer ls.deinit();
+
+ const local_func = ls.toLocal(on_error);
+ const result = local_func.call(js.Value, .{
+ error_event._message,
+ error_event._filename,
+ error_event._line_number,
+ error_event._column_number,
+ err,
+ }) catch null;
+
+ // Per spec: returning true from onerror cancels the event
+ if (result) |r| {
+ if (r.isTrue()) {
+ event._prevent_default = true;
+ }
+ }
+ }
+
try page._event_manager.dispatch(self.asEventTarget(), event);
if (comptime builtin.is_test == false) {
@@ -665,7 +691,7 @@ pub const JsApi = struct {
pub const onload = bridge.accessor(Window.getOnLoad, Window.setOnLoad, .{});
pub const onpageshow = bridge.accessor(Window.getOnPageShow, Window.setOnPageShow, .{});
pub const onpopstate = bridge.accessor(Window.getOnPopState, Window.setOnPopState, .{});
- pub const onerror = bridge.accessor(Window.getOnError, Window.getOnError, .{});
+ pub const onerror = bridge.accessor(Window.getOnError, Window.setOnError, .{});
pub const onunhandledrejection = bridge.accessor(Window.getOnUnhandledRejection, Window.setOnUnhandledRejection, .{});
pub const fetch = bridge.function(Window.fetch, .{});
pub const queueMicrotask = bridge.function(Window.queueMicrotask, .{});