Bug 1946912: Properly expose read-only property to UIA for text fields, r=Jamie

It was possible for <input> and <textarea> to incorrectly report the IsReadOnly
property as "true" to UIA, particularly when empty. There is no text leaf in
empty text inputs, so our TextLeafPoint would point to the text field itself.
Code that retrieves attributes for UIA didn't handle this case, which caused AT
issues, such as Narrator failing to switch out of scan mode automatically.

This revision fixes the issue by properly checking for text fields when figuring
out whether a TextLeafPoint's Accessible is read-only. It also adds a test to
verify the same.

Differential Revision: https://phabricator.services.mozilla.com/D237644
This commit is contained in:
Nathan LaPre
2025-02-13 01:45:57 +00:00
parent c71999aeb4
commit 739b849487
2 changed files with 27 additions and 8 deletions

View File

@@ -538,6 +538,7 @@ addUiaTask(
<div id="superscript-container">a <sup>bcd</sup> ef</div>
<div id="not-hidden-container">a bcd ef</div>
<div id="readonly-container">a <span contenteditable="true">bcd</span> ef</div>
<input id="text-input"/>
<div id="spelling-error-container">a <span aria-invalid="spelling">bcd</span> ef</div>
<div id="grammar-error-container">a <span aria-invalid="grammar">bcd</span> ef</div>
<div id="data-validation-error-container">a <span aria-invalid="true">bcd</span> ef</div>
@@ -792,6 +793,19 @@ addUiaTask(
"IsReadOnly correct"
);
// Verify that text inputs are not read-only by default.
await runPython(`
global range
textInputAcc = findUiaByDomId(doc, "text-input")
range = docText.RangeFromChild(textInputAcc)
`);
info("checking IsReadOnly");
is(
await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
false,
"IsReadOnly correct for text input"
);
// ================== UIA_AnnotationTypesAttributeId - AnnotationType_SpellingError ==================
await runPython(`
global range

View File

@@ -1307,15 +1307,20 @@ struct AttributeTraits<UIA_IsReadOnlyAttributeId> {
if (!aPoint.mAcc) {
return {};
}
// Check the parent of the leaf, since the leaf itself will never be
// editable, but the parent may. Check for both text fields and hypertexts,
// since we might have something like <input> or a contenteditable <span>.
Accessible* acc = aPoint.mAcc;
Accessible* parent = acc->Parent();
if (parent && parent->IsHyperText()) {
acc = parent;
} else {
return Some(true);
// If the TextLeafPoint we're dealing with is itself a hypertext, don't
// bother checking its parent since this is the Accessible we care about.
if (!acc->IsHyperText()) {
// Check the parent of the leaf, since the leaf itself will never be
// editable, but the parent may. Check for both text fields and
// hypertexts, since we might have something like <input> or a
// contenteditable <span>.
Accessible* parent = acc->Parent();
if (parent && parent->IsHyperText()) {
acc = parent;
} else {
return Some(true);
}
}
const uint64_t state = acc->State();
if (state & states::READONLY) {