mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
forms: skip FormData entry for <select> with no selectedness candidate
Per the HTML Living Standard "constructing the form data set" algorithm, a <select> element appends one entry per option whose selectedness is true. A non-multiple <select> derives its selectedness from an explicitly-selected option, falling back to the first non-disabled option in tree order. With zero options (or only disabled options), no option is selected, so no entry should be appended. The singular-select fast path in collectForm previously broke from the block with select.getValue(frame) regardless of whether any option existed, causing FormData to emit a phantom (name, "") entry for an empty <select>. Guard the fast path on the existence of at least one non-disabled <option> child and skip the entry otherwise. Closes #2262
This commit is contained in:
@@ -813,6 +813,72 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=selectWithoutOptions>
|
||||
{
|
||||
// Per HTML spec (constructing form data set, step 5.5), a <select> only
|
||||
// appends entries for options whose selectedness is true. With zero options,
|
||||
// nothing is selected and no entry is appended.
|
||||
const form = document.createElement('form');
|
||||
|
||||
const sel = document.createElement('select');
|
||||
sel.name = 'choice';
|
||||
form.appendChild(sel);
|
||||
|
||||
const sibling = document.createElement('input');
|
||||
sibling.name = 'sibling';
|
||||
sibling.value = 'kept';
|
||||
form.appendChild(sibling);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual(false, fd.has('choice'));
|
||||
testing.expectEqual(null, fd.get('choice'));
|
||||
testing.expectEqual([], fd.getAll('choice'));
|
||||
testing.expectEqual('kept', fd.get('sibling'));
|
||||
testing.expectEqual([['sibling', 'kept']], Array.from(fd.entries()));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=selectMultipleWithoutOptions>
|
||||
{
|
||||
// Same rule for <select multiple> — with zero options, no entry.
|
||||
const form = document.createElement('form');
|
||||
|
||||
const sel = document.createElement('select');
|
||||
sel.name = 'tags';
|
||||
sel.multiple = true;
|
||||
form.appendChild(sel);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual(false, fd.has('tags'));
|
||||
testing.expectEqual([], fd.getAll('tags'));
|
||||
testing.expectEqual([], Array.from(fd.entries()));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=selectAllOptionsDisabled>
|
||||
{
|
||||
// A single-select with only disabled options has no auto-selected option,
|
||||
// so no entry is appended.
|
||||
const form = document.createElement('form');
|
||||
|
||||
const sel = document.createElement('select');
|
||||
sel.name = 'mode';
|
||||
|
||||
const opt = document.createElement('option');
|
||||
opt.value = 'off';
|
||||
opt.disabled = true;
|
||||
opt.textContent = 'off';
|
||||
sel.appendChild(opt);
|
||||
|
||||
form.appendChild(sel);
|
||||
|
||||
const fd = new FormData(form);
|
||||
testing.expectEqual(false, fd.has('mode'));
|
||||
testing.expectEqual(null, fd.get('mode'));
|
||||
testing.expectEqual([], fd.getAll('mode'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=formDataEventModifyFormData>
|
||||
{
|
||||
// Listeners can modify formData during the event
|
||||
|
||||
@@ -196,6 +196,17 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, frame: *F
|
||||
|
||||
if (element.is(Form.Select)) |select| {
|
||||
if (select.getMultiple() == false) {
|
||||
// Per the HTML spec, a single-select's selectedness comes
|
||||
// from an explicitly-selected option, falling back to the
|
||||
// first non-disabled option in tree order. With no options
|
||||
// (or only disabled ones), nothing is selected and no
|
||||
// entry is appended.
|
||||
var children = select.asNode().childrenIterator();
|
||||
const has_candidate = while (children.next()) |child| {
|
||||
const option = child.is(Form.Select.Option) orelse continue;
|
||||
if (!option.getDisabled()) break true;
|
||||
} else false;
|
||||
if (!has_candidate) continue;
|
||||
break :blk select.getValue(frame);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user