1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2007
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Alexander Surkov <surkov.alexander@gmail.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsCoreUtils.h"
40 : #include "nsAccUtils.h"
41 :
42 : #include "nsIAccessibleTypes.h"
43 : #include "Role.h"
44 : #include "States.h"
45 :
46 : #include "nsAccessibilityService.h"
47 : #include "nsARIAMap.h"
48 : #include "nsDocAccessible.h"
49 : #include "nsHyperTextAccessible.h"
50 : #include "nsHTMLTableAccessible.h"
51 : #include "nsTextAccessible.h"
52 : #include "nsXULTreeGridAccessible.h"
53 :
54 : #include "nsIDOMXULContainerElement.h"
55 : #include "nsIDOMXULSelectCntrlEl.h"
56 : #include "nsIDOMXULSelectCntrlItemEl.h"
57 : #include "nsWhitespaceTokenizer.h"
58 : #include "nsComponentManagerUtils.h"
59 :
60 : namespace dom = mozilla::dom;
61 : using namespace mozilla::a11y;
62 :
63 : void
64 0 : nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes,
65 : nsIAtom *aAttrName, nsAString& aAttrValue)
66 : {
67 0 : aAttrValue.Truncate();
68 :
69 0 : aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue);
70 0 : }
71 :
72 : void
73 0 : nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
74 : nsIAtom *aAttrName, const nsAString& aAttrValue)
75 : {
76 0 : nsAutoString oldValue;
77 0 : nsCAutoString attrName;
78 :
79 0 : aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue);
80 0 : }
81 :
82 : void
83 0 : nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
84 : PRInt32 aLevel, PRInt32 aSetSize,
85 : PRInt32 aPosInSet)
86 : {
87 0 : nsAutoString value;
88 :
89 0 : if (aLevel) {
90 0 : value.AppendInt(aLevel);
91 0 : SetAccAttr(aAttributes, nsGkAtoms::level, value);
92 : }
93 :
94 0 : if (aSetSize && aPosInSet) {
95 0 : value.Truncate();
96 0 : value.AppendInt(aPosInSet);
97 0 : SetAccAttr(aAttributes, nsGkAtoms::posinset, value);
98 :
99 0 : value.Truncate();
100 0 : value.AppendInt(aSetSize);
101 0 : SetAccAttr(aAttributes, nsGkAtoms::setsize, value);
102 : }
103 0 : }
104 :
105 : PRInt32
106 0 : nsAccUtils::GetDefaultLevel(nsAccessible *aAccessible)
107 : {
108 0 : roles::Role role = aAccessible->Role();
109 :
110 0 : if (role == roles::OUTLINEITEM)
111 0 : return 1;
112 :
113 0 : if (role == roles::ROW) {
114 0 : nsAccessible* parent = aAccessible->Parent();
115 : // It is a row inside flatten treegrid. Group level is always 1 until it
116 : // is overriden by aria-level attribute.
117 0 : if (parent && parent->Role() == roles::TREE_TABLE)
118 0 : return 1;
119 : }
120 :
121 0 : return 0;
122 : }
123 :
124 : PRInt32
125 0 : nsAccUtils::GetARIAOrDefaultLevel(nsAccessible *aAccessible)
126 : {
127 0 : PRInt32 level = 0;
128 : nsCoreUtils::GetUIntAttr(aAccessible->GetContent(),
129 0 : nsGkAtoms::aria_level, &level);
130 :
131 0 : if (level != 0)
132 0 : return level;
133 :
134 0 : return GetDefaultLevel(aAccessible);
135 : }
136 :
137 : void
138 0 : nsAccUtils::GetPositionAndSizeForXULSelectControlItem(nsIContent *aContent,
139 : PRInt32 *aPosInSet,
140 : PRInt32 *aSetSize)
141 : {
142 0 : nsCOMPtr<nsIDOMXULSelectControlItemElement> item(do_QueryInterface(aContent));
143 0 : if (!item)
144 : return;
145 :
146 0 : nsCOMPtr<nsIDOMXULSelectControlElement> control;
147 0 : item->GetControl(getter_AddRefs(control));
148 0 : if (!control)
149 : return;
150 :
151 0 : PRUint32 itemsCount = 0;
152 0 : control->GetItemCount(&itemsCount);
153 :
154 0 : PRInt32 indexOf = 0;
155 0 : control->GetIndexOfItem(item, &indexOf);
156 :
157 0 : *aSetSize = itemsCount;
158 0 : *aPosInSet = indexOf;
159 :
160 0 : for (PRUint32 index = 0; index < itemsCount; index++) {
161 0 : nsCOMPtr<nsIDOMXULSelectControlItemElement> currItem;
162 0 : control->GetItemAtIndex(index, getter_AddRefs(currItem));
163 0 : nsCOMPtr<nsINode> currNode(do_QueryInterface(currItem));
164 :
165 : nsAccessible* itemAcc = currNode ?
166 0 : GetAccService()->GetAccessible(currNode, nsnull) : nsnull;
167 :
168 0 : if (!itemAcc || itemAcc->State() & states::INVISIBLE) {
169 0 : (*aSetSize)--;
170 0 : if (index < static_cast<PRUint32>(indexOf))
171 0 : (*aPosInSet)--;
172 : }
173 : }
174 :
175 0 : (*aPosInSet)++; // group position is 1-index based.
176 : }
177 :
178 : PRInt32
179 0 : nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
180 : {
181 0 : nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
182 0 : if (!item)
183 0 : return 0;
184 :
185 0 : nsCOMPtr<nsIDOMXULContainerElement> container;
186 0 : item->GetParentContainer(getter_AddRefs(container));
187 0 : if (!container)
188 0 : return 0;
189 :
190 : // Get level of the item.
191 0 : PRInt32 level = -1;
192 0 : while (container) {
193 0 : level++;
194 :
195 0 : nsCOMPtr<nsIDOMXULContainerElement> parentContainer;
196 0 : container->GetParentContainer(getter_AddRefs(parentContainer));
197 0 : parentContainer.swap(container);
198 : }
199 :
200 0 : return level;
201 : }
202 :
203 : void
204 0 : nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
205 : nsIContent *aStartContent,
206 : nsIContent *aTopContent)
207 : {
208 0 : nsAutoString atomic, live, relevant, busy;
209 0 : nsIContent *ancestor = aStartContent;
210 0 : while (ancestor) {
211 :
212 : // container-relevant attribute
213 0 : if (relevant.IsEmpty() &&
214 0 : nsAccUtils::HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) &&
215 0 : ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_relevant, relevant))
216 0 : SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant);
217 :
218 : // container-live, and container-live-role attributes
219 0 : if (live.IsEmpty()) {
220 0 : nsRoleMapEntry *role = GetRoleMapEntry(ancestor);
221 0 : if (nsAccUtils::HasDefinedARIAToken(ancestor,
222 0 : nsGkAtoms::aria_live)) {
223 : ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live,
224 0 : live);
225 0 : } else if (role) {
226 0 : GetLiveAttrValue(role->liveAttRule, live);
227 : }
228 0 : if (!live.IsEmpty()) {
229 0 : SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
230 0 : if (role) {
231 : nsAccUtils::SetAccAttr(aAttributes,
232 : nsGkAtoms::containerLiveRole,
233 0 : NS_ConvertASCIItoUTF16(role->roleString));
234 : }
235 : }
236 : }
237 :
238 : // container-atomic attribute
239 0 : if (atomic.IsEmpty() &&
240 0 : nsAccUtils::HasDefinedARIAToken(ancestor, nsGkAtoms::aria_atomic) &&
241 0 : ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
242 0 : SetAccAttr(aAttributes, nsGkAtoms::containerAtomic, atomic);
243 :
244 : // container-busy attribute
245 0 : if (busy.IsEmpty() &&
246 0 : nsAccUtils::HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) &&
247 0 : ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
248 0 : SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
249 :
250 0 : if (ancestor == aTopContent)
251 0 : break;
252 :
253 0 : ancestor = ancestor->GetParent();
254 0 : if (!ancestor)
255 0 : ancestor = aTopContent; // Use <body>/<frameset>
256 : }
257 0 : }
258 :
259 : bool
260 0 : nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom)
261 : {
262 0 : NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!");
263 :
264 0 : if (!aContent->HasAttr(kNameSpaceID_None, aAtom) ||
265 : aContent->AttrValueIs(kNameSpaceID_None, aAtom,
266 0 : nsGkAtoms::_empty, eCaseMatters) ||
267 : aContent->AttrValueIs(kNameSpaceID_None, aAtom,
268 0 : nsGkAtoms::_undefined, eCaseMatters)) {
269 0 : return false;
270 : }
271 0 : return true;
272 : }
273 :
274 : nsIAtom*
275 0 : nsAccUtils::GetARIAToken(dom::Element* aElement, nsIAtom* aAttr)
276 : {
277 0 : if (!nsAccUtils::HasDefinedARIAToken(aElement, aAttr))
278 0 : return nsGkAtoms::_empty;
279 :
280 : static nsIContent::AttrValuesArray tokens[] =
281 : { &nsGkAtoms::_false, &nsGkAtoms::_true,
282 : &nsGkAtoms::mixed, nsnull};
283 :
284 : PRInt32 idx = aElement->FindAttrValueIn(kNameSpaceID_None,
285 0 : aAttr, tokens, eCaseMatters);
286 0 : if (idx >= 0)
287 0 : return *(tokens[idx]);
288 :
289 0 : return nsnull;
290 : }
291 :
292 : nsAccessible*
293 0 : nsAccUtils::GetAncestorWithRole(nsAccessible *aDescendant, PRUint32 aRole)
294 : {
295 0 : nsAccessible* document = aDescendant->Document();
296 0 : nsAccessible* parent = aDescendant;
297 0 : while ((parent = parent->Parent())) {
298 0 : PRUint32 testRole = parent->Role();
299 0 : if (testRole == aRole)
300 0 : return parent;
301 :
302 0 : if (parent == document)
303 0 : break;
304 : }
305 0 : return nsnull;
306 : }
307 :
308 : nsAccessible*
309 0 : nsAccUtils::GetSelectableContainer(nsAccessible* aAccessible, PRUint64 aState)
310 : {
311 0 : if (!aAccessible)
312 0 : return nsnull;
313 :
314 0 : if (!(aState & states::SELECTABLE))
315 0 : return nsnull;
316 :
317 0 : nsAccessible* parent = aAccessible;
318 0 : while ((parent = parent->Parent()) && !parent->IsSelect()) {
319 0 : if (Role(parent) == nsIAccessibleRole::ROLE_PANE)
320 0 : return nsnull;
321 : }
322 0 : return parent;
323 : }
324 :
325 : nsAccessible*
326 0 : nsAccUtils::GetMultiSelectableContainer(nsINode* aNode)
327 : {
328 0 : nsAccessible* accessible = GetAccService()->GetAccessible(aNode, nsnull);
329 0 : if (accessible) {
330 : nsAccessible* container = GetSelectableContainer(accessible,
331 0 : accessible->State());
332 0 : if (container && container->State() & states::MULTISELECTABLE)
333 0 : return container;
334 : }
335 :
336 0 : return nsnull;
337 : }
338 :
339 : bool
340 0 : nsAccUtils::IsARIASelected(nsAccessible *aAccessible)
341 : {
342 0 : return aAccessible->GetContent()->
343 : AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
344 0 : nsGkAtoms::_true, eCaseMatters);
345 : }
346 :
347 : nsHyperTextAccessible*
348 0 : nsAccUtils::GetTextAccessibleFromSelection(nsISelection* aSelection)
349 : {
350 : // Get accessible from selection's focus DOM point (the DOM point where
351 : // selection is ended).
352 :
353 0 : nsCOMPtr<nsIDOMNode> focusDOMNode;
354 0 : aSelection->GetFocusNode(getter_AddRefs(focusDOMNode));
355 0 : if (!focusDOMNode)
356 0 : return nsnull;
357 :
358 0 : PRInt32 focusOffset = 0;
359 0 : aSelection->GetFocusOffset(&focusOffset);
360 :
361 0 : nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDOMNode));
362 : nsCOMPtr<nsINode> resultNode =
363 0 : nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
364 :
365 : // Get text accessible containing the result node.
366 : nsDocAccessible* doc =
367 0 : GetAccService()->GetDocAccessible(resultNode->OwnerDoc());
368 : nsAccessible* accessible = doc ?
369 0 : doc->GetAccessibleOrContainer(resultNode) : nsnull;
370 0 : if (!accessible) {
371 0 : NS_NOTREACHED("No nsIAccessibleText for selection change event!");
372 0 : return nsnull;
373 : }
374 :
375 0 : do {
376 0 : nsHyperTextAccessible* textAcc = accessible->AsHyperText();
377 0 : if (textAcc)
378 0 : return textAcc;
379 :
380 0 : accessible = accessible->Parent();
381 : } while (accessible);
382 :
383 0 : NS_NOTREACHED("We must reach document accessible implementing nsIAccessibleText!");
384 0 : return nsnull;
385 : }
386 :
387 : nsresult
388 0 : nsAccUtils::ConvertToScreenCoords(PRInt32 aX, PRInt32 aY,
389 : PRUint32 aCoordinateType,
390 : nsAccessNode *aAccessNode,
391 : nsIntPoint *aCoords)
392 : {
393 0 : NS_ENSURE_ARG_POINTER(aCoords);
394 :
395 0 : aCoords->MoveTo(aX, aY);
396 :
397 0 : switch (aCoordinateType) {
398 : case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
399 0 : break;
400 :
401 : case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
402 : {
403 0 : NS_ENSURE_ARG(aAccessNode);
404 0 : *aCoords += GetScreenCoordsForWindow(aAccessNode);
405 0 : break;
406 : }
407 :
408 : case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
409 : {
410 0 : NS_ENSURE_ARG(aAccessNode);
411 0 : *aCoords += GetScreenCoordsForParent(aAccessNode);
412 0 : break;
413 : }
414 :
415 : default:
416 0 : return NS_ERROR_INVALID_ARG;
417 : }
418 :
419 0 : return NS_OK;
420 : }
421 :
422 : nsresult
423 0 : nsAccUtils::ConvertScreenCoordsTo(PRInt32 *aX, PRInt32 *aY,
424 : PRUint32 aCoordinateType,
425 : nsAccessNode *aAccessNode)
426 : {
427 0 : switch (aCoordinateType) {
428 : case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
429 0 : break;
430 :
431 : case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
432 : {
433 0 : NS_ENSURE_ARG(aAccessNode);
434 0 : nsIntPoint coords = nsAccUtils::GetScreenCoordsForWindow(aAccessNode);
435 0 : *aX -= coords.x;
436 0 : *aY -= coords.y;
437 0 : break;
438 : }
439 :
440 : case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
441 : {
442 0 : NS_ENSURE_ARG(aAccessNode);
443 0 : nsIntPoint coords = nsAccUtils::GetScreenCoordsForParent(aAccessNode);
444 0 : *aX -= coords.x;
445 0 : *aY -= coords.y;
446 0 : break;
447 : }
448 :
449 : default:
450 0 : return NS_ERROR_INVALID_ARG;
451 : }
452 :
453 0 : return NS_OK;
454 : }
455 :
456 : nsIntPoint
457 0 : nsAccUtils::GetScreenCoordsForWindow(nsAccessNode *aAccessNode)
458 : {
459 0 : return nsCoreUtils::GetScreenCoordsForWindow(aAccessNode->GetNode());
460 : }
461 :
462 : nsIntPoint
463 0 : nsAccUtils::GetScreenCoordsForParent(nsAccessNode *aAccessNode)
464 : {
465 0 : nsDocAccessible* document = aAccessNode->Document();
466 0 : nsAccessible* parent = document->GetContainerAccessible(aAccessNode->GetNode());
467 0 : if (!parent)
468 0 : return nsIntPoint(0, 0);
469 :
470 0 : nsIFrame *parentFrame = parent->GetFrame();
471 0 : if (!parentFrame)
472 0 : return nsIntPoint(0, 0);
473 :
474 0 : nsIntRect parentRect = parentFrame->GetScreenRectExternal();
475 0 : return nsIntPoint(parentRect.x, parentRect.y);
476 : }
477 :
478 : nsRoleMapEntry*
479 0 : nsAccUtils::GetRoleMapEntry(nsINode *aNode)
480 : {
481 0 : nsIContent *content = nsCoreUtils::GetRoleContent(aNode);
482 0 : nsAutoString roleString;
483 0 : if (!content ||
484 0 : !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString) ||
485 0 : roleString.IsEmpty()) {
486 : // We treat role="" as if the role attribute is absent (per aria spec:8.1.1)
487 0 : return nsnull;
488 : }
489 :
490 0 : nsWhitespaceTokenizer tokenizer(roleString);
491 0 : while (tokenizer.hasMoreTokens()) {
492 : // Do a binary search through table for the next role in role list
493 0 : NS_LossyConvertUTF16toASCII role(tokenizer.nextToken());
494 0 : PRUint32 low = 0;
495 0 : PRUint32 high = nsARIAMap::gWAIRoleMapLength;
496 0 : while (low < high) {
497 0 : PRUint32 index = (low + high) / 2;
498 0 : PRInt32 compare = PL_strcmp(role.get(), nsARIAMap::gWAIRoleMap[index].roleString);
499 0 : if (compare == 0) {
500 : // The role attribute maps to an entry in the role table
501 0 : return &nsARIAMap::gWAIRoleMap[index];
502 : }
503 0 : if (compare < 0) {
504 0 : high = index;
505 : }
506 : else {
507 0 : low = index + 1;
508 : }
509 : }
510 : }
511 :
512 : // Always use some entry if there is a non-empty role string
513 : // To ensure an accessible object is created
514 0 : return &nsARIAMap::gLandmarkRoleMap;
515 : }
516 :
517 : PRUint8
518 0 : nsAccUtils::GetAttributeCharacteristics(nsIAtom* aAtom)
519 : {
520 0 : for (PRUint32 i = 0; i < nsARIAMap::gWAIUnivAttrMapLength; i++)
521 0 : if (*nsARIAMap::gWAIUnivAttrMap[i].attributeName == aAtom)
522 0 : return nsARIAMap::gWAIUnivAttrMap[i].characteristics;
523 :
524 0 : return 0;
525 : }
526 :
527 : bool
528 0 : nsAccUtils::GetLiveAttrValue(PRUint32 aRule, nsAString& aValue)
529 : {
530 0 : switch (aRule) {
531 : case eOffLiveAttr:
532 0 : aValue = NS_LITERAL_STRING("off");
533 0 : return true;
534 : case ePoliteLiveAttr:
535 0 : aValue = NS_LITERAL_STRING("polite");
536 0 : return true;
537 : }
538 :
539 0 : return false;
540 : }
541 :
542 : #ifdef DEBUG_A11Y
543 :
544 : bool
545 : nsAccUtils::IsTextInterfaceSupportCorrect(nsAccessible *aAccessible)
546 : {
547 : // Don't test for accessible docs, it makes us create accessibles too
548 : // early and fire mutation events before we need to
549 : if (aAccessible->IsDoc())
550 : return true;
551 :
552 : bool foundText = false;
553 : PRInt32 childCount = aAccessible->GetChildCount();
554 : for (PRint32 childIdx = 0; childIdx < childCount; childIdx++) {
555 : nsAccessible *child = GetChildAt(childIdx);
556 : if (IsText(child)) {
557 : foundText = true;
558 : break;
559 : }
560 : }
561 :
562 : if (foundText) {
563 : // found text child node
564 : nsCOMPtr<nsIAccessibleText> text = do_QueryObject(aAccessible);
565 : if (!text)
566 : return false;
567 : }
568 :
569 : return true;
570 : }
571 : #endif
572 :
573 : PRUint32
574 0 : nsAccUtils::TextLength(nsAccessible *aAccessible)
575 : {
576 0 : if (!IsText(aAccessible))
577 0 : return 1;
578 :
579 0 : nsTextAccessible* textLeaf = aAccessible->AsTextLeaf();
580 0 : if (textLeaf)
581 0 : return textLeaf->Text().Length();
582 :
583 : // For list bullets (or anything other accessible which would compute its own
584 : // text. They don't have their own frame.
585 : // XXX In the future, list bullets may have frame and anon content, so
586 : // we should be able to remove this at that point
587 0 : nsAutoString text;
588 0 : aAccessible->AppendTextTo(text); // Get all the text
589 0 : return text.Length();
590 : }
591 :
592 : bool
593 0 : nsAccUtils::MustPrune(nsIAccessible *aAccessible)
594 : {
595 0 : PRUint32 role = nsAccUtils::Role(aAccessible);
596 :
597 : // We don't prune buttons any more however AT don't expect children inside of
598 : // button in general, we allow menu buttons to have children to make them
599 : // accessible.
600 : return role == nsIAccessibleRole::ROLE_MENUITEM ||
601 : role == nsIAccessibleRole::ROLE_COMBOBOX_OPTION ||
602 : role == nsIAccessibleRole::ROLE_OPTION ||
603 : role == nsIAccessibleRole::ROLE_ENTRY ||
604 : role == nsIAccessibleRole::ROLE_FLAT_EQUATION ||
605 : role == nsIAccessibleRole::ROLE_PASSWORD_TEXT ||
606 : role == nsIAccessibleRole::ROLE_TOGGLE_BUTTON ||
607 : role == nsIAccessibleRole::ROLE_GRAPHIC ||
608 : role == nsIAccessibleRole::ROLE_SLIDER ||
609 : role == nsIAccessibleRole::ROLE_PROGRESSBAR ||
610 0 : role == nsIAccessibleRole::ROLE_SEPARATOR;
611 : }
612 :
613 : nsresult
614 0 : nsAccUtils::GetHeaderCellsFor(nsIAccessibleTable *aTable,
615 : nsIAccessibleTableCell *aCell,
616 : PRInt32 aRowOrColHeaderCells, nsIArray **aCells)
617 : {
618 0 : nsresult rv = NS_OK;
619 0 : nsCOMPtr<nsIMutableArray> cells = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
620 0 : NS_ENSURE_SUCCESS(rv, rv);
621 :
622 0 : PRInt32 rowIdx = -1;
623 0 : rv = aCell->GetRowIndex(&rowIdx);
624 0 : NS_ENSURE_SUCCESS(rv, rv);
625 :
626 0 : PRInt32 colIdx = -1;
627 0 : rv = aCell->GetColumnIndex(&colIdx);
628 0 : NS_ENSURE_SUCCESS(rv, rv);
629 :
630 0 : bool moveToLeft = aRowOrColHeaderCells == eRowHeaderCells;
631 :
632 : // Move to the left or top to find row header cells or column header cells.
633 0 : PRInt32 index = (moveToLeft ? colIdx : rowIdx) - 1;
634 0 : for (; index >= 0; index--) {
635 0 : PRInt32 curRowIdx = moveToLeft ? rowIdx : index;
636 0 : PRInt32 curColIdx = moveToLeft ? index : colIdx;
637 :
638 0 : nsCOMPtr<nsIAccessible> cell;
639 0 : rv = aTable->GetCellAt(curRowIdx, curColIdx, getter_AddRefs(cell));
640 0 : NS_ENSURE_SUCCESS(rv, rv);
641 :
642 : nsCOMPtr<nsIAccessibleTableCell> tableCellAcc =
643 0 : do_QueryInterface(cell);
644 :
645 : // GetCellAt should always return an nsIAccessibleTableCell (XXX Bug 587529)
646 0 : NS_ENSURE_STATE(tableCellAcc);
647 :
648 0 : PRInt32 origIdx = 1;
649 0 : if (moveToLeft)
650 0 : rv = tableCellAcc->GetColumnIndex(&origIdx);
651 : else
652 0 : rv = tableCellAcc->GetRowIndex(&origIdx);
653 0 : NS_ENSURE_SUCCESS(rv, rv);
654 :
655 0 : if (origIdx == index) {
656 : // Append original header cells only.
657 0 : PRUint32 role = Role(cell);
658 : bool isHeader = moveToLeft ?
659 : role == nsIAccessibleRole::ROLE_ROWHEADER :
660 0 : role == nsIAccessibleRole::ROLE_COLUMNHEADER;
661 :
662 0 : if (isHeader)
663 0 : cells->AppendElement(cell, false);
664 : }
665 : }
666 :
667 0 : NS_ADDREF(*aCells = cells);
668 0 : return NS_OK;
669 : }
|