diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 904f03b7..2b81cf97 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -530,7 +530,7 @@ fn pageDoneCallback(ctx: *anyopaque) !void { self.clearTransferArena(); //We need to handle different navigation types differently. - try self._session.navigation.processNavigation(self); + try self._session.navigation.commitNavigation(self); defer if (comptime IS_DEBUG) { log.debug(.page, "page.load.complete", .{ .url = self.url }); @@ -567,9 +567,6 @@ fn pageDoneCallback(ctx: *anyopaque) !void { }, else => unreachable, } - // We need to handle different navigation types differently. - // @ZIGDOM - // try self._session.navigation.processNavigation(self); } fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void { diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 3358cc53..b79622d0 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -605,4 +605,5 @@ pub const JsApis = flattenTypes(&.{ @import("../webapi/navigation/Navigation.zig"), @import("../webapi/navigation/NavigationEventTarget.zig"), @import("../webapi/navigation/NavigationHistoryEntry.zig"), + @import("../webapi/navigation/NavigationActivation.zig"), }); diff --git a/src/browser/webapi/navigation/Navigation.zig b/src/browser/webapi/navigation/Navigation.zig index 49de232e..424c8d78 100644 --- a/src/browser/webapi/navigation/Navigation.zig +++ b/src/browser/webapi/navigation/Navigation.zig @@ -29,10 +29,11 @@ const EventTarget = @import("../EventTarget.zig"); const Navigation = @This(); const NavigationKind = @import("root.zig").NavigationKind; -const NavigationHistoryEntry = @import("NavigationHistoryEntry.zig"); +const NavigationActivation = @import("NavigationActivation.zig"); const NavigationTransition = @import("root.zig").NavigationTransition; const NavigationState = @import("root.zig").NavigationState; +const NavigationHistoryEntry = @import("NavigationHistoryEntry.zig"); const NavigationCurrentEntryChangeEvent = @import("../event/NavigationCurrentEntryChangeEvent.zig"); const NavigationEventTarget = @import("NavigationEventTarget.zig"); @@ -44,6 +45,7 @@ _index: usize = 0, // Need to be stable pointers, because Events can reference entries. _entries: std.ArrayList(*NavigationHistoryEntry) = .empty, _next_entry_id: usize = 0, +_activation: ?NavigationActivation = null, pub fn init(arena: std.mem.Allocator) Navigation { return Navigation{ ._arena = arena }; @@ -63,6 +65,10 @@ pub fn onNewPage(self: *Navigation, page: *Page) !void { ); } +pub fn getActivation(self: *const Navigation) ?NavigationActivation { + return self._activation; +} + pub fn getCanGoBack(self: *const Navigation) bool { return self._index > 0; } @@ -71,12 +77,18 @@ pub fn getCanGoForward(self: *const Navigation) bool { return self._entries.items.len > self._index + 1; } +pub fn getCurrentEntryOrNull(self: *Navigation) ?*NavigationHistoryEntry { + if (self._entries.items.len > self._index) { + return self._entries.items[self._index]; + } else return null; +} + pub fn getCurrentEntry(self: *Navigation) *NavigationHistoryEntry { // This should never fail. An entry should always be created before // we run the scripts on the page we are loading. std.debug.assert(self._entries.items.len > 0); - return self._entries.items[self._index]; + return self.getCurrentEntryOrNull().?; } pub fn getTransition(_: *const Navigation) ?NavigationTransition { @@ -117,8 +129,8 @@ pub fn forward(self: *Navigation, page: *Page) !NavigationReturn { pub fn updateEntries(self: *Navigation, url: [:0]const u8, kind: NavigationKind, page: *Page, dispatch: bool) !void { switch (kind) { - .replace => { - _ = try self.replaceEntry(url, .{ .source = .navigation, .value = null }, page, dispatch); + .replace => |state| { + _ = try self.replaceEntry(url, .{ .source = .navigation, .value = state }, page, dispatch); }, .push => |state| { _ = try self.pushEntry(url, .{ .source = .navigation, .value = state }, page, dispatch); @@ -131,14 +143,23 @@ pub fn updateEntries(self: *Navigation, url: [:0]const u8, kind: NavigationKind, } // This is for after true navigation processing, where we need to ensure that our entries are up to date. -// This is only really safe to run in the `pageDoneCallback` where we can guarantee that the URL and NavigationKind are correct. -pub fn processNavigation(self: *Navigation, page: *Page) !void { +// +// This is only really safe to run in the `pageDoneCallback` +// where we can guarantee that the URL and NavigationKind are correct. +pub fn commitNavigation(self: *Navigation, page: *Page) !void { const url = page.url; const kind: NavigationKind = self._current_navigation_kind orelse .{ .push = null }; defer self._current_navigation_kind = null; + const from_entry = self.getCurrentEntryOrNull(); try self.updateEntries(url, kind, page, false); + + self._activation = NavigationActivation{ + ._from = from_entry, + ._entry = self.getCurrentEntry(), + ._type = kind.toNavigationType(), + }; } /// Pushes an entry into the Navigation stack WITHOUT actually navigating to it. @@ -401,6 +422,7 @@ pub const JsApi = struct { pub var class_id: bridge.ClassId = undefined; }; + pub const activation = bridge.accessor(Navigation.getActivation, null, .{}); pub const canGoBack = bridge.accessor(Navigation.getCanGoBack, null, .{}); pub const canGoForward = bridge.accessor(Navigation.getCanGoForward, null, .{}); pub const currentEntry = bridge.accessor(Navigation.getCurrentEntry, null, .{}); diff --git a/src/browser/webapi/navigation/NavigationActivation.zig b/src/browser/webapi/navigation/NavigationActivation.zig new file mode 100644 index 00000000..3c161f9b --- /dev/null +++ b/src/browser/webapi/navigation/NavigationActivation.zig @@ -0,0 +1,56 @@ +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); +const js = @import("../../js/js.zig"); + +const NavigationType = @import("root.zig").NavigationType; +const NavigationHistoryEntry = @import("NavigationHistoryEntry.zig"); + +// https://developer.mozilla.org/en-US/docs/Web/API/NavigationActivation +const NavigationActivation = @This(); + +_entry: *NavigationHistoryEntry, +_from: ?*NavigationHistoryEntry = null, +_type: NavigationType, + +pub fn getEntry(self: *const NavigationActivation) *NavigationHistoryEntry { + return self._entry; +} + +pub fn getFrom(self: *const NavigationActivation) ?*NavigationHistoryEntry { + return self._from; +} + +pub fn getNavigationType(self: *const NavigationActivation) []const u8 { + return @tagName(self._type); +} + +pub const JsApi = struct { + pub const bridge = js.Bridge(NavigationActivation); + + pub const Meta = struct { + pub const name = "NavigationActivation"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + + pub const entry = bridge.accessor(NavigationActivation.getEntry, null, .{}); + pub const from = bridge.accessor(NavigationActivation.getFrom, null, .{}); + pub const navigationType = bridge.accessor(NavigationActivation.getNavigationType, null, .{}); +}; diff --git a/src/browser/webapi/navigation/root.zig b/src/browser/webapi/navigation/root.zig index d611cf9f..ef8b20e2 100644 --- a/src/browser/webapi/navigation/root.zig +++ b/src/browser/webapi/navigation/root.zig @@ -38,6 +38,10 @@ pub const NavigationKind = union(NavigationType) { replace: ?[]const u8, traverse: usize, reload, + + pub fn toNavigationType(self: NavigationKind) NavigationType { + return std.meta.activeTag(self); + } }; pub const NavigationState = struct { @@ -45,25 +49,6 @@ pub const NavigationState = struct { value: ?[]const u8, }; -// https://developer.mozilla.org/en-US/docs/Web/API/NavigationActivation -pub const NavigationActivation = struct { - entry: NavigationHistoryEntry, - from: ?NavigationHistoryEntry = null, - type: NavigationType, - - pub fn get_entry(self: *const NavigationActivation) NavigationHistoryEntry { - return self.entry; - } - - pub fn get_from(self: *const NavigationActivation) ?NavigationHistoryEntry { - return self.from; - } - - pub fn get_navigationType(self: *const NavigationActivation) NavigationType { - return self.type; - } -}; - // https://developer.mozilla.org/en-US/docs/Web/API/NavigationTransition pub const NavigationTransition = struct { finished: js.Promise,