Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Fieldset readonly</title>
<link rel="author" title="Teo Eterovic" href="https://github.com/teoeter">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-fieldset-readonly">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<form>
<fieldset id=fs1 readonly>
<legend>
<input type=text id=text_l1>
<textarea id=ta_l1></textarea>
</legend>
<legend>
<input type=text id=text_l2>
</legend>
<input type=text id=text1>
<textarea id=ta1></textarea>
</fieldset>

<fieldset id=fs2 readonly>
<p><legend><input type=text id=text2></legend></p>
<input type=text id=text2b>
</fieldset>

<fieldset id=fs3 readonly>
<fieldset>
<legend><input type=text id=text3></legend>
</fieldset>
<input type=text id=text3b>
</fieldset>
</form>
<script>
test(function () {
assert_true(document.getElementById('fs1').readOnly, "The fieldset IDL readOnly is true");
assert_true(document.getElementById('text1').readOnly, "text input descendant is readonly");
assert_true(document.getElementById('ta1').readOnly, "textarea descendant is readonly");
assert_false(document.getElementById('text_l1').readOnly,
"input in first legend child of fieldset is exempt from readonly");
assert_false(document.getElementById('ta_l1').readOnly,
"textarea in first legend child of fieldset is exempt from readonly");
assert_true(document.getElementById('text_l2').readOnly,
"input in the second legend child is readonly");
}, "The readonly attribute, when specified, causes form control descendants of the fieldset element that support being readonly, excluding those that are descendants of the fieldset element's first legend element child, if any, to be readonly.");

test(function () {
assert_true(document.getElementById('fs2').readOnly, "fs2 is readonly");
assert_true(document.getElementById('text2b').readOnly, "non-legend input is readonly");
assert_true(document.getElementById('text2').readOnly,
"legend not a direct child of fieldset (inside <p>): its input is readonly");
}, "First legend exemption requires legend to be a direct child of fieldset");

test(function () {
assert_true(document.getElementById('fs3').readOnly, "fs3 is readonly");
assert_true(document.getElementById('text3b').readOnly, "non-legend input is readonly");
assert_true(document.getElementById('text3').readOnly,
"legend inside nested fieldset, not a direct child of fs3: its input is readonly");
}, "First legend inside nested fieldset does not exempt its descendants from outer fieldset's readonly");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Fieldset readonly - which control types are affected</title>
<link rel="author" title="Teo Eterovic" href="https://github.com/teoeter">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-fieldset-readonly">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#concept-fe-readonly">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<form>
<fieldset id=fs readonly>
<!-- Input types that support being readonly per spec -->
<input type=text id=i_text>
<input type=search id=i_search>
<input type=url id=i_url>
<input type=tel id=i_tel>
<input type=email id=i_email>
<input type=password id=i_password>
<input type=date id=i_date>
<input type=month id=i_month>
<input type=week id=i_week>
<input type=time id=i_time>
<input type="datetime-local" id=i_dtl>
<input type=number id=i_number>
<textarea id=i_ta></textarea>

<!-- Controls that do NOT support readonly per spec -->
<input type=checkbox id=i_checkbox>
<input type=radio id=i_radio>
<input type=file id=i_file>
<input type=submit id=i_submit>
<input type=reset id=i_reset>
<input type=button id=i_button>
<input type=image id=i_image>
<input type=range id=i_range>
<input type=color id=i_color>
<input type=hidden id=i_hidden>
<button id=i_btn>click</button>
<select id=i_sel><option>a</option></select>
</fieldset>
</form>
<script>
// Per spec, only inputs in states that support readonly, textarea, and FACE
// are affected by the readonly fieldset propagation.
const affected = ['i_text', 'i_search', 'i_url', 'i_tel', 'i_email', 'i_password',
'i_date', 'i_month', 'i_week', 'i_time', 'i_dtl', 'i_number', 'i_ta'];

// The IDL readOnly attribute on these inputs reflects their own content
// attribute. They have no `readonly` content attribute, and the fieldset
// propagation does not apply to these states/types.
const unaffected = ['i_checkbox', 'i_radio', 'i_file', 'i_submit', 'i_reset',
'i_button', 'i_image', 'i_range', 'i_color', 'i_hidden'];

for (const id of affected) {
test(function () {
assert_true(document.getElementById(id).readOnly,
`#${id} should be readonly inside readonly fieldset`);
}, `${id.replace('i_', '')} is read-only inside a readonly fieldset`);
}

for (const id of unaffected) {
test(function () {
// readOnly IDL attribute exists on HTMLInputElement but reflects only its
// own content attribute, which we did not set. The fieldset propagation
// must not flip it on for these types.
assert_false(document.getElementById(id).readOnly,
`#${id} should not have readOnly set`);
}, `${id.replace('i_', '')} input is unaffected by readonly fieldset (readOnly stays false)`);
}

test(function () {
// <button> has no readOnly IDL attribute
assert_false('readOnly' in document.getElementById('i_btn'),
"<button> has no readOnly IDL attribute");
}, "button is unaffected by readonly fieldset");

test(function () {
// <select> has no readOnly IDL attribute
assert_false('readOnly' in document.getElementById('i_sel'),
"<select> has no readOnly IDL attribute");
}, "select is unaffected by readonly fieldset");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Fieldset readonly - nested fieldsets</title>
<link rel="author" title="Teo Eterovic" href="https://github.com/teoeter">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-fieldset-readonly">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<form>
<!-- Outer readonly fieldset contains inner fieldset (no readonly attr) -->
<fieldset id=outer readonly>
<legend><input type=text id=outer_legend_input></legend>
<fieldset id=inner>
<legend><input type=text id=inner_legend_input></legend>
<input type=text id=inner_input>
<textarea id=inner_ta></textarea>
</fieldset>
<input type=text id=outer_input>
</fieldset>

