mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 22:53:28 +00:00
History as compat layer over Navigation
This commit is contained in:
@@ -25,13 +25,6 @@ const Page = @import("../page.zig").Page;
|
|||||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
|
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
|
||||||
const History = @This();
|
const History = @This();
|
||||||
|
|
||||||
const HistoryEntry = struct {
|
|
||||||
url: []const u8,
|
|
||||||
// This is serialized as JSON because
|
|
||||||
// History must survive a JsContext.
|
|
||||||
state: ?[]u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
const ScrollRestorationMode = enum {
|
const ScrollRestorationMode = enum {
|
||||||
pub const ENUM_JS_USE_TAG = true;
|
pub const ENUM_JS_USE_TAG = true;
|
||||||
|
|
||||||
@@ -40,11 +33,9 @@ const ScrollRestorationMode = enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
scroll_restoration: ScrollRestorationMode = .auto,
|
scroll_restoration: ScrollRestorationMode = .auto,
|
||||||
stack: std.ArrayListUnmanaged(HistoryEntry) = .empty,
|
|
||||||
current: ?usize = null,
|
|
||||||
|
|
||||||
pub fn get_length(self: *History) u32 {
|
pub fn get_length(_: *History, page: *Page) u32 {
|
||||||
return @intCast(self.stack.items.len);
|
return @intCast(page.session.navigation.entries.items.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_scrollRestoration(self: *History) ScrollRestorationMode {
|
pub fn get_scrollRestoration(self: *History) ScrollRestorationMode {
|
||||||
@@ -55,29 +46,15 @@ pub fn set_scrollRestoration(self: *History, mode: ScrollRestorationMode) void {
|
|||||||
self.scroll_restoration = mode;
|
self.scroll_restoration = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_state(self: *History, page: *Page) !?js.Value {
|
pub fn get_state(_: *History, page: *Page) !?js.Value {
|
||||||
if (self.current) |curr| {
|
if (page.session.navigation.currentEntry().state) |state| {
|
||||||
const entry = self.stack.items[curr];
|
const value = try js.Value.fromJson(page.js, state);
|
||||||
if (entry.state) |state| {
|
return value;
|
||||||
const value = try js.Value.fromJson(page.js, state);
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 dispatchPopStateEvent(state: ?[]const u8, page: *Page) void {
|
pub fn dispatchPopStateEvent(state: ?[]const u8, page: *Page) void {
|
||||||
log.debug(.script_event, "dispatch popstate event", .{
|
log.debug(.script_event, "dispatch popstate event", .{
|
||||||
.type = "popstate",
|
.type = "popstate",
|
||||||
@@ -101,48 +78,42 @@ fn _dispatchPopStateEvent(state: ?[]const u8, page: *Page) !void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _pushState(self: *History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
|
pub fn _pushState(_: *const History, state: js.Object, _: ?[]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);
|
||||||
|
_ = try page.session.navigation.pushEntry(url, .{ .state = state }, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
|
||||||
const arena = page.session.arena;
|
const arena = page.session.arena;
|
||||||
|
|
||||||
|
const entry = page.session.navigation.currentEntry();
|
||||||
const json = try state.toJson(arena);
|
const json = try state.toJson(arena);
|
||||||
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
|
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
|
||||||
const entry = HistoryEntry{ .state = json, .url = url };
|
|
||||||
try self.stack.append(arena, entry);
|
entry.state = json;
|
||||||
self.current = self.stack.items.len - 1;
|
entry.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _replaceState(self: *History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
|
pub fn go(_: *const History, delta: i32, page: *Page) !void {
|
||||||
const arena = page.session.arena;
|
|
||||||
|
|
||||||
if (self.current) |curr| {
|
|
||||||
const entry = &self.stack.items[curr];
|
|
||||||
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.* = HistoryEntry{ .state = json, .url = url };
|
|
||||||
} else {
|
|
||||||
try self._pushState(state, "", _url, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn go(self: *History, delta: i32, page: *Page) !void {
|
|
||||||
// 0 behaves the same as no argument, both reloading the page.
|
// 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 current = page.session.navigation.index;
|
||||||
const index_s: i64 = @intCast(@as(i64, @intCast(current)) + @as(i64, @intCast(delta)));
|
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) {
|
if (index_s < 0 or index_s > page.session.navigation.entries.items.len - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = @as(usize, @intCast(index_s));
|
const index = @as(usize, @intCast(index_s));
|
||||||
const entry = self.stack.items[index];
|
const entry = page.session.navigation.entries.items[index];
|
||||||
self.current = index;
|
|
||||||
|
|
||||||
if (try page.isSameOrigin(entry.url)) {
|
if (entry.url) |url| {
|
||||||
History.dispatchPopStateEvent(entry.state, page);
|
if (try page.isSameOrigin(url)) {
|
||||||
|
History.dispatchPopStateEvent(entry.state, page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try page.navigateFromWebAPI(entry.url, .{ .reason = .history });
|
_ = try entry.navigate(page, .force);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _go(self: *History, _delta: ?i32, page: *Page) !void {
|
pub fn _go(self: *History, _delta: ?i32, page: *Page) !void {
|
||||||
|
|||||||
@@ -816,7 +816,7 @@ pub const Page = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the navigation after a successful load.
|
// Push the navigation after a successful load.
|
||||||
try self.session.history.pushNavigation(self.url.raw, self);
|
_ = try self.session.navigation.pushEntry(self.url.raw, null, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
testing.expectEqual('auto', history.scrollRestoration);
|
testing.expectEqual('auto', history.scrollRestoration);
|
||||||
|
|
||||||
history.scrollRestoration = 'manual';
|
history.scrollRestoration = 'manual';
|
||||||
history.scrollRestoration = 'foo';
|
|
||||||
testing.expectEqual('manual', history.scrollRestoration);
|
testing.expectEqual('manual', history.scrollRestoration);
|
||||||
|
|
||||||
history.scrollRestoration = 'auto';
|
history.scrollRestoration = 'auto';
|
||||||
|
|||||||
49
src/tests/html/navigation.html
Normal file
49
src/tests/html/navigation.html
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../testing.js"></script>
|
||||||
|
<script id=navigation>
|
||||||
|
testing.expectEqual('object', typeof navigation);
|
||||||
|
testing.expectEqual('object', typeof navigation.currentEntry);
|
||||||
|
|
||||||
|
testing.expectEqual('string', typeof navigation.currentEntry.id);
|
||||||
|
testing.expectEqual('string', typeof navigation.currentEntry.key);
|
||||||
|
testing.expectEqual('string', typeof navigation.currentEntry.url);
|
||||||
|
testing.expectEqual(0, navigation.currentEntry.index);
|
||||||
|
testing.expectEqual(true, navigation.currentEntry.sameDocument);
|
||||||
|
|
||||||
|
let result = navigation.navigate('http://127.0.0.1:9582/xhr/json', {
|
||||||
|
state: { testInProgress: true }
|
||||||
|
});
|
||||||
|
testing.expectEqual('object', typeof result);
|
||||||
|
testing.expectEqual('object', typeof result.committed);
|
||||||
|
testing.expectEqual('object', typeof result.finished);
|
||||||
|
|
||||||
|
testing.expectEqual({ testInProgress: true }, navigation.currentEntry.getState());
|
||||||
|
testing.expectEqual(1, navigation.currentEntry.index);
|
||||||
|
|
||||||
|
testing.expectEqual(true, navigation.canGoBack);
|
||||||
|
testing.expectEqual(false, navigation.canGoForward);
|
||||||
|
|
||||||
|
testing.expectEqual(undefined, navigation.back());
|
||||||
|
|
||||||
|
testing.onPageWait(() => {
|
||||||
|
testing.expectEqual(0, navigation.currentEntry.index);
|
||||||
|
testing.expectEqual(true, navigation.canGoForward);
|
||||||
|
|
||||||
|
testing.expectEqual(undefined, navigation.forward());
|
||||||
|
});
|
||||||
|
|
||||||
|
testing.onPageWait(() => {
|
||||||
|
testing.expectEqual(1, navigation.currentEntry.index);
|
||||||
|
testing.expectEqual({ testInProgress: true }, navigation.currentEntry.getState());
|
||||||
|
|
||||||
|
let targetKey = navigation.currentEntry.key;
|
||||||
|
testing.expectEqual(undefined, navigation.traverseTo(targetKey));
|
||||||
|
});
|
||||||
|
|
||||||
|
navigation.updateCurrentEntry({ state: { updated: true, testComplete: true } });
|
||||||
|
testing.expectEqual({ updated: true, testComplete: true }, navigation.currentEntry.getState());
|
||||||
|
|
||||||
|
testing.onPageWait(() => {
|
||||||
|
testing.expectEqual(true, navigation.currentEntry.getState().testComplete);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user