From 8642328aa1182564237a77a925a5fb45570a5830 Mon Sep 17 00:00:00 2001 From: Muki Kiboigo Date: Thu, 17 Sep 2026 22:57:54 -0700 Subject: [PATCH 1/2] add navigationsuccess and navigationerror to Navigation --- src/browser/webapi/navigation/Navigation.zig | 100 +++++++++++++++++-- 1 file changed, 93 insertions(+), 7 deletions(-) diff --git a/src/browser/webapi/navigation/Navigation.zig b/src/browser/webapi/navigation/Navigation.zig index 20fbe9328..ead126b9f 100644 --- a/src/browser/webapi/navigation/Navigation.zig +++ b/src/browser/webapi/navigation/Navigation.zig @@ -26,6 +26,7 @@ const Factory = @import("../../Factory.zig"); const Event = @import("../Event.zig"); const EventTarget = @import("../EventTarget.zig"); +const ErrorEvent = @import("../event/ErrorEvent.zig"); const log = lp.log; @@ -44,6 +45,8 @@ const NavigationCurrentEntryChangeEvent = @import("../event/NavigationCurrentEnt _proto: *EventTarget, _on_currententrychange: ?js.Function.Global = null, +_on_navigatesuccess: ?js.Function.Global = null, +_on_navigateerror: ?js.Function.Global = null, _current_navigation_kind: ?NavigationKind = null, @@ -66,6 +69,12 @@ pub fn onRemoveFrame(self: *Navigation) void { if (self._on_currententrychange) |cb| cb.release(); self._on_currententrychange = null; + if (self._on_navigatesuccess) |cb| cb.release(); + self._on_navigatesuccess = null; + + if (self._on_navigateerror) |cb| cb.release(); + self._on_navigateerror = null; + for (self._entries.items) |entry| { if (entry._on_dispose) |cb| cb.release(); entry._on_dispose = null; @@ -303,6 +312,48 @@ pub fn replaceEntry( return entry; } +fn fireNavigateSuccess(self: *Navigation, frame: *Frame) void { + if (self._on_navigatesuccess) |ons| { + const event = Event.initTrusted( + .wrap("navigatesuccess"), + null, + frame.page, + ) catch |err| { + log.warn(.event, "Navigation.fireNavigateSuccess", .{ .err = err }); + return; + }; + + self.dispatch(ons, event, frame) catch |err| { + log.warn(.event, "Navigation.fireNavigateSuccess dispatch", .{ .err = err }); + }; + } +} + +fn fireNavigateError(self: *Navigation, reason: js.Value, frame: *Frame) void { + if (self._on_navigateerror) |one| { + const message = std.fmt.allocPrint(frame.call_arena, "{f}", .{reason}) catch "navigate error"; + + const err_event = ErrorEvent.initTrusted( + .wrap("navigateerror"), + .{ + .message = message, + .filename = frame.url, + .lineno = 0, + .colno = 0, + .@"error" = reason.persist() catch null, + }, + frame.page, + ) catch |err| { + log.warn(.event, "Navigation.fireNavigateError", .{ .err = err }); + return; + }; + + self.dispatch(one, err_event.asEvent(), frame) catch |err| { + log.warn(.event, "Navigation.fireNavigateError dispatch", .{ .err = err }); + }; + } +} + fn fireCurrentEntryChangeEvent( self: *Navigation, previous: *NavigationHistoryEntry, @@ -335,6 +386,16 @@ fn fireCurrentEntryChangeEvent( }; } +fn resolveFinished( + self: *Navigation, + resolver: js.PromiseResolver, + comptime source: []const u8, + frame: *Frame, +) void { + resolver.resolve(source, {}); + self.fireNavigateSuccess(frame); +} + const NavigateOptions = struct { history: ?[]const u8 = null, info: ?js.Value = null, @@ -381,9 +442,8 @@ pub fn navigateInner( committed.resolve("navigation push", {}); // todo: Fire navigate event - finished.resolve("navigation push", {}); - _ = try self.pushEntry(url, .{ .source = .navigation, .value = state }, frame, true); + self.resolveFinished(finished, "navigation push", frame); } else { try frame.scheduleNavigation(url, .{ .reason = .navigation, .kind = kind }, .{ .script = frame }); } @@ -394,9 +454,8 @@ pub fn navigateInner( committed.resolve("navigation replace", {}); // todo: Fire navigate event - finished.resolve("navigation replace", {}); - _ = try self.replaceEntry(url, .{ .source = .navigation, .value = state }, frame, true); + self.resolveFinished(finished, "navigation replace", frame); } else { try frame.scheduleNavigation(url, .{ .reason = .navigation, .kind = kind }, .{ .script = frame }); } @@ -409,7 +468,8 @@ pub fn navigateInner( committed.resolve("navigation traverse", {}); // todo: Fire navigate event - finished.resolve("navigation traverse", {}); + self.fireCurrentEntryChangeEvent(previous, kind, frame); + self.resolveFinished(finished, "navigation traverse", frame); } else { try frame.scheduleNavigation(url, .{ .reason = .navigation, .kind = kind }, .{ .script = frame }); } @@ -423,8 +483,6 @@ pub fn navigateInner( try frame.queueHashChange(old_url, new_url); } - self.fireCurrentEntryChangeEvent(previous, kind, frame); - _ = try committed.persist(); _ = try finished.persist(); return .{ @@ -524,6 +582,24 @@ fn setOnCurrentEntryChange(self: *Navigation, listener: ?js.Function) !void { } } +fn getOnNavigateSuccess(self: *Navigation) ?js.Function.Global { + return self._on_navigatesuccess; +} + +fn setOnNavigateSuccess(self: *Navigation, listener: ?js.Function) !void { + if (self._on_navigatesuccess) |old| old.release(); + self._on_navigatesuccess = if (listener) |l| try l.persistWithThis(self) else null; +} + +fn getOnNavigateError(self: *Navigation) ?js.Function.Global { + return self._on_navigateerror; +} + +fn setOnNavigateError(self: *Navigation, listener: ?js.Function) !void { + if (self._on_navigateerror) |old| old.release(); + self._on_navigateerror = if (listener) |l| try l.persistWithThis(self) else null; +} + pub const JsApi = struct { pub const bridge = js.Bridge(Navigation); @@ -551,6 +627,16 @@ pub const JsApi = struct { Navigation.setOnCurrentEntryChange, .{}, ); + pub const onnavigatesuccess = bridge.accessor( + Navigation.getOnNavigateSuccess, + Navigation.setOnNavigateSuccess, + .{}, + ); + pub const onnavigateerror = bridge.accessor( + Navigation.getOnNavigateError, + Navigation.setOnNavigateError, + .{}, + ); }; const testing = @import("../../../testing.zig"); From 4f8536077e32443856cbd01147a03a0e752099c4 Mon Sep 17 00:00:00 2001 From: Muki Kiboigo Date: Mon, 21 Sep 2026 07:28:03 -0700 Subject: [PATCH 2/2] fix fireNavigationSuccess --- src/browser/webapi/navigation/Navigation.zig | 53 +++++++------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/src/browser/webapi/navigation/Navigation.zig b/src/browser/webapi/navigation/Navigation.zig index ead126b9f..508a3c2b7 100644 --- a/src/browser/webapi/navigation/Navigation.zig +++ b/src/browser/webapi/navigation/Navigation.zig @@ -313,45 +313,26 @@ pub fn replaceEntry( } fn fireNavigateSuccess(self: *Navigation, frame: *Frame) void { - if (self._on_navigatesuccess) |ons| { - const event = Event.initTrusted( - .wrap("navigatesuccess"), - null, - frame.page, - ) catch |err| { - log.warn(.event, "Navigation.fireNavigateSuccess", .{ .err = err }); - return; - }; - - self.dispatch(ons, event, frame) catch |err| { - log.warn(.event, "Navigation.fireNavigateSuccess dispatch", .{ .err = err }); - }; + if (!frame.hasDirectListeners( + self.asEventTarget(), + "navigatesuccess", + self._on_navigatesuccess, + )) { + return; } -} -fn fireNavigateError(self: *Navigation, reason: js.Value, frame: *Frame) void { - if (self._on_navigateerror) |one| { - const message = std.fmt.allocPrint(frame.call_arena, "{f}", .{reason}) catch "navigate error"; + const event = Event.initTrusted( + .wrap("navigatesuccess"), + null, + frame.page, + ) catch |err| { + log.warn(.event, "Navigation.fireNavigateSuccess", .{ .err = err }); + return; + }; - const err_event = ErrorEvent.initTrusted( - .wrap("navigateerror"), - .{ - .message = message, - .filename = frame.url, - .lineno = 0, - .colno = 0, - .@"error" = reason.persist() catch null, - }, - frame.page, - ) catch |err| { - log.warn(.event, "Navigation.fireNavigateError", .{ .err = err }); - return; - }; - - self.dispatch(one, err_event.asEvent(), frame) catch |err| { - log.warn(.event, "Navigation.fireNavigateError dispatch", .{ .err = err }); - }; - } + self.dispatch(self._on_navigatesuccess, event, frame) catch |err| { + log.warn(.event, "Navigation.fireNavigateSuccess dispatch", .{ .err = err }); + }; } fn fireCurrentEntryChangeEvent(