2012-05-21 04:12:37 -07:00
|
|
|
/* 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/. */
|
2010-07-02 20:11:35 -07:00
|
|
|
|
|
|
|
#include "AccGroupInfo.h"
|
|
|
|
|
2012-01-11 19:07:35 -08:00
|
|
|
#include "Role.h"
|
2011-04-09 16:38:06 -07:00
|
|
|
#include "States.h"
|
|
|
|
|
2011-07-27 05:43:01 -07:00
|
|
|
using namespace mozilla::a11y;
|
|
|
|
|
2012-05-28 18:18:45 -07:00
|
|
|
AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
|
2012-07-30 07:20:58 -07:00
|
|
|
mPosInSet(0), mSetSize(0), mParent(nullptr)
|
2010-07-02 20:11:35 -07:00
|
|
|
{
|
2010-07-16 07:15:03 -07:00
|
|
|
MOZ_COUNT_CTOR(AccGroupInfo);
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* parent = aItem->Parent();
|
2010-07-02 20:11:35 -07:00
|
|
|
if (!parent)
|
|
|
|
return;
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t indexInParent = aItem->IndexInParent();
|
|
|
|
uint32_t siblingCount = parent->ChildCount();
|
2012-05-25 03:53:45 -07:00
|
|
|
if (indexInParent == -1 ||
|
2012-08-22 08:56:38 -07:00
|
|
|
indexInParent >= static_cast<int32_t>(siblingCount)) {
|
2011-11-15 07:51:03 -08:00
|
|
|
NS_ERROR("Wrong index in parent! Tree invalidation problem.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t level = nsAccUtils::GetARIAOrDefaultLevel(aItem);
|
2010-07-02 20:11:35 -07:00
|
|
|
|
|
|
|
// Compute position in set.
|
|
|
|
mPosInSet = 1;
|
2012-08-22 08:56:38 -07:00
|
|
|
for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) {
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* sibling = parent->GetChildAt(idx);
|
2012-01-11 19:07:35 -08:00
|
|
|
roles::Role siblingRole = sibling->Role();
|
2010-07-02 20:11:35 -07:00
|
|
|
|
|
|
|
// If the sibling is separator then the group is ended.
|
2012-01-11 19:07:35 -08:00
|
|
|
if (siblingRole == roles::SEPARATOR)
|
2010-07-02 20:11:35 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
// If sibling is not visible and hasn't the same base role.
|
2011-04-09 16:38:06 -07:00
|
|
|
if (BaseRole(siblingRole) != aRole || sibling->State() & states::INVISIBLE)
|
2010-07-02 20:11:35 -07:00
|
|
|
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).
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
|
2010-07-02 20:11:35 -07:00
|
|
|
if (siblingLevel < level) {
|
|
|
|
mParent = sibling;
|
|
|
|
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 (sibling->mGroupInfo) {
|
|
|
|
mPosInSet += sibling->mGroupInfo->mPosInSet;
|
|
|
|
mParent = sibling->mGroupInfo->mParent;
|
|
|
|
mSetSize = sibling->mGroupInfo->mSetSize;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mPosInSet++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute set size.
|
|
|
|
mSetSize = mPosInSet;
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) {
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* sibling = parent->GetChildAt(idx);
|
2010-07-02 20:11:35 -07:00
|
|
|
|
2012-01-11 19:07:35 -08:00
|
|
|
roles::Role siblingRole = sibling->Role();
|
2010-07-02 20:11:35 -07:00
|
|
|
|
|
|
|
// If the sibling is separator then the group is ended.
|
2012-01-11 19:07:35 -08:00
|
|
|
if (siblingRole == roles::SEPARATOR)
|
2010-07-02 20:11:35 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
// If sibling is visible and has the same base role
|
2011-04-09 16:38:06 -07:00
|
|
|
if (BaseRole(siblingRole) != aRole || sibling->State() & states::INVISIBLE)
|
2010-07-02 20:11:35 -07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// and check if it's hierarchical flatten structure.
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
|
2010-07-02 20:11:35 -07:00
|
|
|
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 (sibling->mGroupInfo) {
|
|
|
|
mParent = sibling->mGroupInfo->mParent;
|
|
|
|
mSetSize = sibling->mGroupInfo->mSetSize;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSetSize++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mParent)
|
|
|
|
return;
|
|
|
|
|
2012-01-11 19:07:35 -08:00
|
|
|
roles::Role parentRole = parent->Role();
|
2011-07-22 06:40:37 -07:00
|
|
|
if (IsConceptualParent(aRole, parentRole))
|
2010-07-02 20:11:35 -07:00
|
|
|
mParent = parent;
|
|
|
|
|
2011-07-22 06:40:37 -07:00
|
|
|
// In the case of ARIA tree (not ARIA treegrid) a tree can be arranged by
|
|
|
|
// using ARIA groups to organize levels. In this case the parent of the tree
|
|
|
|
// item will be a group and the previous treeitem of that should be the tree
|
|
|
|
// item parent.
|
2012-01-11 19:07:35 -08:00
|
|
|
if (parentRole != roles::GROUPING || aRole != roles::OUTLINEITEM)
|
2010-07-02 20:11:35 -07:00
|
|
|
return;
|
|
|
|
|
2012-05-28 18:18:45 -07:00
|
|
|
Accessible* parentPrevSibling = parent->PrevSibling();
|
2010-09-06 19:41:53 -07:00
|
|
|
if (!parentPrevSibling)
|
|
|
|
return;
|
|
|
|
|
2012-01-11 19:07:35 -08:00
|
|
|
roles::Role parentPrevSiblingRole = parentPrevSibling->Role();
|
|
|
|
if (parentPrevSiblingRole == roles::TEXT_LEAF) {
|
2010-07-02 20:11:35 -07:00
|
|
|
// XXX Sometimes an empty text accessible is in the hierarchy here,
|
|
|
|
// although the text does not appear to be rendered, GetRenderedText()
|
|
|
|
// says that it is so we need to skip past it to find the true
|
|
|
|
// previous sibling.
|
2011-06-06 19:23:13 -07:00
|
|
|
parentPrevSibling = parentPrevSibling->PrevSibling();
|
2010-09-06 19:41:53 -07:00
|
|
|
if (parentPrevSibling)
|
|
|
|
parentPrevSiblingRole = parentPrevSibling->Role();
|
2010-07-02 20:11:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Previous sibling of parent group is a tree item, this is the
|
|
|
|
// conceptual tree item parent.
|
2012-01-11 19:07:35 -08:00
|
|
|
if (parentPrevSiblingRole == roles::OUTLINEITEM)
|
2010-07-02 20:11:35 -07:00
|
|
|
mParent = parentPrevSibling;
|
|
|
|
}
|
2011-07-22 06:40:37 -07:00
|
|
|
|
2013-02-25 23:17:10 -08:00
|
|
|
Accessible*
|
|
|
|
AccGroupInfo::FirstItemOf(Accessible* aContainer)
|
|
|
|
{
|
|
|
|
// ARIA trees can be arranged by ARIA groups, otherwise aria-level works.
|
|
|
|
a11y::role containerRole = aContainer->Role();
|
|
|
|
Accessible* item = aContainer->NextSibling();
|
|
|
|
if (item) {
|
|
|
|
if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
|
|
|
|
item = item->FirstChild();
|
|
|
|
|
|
|
|
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
|
|
|
|
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise it can be a direct child.
|
|
|
|
item = aContainer->FirstChild();
|
|
|
|
if (item && IsConceptualParent(BaseRole(item->Role()), containerRole))
|
|
|
|
return item;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Accessible*
|
|
|
|
AccGroupInfo::NextItemTo(Accessible* aItem)
|
|
|
|
{
|
|
|
|
AccGroupInfo* groupInfo = aItem->GetGroupInfo();
|
|
|
|
if (!groupInfo)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// If the item in middle of the group then search next item in siblings.
|
|
|
|
if (groupInfo->PosInSet() >= groupInfo->SetSize())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
Accessible* parent = aItem->Parent();
|
|
|
|
uint32_t childCount = parent->ChildCount();
|
|
|
|
for (int32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
|
|
|
|
Accessible* nextItem = parent->GetChildAt(idx);
|
|
|
|
AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
|
|
|
|
if (nextGroupInfo &&
|
|
|
|
nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
|
|
|
|
return nextItem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_NOTREACHED("Item in the midle of the group but there's no next item!");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2011-07-22 06:40:37 -07:00
|
|
|
bool
|
2012-01-11 19:07:35 -08:00
|
|
|
AccGroupInfo::IsConceptualParent(role aRole, role aParentRole)
|
2011-07-22 06:40:37 -07:00
|
|
|
{
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if ((aParentRole == roles::TABLE || aParentRole == roles::TREE_TABLE) &&
|
|
|
|
aRole == roles::ROW)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aParentRole == roles::ROW &&
|
|
|
|
(aRole == roles::CELL || aRole == roles::GRID_CELL))
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aParentRole == roles::COMBOBOX_LIST && aRole == roles::COMBOBOX_OPTION)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aParentRole == roles::LISTBOX && aRole == roles::OPTION)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aParentRole == roles::PAGETABLIST && aRole == roles::PAGETAB)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
2012-01-11 19:07:35 -08:00
|
|
|
if ((aParentRole == roles::POPUP_MENU || aParentRole == roles::MENUPOPUP) &&
|
|
|
|
aRole == roles::MENUITEM)
|
2011-07-22 06:40:37 -07:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|