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>
|
||||||
|
|
||||||
|
<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>
|
<script id=imageWithoutName>
|
||||||
{
|
{
|
||||||
// Test that image input without name still submits x and y coordinates
|
// 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 js = @import("../../js/js.zig");
|
||||||
const Page = @import("../../Page.zig");
|
const Page = @import("../../Page.zig");
|
||||||
|
const Node = @import("../Node.zig");
|
||||||
const Form = @import("../element/html/Form.zig");
|
const Form = @import("../element/html/Form.zig");
|
||||||
const Element = @import("../Element.zig");
|
const Element = @import("../Element.zig");
|
||||||
const KeyValueList = @import("../KeyValueList.zig");
|
const KeyValueList = @import("../KeyValueList.zig");
|
||||||
@@ -124,12 +125,17 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa
|
|||||||
var list: KeyValueList = .empty;
|
var list: KeyValueList = .empty;
|
||||||
const form = form_ orelse return list;
|
const form = form_ orelse return list;
|
||||||
|
|
||||||
|
const form_node = form.asNode();
|
||||||
|
|
||||||
var elements = try form.getElements(page);
|
var elements = try form.getElements(page);
|
||||||
var it = try elements.iterator();
|
var it = try elements.iterator();
|
||||||
while (it.next()) |element| {
|
while (it.next()) |element| {
|
||||||
if (element.getAttributeSafe(comptime .wrap("disabled")) != null) {
|
if (element.getAttributeSafe(comptime .wrap("disabled")) != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (isDisabledByFieldset(element, form_node)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle image submitters first - they can submit without a name
|
// Handle image submitters first - they can submit without a name
|
||||||
if (element.is(Form.Input)) |input| {
|
if (element.is(Form.Input)) |input| {
|
||||||
@@ -196,6 +202,41 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa
|
|||||||
return list;
|
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 JsApi = struct {
|
||||||
pub const bridge = js.Bridge(FormData);
|
pub const bridge = js.Bridge(FormData);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user