Bug 1773930: Expose xml-roles object attribute on cached RemoteAccessible. r=morgan

This also changes LocalAccessible::Attributes to ignore the edge case of an empty role attribute when calculating xml-roles.
This is consistent with how we handle the role attribute elsewhere (see aria::GetRoleMapIndex) and makes it easier to handle this consistently between local and remote.

Differential Revision: https://phabricator.services.mozilla.com/D149594
This commit is contained in:
James Teh 2022-06-24 05:28:53 +00:00
parent cbdc83f787
commit e998d303d0
3 changed files with 76 additions and 3 deletions

View File

@ -1088,7 +1088,8 @@ already_AddRefed<AccAttributes> LocalAccessible::Attributes() {
// 'xml-roles' attribute coming from ARIA.
nsString xmlRoles;
if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::role,
xmlRoles)) {
xmlRoles) &&
!xmlRoles.IsEmpty()) {
attributes->SetAttribute(nsGkAtoms::xmlroles, std::move(xmlRoles));
} else if (nsAtom* landmark = LandmarkRole()) {
// 'xml-roles' attribute for landmark.
@ -3589,17 +3590,32 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
if (mContent && mContent->IsElement()) {
fields->SetAttribute(nsGkAtoms::tag, mContent->NodeInfo()->NameAtom());
dom::Element* el = mContent->AsElement();
if (IsTextField() || IsDateTimeField()) {
// Cache text input types. Accessible is recreated if this changes,
// so it is considered immutable.
if (const nsAttrValue* attr =
mContent->AsElement()->GetParsedAttr(nsGkAtoms::type)) {
if (const nsAttrValue* attr = el->GetParsedAttr(nsGkAtoms::type)) {
RefPtr<nsAtom> inputType = attr->GetAsAtom();
if (inputType) {
fields->SetAttribute(nsGkAtoms::textInputType, inputType);
}
}
}
// Changing the role attribute currently re-creates the Accessible, so
// it's immutable in the cache.
if (const nsRoleMapEntry* roleMap = ARIARoleMap()) {
// Most of the time, the role attribute is a single, known role. We
// already send the map index, so we don't need to double up.
if (!el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::role,
roleMap->roleAtom, eIgnoreCase)) {
// Multiple roles or unknown roles are rare, so just send them as a
// string.
nsAutoString role;
el->GetAttr(kNameSpaceID_None, nsGkAtoms::role, role);
fields->SetAttribute(nsGkAtoms::role, std::move(role));
}
}
}
if (frame) {

View File

@ -821,6 +821,28 @@ already_AddRefed<AccAttributes> RemoteAccessibleBase<Derived>::Attributes() {
if (auto ariaAttrs = GetCachedARIAAttributes()) {
ariaAttrs->CopyTo(attributes);
}
nsAutoString role;
mCachedFields->GetAttribute(nsGkAtoms::role, role);
if (role.IsEmpty()) {
bool found = false;
if (const nsRoleMapEntry* roleMap = ARIARoleMap()) {
if (roleMap->roleAtom != nsGkAtoms::_empty) {
// Single, known role.
attributes->SetAttribute(nsGkAtoms::xmlroles, roleMap->roleAtom);
found = true;
}
}
if (!found) {
if (nsAtom* landmark = LandmarkRole()) {
// Landmark role from markup; e.g. HTML <main>.
attributes->SetAttribute(nsGkAtoms::xmlroles, landmark);
}
}
} else {
// Unknown role or multiple roles.
attributes->SetAttribute(nsGkAtoms::xmlroles, std::move(role));
}
}
nsAutoString name;

View File

@ -371,3 +371,38 @@ addAccessibleTask(
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);
/**
* Test support for the xml-roles attribute.
*/
addAccessibleTask(
`
<div id="knownRole" role="main">knownRole</div>
<div id="emptyRole" role="">emptyRole</div>
<div id="unknownRole" role="foo">unknownRole</div>
<div id="multiRole" role="foo main">multiRole</div>
<main id="markup">markup</main>
<main id="markupWithRole" role="banner">markupWithRole</main>
<main id="markupWithEmptyRole" role="">markupWithEmptyRole</main>
`,
async function(browser, docAcc) {
const knownRole = findAccessibleChildByID(docAcc, "knownRole");
testAttrs(knownRole, { "xml-roles": "main" }, true);
const emptyRole = findAccessibleChildByID(docAcc, "emptyRole");
testAbsentAttrs(emptyRole, { "xml-roles": "" });
const unknownRole = findAccessibleChildByID(docAcc, "unknownRole");
testAttrs(unknownRole, { "xml-roles": "foo" }, true);
const multiRole = findAccessibleChildByID(docAcc, "multiRole");
testAttrs(multiRole, { "xml-roles": "foo main" }, true);
const markup = findAccessibleChildByID(docAcc, "markup");
testAttrs(markup, { "xml-roles": "main" }, true);
const markupWithRole = findAccessibleChildByID(docAcc, "markupWithRole");
testAttrs(markupWithRole, { "xml-roles": "banner" }, true);
const markupWithEmptyRole = findAccessibleChildByID(
docAcc,
"markupWithEmptyRole"
);
testAttrs(markupWithEmptyRole, { "xml-roles": "main" }, true);
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);