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
4 changes: 4 additions & 0 deletions src/browser/html/document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ pub const HTMLDocument = struct {
return try parser.documentHTMLGetLocation(Location, self);
}

pub fn set_location(_: *const parser.DocumentHTML, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script });
}

pub fn get_designMode(_: *parser.DocumentHTML) []const u8 {
return "off";
}
Expand Down
22 changes: 5 additions & 17 deletions src/browser/html/form.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;

const parser = @import("../netsurf.zig");
const Page = @import("../page.zig").Page;
const HTMLElement = @import("elements.zig").HTMLElement;
const FormData = @import("../xhr/form_data.zig").FormData;

Expand All @@ -27,6 +28,10 @@ pub const HTMLFormElement = struct {
pub const prototype = *HTMLElement;
pub const subtype = .node;

pub fn _submit(self: *parser.Form, page: *Page) !void {
return page.submitForm(self, null);
}

pub fn _requestSubmit(self: *parser.Form) !void {
try parser.formElementSubmit(self);
}
Expand All @@ -35,20 +40,3 @@ pub const HTMLFormElement = struct {
try parser.formElementReset(self);
}
};

pub const Submission = struct {
method: ?[]const u8,
form_data: FormData,
};

pub fn processSubmission(arena: Allocator, form: *parser.Form) !?Submission {
const form_element: *parser.Element = @ptrCast(form);
const method = try parser.elementGetAttribute(form_element, "method");

return .{
.method = method,
.form_data = try FormData.fromForm(arena, form),
};
}

// Check xhr/form_data.zig for tests
15 changes: 7 additions & 8 deletions src/browser/html/location.zig
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,17 @@ pub const Location = struct {
return "";
}

// TODO
pub fn _assign(_: *Location, url: []const u8) !void {
_ = url;
pub fn _assign(_: *const Location, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script });
}

// TODO
pub fn _replace(_: *Location, url: []const u8) !void {
_ = url;
pub fn _replace(_: *const Location, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script });
}

// TODO
pub fn _reload(_: *Location) !void {}
pub fn _reload(_: *const Location, page: *Page) !void {
return page.navigateFromWebAPI(page.url.raw, .{ .reason = .script });
}

pub fn _toString(self: *Location, page: *Page) ![]const u8 {
return try self.get_href(page);
Expand Down
4 changes: 4 additions & 0 deletions src/browser/html/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ pub const Window = struct {
return &self.location;
}

pub fn set_location(_: *const Window, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script });
}

