mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-21 20:24:42 +00:00
Merge pull request #1891 from lightpanda-io/form-requestSubmit
Some checks failed
zig-test / zig fmt (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
e2e-integration-test / zig build release (push) Has been cancelled
e2e-integration-test / demo-integration-scripts (push) Has been cancelled
Some checks failed
zig-test / zig fmt (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
e2e-integration-test / zig build release (push) Has been cancelled
e2e-integration-test / demo-integration-scripts (push) Has been cancelled
Implement Form.requestSubmit
This commit is contained in:
@@ -343,3 +343,123 @@
|
|||||||
testing.expectEqual('', form.elements['choice'].value)
|
testing.expectEqual('', form.elements['choice'].value)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Test: requestSubmit() fires the submit event (unlike submit()) -->
|
||||||
|
<form id="test_form2" action="/should-not-navigate2" method="get">
|
||||||
|
<input name="q" value="test2">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script id="requestSubmit_fires_submit_event">
|
||||||
|
{
|
||||||
|
const form = $('#test_form2');
|
||||||
|
let submitFired = false;
|
||||||
|
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
submitFired = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
form.requestSubmit();
|
||||||
|
|
||||||
|
testing.expectEqual(true, submitFired);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Test: requestSubmit() with preventDefault stops navigation -->
|
||||||
|
<form id="test_form3" action="/should-not-navigate3" method="get">
|
||||||
|
<input name="q" value="test3">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script id="requestSubmit_respects_preventDefault">
|
||||||
|
{
|
||||||
|
const form = $('#test_form3');
|
||||||
|
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
form.requestSubmit();
|
||||||
|
|
||||||
|
// Form submission was prevented, so no navigation should be scheduled
|
||||||
|
testing.expectEqual(true, true);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Test: requestSubmit() with non-submit-button submitter throws TypeError -->
|
||||||
|
<form id="test_form_rs1" action="/should-not-navigate4" method="get">
|
||||||
|
<input id="rs1_text" type="text" name="q" value="test">
|
||||||
|
<input id="rs1_submit" type="submit" value="Go">
|
||||||
|
<input id="rs1_image" type="image" src="x.png">
|
||||||
|
<button id="rs1_btn_submit" type="submit">Submit</button>
|
||||||
|
<button id="rs1_btn_reset" type="reset">Reset</button>
|
||||||
|
<button id="rs1_btn_button" type="button">Button</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script id="requestSubmit_rejects_non_submit_button">
|
||||||
|
{
|
||||||
|
const form = $('#test_form_rs1');
|
||||||
|
form.addEventListener('submit', (e) => e.preventDefault());
|
||||||
|
|
||||||
|
// A text input is not a submit button — should throw TypeError
|
||||||
|
testing.expectError('TypeError', () => {
|
||||||
|
form.requestSubmit($('#rs1_text'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// A reset button is not a submit button — should throw TypeError
|
||||||
|
testing.expectError('TypeError', () => {
|
||||||
|
form.requestSubmit($('#rs1_btn_reset'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// A <button type="button"> is not a submit button — should throw TypeError
|
||||||
|
testing.expectError('TypeError', () => {
|
||||||
|
form.requestSubmit($('#rs1_btn_button'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// A <div> is not a submit button — should throw TypeError
|
||||||
|
const div = document.createElement('div');
|
||||||
|
form.appendChild(div);
|
||||||
|
testing.expectError('TypeError', () => {
|
||||||
|
form.requestSubmit(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Test: requestSubmit() accepts valid submit buttons -->
|
||||||
|
<script id="requestSubmit_accepts_submit_buttons">
|
||||||
|
{
|
||||||
|
const form = $('#test_form_rs1');
|
||||||
|
let submitCount = 0;
|
||||||
|
form.addEventListener('submit', (e) => { e.preventDefault(); submitCount++; });
|
||||||
|
|
||||||
|
// <input type="submit"> is a valid submitter
|
||||||
|
form.requestSubmit($('#rs1_submit'));
|
||||||
|
testing.expectEqual(1, submitCount);
|
||||||
|
|
||||||
|
// <input type="image"> is a valid submitter
|
||||||
|
form.requestSubmit($('#rs1_image'));
|
||||||
|
testing.expectEqual(2, submitCount);
|
||||||
|
|
||||||
|
// <button type="submit"> is a valid submitter
|
||||||
|
form.requestSubmit($('#rs1_btn_submit'));
|
||||||
|
testing.expectEqual(3, submitCount);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Test: requestSubmit() with submitter not owned by form throws NotFoundError -->
|
||||||
|
<form id="test_form_rs2" action="/should-not-navigate5" method="get">
|
||||||
|
<input type="text" name="q" value="test">
|
||||||
|
</form>
|
||||||
|
<form id="test_form_rs3">
|
||||||
|
<input id="rs3_submit" type="submit" value="Other Submit">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script id="requestSubmit_rejects_wrong_form_submitter">
|
||||||
|
{
|
||||||
|
const form = $('#test_form_rs2');
|
||||||
|
|
||||||
|
// Submit button belongs to a different form — should throw NotFoundError
|
||||||
|
testing.expectError('NotFoundError', () => {
|
||||||
|
form.requestSubmit($('#rs3_submit'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -117,6 +117,47 @@ pub fn submit(self: *Form, page: *Page) !void {
|
|||||||
return page.submitForm(null, self, .{ .fire_event = false });
|
return page.submitForm(null, self, .{ .fire_event = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/forms.html#dom-form-requestsubmit
|
||||||
|
/// Like submit(), but fires the submit event and validates the form.
|
||||||
|
pub fn requestSubmit(self: *Form, submitter: ?*Element, page: *Page) !void {
|
||||||
|
const submitter_element = if (submitter) |s| blk: {
|
||||||
|
// The submitter must be a submit button.
|
||||||
|
if (!isSubmitButton(s)) return error.TypeError;
|
||||||
|
|
||||||
|
// The submitter's form owner must be this form element.
|
||||||
|
const submitter_form = getFormOwner(s, page);
|
||||||
|
if (submitter_form == null or submitter_form.? != self) return error.NotFound;
|
||||||
|
|
||||||
|
break :blk s;
|
||||||
|
} else self.asElement();
|
||||||
|
|
||||||
|
return page.submitForm(submitter_element, self, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the element is a submit button per the HTML spec:
|
||||||
|
/// - <input type="submit"> or <input type="image">
|
||||||
|
/// - <button type="submit"> (including default, since button's default type is "submit")
|
||||||
|
fn isSubmitButton(element: *Element) bool {
|
||||||
|
if (element.is(Input)) |input| {
|
||||||
|
return input._input_type == .submit or input._input_type == .image;
|
||||||
|
}
|
||||||
|
if (element.is(Button)) |button| {
|
||||||
|
return std.mem.eql(u8, button.getType(), "submit");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the form owner of a submittable element (Input or Button).
|
||||||
|
fn getFormOwner(element: *Element, page: *Page) ?*Form {
|
||||||
|
if (element.is(Input)) |input| {
|
||||||
|
return input.getForm(page);
|
||||||
|
}
|
||||||
|
if (element.is(Button)) |button| {
|
||||||
|
return button.getForm(page);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(Form);
|
pub const bridge = js.Bridge(Form);
|
||||||
pub const Meta = struct {
|
pub const Meta = struct {
|
||||||
@@ -132,6 +173,7 @@ pub const JsApi = struct {
|
|||||||
pub const elements = bridge.accessor(Form.getElements, null, .{});
|
pub const elements = bridge.accessor(Form.getElements, null, .{});
|
||||||
pub const length = bridge.accessor(Form.getLength, null, .{});
|
pub const length = bridge.accessor(Form.getLength, null, .{});
|
||||||
pub const submit = bridge.function(Form.submit, .{});
|
pub const submit = bridge.function(Form.submit, .{});
|
||||||
|
pub const requestSubmit = bridge.function(Form.requestSubmit, .{ .dom_exception = true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../../../testing.zig");
|
const testing = @import("../../../../testing.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user