Skip to content

Commit 3fb1677

Browse files
Merge pull request #356 from lightpanda-io/location
dom: first draft for location
2 parents 0c89fa7 + 7b35bb4 commit 3fb1677

File tree

10 files changed

+205
-6
lines changed

10 files changed

+205
-6
lines changed

src/browser/browser.zig

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const apiweb = @import("../apiweb.zig");
3636
const Window = @import("../html/window.zig").Window;
3737
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
3838

39+
const URL = @import("../url/url.zig").URL;
40+
const Location = @import("../html/location.zig").Location;
41+
3942
const storage = @import("../storage/storage.zig");
4043

4144
const FetchResult = @import("../http/Client.zig").Client.FetchResult;
@@ -102,7 +105,9 @@ pub const Session = struct {
102105
loader: Loader,
103106
env: Env = undefined,
104107
inspector: ?jsruntime.Inspector = null,
108+
105109
window: Window,
110+
106111
// TODO move the shed to the browser?
107112
storageShed: storage.Shed,
108113
page: ?Page = null,
@@ -201,6 +206,10 @@ pub const Page = struct {
201206
uri: std.Uri = undefined,
202207
origin: ?[]const u8 = null,
203208

209+
// html url and location
210+
url: ?URL = null,
211+
location: Location = .{},
212+
204213
raw_data: ?[]const u8 = null,
205214

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

256+
if (self.url) |*u| u.deinit(self.arena.allocator());
257+
self.url = null;
258+
self.location.url = null;
259+
self.session.window.replaceLocation(&self.location) catch |e| {
260+
log.err("reset window location: {any}", .{e});
261+
};
262+
247263
// clear netsurf memory arena.
248264
parser.deinit();
249265

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

327+
if (self.url) |*prev| prev.deinit(alloc);
328+
self.url = try URL.constructor(alloc, self.rawuri.?, null);
329+
self.location.url = &self.url.?;
330+
try self.session.window.replaceLocation(&self.location);
331+
311332
// prepare origin value.
312333
var buf = std.ArrayList(u8).init(alloc);
313334
defer buf.deinit();
@@ -391,7 +412,7 @@ pub const Page = struct {
391412

392413
// TODO set the referrer to the document.
393414

394-
self.session.window.replaceDocument(html_doc);
415+
try self.session.window.replaceDocument(html_doc);
395416
self.session.window.setStorageShelf(
396417
try self.session.storageShed.getOrPut(self.origin orelse "null"),
397418
);

src/html/document.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const Node = @import("../dom/node.zig").Node;
2828
const Document = @import("../dom/document.zig").Document;
2929
const NodeList = @import("../dom/nodelist.zig").NodeList;
3030
const HTMLElem = @import("elements.zig");
31+
const Location = @import("location.zig").Location;
3132

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

161+
pub fn get_location(self: *parser.DocumentHTML) !?*Location {
162+
return try parser.documentHTMLGetLocation(Location, self);
163+
}
164+
160165
pub fn get_designMode(_: *parser.DocumentHTML) []const u8 {
161166
return "off";
162167
}

src/html/html.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const HTMLElem = @import("elements.zig");
2323
const Window = @import("window.zig").Window;
2424
const Navigator = @import("navigator.zig").Navigator;
2525
const History = @import("history.zig").History;
26+
const Location = @import("location.zig").Location;
2627

2728
pub const Interfaces = generate.Tuple(.{
2829
HTMLDocument,
@@ -32,4 +33,5 @@ pub const Interfaces = generate.Tuple(.{
3233
Window,
3334
Navigator,
3435
History,
36+
Location,
3537
});

src/html/location.zig

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
2+
//
3+
// Francis Bouvier <[email protected]>
4+
// Pierre Tachoire <[email protected]>
5+
//
6+
// This program is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU Affero General Public License as
8+
// published by the Free Software Foundation, either version 3 of the
9+
// License, or (at your option) any later version.
10+
//
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU Affero General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU Affero General Public License
17+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
19+
const std = @import("std");
20+
21+
const builtin = @import("builtin");
22+
const jsruntime = @import("jsruntime");
23+
24+
const URL = @import("../url/url.zig").URL;
25+
26+
const Case = jsruntime.test_utils.Case;
27+
const checkCases = jsruntime.test_utils.checkCases;
28+
29+
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-location-interface
30+
pub const Location = struct {
31+
pub const mem_guarantied = true;
32+
33+
url: ?*URL = null,
34+
35+
pub fn deinit(_: *Location, _: std.mem.Allocator) void {}
36+
37+
pub fn get_href(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
38+
if (self.url) |u| return u.get_href(alloc);
39+
40+
return "";
41+
}
42+
43+
pub fn get_protocol(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
44+
if (self.url) |u| return u.get_protocol(alloc);
45+
46+
return "";
47+
}
48+
49+
pub fn get_host(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
50+
if (self.url) |u| return u.get_host(alloc);
51+
52+
return "";
53+
}
54+
55+
pub fn get_hostname(self: *Location) []const u8 {
56+
if (self.url) |u| return u.get_hostname();
57+
58+
return "";
59+
}
60+
61+
pub fn get_port(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
62+
if (self.url) |u| return u.get_port(alloc);
63+
64+
return "";
65+
}
66+
67+
pub fn get_pathname(self: *Location) []const u8 {
68+
if (self.url) |u| return u.get_pathname();
69+
70+
return "";
71+
}
72+
73+
pub fn get_search(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
74+
if (self.url) |u| return u.get_search(alloc);
75+
76+
return "";
77+
}
78+
79+
pub fn get_hash(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
80+
if (self.url) |u| return u.get_hash(alloc);
81+
82+
return "";
83+
}
84+
85+
pub fn get_origin(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
86+
if (self.url) |u| return u.get_origin(alloc);
87+
88+
return "";
89+
}
90+
91+
// TODO
92+
pub fn _assign(_: *Location, url: []const u8) !void {
93+
_ = url;
94+
}
95+
96+
// TODO
97+
pub fn _replace(_: *Location, url: []const u8) !void {
98+
_ = url;
99+
}
100+
101+
// TODO
102+
pub fn _reload(_: *Location) !void {}
103+
104+
pub fn _toString(self: *Location, alloc: std.mem.Allocator) ![]const u8 {
105+
return try self.get_href(alloc);
106+
}
107+
};
108+
109+
// Tests
110+
// -----
111+
112+
pub fn testExecFn(
113+
_: std.mem.Allocator,
114+
js_env: *jsruntime.Env,
115+
) anyerror!void {
116+
var location = [_]Case{
117+
.{ .src = "location.href", .ex = "https://lightpanda.io/opensource-browser/" },
118+
.{ .src = "document.location.href", .ex = "https://lightpanda.io/opensource-browser/" },
119+
120+
.{ .src = "location.host", .ex = "lightpanda.io" },
121+
.{ .src = "location.hostname", .ex = "lightpanda.io" },
122+
.{ .src = "location.origin", .ex = "https://lightpanda.io" },
123+
.{ .src = "location.pathname", .ex = "/opensource-browser/" },
124+
.{ .src = "location.hash", .ex = "" },
125+
.{ .src = "location.port", .ex = "" },
126+
.{ .src = "location.search", .ex = "" },
127+
};
128+
try checkCases(js_env, &location);
129+
}

src/html/window.zig

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ const Loop = jsruntime.Loop;
2727
const EventTarget = @import("../dom/event_target.zig").EventTarget;
2828
const Navigator = @import("navigator.zig").Navigator;
2929
const History = @import("history.zig").History;
30+
const Location = @import("location.zig").Location;
3031

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

34+
var emptyLocation = Location{};
35+
3336
// https://dom.spec.whatwg.org/#interface-window-extensions
3437
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#window
3538
pub const Window = struct {
@@ -43,6 +46,7 @@ pub const Window = struct {
4346
document: ?*parser.DocumentHTML = null,
4447
target: []const u8,
4548
history: History = .{},
49+
location: *Location = &emptyLocation,
4650

4751
storageShelf: ?*storage.Shelf = null,
4852

@@ -60,8 +64,17 @@ pub const Window = struct {
6064
};
6165
}
6266

63-
pub fn replaceDocument(self: *Window, doc: *parser.DocumentHTML) void {
67+
pub fn replaceLocation(self: *Window, loc: *Location) !void {
68+
self.location = loc;
69+
70+
if (self.document != null) {
71+
try parser.documentHTMLSetLocation(Location, self.document.?, self.location);
72+
}
73+
}
74+
75+
pub fn replaceDocument(self: *Window, doc: *parser.DocumentHTML) !void {
6476
self.document = doc;
77+
try parser.documentHTMLSetLocation(Location, doc, self.location);
6578
}
6679

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

92+
pub fn get_location(self: *Window) *Location {
93+
return self.location;
94+
}
95+
7996
pub fn get_self(self: *Window) *Window {
8097
return self;
8198
}

src/main_shell.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn execJS(
5555

5656
// alias global as self and window
5757
var window = Window.create(null, null);
58-
window.replaceDocument(doc);
58+
try window.replaceDocument(doc);
5959
window.setStorageShelf(&storageShelf);
6060
try js_env.bindGlobal(window);
6161

src/netsurf/netsurf.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,3 +2264,20 @@ pub fn documentHTMLGetCurrentScript(doc: *DocumentHTML) !?*Script {
22642264
if (elem == null) return null;
22652265
return @ptrCast(elem.?);
22662266
}
2267+
2268+
pub fn documentHTMLSetLocation(T: type, doc: *DocumentHTML, location: *T) !void {
2269+
const l = @as(*anyopaque, @ptrCast(location));
2270+
const err = documentHTMLVtable(doc).set_location.?(doc, l);
2271+
try DOMErr(err);
2272+
}
2273+
2274+
pub fn documentHTMLGetLocation(T: type, doc: *DocumentHTML) !?*T {
2275+
var l: ?*anyopaque = undefined;
2276+
const err = documentHTMLVtable(doc).get_location.?(doc, &l);
2277+
try DOMErr(err);
2278+
2279+
if (l == null) return null;
2280+
2281+
const ptr: *align(@alignOf(*T)) anyopaque = @alignCast(l.?);
2282+
return @as(*T, @ptrCast(ptr));
2283+
}

src/run_tests.zig

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ const Window = @import("html/window.zig").Window;
2929
const xhr = @import("xhr/xhr.zig");
3030
const storage = @import("storage/storage.zig");
3131
const url = @import("url/url.zig");
32+
const URL = url.URL;
3233
const urlquery = @import("url/query.zig");
3334
const Client = @import("asyncio").Client;
35+
const Location = @import("html/location.zig").Location;
3436

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

101-
window.replaceDocument(doc);
103+
var u = try URL.constructor(alloc, "https://lightpanda.io/opensource-browser/", null);
104+
defer u.deinit(alloc);
105+
var location = Location{ .url = &u };
106+
try window.replaceLocation(&location);
107+
108+
try window.replaceDocument(doc);
102109
window.setStorageShelf(&storageShelf);
103110

104111
try js_env.bindGlobal(window);
@@ -139,6 +146,7 @@ fn testsAllExecFn(
139146
@import("polyfill/fetch.zig").testExecFn,
140147
@import("html/navigator.zig").testExecFn,
141148
@import("html/history.zig").testExecFn,
149+
@import("html/location.zig").testExecFn,
142150
};
143151

144152
inline for (testFns) |testFn| {

src/wpt/run.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
9191

9292
// setup global env vars.
9393
var window = Window.create(null, null);
94-
window.replaceDocument(html_doc);
94+
try window.replaceDocument(html_doc);
9595
window.setStorageShelf(&storageShelf);
9696
try js_env.bindGlobal(&window);
9797

0 commit comments

Comments
 (0)