Automatic update from web-platform-tests textarea: Always add a placeholder break to empty elements This CL ensures that empty TEXTAREA elements always contain a placeholder break element. This provides a consistent line-height and baseline, particularly for non-editable textareas (e.g., disabled, read-only, or within an inert subtree) which previously might have collapsed or had incorrect layout metrics. Although the placeholder break is primarily needed for non-editable states, it is added unconditionally because an element's editability can change dynamically (e.g., via the 'inert' attribute on an ancestor), making conditional management complex. Implementation details: - HTMLTextAreaElement: Injects the placeholder break into the inner editor upon creation if the value is empty. - TextControlElement: Manages the lifecycle of the placeholder break when the value changes. - RemoveNodeCommand: Updated to correctly handle 'ref_child' when it points to a placeholder break, ensuring correct undo/redo behavior. - SelectionController: Updated 'IsEditableBoxEmpty' to check the actual text content instead of just 'HasChildren()', accounting for the internal placeholder break. trees. It affects <textarea> with a value ending with `\n`. AX-Relnotes: Placeholder break elements are dropped from accessibility Bug: 510170707 Change-Id: I5871a6890ceb50945b8995293fcbaafa7c6e25c5 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7875830 Reviewed-by: David Tseng <dtseng@chromium.org> Commit-Queue: Kent Tamura <tkent@chromium.org> Reviewed-by: Koji Ishii <kojii@chromium.org> Cr-Commit-Position: refs/heads/main@{#1645788} -- wpt-commits: 640522942a690dae86b3deba5abaae021704e9a3 wpt-pr: 60569
153 lines
6.0 KiB
HTML
153 lines
6.0 KiB
HTML
<!DOCTYPE html>
|
|
<link rel="help" href="https://drafts.csswg.org/css-ui-4/#field-sizing">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<style>
|
|
textarea {
|
|
font-family: monospace;
|
|
}
|
|
|
|
.disable-default {
|
|
field-sizing: content;
|
|
}
|
|
|
|
.small-placeholder {
|
|
font-size: 32px;
|
|
}
|
|
.small-placeholder::placeholder {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.large-placeholder {
|
|
font-size: 20px;
|
|
}
|
|
.large-placeholder::placeholder {
|
|
font-size: 40px;
|
|
}
|
|
</style>
|
|
<body>
|
|
<div id="container"></div>
|
|
<script>
|
|
const DISABLE = 'class="disable-default"';
|
|
const LONG_TEXT = 'The quick brown fox jumps over the lazy dog.';
|
|
|
|
function addElements(source) {
|
|
const container = document.querySelector('#container');
|
|
container.innerHTML = source + source;
|
|
container.lastElementChild.classList.add('disable-default');
|
|
return {
|
|
fixed: container.firstElementChild,
|
|
content: container.lastElementChild
|
|
};
|
|
}
|
|
|
|
function addTwoElements(source1, source2) {
|
|
const container = document.querySelector('#container');
|
|
container.innerHTML = source1 + source2;
|
|
return {
|
|
e1: container.firstElementChild,
|
|
e2: container.lastElementChild
|
|
};
|
|
}
|
|
|
|
test(() => {
|
|
let pair = addElements('<textarea></textarea>');
|
|
// Historically a <textarea> has approximately 20ch x 2lh size by default.
|
|
// A <textarea> with field-sizing:content must be samller than the default.
|
|
assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_less_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addTwoElements(`<textarea ${DISABLE}></textarea>`, `<textarea ${DISABLE}>foo</textarea>`);
|
|
assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
|
|
assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
|
|
}, 'Empty value');
|
|
|
|
test(() => {
|
|
let pair = addElements('<textarea cols="10" rows="3"></textarea>');
|
|
assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_less_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
}, 'Empty value with cols/rows');
|
|
|
|
test(() => {
|
|
let pair = addTwoElements(`<textarea ${DISABLE} disabled></textarea>`,
|
|
`<textarea ${DISABLE}></textarea>`);
|
|
assert_equals(pair.e1.offsetWidth, pair.e2.offsetWidth, 'width');
|
|
assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight, 'height');
|
|
}, 'Disabled textarea with an empty value should have one-line height');
|
|
|
|
test(() => {
|
|
let pair = addElements(`<textarea>${LONG_TEXT}</textarea>`);
|
|
assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
pair = addElements(`<textarea placeholder="${LONG_TEXT}"></textarea>`);
|
|
assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
}, 'Auto width and auto height with a long text');
|
|
|
|
test(() => {
|
|
let pair = addElements('<textarea style="width:20ch; height:2lh"></textarea>');
|
|
assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addElements(`<textarea style="width:20ch; height:2lh">${LONG_TEXT}</textarea>`);
|
|
assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addElements(`<textarea style="width:20ch; height:2lh" placeholder="${LONG_TEXT}"></textarea>`);
|
|
assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
}, 'Explicit width and heigth');
|
|
|
|
test(() => {
|
|
let pair = addElements('<textarea style="width:20ch"></textarea>');
|
|
assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_less_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addElements(`<textarea style="width:20ch">${LONG_TEXT}</textarea>`);
|
|
assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_greater_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addElements(`<textarea style="width:20ch" placeholder="${LONG_TEXT}"></textarea>`);
|
|
assert_equals(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_greater_than(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
}, 'Explicit width and auto height');
|
|
|
|
test(() => {
|
|
let pair = addElements('<textarea style="height:2lh"></textarea>');
|
|
assert_less_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addElements(`<textarea style="height:2lh">${LONG_TEXT}</textarea>`);
|
|
assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
|
|
pair = addElements(`<textarea style="height:2lh" placeholder="${LONG_TEXT}"></textarea>`);
|
|
assert_greater_than(pair.content.offsetWidth, pair.fixed.offsetWidth);
|
|
assert_equals(pair.content.offsetHeight, pair.fixed.offsetHeight);
|
|
}, 'Explicit height and auto width');
|
|
|
|
test(() => {
|
|
let pair = addTwoElements(`<textarea class="disable-default small-placeholder"></textarea>`,
|
|
`<textarea class="disable-default small-placeholder" placeholder="foo"></textarea>`);
|
|
assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth);
|
|
assert_equals(pair.e1.offsetHeight, pair.e2.offsetHeight);
|
|
}, 'Text caret is taller than the placeholder');
|
|
|
|
test(() => {
|
|
let pair = addTwoElements(`<textarea class="disable-default large-placeholder"></textarea>`,
|
|
`<textarea class="disable-default large-placeholder" placeholder="foo"></textarea>`);
|
|
assert_less_than(pair.e1.offsetWidth, pair.e2.offsetWidth, 'width');
|
|
assert_less_than(pair.e1.offsetHeight, pair.e2.offsetHeight, 'height');
|
|
}, 'Text caret is shorter than the placeholder');
|
|
|
|
test(() => {
|
|
const container = document.querySelector('#container');
|
|
container.innerHTML = '<textarea></textarea>';
|
|
const element = container.firstChild;
|
|
const w = element.offsetWidth;
|
|
const h = element.offsetHeight;
|
|
element.classList.add('disable-default');
|
|
assert_less_than(element.offsetWidth, w);
|
|
assert_less_than(element.offsetHeight, h);
|
|
}, 'Update field-sizing property dynamically');
|
|
</script>
|
|
</body>
|