Bug 1757121 part 5: Expose live region attributes on cached RemoteAccessibles. r=morgan

As part of this, aria-relevant is now exposed via object attributes.
This is necessary to ensure it is included in the cache.
There's no good reason not to expose it anyway, since we already expose the other live region attributes.

Differential Revision: https://phabricator.services.mozilla.com/D150380
This commit is contained in:
James Teh 2022-07-12 02:45:58 +00:00
Родитель d0d3ec2479
Коммит 2e5729d4e9
6 изменённых файлов: 149 добавлений и 24 удалений

Просмотреть файл

@ -1384,7 +1384,7 @@ static const AttrCharacteristics gWAIUnivAttrMap[] = {
{nsGkAtoms::aria_posinset, ATTR_BYPASSOBJ }, /* handled via groupPosition */
{nsGkAtoms::aria_pressed, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{nsGkAtoms::aria_readonly, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{nsGkAtoms::aria_relevant, ATTR_BYPASSOBJ | ATTR_GLOBAL },
{nsGkAtoms::aria_relevant, ATTR_GLOBAL },
{nsGkAtoms::aria_required, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
{nsGkAtoms::aria_rowcount, ATTR_VALINT },
{nsGkAtoms::aria_rowindex, ATTR_VALINT },

Просмотреть файл

@ -9,7 +9,8 @@
using namespace mozilla::a11y;
bool AccAttributes::GetAttribute(nsAtom* aAttrName, nsAString& aAttrValue) {
bool AccAttributes::GetAttribute(nsAtom* aAttrName,
nsAString& aAttrValue) const {
if (auto value = mData.Lookup(aAttrName)) {
StringFromValueAndName(aAttrName, *value, aAttrValue);
return true;

Просмотреть файл

@ -156,7 +156,7 @@ class AccAttributes {
}
// Get stringified value
bool GetAttribute(nsAtom* aAttrName, nsAString& aAttrValue);
bool GetAttribute(nsAtom* aAttrName, nsAString& aAttrValue) const;
bool HasAttribute(nsAtom* aAttrName) { return mData.Contains(aAttrName); }

Просмотреть файл

@ -775,6 +775,13 @@ uint64_t RemoteAccessibleBase<Derived>::State() {
template <class Derived>
already_AddRefed<AccAttributes> RemoteAccessibleBase<Derived>::Attributes() {
RefPtr<AccAttributes> attributes = new AccAttributes();
nsAccessibilityService* accService = GetAccService();
if (!accService) {
// The service can be shut down before RemoteAccessibles. If it is shut
// down, we can't calculate some attributes. We're about to die anyway.
return attributes.forget();
}
if (mCachedFields) {
// We use GetAttribute instead of GetAttributeRefPtr because we need
// nsAtom, not const nsAtom.
@ -821,31 +828,36 @@ already_AddRefed<AccAttributes> RemoteAccessibleBase<Derived>::Attributes() {
attributes->SetAttribute(nsGkAtoms::layout_guess, layoutGuess);
}
if (auto ariaAttrs = GetCachedARIAAttributes()) {
ariaAttrs->CopyTo(attributes);
}
accService->MarkupAttributes(this, attributes);
const nsRoleMapEntry* roleMap = ARIARoleMap();
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);
}
if (roleMap && roleMap->roleAtom != nsGkAtoms::_empty) {
// Single, known role.
attributes->SetAttribute(nsGkAtoms::xmlroles, roleMap->roleAtom);
} else 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));
}
if (roleMap) {
nsAutoString live;
if (nsAccUtils::GetLiveAttrValue(roleMap->liveAttRule, live)) {
attributes->SetAttribute(nsGkAtoms::aria_live, std::move(live));
}
}
if (auto ariaAttrs = GetCachedARIAAttributes()) {
ariaAttrs->CopyTo(attributes);
}
nsAccUtils::SetLiveContainerAttributes(attributes, this);
}
nsAutoString name;
@ -891,6 +903,34 @@ Maybe<float> RemoteAccessibleBase<Derived>::Opacity() const {
return Nothing();
}
template <class Derived>
void RemoteAccessibleBase<Derived>::LiveRegionAttributes(
nsAString* aLive, nsAString* aRelevant, Maybe<bool>* aAtomic,
nsAString* aBusy) const {
if (!mCachedFields) {
return;
}
RefPtr<const AccAttributes> attrs = GetCachedARIAAttributes();
if (!attrs) {
return;
}
if (aLive) {
attrs->GetAttribute(nsGkAtoms::aria_live, *aLive);
}
if (aRelevant) {
attrs->GetAttribute(nsGkAtoms::aria_relevant, *aRelevant);
}
if (aAtomic) {
if (auto value =
attrs->GetAttribute<RefPtr<nsAtom>>(nsGkAtoms::aria_atomic)) {
*aAtomic = Some(*value == nsGkAtoms::_true);
}
}
if (aBusy) {
attrs->GetAttribute(nsGkAtoms::aria_busy, *aBusy);
}
}
template <class Derived>
nsAtom* RemoteAccessibleBase<Derived>::GetPrimaryAction() const {
if (mCachedFields) {

Просмотреть файл

@ -193,6 +193,10 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
virtual Maybe<float> Opacity() const override;
virtual void LiveRegionAttributes(nsAString* aLive, nsAString* aRelevant,
Maybe<bool>* aAtomic,
nsAString* aBusy) const override;
virtual uint8_t ActionCount() const override;
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;

Просмотреть файл

@ -381,9 +381,12 @@ addAccessibleTask(
<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>
<main id="landmarkMarkup">landmarkMarkup</main>
<main id="landmarkMarkupWithRole" role="banner">landmarkMarkupWithRole</main>
<main id="landmarkMarkupWithEmptyRole" role="">landmarkMarkupWithEmptyRole</main>
<article id="markup">markup</article>
<article id="markupWithRole" role="banner">markupWithRole</article>
<article id="markupWithEmptyRole" role="">markupWithEmptyRole</article>
`,
async function(browser, docAcc) {
const knownRole = findAccessibleChildByID(docAcc, "knownRole");
@ -394,15 +397,92 @@ addAccessibleTask(
testAttrs(unknownRole, { "xml-roles": "foo" }, true);
const multiRole = findAccessibleChildByID(docAcc, "multiRole");
testAttrs(multiRole, { "xml-roles": "foo main" }, true);
const landmarkMarkup = findAccessibleChildByID(docAcc, "landmarkMarkup");
testAttrs(landmarkMarkup, { "xml-roles": "main" }, true);
const landmarkMarkupWithRole = findAccessibleChildByID(
docAcc,
"landmarkMarkupWithRole"
);
testAttrs(landmarkMarkupWithRole, { "xml-roles": "banner" }, true);
const landmarkMarkupWithEmptyRole = findAccessibleChildByID(
docAcc,
"landmarkMarkupWithEmptyRole"
);
testAttrs(landmarkMarkupWithEmptyRole, { "xml-roles": "main" }, true);
const markup = findAccessibleChildByID(docAcc, "markup");
testAttrs(markup, { "xml-roles": "main" }, true);
testAttrs(markup, { "xml-roles": "article" }, true);
const markupWithRole = findAccessibleChildByID(docAcc, "markupWithRole");
testAttrs(markupWithRole, { "xml-roles": "banner" }, true);
const markupWithEmptyRole = findAccessibleChildByID(
docAcc,
"markupWithEmptyRole"
);
testAttrs(markupWithEmptyRole, { "xml-roles": "main" }, true);
testAttrs(markupWithEmptyRole, { "xml-roles": "article" }, true);
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);
/**
* Test lie region attributes.
*/
addAccessibleTask(
`
<div id="noLive"><p>noLive</p></div>
<output id="liveMarkup"><p>liveMarkup</p></output>
<div id="ariaLive" aria-live="polite"><p>ariaLive</p></div>
<div id="liveRole" role="log"><p>liveRole</p></div>
<div id="nonLiveRole" role="group"><p>nonLiveRole</p></div>
<div id="other" aria-atomic="true" aria-busy="true" aria-relevant="additions"><p>other</p></div>
`,
async function(browser, docAcc) {
const noLive = findAccessibleChildByID(docAcc, "noLive");
for (const acc of [noLive, noLive.firstChild]) {
testAbsentAttrs(acc, {
live: "",
"container-live": "",
"container-live-role": "",
atomic: "",
"container-atomic": "",
busy: "",
"container-busy": "",
relevant: "",
"container-relevant": "",
});
}
const liveMarkup = findAccessibleChildByID(docAcc, "liveMarkup");
testAttrs(liveMarkup, { live: "polite" }, true);
testAttrs(liveMarkup.firstChild, { "container-live": "polite" }, true);
const ariaLive = findAccessibleChildByID(docAcc, "ariaLive");
testAttrs(ariaLive, { live: "polite" }, true);
testAttrs(ariaLive.firstChild, { "container-live": "polite" }, true);
const liveRole = findAccessibleChildByID(docAcc, "liveRole");
testAttrs(liveRole, { live: "polite" }, true);
testAttrs(
liveRole.firstChild,
{ "container-live": "polite", "container-live-role": "log" },
true
);
const nonLiveRole = findAccessibleChildByID(docAcc, "nonLiveRole");
testAbsentAttrs(nonLiveRole, { live: "" });
testAbsentAttrs(nonLiveRole.firstChild, {
"container-live": "",
"container-live-role": "",
});
const other = findAccessibleChildByID(docAcc, "other");
testAttrs(
other,
{ atomic: "true", busy: "true", relevant: "additions" },
true
);
testAttrs(
other.firstChild,
{
"container-atomic": "true",
"container-busy": "true",
"container-relevant": "additions",
},
true
);
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);