1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is mozilla.org code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Alexander Surkov <surkov.alexander@gmail.com> (original author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "AccGroupInfo.h"
39 :
40 : #include "Role.h"
41 : #include "States.h"
42 :
43 : using namespace mozilla::a11y;
44 :
45 0 : AccGroupInfo::AccGroupInfo(nsAccessible* aItem, role aRole) :
46 0 : mPosInSet(0), mSetSize(0), mParent(nsnull)
47 : {
48 0 : MOZ_COUNT_CTOR(AccGroupInfo);
49 0 : nsAccessible* parent = aItem->Parent();
50 0 : if (!parent)
51 0 : return;
52 :
53 0 : PRInt32 indexInParent = aItem->IndexInParent();
54 0 : PRInt32 siblingCount = parent->GetChildCount();
55 0 : if (siblingCount < indexInParent) {
56 0 : NS_ERROR("Wrong index in parent! Tree invalidation problem.");
57 0 : return;
58 : }
59 :
60 0 : PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(aItem);
61 :
62 : // Compute position in set.
63 0 : mPosInSet = 1;
64 0 : for (PRInt32 idx = indexInParent - 1; idx >=0 ; idx--) {
65 0 : nsAccessible* sibling = parent->GetChildAt(idx);
66 0 : roles::Role siblingRole = sibling->Role();
67 :
68 : // If the sibling is separator then the group is ended.
69 0 : if (siblingRole == roles::SEPARATOR)
70 0 : break;
71 :
72 : // If sibling is not visible and hasn't the same base role.
73 0 : if (BaseRole(siblingRole) != aRole || sibling->State() & states::INVISIBLE)
74 0 : continue;
75 :
76 : // Check if it's hierarchical flatten structure, i.e. if the sibling
77 : // level is lesser than this one then group is ended, if the sibling level
78 : // is greater than this one then the group is split by some child elements
79 : // (group will be continued).
80 0 : PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
81 0 : if (siblingLevel < level) {
82 0 : mParent = sibling;
83 0 : break;
84 : }
85 :
86 : // Skip subset.
87 0 : if (siblingLevel > level)
88 0 : continue;
89 :
90 : // If the previous item in the group has calculated group information then
91 : // build group information for this item based on found one.
92 0 : if (sibling->mGroupInfo) {
93 0 : mPosInSet += sibling->mGroupInfo->mPosInSet;
94 0 : mParent = sibling->mGroupInfo->mParent;
95 0 : mSetSize = sibling->mGroupInfo->mSetSize;
96 0 : return;
97 : }
98 :
99 0 : mPosInSet++;
100 : }
101 :
102 : // Compute set size.
103 0 : mSetSize = mPosInSet;
104 :
105 0 : for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
106 0 : nsAccessible* sibling = parent->GetChildAt(idx);
107 :
108 0 : roles::Role siblingRole = sibling->Role();
109 :
110 : // If the sibling is separator then the group is ended.
111 0 : if (siblingRole == roles::SEPARATOR)
112 0 : break;
113 :
114 : // If sibling is visible and has the same base role
115 0 : if (BaseRole(siblingRole) != aRole || sibling->State() & states::INVISIBLE)
116 0 : continue;
117 :
118 : // and check if it's hierarchical flatten structure.
119 0 : PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
120 0 : if (siblingLevel < level)
121 0 : break;
122 :
123 : // Skip subset.
124 0 : if (siblingLevel > level)
125 0 : continue;
126 :
127 : // If the next item in the group has calculated group information then
128 : // build group information for this item based on found one.
129 0 : if (sibling->mGroupInfo) {
130 0 : mParent = sibling->mGroupInfo->mParent;
131 0 : mSetSize = sibling->mGroupInfo->mSetSize;
132 0 : return;
133 : }
134 :
135 0 : mSetSize++;
136 : }
137 :
138 0 : if (mParent)
139 0 : return;
140 :
141 0 : roles::Role parentRole = parent->Role();
142 0 : if (IsConceptualParent(aRole, parentRole))
143 0 : mParent = parent;
144 :
145 : // In the case of ARIA tree (not ARIA treegrid) a tree can be arranged by
146 : // using ARIA groups to organize levels. In this case the parent of the tree
147 : // item will be a group and the previous treeitem of that should be the tree
148 : // item parent.
149 0 : if (parentRole != roles::GROUPING || aRole != roles::OUTLINEITEM)
150 0 : return;
151 :
152 0 : nsAccessible* parentPrevSibling = parent->PrevSibling();
153 0 : if (!parentPrevSibling)
154 0 : return;
155 :
156 0 : roles::Role parentPrevSiblingRole = parentPrevSibling->Role();
157 0 : if (parentPrevSiblingRole == roles::TEXT_LEAF) {
158 : // XXX Sometimes an empty text accessible is in the hierarchy here,
159 : // although the text does not appear to be rendered, GetRenderedText()
160 : // says that it is so we need to skip past it to find the true
161 : // previous sibling.
162 0 : parentPrevSibling = parentPrevSibling->PrevSibling();
163 0 : if (parentPrevSibling)
164 0 : parentPrevSiblingRole = parentPrevSibling->Role();
165 : }
166 :
167 : // Previous sibling of parent group is a tree item, this is the
168 : // conceptual tree item parent.
169 0 : if (parentPrevSiblingRole == roles::OUTLINEITEM)
170 0 : mParent = parentPrevSibling;
171 : }
172 :
173 : bool
174 0 : AccGroupInfo::IsConceptualParent(role aRole, role aParentRole)
175 : {
176 0 : if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
177 0 : return true;
178 0 : if ((aParentRole == roles::TABLE || aParentRole == roles::TREE_TABLE) &&
179 : aRole == roles::ROW)
180 0 : return true;
181 0 : if (aParentRole == roles::ROW &&
182 : (aRole == roles::CELL || aRole == roles::GRID_CELL))
183 0 : return true;
184 0 : if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
185 0 : return true;
186 0 : if (aParentRole == roles::COMBOBOX_LIST && aRole == roles::COMBOBOX_OPTION)
187 0 : return true;
188 0 : if (aParentRole == roles::LISTBOX && aRole == roles::OPTION)
189 0 : return true;
190 0 : if (aParentRole == roles::PAGETABLIST && aRole == roles::PAGETAB)
191 0 : return true;
192 0 : if ((aParentRole == roles::POPUP_MENU || aParentRole == roles::MENUPOPUP) &&
193 : aRole == roles::MENUITEM)
194 0 : return true;
195 :
196 0 : return false;
197 : }
|