Bug 1737944 - P5: Unify GetLevelInternal and GetDefaultLevel as Accessible::GetLevel. r=Jamie

The nsAccUtils method was a "fast" one for calculating set sizes and
conceptual parents. Unified it with
LocalAccessible::GetLevelInternal in Accessible::GetLevel with an
argument.

I also fixed select->optgroup->option group attributes.

Differential Revision: https://phabricator.services.mozilla.com/D134208
This commit is contained in:
Eitan Isaacson 2022-01-04 21:01:36 +00:00
parent 4d4ae8fb45
commit 935107258c
16 changed files with 184 additions and 153 deletions

View File

@ -31,7 +31,7 @@ void AccGroupInfo::Update() {
return;
}
int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem);
int32_t level = GetARIAOrDefaultLevel(mItem);
// Compute position in set.
mPosInSet = 1;
@ -57,7 +57,7 @@ void AccGroupInfo::Update() {
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is split by some child elements
// (group will be continued).
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
int32_t siblingLevel = GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level) {
mParent = sibling;
break;
@ -101,7 +101,7 @@ void AccGroupInfo::Update() {
}
// and check if it's hierarchical flatten structure.
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
int32_t siblingLevel = GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level) break;
// Skip subset.
@ -314,3 +314,13 @@ bool AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole) {
return false;
}
int32_t AccGroupInfo::GetARIAOrDefaultLevel(
const LocalAccessible* aAccessible) {
int32_t level = 0;
aAccessible->ARIAGroupPosition(&level, nullptr, nullptr);
if (level != 0) return level;
return aAccessible->GetLevel(true);
}

View File

