Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/browser/browser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const apiweb = @import("../apiweb.zig");
const Window = @import("../html/window.zig").Window;
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;

const URL = @import("../url/url.zig").URL;
const Location = @import("../html/location.zig").Location;

const storage = @import("../storage/storage.zig");

const FetchResult = @import("../http/Client.zig").Client.FetchResult;
Expand Down Expand Up @@ -102,7 +105,9 @@ pub const Session = struct {
loader: Loader,
env: Env = undefined,
inspector: ?jsruntime.Inspector = null,

window: Window,

// TODO move the shed to the browser?
storageShed: storage.Shed,
page: ?Page = null,
Expand Down Expand Up @@ -201,6 +206,10 @@ pub const Page = struct {
uri: std.Uri = undefined,
origin: ?[]const u8 = null,

// html url and location
url: ?URL = null,
location: Location = .{},

raw_data: ?[]const u8 = null,

fn init(
Expand Down Expand Up @@ -244,6 +253,13 @@ pub const Page = struct {
self.session.env.stop();
// TODO unload document: https://html.spec.whatwg.org/#unloading-documents

if (self.url) |*u| u.deinit(self.arena.allocator());
self.url = null;
self.location.url = null;
self.session.window.replaceLocation(&self.location) catch |e| {
log.err("reset window location: {any}", .{e});
};

// clear netsurf memory arena.
parser.deinit();

Expand Down Expand Up @@ -308,6 +324,11 @@ pub const Page = struct {
self.rawuri = try alloc.dupe(u8, uri);
self.uri = std.Uri.parse(self.rawuri.?) catch try std.Uri.parseAfterScheme("", self.rawuri.?);

if (self.url) |*prev| prev.deinit(alloc);
self.url = try URL.constructor(alloc, self.rawuri.?, null);
self.location.url = &self.url.?;
try self.session.window.replaceLocation(&self.location);

// prepare origin value.
var buf = std.ArrayList(u8).init(alloc);
defer buf.deinit();
Expand Down Expand Up @@ -391,7 +412,7 @@ pub const Page = struct {

// TODO set the referrer to the document.

self.session.window.replaceDocument(html_doc);
try self.session.window.replaceDocument(html_doc);
self.session.window.setStorageShelf(
try self.session.storageShed.getOrPut(self.origin orelse "null"),
);
Expand Down
5 changes: 5 additions & 0 deletions src/html/document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const Node = @import("../dom/node.zig").Node;
const Document = @import("../dom/document.zig").Document;
const NodeList = @import("../dom/nodelist.zig").NodeList;
const HTMLElem = @import("elements.zig");
const Location = @import("location.zig").Location;

const collection = @import("../dom/html_collection.zig");
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
Expand Down Expand Up @@ -157,6 +158,10 @@ pub const HTMLDocument = struct {
return try parser.documentHTMLGetCurrentScript(self);
}

pub fn get_location(self: *parser.DocumentHTML) !?*Location {
return try parser.documentHTMLGetLocation(Location, self);
}

pub fn get_designMode(_: *parser.DocumentHTML) []const u8 {
return "off";
}
Expand Down
2 changes: 2 additions & 0 deletions src/html/html.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const HTMLElem = @import("elements.zig");
const Window = @import("window.zig").Window;
const Navigator = @import("navigator.zig").Navigator;
const History = @import("history.zig").History;
const Location = @import("location.zig").Location;

pub const Interfaces = generate.Tuple(.{
HTMLDocument,
Expand All @@ -32,4 +33,5 @@ pub const Interfaces = generate.Tuple(.{
Window,
Navigator,
History,
Location,
});
129 changes: 129 additions & 0 deletions src/html/location.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <[email protected]>
// Pierre Tachoire <[email protected]>
//
// 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 builtin = @import("builtin");
const jsruntime = @import("jsruntime");

const URL = @import("../url/url.zig").URL;

const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases;

// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-location-interface
pub const Location = struct {
pub const mem_guarantied = true;

url: ?*URL = null,

pub fn deinit(_: *Location, _: std.mem.Allocator) void {}

pub fn get_href(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_href(alloc);

return "";
}

pub fn get_protocol(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_protocol(alloc);

return "";
}

pub fn get_host(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_host(alloc);

return "";
}

pub fn get_hostname(self: *Location) []const u8 {
if (self.url) |u| return u.get_hostname();

return "";
}

pub fn get_port(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_port(alloc);

return "";
}

pub fn get_pathname(self: *Location) []const u8 {
if (self.url) |u| return u.get_pathname();

return "";
}

pub fn get_search(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_search(alloc);

return "";
}

pub fn get_hash(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_hash(alloc);

return "";
}

pub fn get_origin(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
if (self.url) |u| return u.get_origin(alloc);

return "";
}

// TODO
pub fn _assign(_: *Location, url: []const u8) !void {
_ = url;
}

// TODO
pub fn _replace(_: *Location, url: []const u8) !void {
_ = url;
}

// TODO
pub fn _reload(_: *Location) !void {}

pub fn _toString(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
return try self.get_href(alloc);
}
};

// Tests
// -----

pub fn testExecFn(
_: std.mem.Allocator,
js_env: *jsruntime.Env,
) anyerror!void {
var location = [_]Case{
.{ .src = "location.href", .ex = "https://lightpanda.io/opensource-browser/" },
.{ .src = "document.location.href", .ex = "https://lightpanda.io/opensource-browser/" },

.{ .src = "location.host", .ex = "lightpanda.io" },
.{ .src = "location.hostname", .ex = "lightpanda.io" },
.{ .src = "location.origin", .ex = "https://lightpanda.io" },
.{ .src = "location.pathname", .ex = "/opensource-browser/" },
.{ .src = "location.hash", .ex = "" },
.{ .src = "location.port", .ex = "" },
.{ .src = "location.search", .ex = "" },
};
try checkCases(js_env, &location);
}
19 changes: 18 additions & 1 deletion src/html/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ const Loop = jsruntime.Loop;
const EventTarget = @import("../dom/event_target.zig").EventTarget;
const Navigator = @import("navigator.zig").Navigator;
const History = @import("history.zig").History;
const Location = @import("location.zig").Location;

const storage = @import("../storage/storage.zig");

var emptyLocation = Location{};

// https://dom.spec.whatwg.org/#interface-window-extensions
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#window
pub const Window = struct {
Expand All @@ -43,6 +46,7 @@ pub const Window = struct {
document: ?*parser.DocumentHTML = null,
target: []const u8,
history: History = .{},
location: *Location = &emptyLocation,

storageShelf: ?*storage.Shelf = null,

Expand All @@ -60,8 +64,17 @@ pub const Window = struct {
};
}

pub fn replaceDocument(self: *Window, doc: *parser.DocumentHTML) void {
pub fn replaceLocation(self: *Window, loc: *Location) !void {
self.location = loc;

if (self.document != null) {
try parser.documentHTMLSetLocation(Location, self.document.?, self.location);
}
}

pub fn replaceDocument(self: *Window, doc: *parser.DocumentHTML) !void {
self.document = doc;
try parser.documentHTMLSetLocation(Location, doc, self.location);
}

pub fn setStorageShelf(self: *Window, shelf: *storage.Shelf) void {
Expand All @@ -76,6 +89,10 @@ pub const Window = struct {
return &self.navigator;
}

pub fn get_location(self: *Window) *Location {
return self.location;
}

pub fn get_self(self: *Window) *Window {
return self;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main_shell.zig
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn execJS(

// alias global as self and window
var window = Window.create(null, null);
window.replaceDocument(doc);
try window.replaceDocument(doc);
window.setStorageShelf(&storageShelf);
try js_env.bindGlobal(window);

Expand Down
17 changes: 17 additions & 0 deletions src/netsurf/netsurf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2264,3 +2264,20 @@ pub fn documentHTMLGetCurrentScript(doc: *DocumentHTML) !?*Script {
if (elem == null) return null;
return @ptrCast(elem.?);
}

pub fn documentHTMLSetLocation(T: type, doc: *DocumentHTML, location: *T) !void {
const l = @as(*anyopaque, @ptrCast(location));
const err = documentHTMLVtable(doc).set_location.?(doc, l);
try DOMErr(err);
}

pub fn documentHTMLGetLocation(T: type, doc: *DocumentHTML) !?*T {
var l: ?*anyopaque = undefined;
const err = documentHTMLVtable(doc).get_location.?(doc, &l);
try DOMErr(err);

if (l == null) return null;

const ptr: *align(@alignOf(*T)) anyopaque = @alignCast(l.?);
return @as(*T, @ptrCast(ptr));
}
10 changes: 9 additions & 1 deletion src/run_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ const Window = @import("html/window.zig").Window;
const xhr = @import("xhr/xhr.zig");
const storage = @import("storage/storage.zig");
const url = @import("url/url.zig");
const URL = url.URL;
const urlquery = @import("url/query.zig");
const Client = @import("asyncio").Client;
const Location = @import("html/location.zig").Location;

const documentTestExecFn = @import("dom/document.zig").testExecFn;
const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn;
Expand Down Expand Up @@ -98,7 +100,12 @@ fn testExecFn(
// alias global as self and window
var window = Window.create(null, null);

window.replaceDocument(doc);
var u = try URL.constructor(alloc, "https://lightpanda.io/opensource-browser/", null);
defer u.deinit(alloc);
var location = Location{ .url = &u };
try window.replaceLocation(&location);

try window.replaceDocument(doc);
window.setStorageShelf(&storageShelf);

try js_env.bindGlobal(window);
Expand Down Expand Up @@ -139,6 +146,7 @@ fn testsAllExecFn(
@import("polyfill/fetch.zig").testExecFn,
@import("html/navigator.zig").testExecFn,
@import("html/history.zig").testExecFn,
@import("html/location.zig").testExecFn,
};

inline for (testFns) |testFn| {
Expand Down
2 changes: 1 addition & 1 deletion src/wpt/run.zig
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const

// setup global env vars.
var window = Window.create(null, null);
window.replaceDocument(html_doc);
try window.replaceDocument(html_doc);
window.setStorageShelf(&storageShelf);
try js_env.bindGlobal(&window);

Expand Down
2 changes: 1 addition & 1 deletion vendor/netsurf/libdom
Loading