mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1600 from lightpanda-io/formdata_disabled_fieldset
FormData recognizes (and skips over) disabled fieldsets
This commit is contained in:
@@ -602,6 +602,114 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=disabledFieldset>
|
||||
{
|
||||
// Elements inside a disabled fieldset should not be included
|
||||
const form = document.createElement('form');
|
||||
|
||||
const fieldset = document.createElement('fieldset');
|
||||
fieldset.disabled = true;
|
||||
|
||||
const inside = document.createElement('input');
|
||||
inside.name = 'inside';
|
||||
inside.value = 'nope';
|
||||
fieldset.appendChild(inside);
|
||||
|
||||
const outside = document.createElement('input');
|
||||
outside.name = 'outside';
|
||||
outside.value = 'yes';
|
||||
|
||||
form.appendChild(fieldset);
|
||||
form.appendChild(outside);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual(null, fd.get('inside'));
|
||||
testing.expectEqual('yes', fd.get('outside'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=disabledFieldsetLegendExemption>
|
||||
{
|
||||
// Elements inside the FIRST legend of a disabled fieldset are NOT disabled
|
||||
const form = document.createElement('form');
|
||||
|
||||
const fieldset = document.createElement('fieldset');
|
||||
fieldset.disabled = true;
|
||||
|
||||
const legend = document.createElement('legend');
|
||||
const inLegend = document.createElement('input');
|
||||
inLegend.name = 'in-legend';
|
||||
inLegend.value = 'exempt';
|
||||
legend.appendChild(inLegend);
|
||||
|
||||
const notInLegend = document.createElement('input');
|
||||
notInLegend.name = 'not-in-legend';
|
||||
notInLegend.value = 'nope';
|
||||
|
||||
fieldset.appendChild(legend);
|
||||
fieldset.appendChild(notInLegend);
|
||||
form.appendChild(fieldset);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual('exempt', fd.get('in-legend'));
|
||||
testing.expectEqual(null, fd.get('not-in-legend'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=disabledFieldsetSecondLegend>
|
||||
{
|
||||
// Only the FIRST legend gets the exemption; second legend inputs are still disabled
|
||||
const form = document.createElement('form');
|
||||
|
||||
const fieldset = document.createElement('fieldset');
|
||||
fieldset.disabled = true;
|
||||
|
||||
const legend1 = document.createElement('legend');
|
||||
const inLegend1 = document.createElement('input');
|
||||
inLegend1.name = 'first-legend';
|
||||
inLegend1.value = 'exempt';
|
||||
legend1.appendChild(inLegend1);
|
||||
|
||||
const legend2 = document.createElement('legend');
|
||||
const inLegend2 = document.createElement('input');
|
||||
inLegend2.name = 'second-legend';
|
||||
inLegend2.value = 'nope';
|
||||
legend2.appendChild(inLegend2);
|
||||
|
||||
fieldset.appendChild(legend1);
|
||||
fieldset.appendChild(legend2);
|
||||
form.appendChild(fieldset);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual('exempt', fd.get('first-legend'));
|
||||
testing.expectEqual(null, fd.get('second-legend'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=disabledFieldsetNested>
|
||||
{
|
||||
// Outer fieldset disabled: inner enabled fieldset's elements are still disabled
|
||||
const form = document.createElement('form');
|
||||
|
||||
const outer = document.createElement('fieldset');
|
||||
outer.disabled = true;
|
||||
|
||||
const inner = document.createElement('fieldset');
|
||||
// inner is NOT disabled itself
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.name = 'deep';
|
||||
input.value = 'nope';
|
||||
inner.appendChild(input);
|
||||
|
||||
outer.appendChild(inner);
|
||||
form.appendChild(outer);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual(null, fd.get('deep'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=imageWithoutName>
|
||||
{
|
||||
// Test that image input without name still submits x and y coordinates
|
||||
|
||||
@@ -22,6 +22,7 @@ const log = @import("../../../log.zig");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const Node = @import("../Node.zig");
|
||||
const Form = @import("../element/html/Form.zig");
|
||||
const Element = @import("../Element.zig");
|
||||
const KeyValueList = @import("../KeyValueList.zig");
|
||||
@@ -124,12 +125,17 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa
|
||||
var list: KeyValueList = .empty;
|
||||
const form = form_ orelse return list;
|
||||
|
||||
const form_node = form.asNode();
|
||||
|
||||
var elements = try form.getElements(page);
|
||||
var it = try elements.iterator();
|
||||
while (it.next()) |element| {
|
||||
if (element.getAttributeSafe(comptime .wrap("disabled")) != null) {
|
||||
continue;
|
||||
}
|
||||
if (isDisabledByFieldset(element, form_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle image submitters first - they can submit without a name
|
||||
if (element.is(Form.Input)) |input| {
|
||||
@@ -196,6 +202,41 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa
|
||||
return list;
|
||||
}
|
||||
|
||||
// Returns true if `element` is disabled by an ancestor <fieldset disabled>,
|
||||
// stopping the upward walk when the form node is reached.
|
||||
// Per spec, elements inside the first <legend> child of a disabled fieldset
|
||||
// are NOT disabled by that fieldset.
|
||||
fn isDisabledByFieldset(element: *Element, form_node: *Node) bool {
|
||||
const element_node = element.asNode();
|
||||
var current: ?*Node = element_node._parent;
|
||||
while (current) |node| {
|
||||
// Stop at the form boundary (common case optimisation)
|
||||
if (node == form_node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current = node._parent;
|
||||
const el = node.is(Element) orelse continue;
|
||||
|
||||
if (el.getTag() == .fieldset and el.getAttributeSafe(comptime .wrap("disabled")) != null) {
|
||||
// Check if `element` is inside the first <legend> child of this fieldset
|
||||
var child = el.firstElementChild();
|
||||
while (child) |c| {
|
||||
if (c.getTag() == .legend) {
|
||||
// Found the first legend; exempt if element is a descendant
|
||||
if (c.asNode().contains(element_node)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
child = c.nextElementSibling();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(FormData);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user