mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
add toJson object and fromJson value
This commit is contained in:
118
src/browser/html/History.zig
Normal file
118
src/browser/html/History.zig
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Env = @import("../env.zig").Env;
|
||||||
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
|
||||||
|
const History = @This();
|
||||||
|
|
||||||
|
const HistoryEntry = struct {
|
||||||
|
url: ?[]const u8,
|
||||||
|
// Serialized Env.JsObject
|
||||||
|
state: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ScrollRestorationMode = enum {
|
||||||
|
auto,
|
||||||
|
manual,
|
||||||
|
|
||||||
|
pub fn fromString(str: []const u8) ?ScrollRestorationMode {
|
||||||
|
for (std.enums.values(ScrollRestorationMode)) |mode| {
|
||||||
|
if (std.ascii.eqlIgnoreCase(str, @tagName(mode))) {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scrollRestoration: ScrollRestorationMode = .auto,
|
||||||
|
stack: std.ArrayListUnmanaged(HistoryEntry) = .empty,
|
||||||
|
current: ?usize = null,
|
||||||
|
|
||||||
|
pub fn get_length(self: *History) u32 {
|
||||||
|
return @intCast(self.stack.items.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_scrollRestoration(self: *History) []const u8 {
|
||||||
|
return switch (self.scrollRestoration) {
|
||||||
|
.auto => "auto",
|
||||||
|
.manual => "manual",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
if (self.current) |curr| {
|
||||||
|
const entry = self.stack.items[curr];
|
||||||
|
const object = try Env.JsObject.fromJson(page.main_context, entry.state);
|
||||||
|
return object;
|
||||||
|
} 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);
|
||||||
|
const entry = HistoryEntry{ .state = json, .url = url };
|
||||||
|
try self.stack.append(page.session.arena, entry);
|
||||||
|
self.current = self.stack.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _forward(self: *History) void {
|
||||||
|
if (self.current) |curr| {
|
||||||
|
if (curr < self.stack.items.len) {
|
||||||
|
self.current = curr + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testing = @import("../../testing.zig");
|
||||||
|
test "Browser: HTML.History" {
|
||||||
|
try testing.htmlRunner("html/history.html");
|
||||||
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
|
||||||
//
|
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as
|
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
|
|
||||||
pub const History = struct {
|
|
||||||
const ScrollRestorationMode = enum {
|
|
||||||
auto,
|
|
||||||
manual,
|
|
||||||
};
|
|
||||||
|
|
||||||
scrollRestoration: ScrollRestorationMode = .auto,
|
|
||||||
state: std.json.Value = .null,
|
|
||||||
|
|
||||||
// count tracks the history length until we implement correctly pushstate.
|
|
||||||
count: u32 = 0,
|
|
||||||
|
|
||||||
pub fn get_length(self: *History) u32 {
|
|
||||||
// TODO return the real history length value.
|
|
||||||
return self.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_scrollRestoration(self: *History) []const u8 {
|
|
||||||
return switch (self.scrollRestoration) {
|
|
||||||
.auto => "auto",
|
|
||||||
.manual => "manual",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_scrollRestoration(self: *History, mode: []const u8) void {
|
|
||||||
if (std.mem.eql(u8, "manual", mode)) self.scrollRestoration = .manual;
|
|
||||||
if (std.mem.eql(u8, "auto", mode)) self.scrollRestoration = .auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_state(self: *History) std.json.Value {
|
|
||||||
return self.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 _pushState(self: *History, data: []const u8, _: ?[]const u8, url: ?[]const u8) void {
|
|
||||||
self.count += 1;
|
|
||||||
_ = url;
|
|
||||||
_ = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, data: []const u8, _: ?[]const u8, url: ?[]const u8) void {
|
|
||||||
_ = self;
|
|
||||||
_ = url;
|
|
||||||
_ = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO implement the function
|
|
||||||
pub fn _go(self: *History, delta: ?i32) void {
|
|
||||||
_ = self;
|
|
||||||
_ = delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO implement the function
|
|
||||||
pub fn _back(self: *History) void {
|
|
||||||
_ = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO implement the function
|
|
||||||
pub fn _forward(self: *History) void {
|
|
||||||
_ = self;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
|
||||||
test "Browser: HTML.History" {
|
|
||||||
try testing.htmlRunner("html/history.html");
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@ const HTMLElem = @import("elements.zig");
|
|||||||
const SVGElem = @import("svg_elements.zig");
|
const SVGElem = @import("svg_elements.zig");
|
||||||
const Window = @import("window.zig").Window;
|
const Window = @import("window.zig").Window;
|
||||||
const Navigator = @import("navigator.zig").Navigator;
|
const Navigator = @import("navigator.zig").Navigator;
|
||||||
const History = @import("history.zig").History;
|
const History = @import("History.zig");
|
||||||
const Location = @import("location.zig").Location;
|
const Location = @import("location.zig").Location;
|
||||||
const MediaQueryList = @import("media_query_list.zig").MediaQueryList;
|
const MediaQueryList = @import("media_query_list.zig").MediaQueryList;
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const Env = @import("../env.zig").Env;
|
|||||||
const Page = @import("../page.zig").Page;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const Navigator = @import("navigator.zig").Navigator;
|
const Navigator = @import("navigator.zig").Navigator;
|
||||||
const History = @import("history.zig").History;
|
const History = @import("History.zig");
|
||||||
const Location = @import("location.zig").Location;
|
const Location = @import("location.zig").Location;
|
||||||
const Crypto = @import("../crypto/crypto.zig").Crypto;
|
const Crypto = @import("../crypto/crypto.zig").Crypto;
|
||||||
const Console = @import("../console/console.zig").Console;
|
const Console = @import("../console/console.zig").Console;
|
||||||
@@ -54,7 +54,6 @@ pub const Window = struct {
|
|||||||
|
|
||||||
document: *parser.DocumentHTML,
|
document: *parser.DocumentHTML,
|
||||||
target: []const u8 = "",
|
target: []const u8 = "",
|
||||||
history: History = .{},
|
|
||||||
location: Location = .{},
|
location: Location = .{},
|
||||||
storage_shelf: ?*storage.Shelf = null,
|
storage_shelf: ?*storage.Shelf = null,
|
||||||
|
|
||||||
@@ -179,8 +178,8 @@ pub const Window = struct {
|
|||||||
return self.document;
|
return self.document;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_history(self: *Window) *History {
|
pub fn get_history(_: *Window, page: *Page) *History {
|
||||||
return &self.history;
|
return &page.session.history;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The interior height of the window in pixels, including the height of the horizontal scroll bar, if present.
|
// The interior height of the window in pixels, including the height of the horizontal scroll bar, if present.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const Env = @import("env.zig").Env;
|
|||||||
const Page = @import("page.zig").Page;
|
const Page = @import("page.zig").Page;
|
||||||
const Browser = @import("browser.zig").Browser;
|
const Browser = @import("browser.zig").Browser;
|
||||||
const NavigateOpts = @import("page.zig").NavigateOpts;
|
const NavigateOpts = @import("page.zig").NavigateOpts;
|
||||||
|
const History = @import("html/History.zig");
|
||||||
|
|
||||||
const log = @import("../log.zig");
|
const log = @import("../log.zig");
|
||||||
const parser = @import("netsurf.zig");
|
const parser = @import("netsurf.zig");
|
||||||
@@ -53,6 +54,10 @@ pub const Session = struct {
|
|||||||
storage_shed: storage.Shed,
|
storage_shed: storage.Shed,
|
||||||
cookie_jar: storage.CookieJar,
|
cookie_jar: storage.CookieJar,
|
||||||
|
|
||||||
|
// History is persistent across the "tab".
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/History
|
||||||
|
history: History = .{},
|
||||||
|
|
||||||
page: ?Page = null,
|
page: ?Page = null,
|
||||||
|
|
||||||
// If the current page want to navigate to a new page
|
// If the current page want to navigate to a new page
|
||||||
|
|||||||
@@ -2005,6 +2005,12 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
return writer.writeAll(try self.toString());
|
return writer.writeAll(try self.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toJson(self: JsObject, allocator: std.mem.Allocator) ![]u8 {
|
||||||
|
const json_string = try v8.Json.stringify(self.js_context.v8_context, self.js_obj.toValue(), null);
|
||||||
|
const str = try jsStringToZig(allocator, json_string, self.js_context.isolate);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn persist(self: JsObject) !JsObject {
|
pub fn persist(self: JsObject) !JsObject {
|
||||||
var js_context = self.js_context;
|
var js_context = self.js_context;
|
||||||
const js_obj = self.js_obj;
|
const js_obj = self.js_obj;
|
||||||
@@ -2400,6 +2406,12 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
|||||||
const js_context = self.js_context;
|
const js_context = self.js_context;
|
||||||
return valueToString(allocator, self.value, js_context.isolate, js_context.v8_context);
|
return valueToString(allocator, self.value, js_context.isolate, js_context.v8_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fromJson(ctx: *JsContext, json: []const u8) !Value {
|
||||||
|
const json_string = v8.String.initUtf8(ctx.isolate, json);
|
||||||
|
const value = try v8.Json.parse(ctx.v8_context, json_string);
|
||||||
|
return Value{ .js_context = ctx, .value = value };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ValueIterator = struct {
|
pub const ValueIterator = struct {
|
||||||
|
|||||||
@@ -22,3 +22,10 @@
|
|||||||
testing.expectEqual(undefined, history.forward());
|
testing.expectEqual(undefined, history.forward());
|
||||||
testing.expectEqual(undefined, history.back());
|
testing.expectEqual(undefined, history.back());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id=history-states>
|
||||||
|
testing.expectEqual(null, history.state)
|
||||||
|
|
||||||
|
history.pushState({}, { "abc": "def" }, '');
|
||||||
|
testing.expectEqual({ "abc": "def"}, history.state);
|
||||||
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user