Skip to content

Commit 6e0d2a1

Browse files
committed
fix navigation and related tests
1 parent a62cfef commit 6e0d2a1

File tree

14 files changed

+180
-162
lines changed

14 files changed

+180
-162
lines changed

src/browser/html/History.zig

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ fn _dispatchPopStateEvent(state: ?[]const u8, page: *Page) !void {
8181
pub fn _pushState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
8282
const arena = page.session.arena;
8383
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
84-
_ = try page.session.navigation.pushEntry(url, .{ .state = state }, page);
84+
85+
const json = state.toJson(arena) catch return error.DataClone;
86+
_ = try page.session.navigation.pushEntry(url, json, page);
8587
}
8688

8789
pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
@@ -113,7 +115,7 @@ pub fn go(_: *const History, delta: i32, page: *Page) !void {
113115
}
114116
}
115117

116-
_ = try entry.navigate(page, .force);
118+
_ = try page.session.navigation.navigate(entry.url, .{ .traverse = index }, page);
117119
}
118120

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

src/browser/html/Navigation.zig

Lines changed: 119 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,24 @@ const Navigation = @This();
3434
pub const Interfaces = .{
3535
Navigation,
3636
NavigationActivation,
37+
NavigationTransition,
3738
NavigationHistoryEntry,
3839
};
3940

41+
pub const NavigationKind = union(enum) {
42+
initial,
43+
push: ?[]const u8,
44+
replace,
45+
traverse: usize,
46+
reload,
47+
};
48+
4049
pub const prototype = *EventTarget;
4150
base: parser.EventTargetTBase = parser.EventTargetTBase{ .internal_target_type = .plain },
4251

4352
index: usize = 0,
4453
entries: std.ArrayListUnmanaged(NavigationHistoryEntry) = .empty,
4554
next_entry_id: usize = 0,
46-
// TODO: key->index mapping
4755

4856
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry
4957
const NavigationHistoryEntry = struct {
@@ -91,49 +99,17 @@ const NavigationHistoryEntry = struct {
9199
return null;
92100
}
93101
}
94-
95-
pub fn navigate(entry: NavigationHistoryEntry, reload: enum { none, force }, page: *Page) !NavigationReturn {
96-
const arena = page.session.arena;
97-
const url = entry.url orelse return error.MissingURL;
98-
99-
// https://github.com/WICG/navigation-api/issues/95
100-
//
101-
// These will only settle on same-origin navigation (mostly intended for SPAs).
102-
// It is fine (and expected) for these to not settle on cross-origin requests :)
103-
const committed = try page.js.createPromiseResolver(.page);
104-
const finished = try page.js.createPromiseResolver(.page);
105-
106-
const new_url = try URL.parse(url, null);
107-
if (try page.url.eqlDocument(&new_url, arena) or reload == .force) {
108-
page.url = new_url;
109-
try committed.resolve({});
110-
111-
// todo: Fire navigate event
112-
113-
try finished.resolve({});
114-
} else {
115-
// TODO: Change to history
116-
try page.navigateFromWebAPI(url, .{ .reason = .history });
117-
}
118-
119-
return .{
120-
.committed = committed.promise(),
121-
.finished = finished.promise(),
122-
};
123-
}
124102
};
125103

126104
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationActivation
127105
const NavigationActivation = struct {
128106
const NavigationActivationType = enum {
107+
pub const ENUM_JS_USE_TAG = true;
108+
129109
push,
130110
reload,
131111
replace,
132112
traverse,
133-
134-
pub fn toString(self: NavigationActivationType) []const u8 {
135-
return @tagName(self);
136-
}
137113
};
138114

139115
entry: NavigationHistoryEntry,
@@ -153,6 +129,13 @@ const NavigationActivation = struct {
153129
}
154130
};
155131

132+
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationTransition
133+
const NavigationTransition = struct {
134+
finished: js.Promise,
135+
from: NavigationHistoryEntry,
136+
navigation_type: NavigationActivation.NavigationActivationType,
137+
};
138+
156139
pub fn get_canGoBack(self: *const Navigation) bool {
157140
return self.index > 0;
158141
}
@@ -169,6 +152,11 @@ pub fn get_currentEntry(self: *const Navigation) NavigationHistoryEntry {
169152
return self.entries.items[self.index];
170153
}
171154

