diff --git a/src/browser/html/History.zig b/src/browser/html/History.zig index eabae11d..9e5428fd 100644 --- a/src/browser/html/History.zig +++ b/src/browser/html/History.zig @@ -25,9 +25,9 @@ const Page = @import("../page.zig").Page; const History = @This(); const HistoryEntry = struct { - url: ?[]const u8, - // Serialized Env.JsObject - state: []u8, + url: []const u8, + // Serialized as JSON. + state: ?[]u8, }; const ScrollRestorationMode = enum { @@ -64,52 +64,78 @@ pub fn set_scrollRestoration(self: *History, mode: []const u8) void { self.scrollRestoration = ScrollRestorationMode.fromString(mode) orelse self.scrollRestoration; } -pub fn get_state(self: *History, page: *Page) !?Env.JsObject { +pub fn get_state(self: *History, page: *Page) !?Env.Value { if (self.current) |curr| { const entry = self.stack.items[curr]; - const object = try Env.JsObject.fromJson(page.main_context, entry.state); - return object; + if (entry.state) |state| { + const value = try Env.Value.fromJson(page.main_context, state); + return value; + } else { + return null; + } } else { return null; } } -pub fn _pushState(self: *History, state: Env.JsObject, _: ?[]const u8, url: ?[]const u8, page: *Page) !void { - const json = try state.toJson(page.arena); +pub fn pushNavigation(self: *History, _url: []const u8, page: *Page) !void { + const arena = page.session.arena; + const url = try arena.dupe(u8, _url); + + const entry = HistoryEntry{ .state = null, .url = url }; + try self.stack.append(arena, entry); + self.current = self.stack.items.len - 1; +} + +pub fn _pushState(self: *History, state: Env.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { + const arena = page.session.arena; + + const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw); + const json = try state.toJson(page.session.arena); const entry = HistoryEntry{ .state = json, .url = url }; - try self.stack.append(page.session.arena, entry); - self.current = self.stack.items.len; + try self.stack.append(arena, entry); + self.current = self.stack.items.len - 1; } -// TODO implement the function -// data must handle any argument. We could expect a std.json.Value but -// https://github.com/lightpanda-io/zig-js-runtime/issues/267 is missing. -pub fn _replaceState(self: *History, state: Env.JsObject, _: ?[]const u8, url: ?[]const u8) void { - _ = self; - _ = url; - _ = state; -} +pub fn _replaceState(self: *History, state: Env.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { + const arena = page.session.arena; -// TODO implement the function -pub fn _go(self: *History, delta: ?i32) void { - _ = self; - _ = delta; -} - -pub fn _back(self: *History) void { if (self.current) |curr| { - if (curr > 0) { - self.current = curr - 1; - } + const entry = &self.stack.items[curr]; + const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw); + const json = try state.toJson(arena); + entry.* = HistoryEntry{ .state = json, .url = url }; + } else { + try self._pushState(state, "", _url, page); } } -pub fn _forward(self: *History) void { - if (self.current) |curr| { - if (curr < self.stack.items.len) { - self.current = curr + 1; - } +pub fn go(self: *History, delta: i32, page: *Page) !void { + // 0 behaves the same as no argument, both reloading the page. + // If this is getting called, there SHOULD be an entry, atleast from pushNavigation. + const current = self.current.?; + + const index_s: i64 = @intCast(@as(i64, @intCast(current)) + @as(i64, @intCast(delta))); + if (index_s < 0 or index_s > self.stack.items.len - 1) { + return; } + + const index = @as(usize, @intCast(index_s)); + const entry = self.stack.items[index]; + self.current = index; + try page.navigateFromWebAPI(entry.url, .{ .reason = .history }); +} + +pub fn _go(self: *History, _delta: ?i32, page: *Page) !void { + try self.go(_delta orelse 0, page); +} + +pub fn _back(self: *History, page: *Page) !void { + try self.go(-1, page); +} + +pub fn _forward(self: *History, page: *Page) !void { + try self.go(1, page); } const testing = @import("../../testing.zig"); diff --git a/src/browser/page.zig b/src/browser/page.zig index 87b42534..de9d6617 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -806,6 +806,9 @@ pub const Page = struct { unreachable; }, } + + // Push the navigation after a successful load. + try self.session.history.pushNavigation(self.url.raw, self); } fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void { @@ -1136,6 +1139,7 @@ pub const NavigateReason = enum { address_bar, form, script, + history, }; pub const NavigateOpts = struct { diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 719c0782..74beb4cc 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -174,7 +174,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa var cdp = bc.cdp; const reason_: ?[]const u8 = switch (event.opts.reason) { .anchor => "anchorClick", - .script => "scriptInitiated", + .script, .history => "scriptInitiated", .form => switch (event.opts.method) { .GET => "formSubmissionGet", .POST => "formSubmissionPost",