Bug 1849160 - Part 3: Orphaned child accessibles fall back to native role, r=Jamie

This revision changes the behavior of the Role function such that, for child
roles (in parent/child role relationships), the parent role must be present as
an ancestor for the child to have its assigned role. For instance, a "row" node
must have a table in its ancestry tree. To implement this, we walk parents. This
revision also fixes up and removes expected failures for around 18 web platform
tests.

Differential Revision: https://phabricator.services.mozilla.com/D202542
This commit is contained in:
Nathan LaPre 2024-04-10 17:22:13 +00:00
Родитель e9a44ec52c
Коммит 9f61225ecc
7 изменённых файлов: 99 добавлений и 72 удалений

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

@ -1891,6 +1891,16 @@ role LocalAccessible::ARIATransformRole(role aRole) const {
return roles::COMBOBOX_OPTION;
}
// Orphaned option outside the context of a listbox.
const Accessible* listbox = FindAncestorIf([](const Accessible& aAcc) {
const role accRole = aAcc.Role();
return accRole == roles::LISTBOX ? AncestorSearchOption::Found
: accRole == roles::GROUPING ? AncestorSearchOption::Continue
: AncestorSearchOption::NotFound;
});
if (!listbox) {
return NativeRole();
}
} else if (aRole == roles::MENUITEM) {
// Menuitem has a submenu.
if (mContent->IsElement() &&
@ -1900,6 +1910,30 @@ role LocalAccessible::ARIATransformRole(role aRole) const {
return roles::PARENT_MENUITEM;
}
// Orphaned menuitem outside the context of a menu/menubar.
const Accessible* menu = FindAncestorIf([](const Accessible& aAcc) {
const role accRole = aAcc.Role();
return (accRole == roles::MENUBAR || accRole == roles::MENUPOPUP)
? AncestorSearchOption::Found
: accRole == roles::GROUPING ? AncestorSearchOption::Continue
: AncestorSearchOption::NotFound;
});
if (!menu) {
return NativeRole();
}
} else if (aRole == roles::RADIO_MENU_ITEM ||
aRole == roles::CHECK_MENU_ITEM) {
// Orphaned radio/checkbox menuitem outside the context of a menu/menubar.
const Accessible* menu = FindAncestorIf([](const Accessible& aAcc) {
const role accRole = aAcc.Role();
return (accRole == roles::MENUBAR || accRole == roles::MENUPOPUP)
? AncestorSearchOption::Found
: accRole == roles::GROUPING ? AncestorSearchOption::Continue
: AncestorSearchOption::NotFound;
});
if (!menu) {
return NativeRole();
}
} else if (aRole == roles::CELL) {
// A cell inside an ancestor table element that has a grid role needs a
// gridcell role
@ -1908,6 +1942,63 @@ role LocalAccessible::ARIATransformRole(role aRole) const {
if (table && table->IsARIARole(nsGkAtoms::grid)) {
return roles::GRID_CELL;
}
} else if (aRole == roles::ROW) {
// Orphaned rows outside the context of a table.
const LocalAccessible* table = nsAccUtils::TableFor(this);
if (!table) {
return NativeRole();
}
} else if (aRole == roles::ROWGROUP) {
// Orphaned rowgroups outside the context of a table.
const Accessible* table = FindAncestorIf([](const Accessible& aAcc) {
return aAcc.IsTable() ? AncestorSearchOption::Found
: AncestorSearchOption::NotFound;
});
if (!table) {
return NativeRole();
}
} else if (aRole == roles::GRID_CELL || aRole == roles::ROWHEADER ||
aRole == roles::COLUMNHEADER) {
// Orphaned gridcell/rowheader/columnheader outside the context of a row.
const Accessible* row = FindAncestorIf([](const Accessible& aAcc) {
return aAcc.IsTableRow() ? AncestorSearchOption::Found
: AncestorSearchOption::NotFound;
});
if (!row) {
return NativeRole();
}
} else if (aRole == roles::LISTITEM) {
// doc-biblioentry and doc-endnote should not be treated as listitems.
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
if (!roleMapEntry || (roleMapEntry->roleAtom != nsGkAtoms::docBiblioentry &&
roleMapEntry->roleAtom != nsGkAtoms::docEndnote)) {
// Orphaned listitem outside the context of a list.
const Accessible* list = FindAncestorIf([](const Accessible& aAcc) {
return aAcc.IsList() ? AncestorSearchOption::Found
: AncestorSearchOption::Continue;
});
if (!list) {
return NativeRole();
}
}
} else if (aRole == roles::PAGETAB) {
// Orphaned tab outside the context of a tablist.
const Accessible* tablist = FindAncestorIf([](const Accessible& aAcc) {
return aAcc.Role() == roles::PAGETABLIST ? AncestorSearchOption::Found
: AncestorSearchOption::NotFound;
});
if (!tablist) {
return NativeRole();
}
} else if (aRole == roles::OUTLINEITEM) {
// Orphaned treeitem outside the context of a tree.
const Accessible* tree = FindAncestorIf([](const Accessible& aAcc) {
return aAcc.Role() == roles::OUTLINE ? AncestorSearchOption::Found
: AncestorSearchOption::Continue;
});
if (!tree) {
return NativeRole();
}
}
return aRole;
@ -2115,12 +2206,14 @@ Relation LocalAccessible::RelationByType(RelationType aType) const {
if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM ||
roleMapEntry->role == roles::LISTITEM ||
roleMapEntry->role == roles::ROW)) {
Accessible* parent = const_cast<LocalAccessible*>(this)
->GetOrCreateGroupInfo()
->ConceptualParent();
if (parent) {
MOZ_ASSERT(parent->IsLocal());
rel.AppendTarget(parent->AsLocal());
AccGroupInfo* groupInfo =
const_cast<LocalAccessible*>(this)->GetOrCreateGroupInfo();
if (groupInfo) {
Accessible* parent = groupInfo->ConceptualParent();
if (parent) {
MOZ_ASSERT(parent->IsLocal());
rel.AppendTarget(parent->AsLocal());
}
}
}

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

@ -1,18 +0,0 @@
[grid-roles.html]
[orphaned rowheader outside the context of row]
expected: FAIL
[orphaned columnheader outside the context of row]
expected: FAIL
[orphaned button with gridcell role outside the context of row]
expected: FAIL
[orphaned row outside the context of table]
expected: FAIL
[orphaned rowgroup outside the context of row]
expected: FAIL
[orphaned div with gridcell role outside the context of row]
expected: FAIL

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

@ -1,6 +0,0 @@
[list-roles.html]
[orphan p with listitem role]
expected: FAIL
[orphan div with listitem role]
expected: FAIL

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

@ -1,3 +0,0 @@
[listbox-roles.html]
[orphaned option outside the context of listbox]
expected: FAIL

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

@ -1,27 +0,0 @@
[menu-roles.html]
[orphaned menuitem outside the context of menu/menubar]
expected: FAIL
[orphaned menuitemradio outside the context of menu/menubar]
expected: FAIL
[orphaned menuitemcheckbox outside the context of menu/menubar]
expected: FAIL
[orphan button with menuitem role]
expected: FAIL
[orphan button with menuitemcheckbox role]
expected: FAIL
[orphan button with menuitemradio role]
expected: FAIL
[orphan div with menuitem role]
expected: FAIL
[orphan div with menuitemcheckbox role]
expected: FAIL
[orphan div with menuitemradio role]
expected: FAIL

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

@ -1,6 +0,0 @@
[tab-roles.html]
[orphan button with tab role]
expected: FAIL
[orphan span with tab role]
expected: FAIL

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

@ -1,6 +0,0 @@
[tree-roles.html]
[orphaned treeitem outside the context of tree]
expected: FAIL
[orphaned button with treeitem role outside tree context]
expected: FAIL