Skip to content

Commit be75b5b

Browse files
committed
Add URL struct
Combine uri + rawuri into single struct. Try to improve ownership around URIs and URI-like things. - cookie & request can take *const std.Uri (TODO: make them aware of the new URL struct?) - Location (web api) should own its URL (web api URL) - Window should own its Location Most of these changes result in (a) a cleaner Page and (b) not having to carry around 2 nullable objects (URI and rawuri).
1 parent 8b29653 commit be75b5b

File tree

14 files changed

+279
-241
lines changed

14 files changed

+279
-241
lines changed

src/browser/browser.zig

Lines changed: 44 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ const apiweb = @import("../apiweb.zig");
3838
const Window = @import("../html/window.zig").Window;
3939
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
4040

41-
const URL = @import("../url/url.zig").URL;
42-
const Location = @import("../html/location.zig").Location;
43-
41+
const URL = @import("../url.zig").URL;
4442
const storage = @import("../storage/storage.zig");
4543

4644
const http = @import("../http/client.zig");
@@ -253,7 +251,7 @@ pub const Session = struct {
253251
self.env.stop();
254252
// TODO unload document: https://html.spec.whatwg.org/#unloading-documents
255253

256-
self.window.replaceLocation(null) catch |e| {
254+
self.window.replaceLocation(.{ .url = null }) catch |e| {
257255
log.err("reset window location: {any}", .{e});
258256
};
259257

@@ -269,7 +267,7 @@ pub const Session = struct {
269267

270268
fn contextCreated(self: *Session, page: *Page, aux_data: ?[]const u8) void {
271269
log.debug("inspector context created", .{});
272-
self.inspector.contextCreated(&self.env, "", page.origin orelse "://", aux_data);
270+
self.inspector.contextCreated(&self.env, "", (page.origin() catch "://") orelse "://", aux_data);
273271
}
274272
};
275273

@@ -283,14 +281,8 @@ pub const Page = struct {
283281
session: *Session,
284282
doc: ?*parser.Document = null,
285283

286-
// handle url
287-
rawuri: ?[]const u8 = null,
288-
uri: std.Uri = undefined,
289-
origin: ?[]const u8 = null,
290-
291-
// html url and location
284+
// The URL of the page
292285
url: ?URL = null,
293-
location: Location = .{},
294286

295287
raw_data: ?[]const u8 = null,
296288

@@ -342,62 +334,52 @@ pub const Page = struct {
342334
log.debug("wait: OK", .{});
343335
}
344336

337+
fn origin(self: *const Page) !?[]const u8 {
338+
const url = &(self.url orelse return null);
339+
var arr: std.ArrayListUnmanaged(u8) = .{};
340+
try url.origin(arr.writer(self.arena));
341+
return arr.items;
342+
}
343+
345344
// spec reference: https://html.spec.whatwg.org/#document-lifecycle
346345
// - aux_data: extra data forwarded to the Inspector
347346
// see Inspector.contextCreated
348-
pub fn navigate(self: *Page, uri: []const u8, aux_data: ?[]const u8) !void {
347+
pub fn navigate(self: *Page, url_string: []const u8, aux_data: ?[]const u8) !void {
349348
const arena = self.arena;
350349

351-
log.debug("starting GET {s}", .{uri});
350+
log.debug("starting GET {s}", .{url_string});
352351

353-
// if the uri is about:blank, nothing to do.
354-
if (std.mem.eql(u8, "about:blank", uri)) {
352+
// if the url is about:blank, nothing to do.
353+
if (std.mem.eql(u8, "about:blank", url_string)) {
355354
return;
356355
}
357356

358-
self.uri = std.Uri.parse(uri) catch try std.Uri.parseAfterScheme("", uri);
359-
357+
// we don't clone url_string, because we're going to replace self.url
358+
// later in this function, with the final request url (since we might
359+
// redirect)
360+
self.url = try URL.parse(url_string, "https");
360361
self.session.app.telemetry.record(.{ .navigate = .{
361362
.proxy = false,
362-
.tls = std.ascii.eqlIgnoreCase(self.uri.scheme, "https"),
363+
.tls = std.ascii.eqlIgnoreCase(self.url.?.scheme(), "https"),
363364
} });
364365

365366
// load the data
366-
var request = try self.newHTTPRequest(.GET, self.uri, .{ .navigation = true });
367+
var request = try self.newHTTPRequest(.GET, &self.url.?, .{ .navigation = true });
367368
defer request.deinit();
368369

369370
var response = try request.sendSync(.{});
371+
372+
// would be different than self.url in the case of a redirect
373+
self.url = try URL.fromURI(arena, request.uri);
374+
375+
const url = &self.url.?;
370376
const header = response.header;
371-
try self.session.cookie_jar.populateFromResponse(request.uri, &header);
372-
373-
// update uri after eventual redirection
374-
var buf: std.ArrayListUnmanaged(u8) = .{};
375-
try request.uri.writeToStream(.{
376-
.scheme = true,
377-
.authentication = true,
378-
.authority = true,
379-
.path = true,
380-
.query = true,
381-
.fragment = true,
382-
}, buf.writer(arena));
383-
self.rawuri = buf.items;
384-
385-
self.uri = try std.Uri.parse(self.rawuri.?);
377+
try self.session.cookie_jar.populateFromResponse(&url.uri, &header);
386378

387379
// TODO handle fragment in url.
388-
self.url = try URL.constructor(arena, self.rawuri.?, null);
389-
self.location.url = &self.url.?;
390-
try self.session.window.replaceLocation(&self.location);
391-
392-
// prepare origin value.
393-
buf = .{};
394-
try request.uri.writeToStream(.{
395-
.scheme = true,
396-
.authority = true,
397-
}, buf.writer(arena));
398-
self.origin = buf.items;
380+
try self.session.window.replaceLocation(.{ .url = try url.toWebApi(arena) });
399381

400-
log.info("GET {any} {d}", .{ self.uri, header.status });
382+
log.info("GET {any} {d}", .{ url, header.status });
401383

402384
const ct = blk: {
403385
break :blk header.get("content-type") orelse {
@@ -442,12 +424,12 @@ pub const Page = struct {
442424
};
443425
};
444426

445-
pub fn mouseEvent(self: *Page, allocator: Allocator, me: MouseEvent) !?ClickResult {
427+
pub fn mouseEvent(self: *Page, me: MouseEvent) !void {
446428
if (me.type != .pressed) {
447-
return null;
429+
return;
448430
}
449431

450-
const element = self.renderer.getElementAtPosition(me.x, me.y) orelse return null;
432+
const element = self.renderer.getElementAtPosition(me.x, me.y) orelse return;
451433

452434
const event = try parser.mouseEventCreate();
453435
defer parser.mouseEventDestroy(event);
@@ -458,20 +440,6 @@ pub const Page = struct {
458440
.y = me.y,
459441
});
460442
_ = try parser.elementDispatchEvent(element, @ptrCast(event));
461-
462-
if ((try parser.mouseEventDefaultPrevented(event)) == true) {
463-
return null;
464-
}
465-
466-
const node = parser.elementToNode(element);
467-
const tag = try parser.nodeName(node);
468-
if (std.ascii.eqlIgnoreCase(tag, "a")) {
469-
const href = (try parser.elementGetAttribute(element, "href")) orelse return null;
470-
var buf = try allocator.alloc(u8, 1024);
471-
return .{ .navigate = try std.Uri.resolve_inplace(self.uri, href, &buf) };
472-
}
473-
474-
return null;
475443
}
476444

477445
// https://html.spec.whatwg.org/#read-html
@@ -495,13 +463,13 @@ pub const Page = struct {
495463
// https://html.spec.whatwg.org/#reporting-document-loading-status
496464

497465
// inject the URL to the document including the fragment.
498-
try parser.documentSetDocumentURI(doc, self.rawuri orelse "about:blank");
466+
try parser.documentSetDocumentURI(doc, if (self.url) |*url| url.raw else "about:blank");
499467

500468
const session = self.session;
501469
// TODO set the referrer to the document.
502470
try session.window.replaceDocument(html_doc);
503471
session.window.setStorageShelf(
504-
try session.storage_shed.getOrPut(self.origin orelse "null"),
472+
try session.storage_shed.getOrPut((try self.origin()) orelse "null"),
505473
);
506474

507475
// https://html.spec.whatwg.org/#read-html
@@ -511,7 +479,7 @@ pub const Page = struct {
511479

512480
// replace the user context document with the new one.
513481
try session.env.setUserContext(.{
514-
.uri = self.uri,
482+
.url = @ptrCast(&self.url.?),
515483
.document = html_doc,
516484
.renderer = @ptrCast(&self.renderer),
517485
.cookie_jar = @ptrCast(&self.session.cookie_jar),
@@ -675,9 +643,6 @@ pub const Page = struct {
675643
fn fetchData(self: *const Page, arena: Allocator, src: []const u8, base: ?[]const u8) ![]const u8 {
676644
log.debug("starting fetch {s}", .{src});
677645

678-
var buffer: [1024]u8 = undefined;
679-
var b: []u8 = buffer[0..];
680-
681646
var res_src = src;
682647

683648
// if a base path is given, we resolve src using base.
@@ -687,19 +652,20 @@ pub const Page = struct {
687652
res_src = try std.fs.path.resolve(arena, &.{ _dir, src });
688653
}
689654
}
690-
const u = try std.Uri.resolve_inplace(self.uri, res_src, &b);
655+
var origin_url = &self.url.?;
656+
const url = try origin_url.resolve(arena, res_src);
691657

692-
var request = try self.newHTTPRequest(.GET, u, .{
693-
.origin_uri = self.uri,
658+
var request = try self.newHTTPRequest(.GET, &url, .{
659+
.origin_uri = &origin_url.uri,
694660
.navigation = false,
695661
});
696662
defer request.deinit();
697663

698664
var response = try request.sendSync(.{});
699665
var header = response.header;
700-
try self.session.cookie_jar.populateFromResponse(u, &header);
666+
try self.session.cookie_jar.populateFromResponse(&url.uri, &header);
701667

702-
log.info("fetch {any}: {d}", .{ u, header.status });
668+
log.info("fetch {any}: {d}", .{ url, header.status });
703669

704670
if (header.status != 200) {
705671
return FetchError.BadStatusCode;
@@ -726,13 +692,13 @@ pub const Page = struct {
726692
try s.eval(arena, &self.session.env, body);
727693
}
728694

729-
fn newHTTPRequest(self: *const Page, method: http.Request.Method, uri: std.Uri, opts: storage.cookie.LookupOpts) !http.Request {
695+
fn newHTTPRequest(self: *const Page, method: http.Request.Method, url: *const URL, opts: storage.cookie.LookupOpts) !http.Request {
730696
const session = self.session;
731-
var request = try session.http_client.request(method, uri);
697+
var request = try session.http_client.request(method, &url.uri);
732698
errdefer request.deinit();
733699

734700
var arr: std.ArrayListUnmanaged(u8) = .{};
735-
try session.cookie_jar.forRequest(uri, arr.writer(self.arena), opts);
701+
try session.cookie_jar.forRequest(&url.uri, arr.writer(self.arena), opts);
736702

737703
if (arr.items.len > 0) {
738704
try request.addHeader("Cookie", arr.items, .{});

src/cdp/cdp.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
335335

336336
pub fn getURL(self: *const Self) ?[]const u8 {
337337
const page = self.session.currentPage() orelse return null;
338-
return page.rawuri;
338+
return if (page.url) |*url| url.raw else null;
339339
}
340340

341341
pub fn onInspectorResponse(ctx: *anyopaque, _: u32, msg: []const u8) void {

src/cdp/domains/input.zig

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,7 @@ fn dispatchMouseEvent(cmd: anytype) !void {
6464
else => unreachable,
6565
},
6666
};
67-
const click_result = (try page.mouseEvent(cmd.arena, mouse_event)) orelse return;
68-
69-
switch (click_result) {
70-
.navigate => |uri| try clickNavigate(cmd, uri),
71-
}
67+
try page.mouseEvent(mouse_event);
7268
// result already sent
7369
}
7470

src/cdp/testing.zig

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const Testing = @This();
2424

2525
const main = @import("cdp.zig");
2626
const parser = @import("netsurf");
27+
const URL = @import("../url.zig").URL;
2728
const App = @import("../app.zig").App;
2829

2930
const base = @import("../testing.zig");
@@ -85,8 +86,8 @@ const Session = struct {
8586
return error.MockBrowserPageAlreadyExists;
8687
}
8788
self.page = .{
88-
.rawuri = "",
8989
.session = self,
90+
.url = URL.parse("https://lightpanda.io/", null) catch unreachable,
9091
.aux_data = try self.arena.dupe(u8, aux_data orelse ""),
9192
};
9293
return &self.page.?;
@@ -104,7 +105,7 @@ const Session = struct {
104105

105106
const Page = struct {
106107
session: *Session,
107-
rawuri: []const u8,
108+
url: ?URL = null,
108109
aux_data: []const u8 = "",
109110
doc: ?*parser.Document = null,
110111

@@ -114,10 +115,7 @@ const Page = struct {
114115
}
115116

116117
const MouseEvent = @import("../browser/browser.zig").Page.MouseEvent;
117-
const ClickResult = @import("../browser/browser.zig").Page.ClickResult;
118-
pub fn mouseEvent(_: *Page, _: Allocator, _: MouseEvent) !?ClickResult {
119-
return null;
120-
}
118+
pub fn mouseEvent(_: *Page, _: MouseEvent) !void {}
121119
};
122120

123121
const Client = struct {

src/html/location.zig

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,60 +30,60 @@ const checkCases = jsruntime.test_utils.checkCases;
3030
pub const Location = struct {
3131
pub const mem_guarantied = true;
3232

33-
url: ?*URL = null,
33+
url: ?URL = null,
3434

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

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

4040
return "";
4141
}
4242

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

4646
return "";
4747
}
4848

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

5252
return "";
5353
}
5454

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

5858
return "";
5959
}
6060

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

6464
return "";
6565
}
6666

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

7070
return "";
7171
}
7272

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

7676
return "";
7777
}
7878

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

8282
return "";
8383
}
8484

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

8888
return "";
8989
}

0 commit comments

Comments
 (0)