Skip to content

Commit c80deeb

Browse files
authored
Merge pull request #738 from lightpanda-io/buttons_submit_form
Submit input and button submits can now submit forms
2 parents 1b87f96 + 4644e55 commit c80deeb

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

src/browser/html/form.zig

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ pub const HTMLFormElement = struct {
3232
return page.submitForm(self, null);
3333
}
3434

35-
pub fn _requestSubmit(self: *parser.Form) !void {
36-
try parser.formElementSubmit(self);
37-
}
38-
3935
pub fn _reset(self: *parser.Form) !void {
4036
try parser.formElementReset(self);
4137
}

src/browser/page.zig

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ pub const Page = struct {
9292
// current_script could by fetch module to resolve module's url to fetch.
9393
current_script: ?*const Script = null,
9494

95+
// indicates intention to navigate to another page on the next loop execution.
96+
delayed_navigation: bool = false,
97+
9598
pub fn init(self: *Page, arena: Allocator, session: *Session) !void {
9699
const browser = session.browser;
97100
self.* = .{
@@ -553,6 +556,25 @@ pub const Page = struct {
553556
const href = (try parser.elementGetAttribute(element, "href")) orelse return;
554557
try self.navigateFromWebAPI(href, .{});
555558
},
559+
.input => {
560+
const element: *parser.Element = @ptrCast(node);
561+
const input_type = (try parser.elementGetAttribute(element, "type")) orelse return;
562+
if (std.ascii.eqlIgnoreCase(input_type, "submit")) {
563+
return self.elementSubmitForm(element);
564+
}
565+
},
566+
.button => {
567+
const element: *parser.Element = @ptrCast(node);
568+
const button_type = (try parser.elementGetAttribute(element, "type")) orelse return;
569+
if (std.ascii.eqlIgnoreCase(button_type, "submit")) {
570+
return self.elementSubmitForm(element);
571+
}
572+
if (std.ascii.eqlIgnoreCase(button_type, "reset")) {
573+
if (try self.formForElement(element)) |form| {
574+
return parser.formElementReset(form);
575+
}
576+
}
577+
},
556578
else => {},
557579
}
558580
}
@@ -561,6 +583,7 @@ pub const Page = struct {
561583
// The page.arena is safe to use here, but the transfer_arena exists
562584
// specifically for this type of lifetime.
563585
pub fn navigateFromWebAPI(self: *Page, url: []const u8, opts: NavigateOpts) !void {
586+
self.delayed_navigation = true;
564587
const arena = self.session.transfer_arena;
565588
const navi = try arena.create(DelayedNavigation);
566589
navi.* = .{
@@ -616,6 +639,30 @@ pub const Page = struct {
616639

617640
try self.navigateFromWebAPI(action, opts);
618641
}
642+
643+
fn elementSubmitForm(self: *Page, element: *parser.Element) !void {
644+
const form = (try self.formForElement(element)) orelse return;
645+
return self.submitForm(@ptrCast(form), @ptrCast(element));
646+
}
647+
648+
fn formForElement(self: *Page, element: *parser.Element) !?*parser.Form {
649+
if (try parser.elementGetAttribute(element, "disabled") != null) {
650+
return null;
651+
}
652+
653+
if (try parser.elementGetAttribute(element, "form")) |form_id| {
654+
const document = parser.documentHTMLToDocument(self.window.document);
655+
const form_element = try parser.documentGetElementById(document, form_id) orelse return null;
656+
if (try parser.elementHTMLGetTagType(@ptrCast(form_element)) == .form) {
657+
return @ptrCast(form_element);
658+
}
659+
return null;
660+
}
661+
662+
const Element = @import("dom/element.zig").Element;
663+
const form = (try Element._closest(element, "form", self)) orelse return null;
664+
return @ptrCast(form);
665+
}
619666
};
620667

621668
const DelayedNavigation = struct {

src/browser/session.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,14 @@ pub const Session = struct {
128128
// it isn't null!
129129
std.debug.assert(self.page != null);
130130

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

133140
// it's safe to use the transfer arena here, because the page will
134141
// eventually clone the URL using its own page_arena (after it gets

src/browser/xhr/form_data.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
268268
var entries: Entry.List = .empty;
269269
try entries.ensureTotalCapacity(arena, len);
270270

271+
var submitter_included = false;
271272
const submitter_name_ = try getSubmitterName(submitter_);
272273

273274
for (0..len) |i| {
@@ -295,6 +296,8 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
295296
.key = try std.fmt.allocPrint(arena, "{s}.y", .{name}),
296297
.value = "0",
297298
});
299+
300+
submitter_included = true;
298301
}
299302
}
300303
continue;
@@ -309,6 +312,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
309312
if (submitter_name_ == null or !std.mem.eql(u8, submitter_name_.?, name)) {
310313
continue;
311314
}
315+
submitter_included = true;
312316
}
313317
const value = (try parser.elementGetAttribute(element, "value")) orelse "";
314318
try entries.append(arena, .{ .key = name, .value = value });
@@ -326,6 +330,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
326330
if (std.mem.eql(u8, submitter_name, name)) {
327331
const value = (try parser.elementGetAttribute(element, "value")) orelse "";
328332
try entries.append(arena, .{ .key = name, .value = value });
333+
submitter_included = true;
329334
}
330335
},
331336
else => {
@@ -335,6 +340,15 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page
335340
}
336341
}
337342

343+
if (submitter_included == false) {
344+
if (submitter_) |submitter| {
345+
// this can happen if the submitter is outside the form, but associated
346+
// with the form via a form=ID attribute
347+
const value = (try parser.elementGetAttribute(@ptrCast(submitter), "value")) orelse "";
348+
try entries.append(arena, .{ .key = submitter_name_.?, .value = value });
349+
}
350+
}
351+
338352
return entries;
339353
}
340354

0 commit comments

Comments
 (0)