Skip to content

Commit a62cfef

Browse files
committed
History as compat layer over Navigation
1 parent ce8674b commit a62cfef

File tree

4 files changed

+74
-55
lines changed

4 files changed

+74
-55
lines changed

src/browser/html/History.zig

Lines changed: 24 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ const Page = @import("../page.zig").Page;
2525
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
2626
const History = @This();
2727

28-
const HistoryEntry = struct {
29-
url: []const u8,
30-
// This is serialized as JSON because
31-
// History must survive a JsContext.
32-
state: ?[]u8,
33-
};
34-
3528
const ScrollRestorationMode = enum {
3629
pub const ENUM_JS_USE_TAG = true;
3730

@@ -40,11 +33,9 @@ const ScrollRestorationMode = enum {
4033
};
4134

4235
scroll_restoration: ScrollRestorationMode = .auto,
43-
stack: std.ArrayListUnmanaged(HistoryEntry) = .empty,
44-
current: ?usize = null,
4536

46-
pub fn get_length(self: *History) u32 {
47-
return @intCast(self.stack.items.len);
37+
pub fn get_length(_: *History, page: *Page) u32 {
38+
return @intCast(page.session.navigation.entries.items.len);
4839
}
4940

5041
pub fn get_scrollRestoration(self: *History) ScrollRestorationMode {
@@ -55,29 +46,15 @@ pub fn set_scrollRestoration(self: *History, mode: ScrollRestorationMode) void {
5546
self.scroll_restoration = mode;
5647
}
5748

58-
pub fn get_state(self: *History, page: *Page) !?js.Value {
59-
if (self.current) |curr| {
60-
const entry = self.stack.items[curr];
61-
if (entry.state) |state| {
62-
const value = try js.Value.fromJson(page.js, state);
63-
return value;
64-
} else {
65-
return null;
66-
}
49+
pub fn get_state(_: *History, page: *Page) !?js.Value {
50+
if (page.session.navigation.currentEntry().state) |state| {
51+
const value = try js.Value.fromJson(page.js, state);
52+
return value;
6753
} else {
6854
return null;
6955
}
7056
}
7157

72-
pub fn pushNavigation(self: *History, _url: []const u8, page: *Page) !void {
73-
const arena = page.session.arena;
74-
const url = try arena.dupe(u8, _url);
75-
76-
const entry = HistoryEntry{ .state = null, .url = url };
77-
try self.stack.append(arena, entry);
78-
self.current = self.stack.items.len - 1;
79-
}
80-
8158
pub fn dispatchPopStateEvent(state: ?[]const u8, page: *Page) void {
8259
log.debug(.script_event, "dispatch popstate event", .{
8360
.type = "popstate",
@@ -101,48 +78,42 @@ fn _dispatchPopStateEvent(state: ?[]const u8, page: *Page) !void {
10178
);
10279
}
10380

104-
pub fn _pushState(self: *History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
81+
pub fn _pushState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
10582
const arena = page.session.arena;
106-
107-
const json = try state.toJson(arena);
10883
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
109-
const entry = HistoryEntry{ .state = json, .url = url };
110-
try self.stack.append(arena, entry);
111-
self.current = self.stack.items.len - 1;
84+
_ = try page.session.navigation.pushEntry(url, .{ .state = state }, page);
11285
}
11386

114-
pub fn _replaceState(self: *History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
87+
pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
11588
const arena = page.session.arena;
11689

117-
if (self.current) |curr| {
118-
const entry = &self.stack.items[curr];
119-
const json = try state.toJson(arena);
120-
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
121-
entry.* = HistoryEntry{ .state = json, .url = url };
122-
} else {
123-
try self._pushState(state, "", _url, page);
124-
}
90+
const entry = page.session.navigation.currentEntry();
91+
const json = try state.toJson(arena);
92+
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
93+
94+
entry.state = json;
95+
entry.url = url;
12596
}
12697

127-
pub fn go(self: *History, delta: i32, page: *Page) !void {
98+
pub fn go(_: *const History, delta: i32, page: *Page) !void {
12899
// 0 behaves the same as no argument, both reloading the page.
129-
// If this is getting called, there SHOULD be an entry, atleast from pushNavigation.
130-
const current = self.current.?;
131100

101+
const current = page.session.navigation.index;
132102
const index_s: i64 = @intCast(@as(i64, @intCast(current)) + @as(i64, @intCast(delta)));
133-
if (index_s < 0 or index_s > self.stack.items.len - 1) {
103+
if (index_s < 0 or index_s > page.session.navigation.entries.items.len - 1) {
134104
return;
135105
}
136106

137107
const index = @as(usize, @intCast(index_s));
138-
const entry = self.stack.items[index];
139-
self.current = index;
108+
const entry = page.session.navigation.entries.items[index];
140109

141-
if (try page.isSameOrigin(entry.url)) {
142-
History.dispatchPopStateEvent(entry.state, page);
110+
if (entry.url) |url| {
111+
if (try page.isSameOrigin(url)) {
112+
History.dispatchPopStateEvent(entry.state, page);
113+
}
143114
}
144115

145-
try page.navigateFromWebAPI(entry.url, .{ .reason = .history });
116+
_ = try entry.navigate(page, .force);
146117
}
147118

148119
pub fn _go(self: *History, _delta: ?i32, page: *Page) !void {

src/browser/page.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ pub const Page = struct {
803803
}
804804

805805
// Push the navigation after a successful load.
806-
try self.session.history.pushNavigation(self.url.raw, self);
806+
_ = try self.session.navigation.pushEntry(self.url.raw, null, self);
807807
}
808808

809809
fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void {

src/tests/html/history.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
testing.expectEqual('auto', history.scrollRestoration);
66

77
history.scrollRestoration = 'manual';
8-
history.scrollRestoration = 'foo';
98
testing.expectEqual('manual', history.scrollRestoration);
109

1110
history.scrollRestoration = 'auto';

src/tests/html/navigation.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<script src="../testing.js"></script>
3+
<script id=navigation>
4+
testing.expectEqual('object', typeof navigation);
5+
testing.expectEqual('object', typeof navigation.currentEntry);
6+
7+
testing.expectEqual('string', typeof navigation.currentEntry.id);
8+
testing.expectEqual('string', typeof navigation.currentEntry.key);
9+
testing.expectEqual('string', typeof navigation.currentEntry.url);
10+
testing.expectEqual(0, navigation.currentEntry.index);
11+
testing.expectEqual(true, navigation.currentEntry.sameDocument);
12+
13+
let result = navigation.navigate('http://127.0.0.1:9582/xhr/json', {
14+
state: { testInProgress: true }
15+
});
16+
testing.expectEqual('object', typeof result);
17+
testing.expectEqual('object', typeof result.committed);
18+
testing.expectEqual('object', typeof result.finished);
19+
20+
testing.expectEqual({ testInProgress: true }, navigation.currentEntry.getState());
21+
testing.expectEqual(1, navigation.currentEntry.index);
22+
23+
testing.expectEqual(true, navigation.canGoBack);
24+
testing.expectEqual(false, navigation.canGoForward);
25+
26+
testing.expectEqual(undefined, navigation.back());
27+
28+
testing.onPageWait(() => {
29+
testing.expectEqual(0, navigation.currentEntry.index);
30+
testing.expectEqual(true, navigation.canGoForward);
31+
32+
testing.expectEqual(undefined, navigation.forward());
33+
});
34+
35+
testing.onPageWait(() => {
36+
testing.expectEqual(1, navigation.currentEntry.index);
37+
testing.expectEqual({ testInProgress: true }, navigation.currentEntry.getState());
38+
39+
let targetKey = navigation.currentEntry.key;
40+
testing.expectEqual(undefined, navigation.traverseTo(targetKey));
41+
});
42+
43+
navigation.updateCurrentEntry({ state: { updated: true, testComplete: true } });
44+
testing.expectEqual({ updated: true, testComplete: true }, navigation.currentEntry.getState());
45+
46+
testing.onPageWait(() => {
47+
testing.expectEqual(true, navigation.currentEntry.getState().testComplete);
48+
});
49+
</script>

0 commit comments

Comments
 (0)