add NavigationActivation

This commit is contained in:
Muki Kiboigo
2025-12-09 15:47:09 -08:00
parent ddb83cf9c5
commit 7c9d7259e6
5 changed files with 90 additions and 29 deletions

View File

@@ -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 {

View File

@@ -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"),
});

View File

@@ -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, .{});

View File

@@ -0,0 +1,56 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// 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 <https://www.gnu.org/licenses/>.
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, .{});
};

View File

@@ -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,