1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/base/AccGroupInfo.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,227 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "AccGroupInfo.h" 1.9 +#include "nsAccUtils.h" 1.10 + 1.11 +#include "Role.h" 1.12 +#include "States.h" 1.13 + 1.14 +using namespace mozilla::a11y; 1.15 + 1.16 +AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) : 1.17 + mPosInSet(0), mSetSize(0), mParent(nullptr), mItem(aItem), mRole(aRole) 1.18 +{ 1.19 + MOZ_COUNT_CTOR(AccGroupInfo); 1.20 + Update(); 1.21 +} 1.22 + 1.23 +void 1.24 +AccGroupInfo::Update() 1.25 +{ 1.26 + Accessible* parent = mItem->Parent(); 1.27 + if (!parent) 1.28 + return; 1.29 + 1.30 + int32_t indexInParent = mItem->IndexInParent(); 1.31 + uint32_t siblingCount = parent->ChildCount(); 1.32 + if (indexInParent == -1 || 1.33 + indexInParent >= static_cast<int32_t>(siblingCount)) { 1.34 + NS_ERROR("Wrong index in parent! Tree invalidation problem."); 1.35 + return; 1.36 + } 1.37 + 1.38 + int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem); 1.39 + 1.40 + // Compute position in set. 1.41 + mPosInSet = 1; 1.42 + for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) { 1.43 + Accessible* sibling = parent->GetChildAt(idx); 1.44 + roles::Role siblingRole = sibling->Role(); 1.45 + 1.46 + // If the sibling is separator then the group is ended. 1.47 + if (siblingRole == roles::SEPARATOR) 1.48 + break; 1.49 + 1.50 + // If sibling is not visible and hasn't the same base role. 1.51 + if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE) 1.52 + continue; 1.53 + 1.54 + // Check if it's hierarchical flatten structure, i.e. if the sibling 1.55 + // level is lesser than this one then group is ended, if the sibling level 1.56 + // is greater than this one then the group is split by some child elements 1.57 + // (group will be continued). 1.58 + int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling); 1.59 + if (siblingLevel < level) { 1.60 + mParent = sibling; 1.61 + break; 1.62 + } 1.63 + 1.64 + // Skip subset. 1.65 + if (siblingLevel > level) 1.66 + continue; 1.67 + 1.68 + // If the previous item in the group has calculated group information then 1.69 + // build group information for this item based on found one. 1.70 + if (sibling->mGroupInfo) { 1.71 + mPosInSet += sibling->mGroupInfo->mPosInSet; 1.72 + mParent = sibling->mGroupInfo->mParent; 1.73 + mSetSize = sibling->mGroupInfo->mSetSize; 1.74 + return; 1.75 + } 1.76 + 1.77 + mPosInSet++; 1.78 + } 1.79 + 1.80 + // Compute set size. 1.81 + mSetSize = mPosInSet; 1.82 + 1.83 + for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) { 1.84 + Accessible* sibling = parent->GetChildAt(idx); 1.85 + 1.86 + roles::Role siblingRole = sibling->Role(); 1.87 + 1.88 + // If the sibling is separator then the group is ended. 1.89 + if (siblingRole == roles::SEPARATOR) 1.90 + break; 1.91 + 1.92 + // If sibling is visible and has the same base role 1.93 + if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE) 1.94 + continue; 1.95 + 1.96 + // and check if it's hierarchical flatten structure. 1.97 + int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling); 1.98 + if (siblingLevel < level) 1.99 + break; 1.100 + 1.101 + // Skip subset. 1.102 + if (siblingLevel > level) 1.103 + continue; 1.104 + 1.105 + // If the next item in the group has calculated group information then 1.106 + // build group information for this item based on found one. 1.107 + if (sibling->mGroupInfo) { 1.108 + mParent = sibling->mGroupInfo->mParent; 1.109 + mSetSize = sibling->mGroupInfo->mSetSize; 1.110 + return; 1.111 + } 1.112 + 1.113 + mSetSize++; 1.114 + } 1.115 + 1.116 + if (mParent) 1.117 + return; 1.118 + 1.119 + roles::Role parentRole = parent->Role(); 1.120 + if (ShouldReportRelations(mRole, parentRole)) 1.121 + mParent = parent; 1.122 + 1.123 + // ARIA tree and list can be arranged by using ARIA groups to organize levels. 1.124 + if (parentRole != roles::GROUPING) 1.125 + return; 1.126 + 1.127 + // Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a 1.128 + // parent. In other words the parent of the tree item will be a group and 1.129 + // the previous tree item of the group is a conceptual parent of the tree 1.130 + // item. 1.131 + if (mRole == roles::OUTLINEITEM) { 1.132 + Accessible* parentPrevSibling = parent->PrevSibling(); 1.133 + if (parentPrevSibling && parentPrevSibling->Role() == mRole) { 1.134 + mParent = parentPrevSibling; 1.135 + return; 1.136 + } 1.137 + } 1.138 + 1.139 + // Way #2 for ARIA list and tree: group is a child of an item. In other words 1.140 + // the parent of the item will be a group and containing item of the group is 1.141 + // a conceptual parent of the item. 1.142 + if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) { 1.143 + Accessible* grandParent = parent->Parent(); 1.144 + if (grandParent && grandParent->Role() == mRole) 1.145 + mParent = grandParent; 1.146 + } 1.147 +} 1.148 + 1.149 +Accessible* 1.150 +AccGroupInfo::FirstItemOf(Accessible* aContainer) 1.151 +{ 1.152 + // ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a 1.153 + // group is a parent) or by aria-level. 1.154 + a11y::role containerRole = aContainer->Role(); 1.155 + Accessible* item = aContainer->NextSibling(); 1.156 + if (item) { 1.157 + if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING) 1.158 + item = item->FirstChild(); 1.159 + 1.160 + if (item) { 1.161 + AccGroupInfo* itemGroupInfo = item->GetGroupInfo(); 1.162 + if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer) 1.163 + return item; 1.164 + } 1.165 + } 1.166 + 1.167 + // ARIA list and tree can be arranged by ARIA groups case #2 (group is 1.168 + // a child of an item). 1.169 + item = aContainer->LastChild(); 1.170 + if (!item) 1.171 + return nullptr; 1.172 + 1.173 + if (item->Role() == roles::GROUPING && 1.174 + (containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) { 1.175 + item = item->FirstChild(); 1.176 + if (item) { 1.177 + AccGroupInfo* itemGroupInfo = item->GetGroupInfo(); 1.178 + if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer) 1.179 + return item; 1.180 + } 1.181 + } 1.182 + 1.183 + // Otherwise, it can be a direct child if the container is a list or tree. 1.184 + item = aContainer->FirstChild(); 1.185 + if (ShouldReportRelations(item->Role(), containerRole)) 1.186 + return item; 1.187 + 1.188 + return nullptr; 1.189 +} 1.190 + 1.191 +Accessible* 1.192 +AccGroupInfo::NextItemTo(Accessible* aItem) 1.193 +{ 1.194 + AccGroupInfo* groupInfo = aItem->GetGroupInfo(); 1.195 + if (!groupInfo) 1.196 + return nullptr; 1.197 + 1.198 + // If the item in middle of the group then search next item in siblings. 1.199 + if (groupInfo->PosInSet() >= groupInfo->SetSize()) 1.200 + return nullptr; 1.201 + 1.202 + Accessible* parent = aItem->Parent(); 1.203 + uint32_t childCount = parent->ChildCount(); 1.204 + for (int32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) { 1.205 + Accessible* nextItem = parent->GetChildAt(idx); 1.206 + AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo(); 1.207 + if (nextGroupInfo && 1.208 + nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) { 1.209 + return nextItem; 1.210 + } 1.211 + } 1.212 + 1.213 + NS_NOTREACHED("Item in the middle of the group but there's no next item!"); 1.214 + return nullptr; 1.215 +} 1.216 + 1.217 +bool 1.218 +AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole) 1.219 +{ 1.220 + // We only want to report hierarchy-based node relations for items in tree or 1.221 + // list form. ARIA level/owns relations are always reported. 1.222 + if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM) 1.223 + return true; 1.224 + if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW) 1.225 + return true; 1.226 + if (aParentRole == roles::LIST && aRole == roles::LISTITEM) 1.227 + return true; 1.228 + 1.229 + return false; 1.230 +}