155+
pub fn get_transition(_: *const Navigation) ?NavigationTransition {
156+
// For now, all transitions are just considered complete.
157+
return null;
158+
}
159+
172160
const NavigationReturn = struct {
173161
committed: js.Promise,
174162
finished: js.Promise,
@@ -183,7 +171,7 @@ pub fn _back(self: *Navigation, page: *Page) !NavigationReturn {
183171
const next_entry = self.entries.items[new_index];
184172
self.index = new_index;
185173

186-
return next_entry.navigate(.none, page);
174+
return self.navigate(next_entry.url, .{ .traverse = new_index }, page);
187175
}
188176

189177
pub fn _entries(self: *const Navigation) []NavigationHistoryEntry {
@@ -199,15 +187,33 @@ pub fn _forward(self: *Navigation, page: *Page) !NavigationReturn {
199187
const next_entry = self.entries.items[new_index];
200188
self.index = new_index;
201189

202-
return next_entry.navigate(.none, page);
190+
return self.navigate(next_entry.url, .{ .traverse = new_index }, page);
191+
}
192+
193+
// This is for after true navigation processing, where we need to ensure that our entries are up to date.
194+
pub fn processNavigation(self: *Navigation, url: []const u8, kind: NavigationKind, page: *Page) !void {
195+
switch (kind) {
196+
.initial => {
197+
_ = try self.pushEntry(url, null, page);
198+
},
199+
.replace => {
200+
// When replacing, we just update the URL but the state is nullified.
201+
const entry = self.currentEntry();
202+
entry.url = url;
203+
entry.state = null;
204+
},
205+
.push => |state| {
206+
_ = try self.pushEntry(url, state, page);
207+
},
208+
.traverse, .reload => {},
209+
}
203210
}
204211

205212
/// Pushes an entry into the Navigation stack WITHOUT actually navigating to it.
206213
/// For that, use `navigate`.
207-
pub fn pushEntry(self: *Navigation, _url: ?[]const u8, _opts: ?NavigateOptions, page: *Page) !NavigationHistoryEntry {
214+
pub fn pushEntry(self: *Navigation, _url: ?[]const u8, state: ?[]const u8, page: *Page) !NavigationHistoryEntry {
208215
const arena = page.session.arena;
209216

210-
const options = _opts orelse NavigateOptions{};
211217
const url = if (_url) |u| try arena.dupe(u8, u) else null;
212218

213219
// truncates our history here.
@@ -221,14 +227,6 @@ pub fn pushEntry(self: *Navigation, _url: ?[]const u8, _opts: ?NavigateOptions,
221227

222228
const id_str = try std.fmt.allocPrint(arena, "{d}", .{id});
223229

224-
const state: ?[]const u8 = blk: {
225-
if (options.state) |s| {
226-
break :blk s.toJson(arena) catch return error.DataClone;
227-
} else {
228-
break :blk null;
229-
}
230-
};
231-
232230
const entry = NavigationHistoryEntry{
233231
.id = id_str,
234232
.key = id_str,
@@ -237,7 +235,6 @@ pub fn pushEntry(self: *Navigation, _url: ?[]const u8, _opts: ?NavigateOptions,
237235
};
238236

239237
try self.entries.append(arena, entry);
240-
241238
return entry;
242239
}
243240

@@ -255,9 +252,67 @@ const NavigateOptions = struct {
255252
history: NavigateOptionsHistory = .auto,
256253
};
257254

255+
pub fn navigate(
256+
self: *Navigation,
257+
_url: ?[]const u8,
258+
kind: NavigationKind,
259+
page: *Page,
260+
) !NavigationReturn {
261+
const arena = page.session.arena;
262+
const url = _url orelse return error.MissingURL;
263+
264+
// https://github.com/WICG/navigation-api/issues/95
265+
//
266+
// These will only settle on same-origin navigation (mostly intended for SPAs).
267+
// It is fine (and expected) for these to not settle on cross-origin requests :)
268+
const committed = try page.js.createPromiseResolver(.page);
269+
const finished = try page.js.createPromiseResolver(.page);
270+
271+
const new_url = try URL.parse(url, null);
272+
const is_same_document = try page.url.eqlDocument(&new_url, arena);
273+
274+
switch (kind) {
275+
.push => |state| {
276+
if (is_same_document) {
277+
page.url = new_url;
278+
try committed.resolve({});
279+
// todo: Fire navigate event
280+
try finished.resolve({});
281+
282+
_ = try self.pushEntry(url, state, page);
283+
} else {
284+
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
285+
}
286+
},
287+
.traverse => |index| {
288+
self.index = index;
289+
290+
if (is_same_document) {
291+
page.url = new_url;
292+
293+
try committed.resolve({});
294+
// todo: Fire navigate event
295+
try finished.resolve({});
296+
} else {
297+
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
298+
}
299+
},
300+
.reload => {
301+
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
302+
},
303+
else => unreachable,
304+
}
305+
306+
return .{
307+
.committed = committed.promise(),
308+
.finished = finished.promise(),
309+
};
310+
}
311+
258312
pub fn _navigate(self: *Navigation, _url: []const u8, _opts: ?NavigateOptions, page: *Page) !NavigationReturn {
259-
const entry = try self.pushEntry(_url, _opts, page);
260-
return entry.navigate(.none, page);
313+
const opts = _opts orelse NavigateOptions{};
314+
const json = if (opts.state) |state| state.toJson(page.session.arena) catch return error.DataClone else null;
315+
return try self.navigate(_url, .{ .push = json }, page);
261316
}
262317

263318
pub const ReloadOptions = struct {
@@ -274,15 +329,23 @@ pub fn _reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigatio
274329
entry.state = state.toJson(arena) catch return error.DataClone;
275330
}
276331

277-
return entry.navigate(.force, page);
332+
return self.navigate(entry.url, .reload, page);
278333
}
279334

280-
pub fn _transition(_: *const Navigation) !NavigationReturn {
281-
unreachable;
282-
}
335+
pub const TraverseToOptions = struct {
336+
info: ?js.Object = null,
337+
};
338+
339+
pub fn _traverseTo(self: *Navigation, key: []const u8, _: ?TraverseToOptions, page: *Page) !NavigationReturn {
340+
// const opts = _opts orelse TraverseToOptions{};
341+
342+
for (self.entries.items, 0..) |entry, i| {
343+
if (std.mem.eql(u8, key, entry.key)) {
344+
return try self.navigate(entry.url, .{ .traverse = i }, page);
345+
}
346+
}
283347

284-
pub fn _traverseTo(_: *const Navigation, _: []const u8) !NavigationReturn {
285-
unreachable;
348+
return error.InvalidStateError;
286349
}
287350

288351
pub const UpdateCurrentEntryOptions = struct {

src/browser/html/document.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ pub const HTMLDocument = struct {
195195
}
196196

197197
pub fn set_location(_: *const parser.DocumentHTML, url: []const u8, page: *Page) !void {
198-
return page.navigateFromWebAPI(url, .{ .reason = .script });
198+
return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
199199
}
200200

201201
pub fn get_designMode(_: *parser.DocumentHTML) []const u8 {

src/browser/html/location.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ pub const Location = struct {
7070
}
7171

7272
pub fn _assign(_: *const Location, url: []const u8, page: *Page) !void {
73-
return page.navigateFromWebAPI(url, .{ .reason = .script });
73+
return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
7474
}
7575

7676
pub fn _replace(_: *const Location, url: []const u8, page: *Page) !void {
77-
return page.navigateFromWebAPI(url, .{ .reason = .script });
77+
return page.navigateFromWebAPI(url, .{ .reason = .script }, .replace);
7878
}
7979

8080
pub fn _reload(_: *const Location, page: *Page) !void {
81-
return page.navigateFromWebAPI(page.url.raw, .{ .reason = .script });
81+
return page.navigateFromWebAPI(page.url.raw, .{ .reason = .script }, .reload);
8282
}
8383

8484
pub fn _toString(self: *Location, page: *Page) ![]const u8 {

src/browser/html/window.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ pub const Window = struct {
150150
}
151151

152152
pub fn set_location(_: *const Window, url: []const u8, page: *Page) !void {
153-
return page.navigateFromWebAPI(url, .{ .reason = .script });
153+
return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
154154
}
155155

156156
pub fn get_console(self: *Window) *Console {

0 commit comments

Comments
 (0)