pub fn get_console(self: *Window) *Console {
return &self.console;
}
Expand Down
73 changes: 54 additions & 19 deletions src/browser/page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,9 @@ pub const Page = struct {
self.url = request_url;

// load the data
var request = try self.newHTTPRequest(.GET, &self.url, .{ .navigation = true });
var request = try self.newHTTPRequest(opts.method, &self.url, .{ .navigation = true });
defer request.deinit();
request.body = opts.body;
request.notification = notification;

notification.dispatch(.page_navigate, &.{
Expand Down Expand Up @@ -541,23 +542,26 @@ pub const Page = struct {
.a => {
const element: *parser.Element = @ptrCast(node);
const href = (try parser.elementGetAttribute(element, "href")) orelse return;

// We cannot navigate immediately as navigating will delete the DOM tree, which holds this event's node.
// As such we schedule the function to be called as soon as possible.
// NOTE Using the page.arena assumes that the scheduling loop does use this object after invoking the callback
// If that changes we may want to consider storing DelayedNavigation in the session instead.
const arena = self.arena;
const navi = try arena.create(DelayedNavigation);
navi.* = .{
.session = self.session,
.href = try arena.dupe(u8, href),
};
_ = try self.loop.timeout(0, &navi.navigate_node);
try self.navigateFromWebAPI(href, .{});
},
else => {},
}
}

// As such we schedule the function to be called as soon as possible.
// The page.arena is safe to use here, but the transfer_arena exists
// specifically for this type of lifetime.
pub fn navigateFromWebAPI(self: *Page, url: []const u8, opts: NavigateOpts) !void {
const arena = self.session.transfer_arena;
const navi = try arena.create(DelayedNavigation);
navi.* = .{
.opts = opts,
.session = self.session,
.url = try arena.dupe(u8, url),
};
_ = try self.loop.timeout(0, &navi.navigate_node);
}

pub fn getOrCreateNodeWrapper(self: *Page, comptime T: type, node: *parser.Node) !*T {
if (try self.getNodeWrapper(T, node)) |wrap| {
return wrap;
Expand All @@ -576,19 +580,46 @@ pub const Page = struct {
}
return null;
}

pub fn submitForm(self: *Page, form: *parser.Form, submitter: ?*parser.ElementHTML) !void {
const FormData = @import("xhr/form_data.zig").FormData;

const transfer_arena = self.session.transfer_arena;
var form_data = try FormData.fromForm(form, submitter, self);

const encoding = try parser.elementGetAttribute(@ptrCast(form), "enctype");

var buf: std.ArrayListUnmanaged(u8) = .empty;
try form_data.write(encoding, buf.writer(transfer_arena));

const method = try parser.elementGetAttribute(@ptrCast(form), "method") orelse "";
var action = try parser.elementGetAttribute(@ptrCast(form), "action") orelse self.url.raw;

var opts = NavigateOpts{
.reason = .form,
};
if (std.ascii.eqlIgnoreCase(method, "post")) {
opts.method = .POST;
opts.body = buf.items;
} else {
action = try URL.concatQueryString(transfer_arena, action, buf.items);
}

try self.navigateFromWebAPI(action, opts);
}
};

const DelayedNavigation = struct {
navigate_node: Loop.CallbackNode = .{ .func = DelayedNavigation.delay_navigate },
url: []const u8,
session: *Session,
href: []const u8,
opts: NavigateOpts,
navigate_node: Loop.CallbackNode = .{ .func = delayNavigate },

fn delay_navigate(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
fn delayNavigate(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
_ = repeat_delay;
const self: *DelayedNavigation = @fieldParentPtr("navigate_node", node);
self.session.pageNavigate(self.href) catch |err| {
// TODO: should we trigger a specific event here?
log.err(.page, "delayed navigation error", .{ .err = err });
self.session.pageNavigate(self.url, self.opts) catch |err| {
log.err(.page, "delayed navigation error", .{ .err = err, .url = self.url });
};
}
};
Expand Down Expand Up @@ -696,11 +727,15 @@ const Script = struct {
pub const NavigateReason = enum {
anchor,
address_bar,
form,
script,
};

pub const NavigateOpts = struct {
cdp_id: ?i64 = null,
reason: NavigateReason = .address_bar,
method: http.Request.Method = .GET,
body: ?[]const u8 = null,
};

fn timestamp() u32 {
Expand Down
7 changes: 3 additions & 4 deletions src/browser/session.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const Allocator = std.mem.Allocator;
const Env = @import("env.zig").Env;
const Page = @import("page.zig").Page;
const Browser = @import("browser.zig").Browser;
const NavigateOpts = @import("page.zig").NavigateOpts;

const log = @import("../log.zig");
const parser = @import("netsurf.zig");
Expand Down Expand Up @@ -122,7 +123,7 @@ pub const Session = struct {
return &(self.page orelse return null);
}

pub fn pageNavigate(self: *Session, url_string: []const u8) !void {
pub fn pageNavigate(self: *Session, url_string: []const u8, opts: NavigateOpts) !void {
// currently, this is only called from the page, so let's hope
// it isn't null!
std.debug.assert(self.page != null);
Expand All @@ -136,8 +137,6 @@ pub const Session = struct {

self.removePage();
var page = try self.createPage();
return page.navigate(url, .{
.reason = .anchor,
});
return page.navigate(url, opts);
}
};
Loading