diff --git a/src/browser/html/History.zig b/src/browser/html/History.zig index aa491518d..69739f6a1 100644 --- a/src/browser/html/History.zig +++ b/src/browser/html/History.zig @@ -48,7 +48,7 @@ pub fn set_scrollRestoration(self: *History, mode: ScrollRestorationMode) void { } pub fn get_state(_: *History, page: *Page) !?js.Value { - if (page.session.navigation.currentEntry().state) |state| { + if (page.session.navigation.currentEntry().state.value) |state| { const value = try js.Value.fromJson(page.js, state); return value; } else { @@ -61,18 +61,15 @@ pub fn _pushState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[] const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw); const json = state.toJson(arena) catch return error.DataClone; - _ = try page.session.navigation.pushEntry(url, json, page, true); + _ = try page.session.navigation.pushEntry(url, .{ .source = .history, .value = json }, page, true); } pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { const arena = page.session.arena; - - const entry = page.session.navigation.currentEntry(); - const json = try state.toJson(arena); const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw); - entry.state = json; - entry.url = url; + const json = try state.toJson(arena); + _ = try page.session.navigation.replaceEntry(url, .{ .source = .history, .value = json }, page, true); } pub fn go(_: *const History, delta: i32, page: *Page) !void { @@ -89,7 +86,7 @@ pub fn go(_: *const History, delta: i32, page: *Page) !void { if (entry.url) |url| { if (try page.isSameOrigin(url)) { - PopStateEvent.dispatch(entry.state, page); + PopStateEvent.dispatch(entry.state.value, page); } } diff --git a/src/browser/html/location.zig b/src/browser/html/location.zig index 458178457..dc959e87a 100644 --- a/src/browser/html/location.zig +++ b/src/browser/html/location.zig @@ -56,7 +56,7 @@ pub const Location = struct { break :blk try std.fmt.allocPrint(page.arena, "#{s}", .{hash}); }; - return page.navigateFromWebAPI(normalized_hash, .{ .reason = .script }, .replace); + return page.navigateFromWebAPI(normalized_hash, .{ .reason = .script }, .{ .replace = null }); } pub fn get_protocol(self: *Location) []const u8 { @@ -96,7 +96,7 @@ pub const Location = struct { } pub fn _replace(_: *const Location, url: []const u8, page: *Page) !void { - return page.navigateFromWebAPI(url, .{ .reason = .script }, .replace); + return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .replace = null }); } pub fn _reload(_: *const Location, page: *Page) !void { diff --git a/src/browser/navigation/Navigation.zig b/src/browser/navigation/Navigation.zig index 3c6bfcfd9..9f2da76cc 100644 --- a/src/browser/navigation/Navigation.zig +++ b/src/browser/navigation/Navigation.zig @@ -35,6 +35,7 @@ const Navigation = @This(); const NavigationKind = @import("root.zig").NavigationKind; const NavigationHistoryEntry = @import("root.zig").NavigationHistoryEntry; const NavigationTransition = @import("root.zig").NavigationTransition; +const NavigationState = @import("root.zig").NavigationState; const NavigationCurrentEntryChangeEvent = @import("root.zig").NavigationCurrentEntryChangeEvent; const NavigationEventTarget = @import("NavigationEventTarget.zig"); @@ -110,10 +111,10 @@ pub fn _forward(self: *Navigation, page: *Page) !NavigationReturn { pub fn updateEntries(self: *Navigation, url: []const u8, kind: NavigationKind, page: *Page, dispatch: bool) !void { switch (kind) { .replace => { - _ = try self.replaceEntry(url, null, page, dispatch); + _ = try self.replaceEntry(url, .{ .source = .navigation, .value = null }, page, dispatch); }, .push => |state| { - _ = try self.pushEntry(url, state, page, dispatch); + _ = try self.pushEntry(url, .{ .source = .navigation, .value = state }, page, dispatch); }, .traverse => |index| { self.index = index; @@ -132,7 +133,13 @@ pub fn processNavigation(self: *Navigation, page: *Page) !void { /// Pushes an entry into the Navigation stack WITHOUT actually navigating to it. /// For that, use `navigate`. -pub fn pushEntry(self: *Navigation, _url: []const u8, state: ?[]const u8, page: *Page, dispatch: bool) !*NavigationHistoryEntry { +pub fn pushEntry( + self: *Navigation, + _url: []const u8, + state: NavigationState, + page: *Page, + dispatch: bool, +) !*NavigationHistoryEntry { const arena = page.session.arena; const url = try arena.dupe(u8, _url); @@ -160,18 +167,24 @@ pub fn pushEntry(self: *Navigation, _url: []const u8, state: ?[]const u8, page: // we don't always have a current entry... const previous = if (self.entries.items.len > 0) self.currentEntry() else null; try self.entries.append(arena, entry); + self.index = index; + if (previous) |prev| { if (dispatch) { NavigationCurrentEntryChangeEvent.dispatch(self, prev, .push); } } - self.index = index; - return entry; } -pub fn replaceEntry(self: *Navigation, _url: []const u8, state: ?[]const u8, page: *Page, dispatch: bool) !*NavigationHistoryEntry { +pub fn replaceEntry( + self: *Navigation, + _url: []const u8, + state: NavigationState, + page: *Page, + dispatch: bool, +) !*NavigationHistoryEntry { const arena = page.session.arena; const url = try arena.dupe(u8, _url); @@ -184,7 +197,7 @@ pub fn replaceEntry(self: *Navigation, _url: []const u8, state: ?[]const u8, pag const entry = try arena.create(NavigationHistoryEntry); entry.* = NavigationHistoryEntry{ .id = id_str, - .key = id_str, + .key = previous.key, .url = url, .state = state, }; @@ -242,7 +255,20 @@ pub fn navigate( // todo: Fire navigate event try finished.resolve({}); - _ = try self.pushEntry(url, state, page, true); + _ = try self.pushEntry(url, .{ .source = .navigation, .value = state }, page, true); + } else { + try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind); + } + }, + .replace => |state| { + if (is_same_document) { + page.url = new_url; + + try committed.resolve({}); + // todo: Fire navigate event + try finished.resolve({}); + + _ = try self.replaceEntry(url, .{ .source = .navigation, .value = state }, page, true); } else { try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind); } @@ -263,7 +289,6 @@ pub fn navigate( .reload => { try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind); }, - else => unreachable, } return .{ @@ -275,7 +300,13 @@ pub fn navigate( pub fn _navigate(self: *Navigation, _url: []const u8, _opts: ?NavigateOptions, page: *Page) !NavigationReturn { const opts = _opts orelse NavigateOptions{}; const json = if (opts.state) |state| state.toJson(page.session.arena) catch return error.DataClone else null; - return try self.navigate(_url, .{ .push = json }, page); + + const kind: NavigationKind = switch (opts.history) { + .replace => .{ .replace = json }, + .push, .auto => .{ .push = json }, + }; + + return try self.navigate(_url, kind, page); } pub const ReloadOptions = struct { @@ -290,7 +321,7 @@ pub fn _reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigatio const entry = self.currentEntry(); if (opts.state) |state| { const previous = entry; - entry.state = state.toJson(arena) catch return error.DataClone; + entry.state = .{ .source = .navigation, .value = state.toJson(arena) catch return error.DataClone }; NavigationCurrentEntryChangeEvent.dispatch(self, previous, .reload); } @@ -323,6 +354,6 @@ pub fn _updateCurrentEntry(self: *Navigation, options: UpdateCurrentEntryOptions const arena = page.session.arena; const previous = self.currentEntry(); - self.currentEntry().state = options.state.toJson(arena) catch return error.DataClone; + self.currentEntry().state = .{ .source = .navigation, .value = options.state.toJson(arena) catch return error.DataClone }; NavigationCurrentEntryChangeEvent.dispatch(self, previous, null); } diff --git a/src/browser/navigation/root.zig b/src/browser/navigation/root.zig index bfe5830f1..969a99971 100644 --- a/src/browser/navigation/root.zig +++ b/src/browser/navigation/root.zig @@ -51,11 +51,16 @@ pub const NavigationType = enum { pub const NavigationKind = union(NavigationType) { push: ?[]const u8, - replace, + replace: ?[]const u8, traverse: usize, reload, }; +pub const NavigationState = struct { + source: enum { history, navigation }, + value: ?[]const u8, +}; + // https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry pub const NavigationHistoryEntry = struct { pub const prototype = *EventTarget; @@ -64,7 +69,7 @@ pub const NavigationHistoryEntry = struct { id: []const u8, key: []const u8, url: ?[]const u8, - state: ?[]const u8, + state: NavigationState, pub fn get_id(self: *const NavigationHistoryEntry) []const u8 { return self.id; @@ -95,12 +100,16 @@ pub const NavigationHistoryEntry = struct { return self.url; } - pub fn _getState(self: *const NavigationHistoryEntry, page: *Page) !?js.Value { - if (self.state) |state| { - return try js.Value.fromJson(page.js, state); - } else { - return null; + pub const StateReturn = union(enum) { value: ?js.Value, undefined: void }; + + pub fn _getState(self: *const NavigationHistoryEntry, page: *Page) !StateReturn { + if (self.state.source == .navigation) { + if (self.state.value) |value| { + return .{ .value = try js.Value.fromJson(page.js, value) }; + } } + + return .undefined; } };