<!-- Inner readonly fieldset inside non-readonly outer fieldset -->
<fieldset id=outer2>
<fieldset id=inner2 readonly>
<input type=text id=inner2_input>
</fieldset>
<input type=text id=outer2_input>
</fieldset>

<!-- Nested readonly fieldsets — outer legend should exempt one level -->
<fieldset id=outer3 readonly>
<legend>
<fieldset id=inner3>
<input type=text id=inner3_input>
</fieldset>
</legend>
<input type=text id=outer3_input>
</fieldset>
</form>
<script>
test(function () {
assert_true(document.getElementById('outer').readOnly, "outer fieldset is readonly");
assert_false(document.getElementById('inner').readOnly, "inner fieldset IDL is not readonly");
assert_true(document.getElementById('inner_input').readOnly,
"input in nested fieldset (descendant of readonly outer) is readonly");
assert_true(document.getElementById('inner_ta').readOnly,
"textarea in nested fieldset is readonly");
assert_true(document.getElementById('outer_input').readOnly,
"direct child input of outer readonly fieldset is readonly");
}, "Outer readonly fieldset propagates to descendants inside inner non-readonly fieldset");

test(function () {
// inner legend of outer is the first legend child of outer — its descendants are exempt
assert_false(document.getElementById('outer_legend_input').readOnly,
"input in first legend of outer fieldset is exempt");
// inner fieldset's legend is not a child of outer
assert_true(document.getElementById('inner_legend_input').readOnly,
"input in inner fieldset's legend is still readonly (not exempt from outer)");
}, "Legend exemption does not extend to legends of nested fieldsets");

test(function () {
assert_false(document.getElementById('outer2').readOnly, "outer2 is not readonly");
assert_true(document.getElementById('inner2').readOnly, "inner2 fieldset is readonly");
assert_true(document.getElementById('inner2_input').readOnly,
"input inside inner2 readonly fieldset is readonly");
assert_false(document.getElementById('outer2_input').readOnly,
"input in outer2 (non-readonly) fieldset is not readonly");
}, "Inner readonly fieldset only makes its own descendants readonly");

test(function () {
// inner3 is inside the first legend child of outer3 — it and its descendants are exempt
assert_false(document.getElementById('inner3_input').readOnly,
"input inside fieldset in first legend child is exempt from outer readonly");
assert_true(document.getElementById('outer3_input').readOnly,
"input outside legend is readonly");
}, "Fieldset inside first legend child is fully exempt from outer readonly");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Fieldset readonly - formReadonlyCallback on form-associated custom elements</title>
<link rel="author" title="Teo Eterovic" href="https://github.com/teoeter">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-fieldset-readonly">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
customElements.define('x-face', class extends HTMLElement {
static formAssociated = true;
constructor() {
super();
this._internals = this.attachInternals();
this.readonlyCalls = [];
}
formReadonlyCallback(isReadOnly) {
this.readonlyCalls.push(isReadOnly);
}
});

// Wait for any pending custom-element reaction queue to flush. A microtask
// is sufficient: form-associated callbacks are dispatched via the custom
// element reactions algorithm, which runs at microtask boundaries.
function flush() {
return Promise.resolve();
}
</script>
<form>
<fieldset id=fs1>
<x-face id=face1></x-face>
</fieldset>

<fieldset id=fs2 readonly>
<legend><x-face id=face_in_legend></x-face></legend>
<x-face id=face_outside_legend></x-face>
</fieldset>
</form>
<script>
promise_test(async function () {
const fs = document.getElementById('fs1');
const face = document.getElementById('face1');

// Initially fs1 has no readonly attribute.
await flush();
assert_equals(face.readonlyCalls.length, 0, "no calls before any change");

fs.setAttribute('readonly', '');
await flush();
assert_equals(face.readonlyCalls.length, 1, "callback fired once on becoming readonly");
assert_true(face.readonlyCalls[0], "argument is true when becoming readonly");

fs.removeAttribute('readonly');
await flush();
assert_equals(face.readonlyCalls.length, 2, "callback fired again on becoming non-readonly");
assert_false(face.readonlyCalls[1], "argument is false when no longer readonly");
}, "formReadonlyCallback fires when fieldset readonly attribute is added/removed");

promise_test(async function () {
const faceOutside = document.getElementById('face_outside_legend');
const faceInLegend = document.getElementById('face_in_legend');

await flush();
// The FACE outside the legend should have been told it's readonly.
assert_true(faceOutside.readonlyCalls.includes(true),
"FACE outside legend received formReadonlyCallback(true)");
// The FACE inside the first legend child must NOT have been made readonly.
assert_false(faceInLegend.readonlyCalls.includes(true),
"FACE in first legend child did not receive formReadonlyCallback(true)");
}, "formReadonlyCallback respects the first-legend-child exemption");

promise_test(async function () {
// Toggling the attribute on a fieldset whose only FACE is inside the
// first legend child should not trigger the callback on that FACE.
const fs = document.getElementById('fs2');
const faceInLegend = document.getElementById('face_in_legend');
const before = faceInLegend.readonlyCalls.length;

fs.removeAttribute('readonly');
await flush();
fs.setAttribute('readonly', '');
await flush();

assert_equals(faceInLegend.readonlyCalls.length, before,
"FACE in legend gets no callback on readonly toggle");
}, "Toggling readonly does not call formReadonlyCallback on FACE inside first legend");
</script>
Loading