/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This rule finds candidate siblings for compound widget children. class CompoundWidgetSiblingRule : public PivotRule { public:
CompoundWidgetSiblingRule() = delete; explicit CompoundWidgetSiblingRule(role aRole) : mRole(aRole) {}
uint16_t Match(Accessible* aAcc) override { // If the acc has a matching role, that's a valid sibling. If the acc is // separator then the group is ended. Return a match for separators with // the assumption that the caller will check for the role of the returned // accessible. const role accRole = aAcc->Role(); if (BaseRole(accRole) == mRole || accRole == role::SEPARATOR) { return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
// Ignore generic accessibles, but keep searching through the subtree for // siblings. if (aAcc->IsGeneric()) { return nsIAccessibleTraversalRule::FILTER_IGNORE;
}
// Search backwards through the tree for candidate siblings.
Accessible* candidateSibling = const_cast<Accessible*>(mItem);
Pivot pivot{parent};
CompoundWidgetSiblingRule widgetSiblingRule{mRole}; while ((candidateSibling = pivot.Prev(candidateSibling, widgetSiblingRule)) &&
candidateSibling != parent) { // If the sibling is separator then the group is ended. if (candidateSibling->Role() == roles::SEPARATOR) { break;
}
const AccGroupInfo* siblingGroupInfo = candidateSibling->GetGroupInfo(); // Skip invisible siblings. // If the sibling has calculated group info, that means it's visible. if (!siblingGroupInfo && candidateSibling->State() & states::INVISIBLE) { continue;
}
// Check if it's hierarchical flatten structure, i.e. if the sibling // 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). const int32_t siblingLevel = GetARIAOrDefaultLevel(candidateSibling); if (siblingLevel < level) {
mParentId = candidateSibling->ID(); break;
}
// Skip subset. if (siblingLevel > level) { continue;
}
// If the previous item in the group has calculated group information then // build group information for this item based on found one. if (siblingGroupInfo) {
mPosInSet += siblingGroupInfo->mPosInSet;
mParentId = siblingGroupInfo->mParentId;
mSetSize = siblingGroupInfo->mSetSize; return;
}
mPosInSet++;
}
// Compute set size.
mSetSize = mPosInSet;
candidateSibling = const_cast<Accessible*>(mItem); while ((candidateSibling = pivot.Next(candidateSibling, widgetSiblingRule)) &&
candidateSibling != parent) { // If the sibling is separator then the group is ended. if (candidateSibling->Role() == roles::SEPARATOR) { break;
}
const AccGroupInfo* siblingGroupInfo = candidateSibling->GetGroupInfo(); // Skip invisible siblings. // If the sibling has calculated group info, that means it's visible. if (!siblingGroupInfo && candidateSibling->State() & states::INVISIBLE) { continue;
}
// and check if it's hierarchical flatten structure. const int32_t siblingLevel = GetARIAOrDefaultLevel(candidateSibling); if (siblingLevel < level) { break;
}
// Skip subset. if (siblingLevel > level) { continue;
}
// If the next item in the group has calculated group information then // build group information for this item based on found one. if (siblingGroupInfo) {
mParentId = siblingGroupInfo->mParentId;
mSetSize = siblingGroupInfo->mSetSize; return;
}
// ARIA tree and list can be arranged by using ARIA groups to organize levels. if (parentRole != roles::GROUPING) { return;
}
// Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a // parent. In other words the parent of the tree item will be a group and // the previous tree item of the group is a conceptual parent of the tree // item. if (mRole == roles::OUTLINEITEM) { // Find the relevant grandparent of the item. Use that parent as the root // and find the previous outline item sibling within that root.
Accessible* grandParent = parent->GetNonGenericParent();
MOZ_ASSERT(grandParent);
Pivot pivot{grandParent};
CompoundWidgetSiblingRule parentSiblingRule{mRole};
Accessible* parentPrevSibling = pivot.Prev(parent, widgetSiblingRule); if (parentPrevSibling && parentPrevSibling->Role() == mRole) {
mParentId = parentPrevSibling->ID(); return;
}
}
// Way #2 for ARIA list and tree: group is a child of an item. In other words // the parent of the item will be a group and containing item of the group is // a conceptual parent of the item. if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) {
Accessible* grandParent = parent->GetNonGenericParent(); if (grandParent && grandParent->Role() == mRole) {
mParentId = grandParent->ID();
}
}
}
AccGroupInfo* AccGroupInfo::CreateGroupInfo(const Accessible* aAccessible) {
mozilla::a11y::role role = aAccessible->Role(); if (role != mozilla::a11y::roles::ROW &&
role != mozilla::a11y::roles::OUTLINEITEM &&
role != mozilla::a11y::roles::OPTION &&
role != mozilla::a11y::roles::LISTITEM &&
role != mozilla::a11y::roles::MENUITEM &&
role != mozilla::a11y::roles::COMBOBOX_OPTION &&
role != mozilla::a11y::roles::RICH_OPTION &&
role != mozilla::a11y::roles::CHECK_RICH_OPTION &&
role != mozilla::a11y::roles::PARENT_MENUITEM &&
role != mozilla::a11y::roles::CHECK_MENU_ITEM &&
role != mozilla::a11y::roles::RADIO_MENU_ITEM &&
role != mozilla::a11y::roles::RADIOBUTTON &&
role != mozilla::a11y::roles::PAGETAB &&
role != mozilla::a11y::roles::COMMENT) { return nullptr;
}
AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role)); return info;
}
Accessible* AccGroupInfo::FirstItemOf(const Accessible* aContainer) { // ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a // group is a parent) or by aria-level.
a11y::role containerRole = aContainer->Role();
Accessible* item = aContainer->NextSibling(); if (item) { if (containerRole == roles::OUTLINEITEM &&
item->Role() == roles::GROUPING) {
item = item->FirstChild();
}
if (item) {
AccGroupInfo* itemGroupInfo = item->GetOrCreateGroupInfo(); if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer) { return item;
}
}
}
// ARIA list and tree can be arranged by ARIA groups case #2 (group is // a child of an item).
item = aContainer->LastChild(); if (!item) return nullptr;
// Otherwise, it can be a direct child if the container is a list or tree.
item = aContainer->FirstChild(); if (ShouldReportRelations(item->Role(), containerRole)) return item;
return nullptr;
}
uint32_t AccGroupInfo::TotalItemCount(Accessible* aContainer, bool* aIsHierarchical) {
uint32_t itemCount = 0; switch (aContainer->Role()) { case roles::GRID: case roles::TABLE: if (auto val = aContainer->GetIntARIAAttr(nsGkAtoms::aria_rowcount)) { if (*val >= 0) { return *val;
}
} if (TableAccessible* tableAcc = aContainer->AsTable()) { return tableAcc->RowCount();
} break; case roles::ROW: if (Accessible* table = nsAccUtils::TableFor(aContainer)) { if (auto val = table->GetIntARIAAttr(nsGkAtoms::aria_colcount)) { if (*val >= 0) { return *val;
}
} if (TableAccessible* tableAcc = table->AsTable()) { return tableAcc->ColCount();
}
} break; case roles::OUTLINE: case roles::LIST: case roles::MENUBAR: case roles::MENUPOPUP: case roles::COMBOBOX: case roles::GROUPING: case roles::ROWGROUP: case roles::TREE_TABLE: case roles::COMBOBOX_LIST: case roles::LISTBOX: case roles::DEFINITION_LIST: case roles::EDITCOMBOBOX: case roles::RADIO_GROUP: case roles::PAGETABLIST: {
Accessible* childItem = AccGroupInfo::FirstItemOf(aContainer); if (!childItem) {
childItem = aContainer->FirstChild(); if (childItem && childItem->IsTextLeaf()) { // First child can be a text leaf, check its sibling for an item.
childItem = childItem->NextSibling();
}
}
MOZ_ASSERT_UNREACHABLE( "Item in the middle of the group but there's no next item!"); return nullptr;
}
size_t AccGroupInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { // We don't count mParentId or mItem since they (should be) counted // as part of the document. return aMallocSizeOf(this);
}
bool AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole) { // We only want to report hierarchy-based node relations for items in tree or // list form. ARIA level/owns relations are always reported. if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM) returntrue; if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW) returntrue; if (aParentRole == roles::LIST && aRole == roles::LISTITEM) returntrue;
Accessible* AccGroupInfo::ConceptualParent() const { if (!mParentId) { // The conceptual parent can never be the document, so id 0 means none. return nullptr;
} if (Accessible* doc =
nsAccUtils::DocumentFor(const_cast<Accessible*>(mItem))) { return nsAccUtils::GetAccessibleByID(doc, mParentId);
} return nullptr;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.