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: 0 additions & 4 deletions src/browser/html/form.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ pub const HTMLFormElement = struct {
return page.submitForm(self, null);
}

pub fn _requestSubmit(self: *parser.Form) !void {
try parser.formElementSubmit(self);
}

pub fn _reset(self: *parser.Form) !void {
try parser.formElementReset(self);
}
Expand Down
47 changes: 47 additions & 0 deletions src/browser/page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ pub const Page = struct {
// current_script could by fetch module to resolve module's url to fetch.
current_script: ?*const Script = null,

// indicates intention to navigate to another page on the next loop execution.
delayed_navigation: bool = false,

pub fn init(self: *Page, arena: Allocator, session: *Session) !void {
const browser = session.browser;
self.* = .{
Expand Down Expand Up @@ -553,6 +556,25 @@ pub const Page = struct {
const href = (try parser.elementGetAttribute(element, "href")) orelse return;
try self.navigateFromWebAPI(href, .{});
},
.input => {
const element: *parser.Element = @ptrCast(node);
const input_type = (try parser.elementGetAttribute(element, "type")) orelse return;
if (std.ascii.eqlIgnoreCase(input_type, "submit")) {
return self.elementSubmitForm(element);
}
},
.button => {
const element: *parser.Element = @ptrCast(node);
const button_type = (try parser.elementGetAttribute(element, "type")) orelse return;
if (std.ascii.eqlIgnoreCase(button_type, "submit")) {
return self.elementSubmitForm(element);
}
if (std.ascii.eqlIgnoreCase(button_type, "reset")) {
if (try self.formForElement(element)) |form| {
return parser.formElementReset(form);
}
}
},
else => {},
}
}
Expand All @@ -561,6 +583,7 @@ pub const Page = struct {
// 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 {
self.delayed_navigation = true;
const arena = self.session.transfer_arena;
const navi = try arena.create(DelayedNavigation);
navi.* = .{
Expand Down Expand Up @@ -616,6 +639,30 @@ pub const Page = struct {

try self.navigateFromWebAPI(action, opts);
}

fn elementSubmitForm(self: *Page, element: *parser.Element) !void {
const form = (try self.formForElement(element)) orelse return;
return self.submitForm(@ptrCast(form), @ptrCast(element));
}

fn formForElement(self: *Page, element: *parser.Element) !?*parser.Form {
if (try parser.elementGetAttribute(element, "disabled") != null) {
return null;
}

if (try parser.elementGetAttribute(element, "form")) |form_id| {
const document = parser.documentHTMLToDocument(self.window.document);
const form_element = try parser.documentGetElementById(document, form_id) orelse return null;
if (try parser.elementHTMLGetTagType(@ptrCast(form_element)) == .form) {
return @ptrCast(form_element);
}
return null;
}

const Element = @import("dom/element.zig").Element;
const form = (try Element._closest(element, "form", self)) orelse return null;
return @ptrCast(form);
}
};

const DelayedNavigation = struct {
Expand Down
9 changes: 8 additions & 1 deletion src/browser/session.zig
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,14 @@ pub const Session = struct {
// it isn't null!
std.debug.assert(self.page != null);

defer _ = self.browser.transfer_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 });
defer if (self.page) |*p| {
if (!p.delayed_navigation) {
// If, while loading the page, we intend to navigate to another
// page, then we need to keep the transfer_arena around, as this
// sub-navigation is probably using it.
_ = self.browser.transfer_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 });
}
};

// it's safe to use the transfer arena here, because the page will
// eventually clone the URL using its own page_arena (after it gets
Expand Down
14 changes: 14 additions & 0 deletions src/browser/xhr/form_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
var entries: Entry.List = .empty;
try entries.ensureTotalCapacity(arena, len);

var submitter_included = false;
const submitter_name_ = try getSubmitterName(submitter_);

for (0..len) |i| {
Expand Down Expand Up @@ -295,6 +296,8 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
.key = try std.fmt.allocPrint(arena, "{s}.y", .{name}),
.value = "0",
});

submitter_included = true;
}
}
continue;
Expand All @@ -309,6 +312,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
if (submitter_name_ == null or !std.mem.eql(u8, submitter_name_.?, name)) {
continue;
}
submitter_included = true;
}
const value = (try parser.elementGetAttribute(element, "value")) orelse "";
try entries.append(arena, .{ .key = name, .value = value });
Expand All @@ -326,6 +330,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
if (std.mem.eql(u8, submitter_name, name)) {
const value = (try parser.elementGetAttribute(element, "value")) orelse "";
try entries.append(arena, .{ .key = name, .value = value });
submitter_included = true;
}
},
else => {
Expand All @@ -335,6 +340,15 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
}
}

if (submitter_included == false) {
if (submitter_) |submitter| {
// this can happen if the submitter is outside the form, but associated
// with the form via a form=ID attribute
const value = (try parser.elementGetAttribute(@ptrCast(submitter), "value")) orelse "";
try entries.append(arena, .{ .key = submitter_name_.?, .value = value });
}
}

return entries;
}

Expand Down