зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
d0d3ec2479
Коммит
2e5729d4e9
|
@ -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 }
|
||||
);
|
||||
|
|
Загрузка…
Ссылка в новой задаче