@ -94,6 +94,12 @@ class AccGroupInfo {
*/
static bool ShouldReportRelations(a11y::role aRole, a11y::role aParentRole);
/**
* Return ARIA level value or the default one if ARIA is missed for the
* given accessible.
*/
static int32_t GetARIAOrDefaultLevel(const LocalAccessible* aAccessible);
uint32_t mPosInSet;
uint32_t mSetSize;
LocalAccessible* mParent;

View File

@ -43,31 +43,6 @@ void nsAccUtils::SetAccGroupAttrs(AccAttributes* aAttributes, int32_t aLevel,
}
}
int32_t nsAccUtils::GetDefaultLevel(const LocalAccessible* aAccessible) {
roles::Role role = aAccessible->Role();
if (role == roles::OUTLINEITEM) return 1;
if (role == roles::ROW) {
LocalAccessible* parent = aAccessible->LocalParent();
// It is a row inside flatten treegrid. Group level is always 1 until it
// is overriden by aria-level attribute.
if (parent && parent->Role() == roles::TREE_TABLE) return 1;
}
return 0;
}
int32_t nsAccUtils::GetARIAOrDefaultLevel(const LocalAccessible* aAccessible) {
int32_t level = 0;
nsCoreUtils::GetUIntAttr(aAccessible->GetContent(), nsGkAtoms::aria_level,
&level);
if (level != 0) return level;
return GetDefaultLevel(aAccessible);
}
int32_t nsAccUtils::GetLevelForXULContainerItem(nsIContent* aContent) {
nsCOMPtr<nsIDOMXULContainerItemElement> item =
aContent->AsElement()->AsXULContainerItem();

View File

@ -37,17 +37,6 @@ class nsAccUtils {
static void SetAccGroupAttrs(AccAttributes* aAttributes, int32_t aLevel,
int32_t aSetSize, int32_t aPosInSet);
/**
* Get default value of the level for the given accessible.
*/
static int32_t GetDefaultLevel(const LocalAccessible* aAcc);
/**
* Return ARIA level value or the default one if ARIA is missed for the
* given accessible.
*/
static int32_t GetARIAOrDefaultLevel(const LocalAccessible* aAccessible);
/**
* Compute group level for nsIDOMXULContainerItemElement node.
*/

View File

@ -5,6 +5,7 @@
#include "Accessible.h"
#include "ARIAMap.h"
#include "States.h"
#include "mozilla/a11y/HyperTextAccessibleBase.h"
using namespace mozilla;
@ -77,3 +78,135 @@ uint32_t Accessible::StartOffset() {
parent ? parent->AsHyperTextBase() : nullptr;
return hyperText ? hyperText->GetChildOffset(this) : 0;
}
int32_t Accessible::GetLevel(bool aFast) const {
int32_t level = 0;
if (!Parent()) return level;
roles::Role role = Role();
if (role == roles::OUTLINEITEM) {
// Always expose 'level' attribute for 'outlineitem' accessible. The number
// of nested 'grouping' accessibles containing 'outlineitem' accessible is
// its level.
level = 1;
if (!aFast) {
const Accessible* parent = this;
while ((parent = parent->Parent())) {
roles::Role parentRole = parent->Role();
if (parentRole == roles::OUTLINE) break;
if (parentRole == roles::GROUPING) ++level;
}
}
} else if (role == roles::LISTITEM && !aFast) {
// Expose 'level' attribute on nested lists. We support two hierarchies:
// a) list -> listitem -> list -> listitem (nested list is a last child
// of listitem of the parent list);
// b) list -> listitem -> group -> listitem (nested listitems are contained
// by group that is a last child of the parent listitem).
// Calculate 'level' attribute based on number of parent listitems.
level = 0;
const Accessible* parent = this;
while ((parent = parent->Parent())) {
roles::Role parentRole = parent->Role();
if (parentRole == roles::LISTITEM) {
++level;
} else if (parentRole != roles::LIST && parentRole != roles::GROUPING) {
break;
}
}
if (level == 0) {
// If this listitem is on top of nested lists then expose 'level'
// attribute.
parent = Parent();
uint32_t siblingCount = parent->ChildCount();
for (uint32_t siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
Accessible* sibling = parent->ChildAt(siblingIdx);
Accessible* siblingChild = sibling->LastChild();
if (siblingChild) {
roles::Role lastChildRole = siblingChild->Role();
if (lastChildRole == roles::LIST ||
lastChildRole == roles::GROUPING) {
return 1;
}
}
}
} else {
++level; // level is 1-index based
}
} else if (role == roles::OPTION || role == roles::COMBOBOX_OPTION) {
if (const Accessible* parent = Parent()) {
if (parent->IsHTMLOptGroup()) {
return 2;
}
if (parent->IsListControl() && !parent->ARIARoleMap()) {
// This is for HTML selects only.
if (aFast) {
return 1;
}
for (Accessible* child = parent->FirstChild(); child;
child = child->NextSibling()) {
if (child->IsHTMLOptGroup()) {
return 1;
}
}
}
}
} else if (role == roles::HEADING) {
nsAtom* tagName = TagName();
if (tagName == nsGkAtoms::h1) {
return 1;
}
if (tagName == nsGkAtoms::h2) {
return 2;
}
if (tagName == nsGkAtoms::h3) {
return 3;
}
if (tagName == nsGkAtoms::h4) {
return 4;
}
if (tagName == nsGkAtoms::h5) {
return 5;
}
if (tagName == nsGkAtoms::h6) {
return 6;
}
const nsRoleMapEntry* ariaRole = this->ARIARoleMap();
if (ariaRole && ariaRole->Is(nsGkAtoms::heading)) {
// An aria heading with no aria level has a default level of 2.
return 2;
}
} else if (role == roles::COMMENT) {
// For comments, count the ancestor elements with the same role to get the
// level.
level = 1;
if (!aFast) {
const Accessible* parent = this;
while ((parent = parent->Parent())) {
roles::Role parentRole = parent->Role();
if (parentRole == roles::COMMENT) {
++level;
}
}
}
} else if (role == roles::ROW) {
// It is a row inside flatten treegrid. Group level is always 1 until it
// is overriden by aria-level attribute.
const Accessible* parent = Parent();
if (parent->Role() == roles::TREE_TABLE) {
return 1;
}
}
return level;
}

View File

@ -300,6 +300,14 @@ class Accessible {
virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize,
int32_t* aPosInSet) const = 0;
/*
* Return calculated group level based on accessible hierarchy.
*
* @param aFast [in] Don't climb up tree. Calculate level from aria and
* roles.
*/
virtual int32_t GetLevel(bool aFast) const;
private:
static const uint8_t kTypeBits = 6;
static const uint8_t kGenericTypesBits = 18;

View File

@ -1239,13 +1239,6 @@ already_AddRefed<AccAttributes> HyperTextAccessible::DefaultTextAttributes() {
return attributes.forget();
}
int32_t HyperTextAccessible::GetLevelInternal() {
if (auto* heading = dom::HTMLHeadingElement::FromNode(mContent)) {
return heading->AccessibilityLevel();
}
return AccessibleWrap::GetLevelInternal();
}
void HyperTextAccessible::SetMathMLXMLRoles(AccAttributes* aAttributes) {
// Add MathML xmlroles based on the position inside the parent.
LocalAccessible* parent = LocalParent();

View File

@ -51,7 +51,6 @@ class HyperTextAccessible : public AccessibleWrap,
// LocalAccessible
virtual nsAtom* LandmarkRole() const override;
virtual int32_t GetLevelInternal() override;
virtual already_AddRefed<AccAttributes> NativeAttributes() override;
virtual mozilla::a11y::role NativeRole() const override;
virtual uint64_t NativeState() const override;

View File

@ -1467,7 +1467,7 @@ GroupPos LocalAccessible::GroupPosition() {
// Calculate group level if ARIA is missed.
if (groupPos.level == 0) {
int32_t level = GetLevelInternal();
int32_t level = GetLevel(false);
if (level != 0) {
groupPos.level = level;
} else {
@ -3318,83 +3318,6 @@ void LocalAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
}
}
int32_t LocalAccessible::GetLevelInternal() {
int32_t level = nsAccUtils::GetDefaultLevel(this);
if (!IsBoundToParent()) return level;
roles::Role role = Role();
if (role == roles::OUTLINEITEM) {
// Always expose 'level' attribute for 'outlineitem' accessible. The number
// of nested 'grouping' accessibles containing 'outlineitem' accessible is
// its level.
level = 1;
LocalAccessible* parent = this;
while ((parent = parent->LocalParent())) {
roles::Role parentRole = parent->Role();
if (parentRole == roles::OUTLINE) break;
if (parentRole == roles::GROUPING) ++level;
}
} else if (role == roles::LISTITEM) {
// Expose 'level' attribute on nested lists. We support two hierarchies:
// a) list -> listitem -> list -> listitem (nested list is a last child
// of listitem of the parent list);
// b) list -> listitem -> group -> listitem (nested listitems are contained
// by group that is a last child of the parent listitem).
// Calculate 'level' attribute based on number of parent listitems.
level = 0;
LocalAccessible* parent = this;
while ((parent = parent->LocalParent())) {
roles::Role parentRole = parent->Role();
if (parentRole == roles::LISTITEM) {
++level;
} else if (parentRole != roles::LIST && parentRole != roles::GROUPING) {
break;
}
}
if (level == 0) {
// If this listitem is on top of nested lists then expose 'level'
// attribute.
parent = LocalParent();
uint32_t siblingCount = parent->ChildCount();
for (uint32_t siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
LocalAccessible* sibling = parent->LocalChildAt(siblingIdx);
LocalAccessible* siblingChild = sibling->LocalLastChild();
if (siblingChild) {
roles::Role lastChildRole = siblingChild->Role();
if (lastChildRole == roles::LIST ||
lastChildRole == roles::GROUPING) {
return 1;
}
}
}
} else {
++level; // level is 1-index based
}
} else if (role == roles::COMMENT) {
// For comments, count the ancestor elements with the same role to get the
// level.
level = 1;
LocalAccessible* parent = this;
while ((parent = parent->LocalParent())) {
roles::Role parentRole = parent->Role();
if (parentRole == roles::COMMENT) {
++level;
}
}
}
return level;
}
nsAtom* LocalAccessible::TagName() const {
return mContent && mContent->IsElement() ? mContent->NodeInfo()->NameAtom()
: nullptr;

View File

@ -271,11 +271,6 @@ class LocalAccessible : public nsISupports, public Accessible {
*/
virtual LocalAccessible* FocusedChild();
/**
* Return calculated group level based on accessible hierarchy.
*/
virtual int32_t GetLevelInternal();
/**
* Calculate position in group and group size ('posinset' and 'setsize') based
* on accessible hierarchy.

View File

@ -212,19 +212,6 @@ uint64_t HTMLSelectOptionAccessible::NativeInteractiveState() const {
: states::FOCUSABLE | states::SELECTABLE;
}
int32_t HTMLSelectOptionAccessible::GetLevelInternal() {
nsIContent* parentContent = mContent->GetParent();
int32_t level =
parentContent->NodeInfo()->Equals(nsGkAtoms::optgroup) ? 2 : 1;
if (level == 1 && Role() != roles::HEADING) {
level = 0; // In a single level list, the level is irrelevant
}
return level;
}
nsRect HTMLSelectOptionAccessible::RelativeBounds(
nsIFrame** aBoundingFrame) const {
LocalAccessible* combobox = GetCombobox();

View File

@ -67,7 +67,6 @@ class HTMLSelectOptionAccessible : public HyperTextAccessibleWrap {
virtual uint64_t NativeState() const override;
virtual uint64_t NativeInteractiveState() const override;
virtual int32_t GetLevelInternal() override;
virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
virtual void SetSelected(bool aSelect) override;

View File

@ -21,6 +21,15 @@ addAccessibleTask(
<select size="4">
<option id="opt1">option1</option>
<option id="opt2">option2</option>
</select>
<select size="4">
<optgroup id="select2_optgroup" label="group">
<option id="select2_opt1">option1</option>
<option id="select2_opt2">option2</option>
</optgroup>
<option id="select2_opt3">option3</option>
<option id="select2_opt4">option4</option>
</select>`,
async function(browser, accDoc) {
let getAcc = id => findAccessibleChildByID(accDoc, id);
@ -39,6 +48,13 @@ addAccessibleTask(
// HTML select
testGroupAttrs(getAcc("opt1"), 1, 2);
testGroupAttrs(getAcc("opt2"), 2, 2);
// ////////////////////////////////////////////////////////////////////////
// HTML select with optgroup
testGroupAttrs(getAcc("select2_opt3"), 1, 2, 1);
testGroupAttrs(getAcc("select2_opt4"), 2, 2, 1);
testGroupAttrs(getAcc("select2_opt1"), 1, 2, 2);
testGroupAttrs(getAcc("select2_opt2"), 2, 2, 2);
}
);

View File

@ -32,13 +32,11 @@
testGroupAttrs("opt2", 2, 2);
// ////////////////////////////////////////////////////////////////////////
// HTML select with options
// XXX bug 469123
// testGroupAttrs("select2_optgroup", 1, 3, 1);
// testGroupAttrs("select2_opt3", 2, 3, 1);
// testGroupAttrs("select2_opt4", 3, 3, 1);
// testGroupAttrs("select2_opt1", 1, 2, 2);
// testGroupAttrs("select2_opt2", 2, 2, 2);
// HTML select with optgroup
testGroupAttrs("select2_opt3", 1, 2, 1);
testGroupAttrs("select2_opt4", 2, 2, 1);
testGroupAttrs("select2_opt1", 1, 2, 2);
testGroupAttrs("select2_opt2", 2, 2, 2);
// ////////////////////////////////////////////////////////////////////////
// HTML input@type="radio" within form

View File

@ -240,7 +240,7 @@ role XULMenuitemAccessible::NativeRole() const {
return roles::MENUITEM;
}
int32_t XULMenuitemAccessible::GetLevelInternal() {
int32_t XULMenuitemAccessible::GetLevel(bool aFast) const {
return nsAccUtils::GetLevelForXULContainerItem(mContent);
}

View File

@ -26,7 +26,6 @@ class XULMenuitemAccessible : public AccessibleWrap {
virtual a11y::role NativeRole() const override;
virtual uint64_t NativeState() const override;
virtual uint64_t NativeInteractiveState() const override;
virtual int32_t GetLevelInternal() override;
// ActionAccessible
virtual uint8_t ActionCount() const override;
@ -43,6 +42,7 @@ class XULMenuitemAccessible : public AccessibleWrap {
protected:
// LocalAccessible
virtual ENameValueFlag NativeName(nsString& aName) const override;
virtual int32_t GetLevel(bool aFast) const override;
};
/**