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 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * John Gaunt (jgaunt@netscape.com)
24 : * Aaron Leventhal (aaronl@netscape.com)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsAccessible.h"
41 :
42 : #include "nsIXBLAccessible.h"
43 :
44 : #include "AccGroupInfo.h"
45 : #include "AccIterator.h"
46 : #include "nsAccUtils.h"
47 : #include "nsAccEvent.h"
48 : #include "nsAccessibleRelation.h"
49 : #include "nsAccessibilityService.h"
50 : #include "nsAccTreeWalker.h"
51 : #include "nsIAccessibleRelation.h"
52 : #include "nsEventShell.h"
53 : #include "nsRootAccessible.h"
54 : #include "nsTextEquivUtils.h"
55 : #include "Relation.h"
56 : #include "Role.h"
57 : #include "States.h"
58 : #include "StyleInfo.h"
59 :
60 : #include "nsIDOMCSSValue.h"
61 : #include "nsIDOMCSSPrimitiveValue.h"
62 : #include "nsIDOMElement.h"
63 : #include "nsIDOMDocument.h"
64 : #include "nsIDOMDocumentXBL.h"
65 : #include "nsIDOMHTMLDocument.h"
66 : #include "nsIDOMHTMLFormElement.h"
67 : #include "nsIDOMNodeFilter.h"
68 : #include "nsIDOMHTMLElement.h"
69 : #include "nsIDOMTreeWalker.h"
70 : #include "nsIDOMXULButtonElement.h"
71 : #include "nsIDOMXULDocument.h"
72 : #include "nsIDOMXULElement.h"
73 : #include "nsIDOMXULLabelElement.h"
74 : #include "nsIDOMXULSelectCntrlEl.h"
75 : #include "nsIDOMXULSelectCntrlItemEl.h"
76 : #include "nsPIDOMWindow.h"
77 :
78 : #include "nsIDocument.h"
79 : #include "nsIContent.h"
80 : #include "nsIForm.h"
81 : #include "nsIFormControl.h"
82 :
83 : #include "nsLayoutUtils.h"
84 : #include "nsIPresShell.h"
85 : #include "nsPresContext.h"
86 : #include "nsIFrame.h"
87 : #include "nsIView.h"
88 : #include "nsIDocShellTreeItem.h"
89 : #include "nsIScrollableFrame.h"
90 : #include "nsFocusManager.h"
91 :
92 : #include "nsXPIDLString.h"
93 : #include "nsUnicharUtils.h"
94 : #include "nsReadableUtils.h"
95 : #include "prdtoa.h"
96 : #include "nsIAtom.h"
97 : #include "nsIURI.h"
98 : #include "nsArrayUtils.h"
99 : #include "nsIMutableArray.h"
100 : #include "nsIObserverService.h"
101 : #include "nsIServiceManager.h"
102 : #include "nsWhitespaceTokenizer.h"
103 : #include "nsAttrName.h"
104 : #include "nsNetUtil.h"
105 : #include "nsEventStates.h"
106 :
107 : #ifdef NS_DEBUG
108 : #include "nsIDOMCharacterData.h"
109 : #endif
110 :
111 : #include "mozilla/unused.h"
112 : #include "mozilla/Preferences.h"
113 : #include "mozilla/dom/Element.h"
114 :
115 : using namespace mozilla;
116 : using namespace mozilla::a11y;
117 :
118 :
119 : ////////////////////////////////////////////////////////////////////////////////
120 : // nsAccessible. nsISupports
121 :
122 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessible)
123 :
124 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsAccessible, nsAccessNode)
125 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mParent");
126 0 : cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mParent.get()));
127 :
128 0 : PRUint32 i, length = tmp->mChildren.Length();
129 0 : for (i = 0; i < length; ++i) {
130 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
131 0 : cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mChildren[i].get()));
132 : }
133 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
134 :
135 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsAccessible, nsAccessNode)
136 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
137 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildren)
138 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
139 :
140 0 : NS_IMPL_ADDREF_INHERITED(nsAccessible, nsAccessNode)
141 0 : NS_IMPL_RELEASE_INHERITED(nsAccessible, nsAccessNode)
142 :
143 0 : nsresult nsAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
144 : {
145 : // Custom-built QueryInterface() knows when we support nsIAccessibleSelectable
146 : // based on role attribute and aria-multiselectable
147 0 : *aInstancePtr = nsnull;
148 :
149 0 : if (aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
150 0 : *aInstancePtr = &NS_CYCLE_COLLECTION_NAME(nsAccessible);
151 0 : return NS_OK;
152 : }
153 :
154 0 : if (aIID.Equals(NS_GET_IID(nsIAccessible))) {
155 0 : *aInstancePtr = static_cast<nsIAccessible*>(this);
156 0 : NS_ADDREF_THIS();
157 0 : return NS_OK;
158 : }
159 :
160 0 : if (aIID.Equals(NS_GET_IID(nsAccessible))) {
161 0 : *aInstancePtr = static_cast<nsAccessible*>(this);
162 0 : NS_ADDREF_THIS();
163 0 : return NS_OK;
164 : }
165 :
166 0 : if (aIID.Equals(NS_GET_IID(nsIAccessibleSelectable))) {
167 0 : if (IsSelect()) {
168 0 : *aInstancePtr = static_cast<nsIAccessibleSelectable*>(this);
169 0 : NS_ADDREF_THIS();
170 0 : return NS_OK;
171 : }
172 0 : return NS_ERROR_NO_INTERFACE;
173 : }
174 :
175 0 : if (aIID.Equals(NS_GET_IID(nsIAccessibleValue))) {
176 0 : if (mRoleMapEntry && mRoleMapEntry->valueRule != eNoValue) {
177 0 : *aInstancePtr = static_cast<nsIAccessibleValue*>(this);
178 0 : NS_ADDREF_THIS();
179 0 : return NS_OK;
180 : }
181 : }
182 :
183 0 : if (aIID.Equals(NS_GET_IID(nsIAccessibleHyperLink))) {
184 0 : if (IsLink()) {
185 0 : *aInstancePtr = static_cast<nsIAccessibleHyperLink*>(this);
186 0 : NS_ADDREF_THIS();
187 0 : return NS_OK;
188 : }
189 0 : return NS_ERROR_NO_INTERFACE;
190 : }
191 :
192 0 : return nsAccessNodeWrap::QueryInterface(aIID, aInstancePtr);
193 : }
194 :
195 0 : nsAccessible::nsAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
196 : nsAccessNodeWrap(aContent, aDoc),
197 : mParent(nsnull), mIndexInParent(-1), mFlags(eChildrenUninitialized),
198 0 : mIndexOfEmbeddedChild(-1), mRoleMapEntry(nsnull)
199 : {
200 : #ifdef NS_DEBUG_X
201 : {
202 : nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aShell));
203 : printf(">>> %p Created Acc - DOM: %p PS: %p",
204 : (void*)static_cast<nsIAccessible*>(this), (void*)aNode,
205 : (void*)shell.get());
206 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
207 : if (content) {
208 : printf(" Con: %s@%p",
209 : NS_ConvertUTF16toUTF8(content->NodeInfo()->QualifiedName()).get(),
210 : (void *)content.get());
211 : nsAutoString buf;
212 : if (NS_SUCCEEDED(GetName(buf))) {
213 : printf(" Name:[%s]", NS_ConvertUTF16toUTF8(buf).get());
214 : }
215 : }
216 : printf("\n");
217 : }
218 : #endif
219 0 : }
220 :
221 : //-----------------------------------------------------
222 : // destruction
223 : //-----------------------------------------------------
224 0 : nsAccessible::~nsAccessible()
225 : {
226 0 : }
227 :
228 : void
229 0 : nsAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
230 : {
231 0 : mRoleMapEntry = aRoleMapEntry;
232 0 : }
233 :
234 : NS_IMETHODIMP
235 0 : nsAccessible::GetDocument(nsIAccessibleDocument **aDocument)
236 : {
237 0 : NS_ENSURE_ARG_POINTER(aDocument);
238 :
239 0 : NS_IF_ADDREF(*aDocument = Document());
240 0 : return NS_OK;
241 : }
242 :
243 : NS_IMETHODIMP
244 0 : nsAccessible::GetDOMNode(nsIDOMNode **aDOMNode)
245 : {
246 0 : NS_ENSURE_ARG_POINTER(aDOMNode);
247 0 : *aDOMNode = nsnull;
248 :
249 0 : nsINode *node = GetNode();
250 0 : if (node)
251 0 : CallQueryInterface(node, aDOMNode);
252 :
253 0 : return NS_OK;
254 : }
255 :
256 : NS_IMETHODIMP
257 0 : nsAccessible::GetRootDocument(nsIAccessibleDocument **aRootDocument)
258 : {
259 0 : NS_ENSURE_ARG_POINTER(aRootDocument);
260 :
261 0 : nsRootAccessible* rootDocument = RootAccessible();
262 0 : NS_IF_ADDREF(*aRootDocument = rootDocument);
263 0 : return NS_OK;
264 : }
265 :
266 : NS_IMETHODIMP
267 0 : nsAccessible::GetInnerHTML(nsAString& aInnerHTML)
268 : {
269 0 : aInnerHTML.Truncate();
270 :
271 0 : nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
272 0 : NS_ENSURE_TRUE(htmlElement, NS_ERROR_NULL_POINTER);
273 :
274 0 : return htmlElement->GetInnerHTML(aInnerHTML);
275 : }
276 :
277 : NS_IMETHODIMP
278 0 : nsAccessible::GetLanguage(nsAString& aLanguage)
279 : {
280 0 : Language(aLanguage);
281 0 : return NS_OK;
282 : }
283 :
284 : NS_IMETHODIMP
285 0 : nsAccessible::GetName(nsAString& aName)
286 : {
287 0 : aName.Truncate();
288 :
289 0 : if (IsDefunct())
290 0 : return NS_ERROR_FAILURE;
291 :
292 0 : GetARIAName(aName);
293 0 : if (!aName.IsEmpty())
294 0 : return NS_OK;
295 :
296 0 : nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mContent));
297 0 : if (xblAccessible) {
298 0 : xblAccessible->GetAccessibleName(aName);
299 0 : if (!aName.IsEmpty())
300 0 : return NS_OK;
301 : }
302 :
303 0 : nsresult rv = GetNameInternal(aName);
304 0 : NS_ENSURE_SUCCESS(rv, rv);
305 :
306 0 : if (!aName.IsEmpty())
307 0 : return NS_OK;
308 :
309 : // In the end get the name from tooltip.
310 0 : nsIAtom *tooltipAttr = nsnull;
311 :
312 0 : if (mContent->IsHTML())
313 0 : tooltipAttr = nsGkAtoms::title;
314 0 : else if (mContent->IsXUL())
315 0 : tooltipAttr = nsGkAtoms::tooltiptext;
316 : else
317 0 : return NS_OK;
318 :
319 : // XXX: if CompressWhiteSpace worked on nsAString we could avoid a copy.
320 0 : nsAutoString name;
321 0 : if (mContent->GetAttr(kNameSpaceID_None, tooltipAttr, name)) {
322 0 : name.CompressWhitespace();
323 0 : aName = name;
324 0 : return NS_OK_NAME_FROM_TOOLTIP;
325 : }
326 :
327 0 : if (rv != NS_OK_EMPTY_NAME)
328 0 : aName.SetIsVoid(true);
329 :
330 0 : return NS_OK;
331 : }
332 :
333 : NS_IMETHODIMP
334 0 : nsAccessible::GetDescription(nsAString& aDescription)
335 : {
336 0 : if (IsDefunct())
337 0 : return NS_ERROR_FAILURE;
338 :
339 0 : nsAutoString desc;
340 0 : Description(desc);
341 0 : aDescription.Assign(desc);
342 :
343 0 : return NS_OK;
344 : }
345 :
346 : void
347 0 : nsAccessible::Description(nsString& aDescription)
348 : {
349 : // There are 4 conditions that make an accessible have no accDescription:
350 : // 1. it's a text node; or
351 : // 2. It has no DHTML describedby property
352 : // 3. it doesn't have an accName; or
353 : // 4. its title attribute already equals to its accName nsAutoString name;
354 :
355 0 : if (mContent->IsNodeOfType(nsINode::eTEXT))
356 0 : return;
357 :
358 : nsTextEquivUtils::
359 : GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
360 0 : aDescription);
361 :
362 0 : if (aDescription.IsEmpty()) {
363 0 : bool isXUL = mContent->IsXUL();
364 0 : if (isXUL) {
365 : // Try XUL <description control="[id]">description text</description>
366 0 : XULDescriptionIterator iter(Document(), mContent);
367 0 : nsAccessible* descr = nsnull;
368 0 : while ((descr = iter.Next()))
369 : nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
370 0 : &aDescription);
371 : }
372 :
373 0 : if (aDescription.IsEmpty()) {
374 : nsIAtom *descAtom = isXUL ? nsGkAtoms::tooltiptext :
375 0 : nsGkAtoms::title;
376 0 : if (mContent->GetAttr(kNameSpaceID_None, descAtom, aDescription)) {
377 0 : nsAutoString name;
378 0 : GetName(name);
379 0 : if (name.IsEmpty() || aDescription == name)
380 : // Don't use tooltip for a description if this object
381 : // has no name or the tooltip is the same as the name
382 0 : aDescription.Truncate();
383 : }
384 : }
385 : }
386 0 : aDescription.CompressWhitespace();
387 : }
388 :
389 : NS_IMETHODIMP
390 0 : nsAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
391 : {
392 0 : aAccessKey.Truncate();
393 :
394 0 : if (IsDefunct())
395 0 : return NS_ERROR_FAILURE;
396 :
397 0 : AccessKey().ToString(aAccessKey);
398 0 : return NS_OK;
399 : }
400 :
401 : KeyBinding
402 0 : nsAccessible::AccessKey() const
403 : {
404 0 : PRUint32 key = nsCoreUtils::GetAccessKeyFor(mContent);
405 0 : if (!key && mContent->IsElement()) {
406 0 : nsAccessible* label = nsnull;
407 :
408 : // Copy access key from label node.
409 0 : if (mContent->IsHTML()) {
410 : // Unless it is labeled via an ancestor <label>, in which case that would
411 : // be redundant.
412 : HTMLLabelIterator iter(Document(), this,
413 0 : HTMLLabelIterator::eSkipAncestorLabel);
414 0 : label = iter.Next();
415 :
416 0 : } else if (mContent->IsXUL()) {
417 0 : XULLabelIterator iter(Document(), mContent);
418 0 : label = iter.Next();
419 : }
420 :
421 0 : if (label)
422 0 : key = nsCoreUtils::GetAccessKeyFor(label->GetContent());
423 : }
424 :
425 0 : if (!key)
426 0 : return KeyBinding();
427 :
428 : // Get modifier mask. Use ui.key.generalAccessKey (unless it is -1).
429 0 : switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
430 : case -1:
431 : break;
432 : case nsIDOMKeyEvent::DOM_VK_SHIFT:
433 0 : return KeyBinding(key, KeyBinding::kShift);
434 : case nsIDOMKeyEvent::DOM_VK_CONTROL:
435 0 : return KeyBinding(key, KeyBinding::kControl);
436 : case nsIDOMKeyEvent::DOM_VK_ALT:
437 0 : return KeyBinding(key, KeyBinding::kAlt);
438 : case nsIDOMKeyEvent::DOM_VK_META:
439 0 : return KeyBinding(key, KeyBinding::kMeta);
440 : default:
441 0 : return KeyBinding();
442 : }
443 :
444 : // Determine the access modifier used in this context.
445 0 : nsIDocument* document = mContent->GetCurrentDoc();
446 0 : if (!document)
447 0 : return KeyBinding();
448 0 : nsCOMPtr<nsISupports> container = document->GetContainer();
449 0 : if (!container)
450 0 : return KeyBinding();
451 0 : nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
452 0 : if (!treeItem)
453 0 : return KeyBinding();
454 :
455 0 : nsresult rv = NS_ERROR_FAILURE;
456 0 : PRInt32 itemType = 0, modifierMask = 0;
457 0 : treeItem->GetItemType(&itemType);
458 0 : switch (itemType) {
459 : case nsIDocShellTreeItem::typeChrome:
460 0 : rv = Preferences::GetInt("ui.key.chromeAccess", &modifierMask);
461 0 : break;
462 : case nsIDocShellTreeItem::typeContent:
463 0 : rv = Preferences::GetInt("ui.key.contentAccess", &modifierMask);
464 0 : break;
465 : }
466 :
467 0 : return NS_SUCCEEDED(rv) ? KeyBinding(key, modifierMask) : KeyBinding();
468 : }
469 :
470 : KeyBinding
471 0 : nsAccessible::KeyboardShortcut() const
472 : {
473 0 : return KeyBinding();
474 : }
475 :
476 : NS_IMETHODIMP
477 0 : nsAccessible::GetParent(nsIAccessible **aParent)
478 : {
479 0 : NS_ENSURE_ARG_POINTER(aParent);
480 0 : if (IsDefunct())
481 0 : return NS_ERROR_FAILURE;
482 :
483 0 : NS_IF_ADDREF(*aParent = Parent());
484 0 : return *aParent ? NS_OK : NS_ERROR_FAILURE;
485 : }
486 :
487 : /* readonly attribute nsIAccessible nextSibling; */
488 : NS_IMETHODIMP
489 0 : nsAccessible::GetNextSibling(nsIAccessible **aNextSibling)
490 : {
491 0 : NS_ENSURE_ARG_POINTER(aNextSibling);
492 0 : *aNextSibling = nsnull;
493 :
494 0 : if (IsDefunct())
495 0 : return NS_ERROR_FAILURE;
496 :
497 0 : nsresult rv = NS_OK;
498 0 : NS_IF_ADDREF(*aNextSibling = GetSiblingAtOffset(1, &rv));
499 0 : return rv;
500 : }
501 :
502 : /* readonly attribute nsIAccessible previousSibling; */
503 : NS_IMETHODIMP
504 0 : nsAccessible::GetPreviousSibling(nsIAccessible * *aPreviousSibling)
505 : {
506 0 : NS_ENSURE_ARG_POINTER(aPreviousSibling);
507 0 : *aPreviousSibling = nsnull;
508 :
509 0 : if (IsDefunct())
510 0 : return NS_ERROR_FAILURE;
511 :
512 0 : nsresult rv = NS_OK;
513 0 : NS_IF_ADDREF(*aPreviousSibling = GetSiblingAtOffset(-1, &rv));
514 0 : return rv;
515 : }
516 :
517 : /* readonly attribute nsIAccessible firstChild; */
518 : NS_IMETHODIMP
519 0 : nsAccessible::GetFirstChild(nsIAccessible **aFirstChild)
520 : {
521 0 : NS_ENSURE_ARG_POINTER(aFirstChild);
522 0 : *aFirstChild = nsnull;
523 :
524 0 : if (IsDefunct())
525 0 : return NS_ERROR_FAILURE;
526 :
527 0 : PRInt32 childCount = GetChildCount();
528 0 : NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
529 :
530 0 : if (childCount > 0)
531 0 : NS_ADDREF(*aFirstChild = GetChildAt(0));
532 :
533 0 : return NS_OK;
534 : }
535 :
536 : /* readonly attribute nsIAccessible lastChild; */
537 : NS_IMETHODIMP
538 0 : nsAccessible::GetLastChild(nsIAccessible **aLastChild)
539 : {
540 0 : NS_ENSURE_ARG_POINTER(aLastChild);
541 0 : *aLastChild = nsnull;
542 :
543 0 : if (IsDefunct())
544 0 : return NS_ERROR_FAILURE;
545 :
546 0 : PRInt32 childCount = GetChildCount();
547 0 : NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
548 :
549 0 : NS_IF_ADDREF(*aLastChild = GetChildAt(childCount - 1));
550 0 : return NS_OK;
551 : }
552 :
553 : NS_IMETHODIMP
554 0 : nsAccessible::GetChildAt(PRInt32 aChildIndex, nsIAccessible **aChild)
555 : {
556 0 : NS_ENSURE_ARG_POINTER(aChild);
557 0 : *aChild = nsnull;
558 :
559 0 : if (IsDefunct())
560 0 : return NS_ERROR_FAILURE;
561 :
562 0 : PRInt32 childCount = GetChildCount();
563 0 : NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
564 :
565 : // If child index is negative, then return last child.
566 : // XXX: do we really need this?
567 0 : if (aChildIndex < 0)
568 0 : aChildIndex = childCount - 1;
569 :
570 0 : nsAccessible* child = GetChildAt(aChildIndex);
571 0 : if (!child)
572 0 : return NS_ERROR_INVALID_ARG;
573 :
574 0 : NS_ADDREF(*aChild = child);
575 0 : return NS_OK;
576 : }
577 :
578 : // readonly attribute nsIArray children;
579 : NS_IMETHODIMP
580 0 : nsAccessible::GetChildren(nsIArray **aOutChildren)
581 : {
582 0 : NS_ENSURE_ARG_POINTER(aOutChildren);
583 0 : *aOutChildren = nsnull;
584 :
585 0 : if (IsDefunct())
586 0 : return NS_ERROR_FAILURE;
587 :
588 0 : PRInt32 childCount = GetChildCount();
589 0 : NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
590 :
591 0 : nsresult rv = NS_OK;
592 : nsCOMPtr<nsIMutableArray> children =
593 0 : do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
594 0 : NS_ENSURE_SUCCESS(rv, rv);
595 :
596 0 : for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
597 0 : nsIAccessible* child = GetChildAt(childIdx);
598 0 : children->AppendElement(child, false);
599 : }
600 :
601 0 : NS_ADDREF(*aOutChildren = children);
602 0 : return NS_OK;
603 : }
604 :
605 : bool
606 0 : nsAccessible::CanHaveAnonChildren()
607 : {
608 0 : return true;
609 : }
610 :
611 : /* readonly attribute long childCount; */
612 : NS_IMETHODIMP
613 0 : nsAccessible::GetChildCount(PRInt32 *aChildCount)
614 : {
615 0 : NS_ENSURE_ARG_POINTER(aChildCount);
616 :
617 0 : if (IsDefunct())
618 0 : return NS_ERROR_FAILURE;
619 :
620 0 : *aChildCount = GetChildCount();
621 0 : return *aChildCount != -1 ? NS_OK : NS_ERROR_FAILURE;
622 : }
623 :
624 : /* readonly attribute long indexInParent; */
625 : NS_IMETHODIMP
626 0 : nsAccessible::GetIndexInParent(PRInt32 *aIndexInParent)
627 : {
628 0 : NS_ENSURE_ARG_POINTER(aIndexInParent);
629 :
630 0 : *aIndexInParent = IndexInParent();
631 0 : return *aIndexInParent != -1 ? NS_OK : NS_ERROR_FAILURE;
632 : }
633 :
634 : void
635 0 : nsAccessible::TranslateString(const nsAString& aKey, nsAString& aStringOut)
636 : {
637 0 : nsXPIDLString xsValue;
638 :
639 0 : gStringBundle->GetStringFromName(PromiseFlatString(aKey).get(), getter_Copies(xsValue));
640 0 : aStringOut.Assign(xsValue);
641 0 : }
642 :
643 : PRUint64
644 0 : nsAccessible::VisibilityState()
645 : {
646 0 : PRUint64 vstates = states::INVISIBLE | states::OFFSCREEN;
647 :
648 0 : nsIFrame* frame = GetFrame();
649 0 : if (!frame)
650 0 : return vstates;
651 :
652 0 : nsIPresShell* shell(mDoc->PresShell());
653 0 : if (!shell)
654 0 : return vstates;
655 :
656 : // We need to know if at least a kMinPixels around the object is visible,
657 : // otherwise it will be marked states::OFFSCREEN.
658 0 : const PRUint16 kMinPixels = 12;
659 0 : const nsSize frameSize = frame->GetSize();
660 : const nsRectVisibility rectVisibility =
661 0 : shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
662 0 : nsPresContext::CSSPixelsToAppUnits(kMinPixels));
663 :
664 0 : if (rectVisibility == nsRectVisibility_kVisible)
665 0 : vstates &= ~states::OFFSCREEN;
666 :
667 : // Zero area rects can occur in the first frame of a multi-frame text flow,
668 : // in which case the rendered text is not empty and the frame should not be
669 : // marked invisible.
670 : // XXX Can we just remove this check? Why do we need to mark empty
671 : // text invisible?
672 0 : if (frame->GetType() == nsGkAtoms::textFrame &&
673 0 : !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
674 0 : frame->GetRect().IsEmpty()) {
675 0 : nsAutoString renderedText;
676 0 : frame->GetRenderedText(&renderedText, nsnull, nsnull, 0, 1);
677 0 : if (renderedText.IsEmpty())
678 0 : return vstates;
679 :
680 : }
681 :
682 : // XXX Do we really need to cross from content to chrome ancestor?
683 0 : if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY))
684 0 : return vstates;
685 :
686 : // Assume we are visible enough.
687 0 : return vstates &= ~states::INVISIBLE;
688 : }
689 :
690 : PRUint64
691 0 : nsAccessible::NativeState()
692 : {
693 0 : PRUint64 state = 0;
694 :
695 0 : nsDocAccessible* document = Document();
696 0 : if (!document || !document->IsInDocument(this))
697 0 : state |= states::STALE;
698 :
699 0 : bool disabled = false;
700 0 : if (mContent->IsElement()) {
701 0 : nsEventStates elementState = mContent->AsElement()->State();
702 :
703 0 : if (elementState.HasState(NS_EVENT_STATE_INVALID))
704 0 : state |= states::INVALID;
705 :
706 0 : if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
707 0 : state |= states::REQUIRED;
708 :
709 0 : disabled = mContent->IsHTML() ?
710 0 : (elementState.HasState(NS_EVENT_STATE_DISABLED)) :
711 0 : (mContent->AttrValueIs(kNameSpaceID_None,
712 : nsGkAtoms::disabled,
713 : nsGkAtoms::_true,
714 0 : eCaseMatters));
715 : }
716 :
717 : // Set unavailable state based on disabled state, otherwise set focus states
718 0 : if (disabled) {
719 0 : state |= states::UNAVAILABLE;
720 : }
721 0 : else if (mContent->IsElement()) {
722 0 : nsIFrame* frame = GetFrame();
723 0 : if (frame && frame->IsFocusable())
724 0 : state |= states::FOCUSABLE;
725 :
726 0 : if (FocusMgr()->IsFocused(this))
727 0 : state |= states::FOCUSED;
728 : }
729 :
730 : // Gather states::INVISIBLE and states::OFFSCREEN flags for this object.
731 0 : state |= VisibilityState();
732 :
733 0 : nsIFrame *frame = GetFrame();
734 0 : if (frame && (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
735 0 : state |= states::FLOATING;
736 :
737 : // Check if a XUL element has the popup attribute (an attached popup menu).
738 0 : if (mContent->IsXUL())
739 0 : if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
740 0 : state |= states::HASPOPUP;
741 :
742 : // Add 'linked' state for simple xlink.
743 0 : if (nsCoreUtils::IsXLink(mContent))
744 0 : state |= states::LINKED;
745 :
746 0 : return state;
747 : }
748 :
749 : /* readonly attribute boolean focusedChild; */
750 : NS_IMETHODIMP
751 0 : nsAccessible::GetFocusedChild(nsIAccessible** aChild)
752 : {
753 0 : NS_ENSURE_ARG_POINTER(aChild);
754 0 : *aChild = nsnull;
755 :
756 0 : if (IsDefunct())
757 0 : return NS_ERROR_FAILURE;
758 :
759 0 : NS_IF_ADDREF(*aChild = FocusedChild());
760 0 : return NS_OK;
761 : }
762 :
763 : nsAccessible*
764 0 : nsAccessible::FocusedChild()
765 : {
766 0 : nsAccessible* focus = FocusMgr()->FocusedAccessible();
767 0 : if (focus && (focus == this || focus->Parent() == this))
768 0 : return focus;
769 :
770 0 : return nsnull;
771 : }
772 :
773 : // nsAccessible::ChildAtPoint()
774 : nsAccessible*
775 0 : nsAccessible::ChildAtPoint(PRInt32 aX, PRInt32 aY,
776 : EWhichChildAtPoint aWhichChild)
777 : {
778 : // If we can't find the point in a child, we will return the fallback answer:
779 : // we return |this| if the point is within it, otherwise nsnull.
780 0 : PRInt32 x = 0, y = 0, width = 0, height = 0;
781 0 : nsresult rv = GetBounds(&x, &y, &width, &height);
782 0 : NS_ENSURE_SUCCESS(rv, nsnull);
783 :
784 0 : nsAccessible* fallbackAnswer = nsnull;
785 0 : if (aX >= x && aX < x + width && aY >= y && aY < y + height)
786 0 : fallbackAnswer = this;
787 :
788 0 : if (nsAccUtils::MustPrune(this)) // Do not dig any further
789 0 : return fallbackAnswer;
790 :
791 : // Search an accessible at the given point starting from accessible document
792 : // because containing block (see CSS2) for out of flow element (for example,
793 : // absolutely positioned element) may be different from its DOM parent and
794 : // therefore accessible for containing block may be different from accessible
795 : // for DOM parent but GetFrameForPoint() should be called for containing block
796 : // to get an out of flow element.
797 0 : nsDocAccessible* accDocument = Document();
798 0 : NS_ENSURE_TRUE(accDocument, nsnull);
799 :
800 0 : nsIFrame *frame = accDocument->GetFrame();
801 0 : NS_ENSURE_TRUE(frame, nsnull);
802 :
803 0 : nsPresContext *presContext = frame->PresContext();
804 :
805 0 : nsRect screenRect = frame->GetScreenRectInAppUnits();
806 0 : nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.x,
807 0 : presContext->DevPixelsToAppUnits(aY) - screenRect.y);
808 :
809 0 : nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
810 0 : nsIFrame *foundFrame = presShell->GetFrameForPoint(frame, offset);
811 :
812 0 : nsIContent* content = nsnull;
813 0 : if (!foundFrame || !(content = foundFrame->GetContent()))
814 0 : return fallbackAnswer;
815 :
816 : // Get accessible for the node with the point or the first accessible in
817 : // the DOM parent chain.
818 0 : nsDocAccessible* contentDocAcc = GetAccService()->
819 0 : GetDocAccessible(content->OwnerDoc());
820 :
821 : // contentDocAcc in some circumstances can be NULL. See bug 729861
822 0 : NS_ASSERTION(contentDocAcc, "could not get the document accessible");
823 0 : if (!contentDocAcc)
824 0 : return fallbackAnswer;
825 :
826 0 : nsAccessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
827 0 : if (!accessible)
828 0 : return fallbackAnswer;
829 :
830 0 : if (accessible == this) {
831 : // Manually walk through accessible children and see if the are within this
832 : // point. Skip offscreen or invisible accessibles. This takes care of cases
833 : // where layout won't walk into things for us, such as image map areas and
834 : // sub documents (XXX: subdocuments should be handled by methods of
835 : // nsOuterDocAccessibles).
836 0 : PRInt32 childCount = GetChildCount();
837 0 : for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
838 0 : nsAccessible *child = GetChildAt(childIdx);
839 :
840 : PRInt32 childX, childY, childWidth, childHeight;
841 0 : child->GetBounds(&childX, &childY, &childWidth, &childHeight);
842 0 : if (aX >= childX && aX < childX + childWidth &&
843 : aY >= childY && aY < childY + childHeight &&
844 0 : (child->State() & states::INVISIBLE) == 0) {
845 :
846 0 : if (aWhichChild == eDeepestChild)
847 0 : return child->ChildAtPoint(aX, aY, eDeepestChild);
848 :
849 0 : return child;
850 : }
851 : }
852 :
853 : // The point is in this accessible but not in a child. We are allowed to
854 : // return |this| as the answer.
855 0 : return accessible;
856 : }
857 :
858 : // Since DOM node of obtained accessible may be out of flow then we should
859 : // ensure obtained accessible is a child of this accessible.
860 0 : nsAccessible* child = accessible;
861 0 : while (true) {
862 0 : nsAccessible* parent = child->Parent();
863 0 : if (!parent) {
864 : // Reached the top of the hierarchy. These bounds were inside an
865 : // accessible that is not a descendant of this one.
866 0 : return fallbackAnswer;
867 : }
868 :
869 0 : if (parent == this)
870 0 : return aWhichChild == eDeepestChild ? accessible : child;
871 :
872 0 : child = parent;
873 : }
874 :
875 : return nsnull;
876 : }
877 :
878 : // nsIAccessible getChildAtPoint(in long x, in long y)
879 : NS_IMETHODIMP
880 0 : nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
881 : nsIAccessible **aAccessible)
882 : {
883 0 : NS_ENSURE_ARG_POINTER(aAccessible);
884 0 : *aAccessible = nsnull;
885 :
886 0 : if (IsDefunct())
887 0 : return NS_ERROR_FAILURE;
888 :
889 0 : NS_IF_ADDREF(*aAccessible = ChildAtPoint(aX, aY, eDirectChild));
890 0 : return NS_OK;
891 : }
892 :
893 : // nsIAccessible getDeepestChildAtPoint(in long x, in long y)
894 : NS_IMETHODIMP
895 0 : nsAccessible::GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY,
896 : nsIAccessible **aAccessible)
897 : {
898 0 : NS_ENSURE_ARG_POINTER(aAccessible);
899 0 : *aAccessible = nsnull;
900 :
901 0 : if (IsDefunct())
902 0 : return NS_ERROR_FAILURE;
903 :
904 0 : NS_IF_ADDREF(*aAccessible = ChildAtPoint(aX, aY, eDeepestChild));
905 0 : return NS_OK;
906 : }
907 :
908 0 : void nsAccessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame)
909 : {
910 : /*
911 : * This method is used to determine the bounds of a content node.
912 : * Because HTML wraps and links are not always rectangular, this
913 : * method uses the following algorithm:
914 : *
915 : * 1) Start with an empty rectangle
916 : * 2) Add the rect for the primary frame from for the DOM node.
917 : * 3) For each next frame at the same depth with the same DOM node, add that rect to total
918 : * 4) If that frame is an inline frame, search deeper at that point in the tree, adding all rects
919 : */
920 :
921 : // Initialization area
922 0 : *aBoundingFrame = nsnull;
923 0 : nsIFrame *firstFrame = GetBoundsFrame();
924 0 : if (!firstFrame)
925 0 : return;
926 :
927 : // Find common relative parent
928 : // This is an ancestor frame that will incompass all frames for this content node.
929 : // We need the relative parent so we can get absolute screen coordinates
930 0 : nsIFrame *ancestorFrame = firstFrame;
931 :
932 0 : while (ancestorFrame) {
933 0 : *aBoundingFrame = ancestorFrame;
934 : // If any other frame type, we only need to deal with the primary frame
935 : // Otherwise, there may be more frames attached to the same content node
936 0 : if (ancestorFrame->GetType() != nsGkAtoms::inlineFrame &&
937 0 : ancestorFrame->GetType() != nsGkAtoms::textFrame)
938 0 : break;
939 0 : ancestorFrame = ancestorFrame->GetParent();
940 : }
941 :
942 0 : nsIFrame *iterFrame = firstFrame;
943 0 : nsCOMPtr<nsIContent> firstContent(mContent);
944 0 : nsIContent* iterContent = firstContent;
945 0 : PRInt32 depth = 0;
946 :
947 : // Look only at frames below this depth, or at this depth (if we're still on the content node we started with)
948 0 : while (iterContent == firstContent || depth > 0) {
949 : // Coordinates will come back relative to parent frame
950 0 : nsRect currFrameBounds = iterFrame->GetRect();
951 :
952 : // Make this frame's bounds relative to common parent frame
953 : currFrameBounds +=
954 0 : iterFrame->GetParent()->GetOffsetToExternal(*aBoundingFrame);
955 :
956 : // Add this frame's bounds to total
957 0 : aTotalBounds.UnionRect(aTotalBounds, currFrameBounds);
958 :
959 0 : nsIFrame *iterNextFrame = nsnull;
960 :
961 0 : if (iterFrame->GetType() == nsGkAtoms::inlineFrame) {
962 : // Only do deeper bounds search if we're on an inline frame
963 : // Inline frames can contain larger frames inside of them
964 0 : iterNextFrame = iterFrame->GetFirstPrincipalChild();
965 : }
966 :
967 0 : if (iterNextFrame)
968 0 : ++depth; // Child was found in code above this: We are going deeper in this iteration of the loop
969 : else {
970 : // Use next sibling if it exists, or go back up the tree to get the first next-in-flow or next-sibling
971 : // within our search
972 0 : while (iterFrame) {
973 0 : iterNextFrame = iterFrame->GetNextContinuation();
974 0 : if (!iterNextFrame)
975 0 : iterNextFrame = iterFrame->GetNextSibling();
976 0 : if (iterNextFrame || --depth < 0)
977 0 : break;
978 0 : iterFrame = iterFrame->GetParent();
979 : }
980 : }
981 :
982 : // Get ready for the next round of our loop
983 0 : iterFrame = iterNextFrame;
984 0 : if (iterFrame == nsnull)
985 : break;
986 0 : iterContent = nsnull;
987 0 : if (depth == 0)
988 0 : iterContent = iterFrame->GetContent();
989 : }
990 : }
991 :
992 :
993 : /* void getBounds (out long x, out long y, out long width, out long height); */
994 : NS_IMETHODIMP
995 0 : nsAccessible::GetBounds(PRInt32* aX, PRInt32* aY,
996 : PRInt32* aWidth, PRInt32* aHeight)
997 : {
998 0 : NS_ENSURE_ARG_POINTER(aX);
999 0 : *aX = 0;
1000 0 : NS_ENSURE_ARG_POINTER(aY);
1001 0 : *aY = 0;
1002 0 : NS_ENSURE_ARG_POINTER(aWidth);
1003 0 : *aWidth = 0;
1004 0 : NS_ENSURE_ARG_POINTER(aHeight);
1005 0 : *aHeight = 0;
1006 :
1007 0 : if (IsDefunct())
1008 0 : return NS_ERROR_FAILURE;
1009 :
1010 0 : nsIPresShell* presShell = mDoc->PresShell();
1011 :
1012 : // This routine will get the entire rectangle for all the frames in this node.
1013 : // -------------------------------------------------------------------------
1014 : // Primary Frame for node
1015 : // Another frame, same node <- Example
1016 : // Another frame, same node
1017 :
1018 0 : nsRect unionRectTwips;
1019 0 : nsIFrame* boundingFrame = nsnull;
1020 0 : GetBoundsRect(unionRectTwips, &boundingFrame); // Unions up all primary frames for this node and all siblings after it
1021 0 : NS_ENSURE_STATE(boundingFrame);
1022 :
1023 0 : nsPresContext* presContext = presShell->GetPresContext();
1024 0 : *aX = presContext->AppUnitsToDevPixels(unionRectTwips.x);
1025 0 : *aY = presContext->AppUnitsToDevPixels(unionRectTwips.y);
1026 0 : *aWidth = presContext->AppUnitsToDevPixels(unionRectTwips.width);
1027 0 : *aHeight = presContext->AppUnitsToDevPixels(unionRectTwips.height);
1028 :
1029 : // We have the union of the rectangle, now we need to put it in absolute screen coords
1030 0 : nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
1031 0 : ToNearestPixels(presContext->AppUnitsPerDevPixel());
1032 0 : *aX += orgRectPixels.x;
1033 0 : *aY += orgRectPixels.y;
1034 :
1035 0 : return NS_OK;
1036 : }
1037 :
1038 : // helpers
1039 :
1040 0 : nsIFrame* nsAccessible::GetBoundsFrame()
1041 : {
1042 0 : return GetFrame();
1043 : }
1044 :
1045 : /* void removeSelection (); */
1046 0 : NS_IMETHODIMP nsAccessible::SetSelected(bool aSelect)
1047 : {
1048 : // Add or remove selection
1049 0 : if (IsDefunct())
1050 0 : return NS_ERROR_FAILURE;
1051 :
1052 0 : if (State() & states::SELECTABLE) {
1053 : nsAccessible* multiSelect =
1054 0 : nsAccUtils::GetMultiSelectableContainer(mContent);
1055 0 : if (!multiSelect) {
1056 0 : return aSelect ? TakeFocus() : NS_ERROR_FAILURE;
1057 : }
1058 :
1059 0 : if (mRoleMapEntry) {
1060 0 : if (aSelect) {
1061 : return mContent->SetAttr(kNameSpaceID_None,
1062 : nsGkAtoms::aria_selected,
1063 0 : NS_LITERAL_STRING("true"), true);
1064 : }
1065 0 : return mContent->UnsetAttr(kNameSpaceID_None,
1066 0 : nsGkAtoms::aria_selected, true);
1067 : }
1068 : }
1069 :
1070 0 : return NS_OK;
1071 : }
1072 :
1073 : /* void takeSelection (); */
1074 0 : NS_IMETHODIMP nsAccessible::TakeSelection()
1075 : {
1076 : // Select only this item
1077 0 : if (IsDefunct())
1078 0 : return NS_ERROR_FAILURE;
1079 :
1080 0 : if (State() & states::SELECTABLE) {
1081 : nsAccessible* multiSelect =
1082 0 : nsAccUtils::GetMultiSelectableContainer(mContent);
1083 0 : if (multiSelect)
1084 0 : multiSelect->ClearSelection();
1085 :
1086 0 : return SetSelected(true);
1087 : }
1088 :
1089 0 : return NS_ERROR_FAILURE;
1090 : }
1091 :
1092 : NS_IMETHODIMP
1093 0 : nsAccessible::TakeFocus()
1094 : {
1095 0 : if (IsDefunct())
1096 0 : return NS_ERROR_FAILURE;
1097 :
1098 0 : nsIFrame *frame = GetFrame();
1099 0 : NS_ENSURE_STATE(frame);
1100 :
1101 0 : nsIContent* focusContent = mContent;
1102 :
1103 : // If the accessible focus is managed by container widget then focus the
1104 : // widget and set the accessible as its current item.
1105 0 : if (!frame->IsFocusable()) {
1106 0 : nsAccessible* widget = ContainerWidget();
1107 0 : if (widget && widget->AreItemsOperable()) {
1108 0 : nsIContent* widgetElm = widget->GetContent();
1109 0 : nsIFrame* widgetFrame = widgetElm->GetPrimaryFrame();
1110 0 : if (widgetFrame && widgetFrame->IsFocusable()) {
1111 0 : focusContent = widgetElm;
1112 0 : widget->SetCurrentItem(this);
1113 : }
1114 : }
1115 : }
1116 :
1117 0 : nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
1118 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
1119 0 : if (fm)
1120 0 : fm->SetFocus(element, 0);
1121 :
1122 0 : return NS_OK;
1123 : }
1124 :
1125 : nsresult
1126 0 : nsAccessible::GetHTMLName(nsAString& aLabel)
1127 : {
1128 0 : nsAutoString label;
1129 :
1130 0 : nsAccessible* labelAcc = nsnull;
1131 0 : HTMLLabelIterator iter(Document(), this);
1132 0 : while ((labelAcc = iter.Next())) {
1133 : nsresult rv = nsTextEquivUtils::
1134 0 : AppendTextEquivFromContent(this, labelAcc->GetContent(), &label);
1135 0 : NS_ENSURE_SUCCESS(rv, rv);
1136 :
1137 0 : label.CompressWhitespace();
1138 : }
1139 :
1140 0 : if (label.IsEmpty())
1141 0 : return nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
1142 :
1143 0 : aLabel = label;
1144 0 : return NS_OK;
1145 : }
1146 :
1147 : /**
1148 : * 3 main cases for XUL Controls to be labeled
1149 : * 1 - control contains label="foo"
1150 : * 2 - control has, as a child, a label element
1151 : * - label has either value="foo" or children
1152 : * 3 - non-child label contains control="controlID"
1153 : * - label has either value="foo" or children
1154 : * Once a label is found, the search is discontinued, so a control
1155 : * that has a label child as well as having a label external to
1156 : * the control that uses the control="controlID" syntax will use
1157 : * the child label for its Name.
1158 : */
1159 : nsresult
1160 0 : nsAccessible::GetXULName(nsAString& aLabel)
1161 : {
1162 : // CASE #1 (via label attribute) -- great majority of the cases
1163 0 : nsresult rv = NS_OK;
1164 :
1165 0 : nsAutoString label;
1166 0 : nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl(do_QueryInterface(mContent));
1167 0 : if (labeledEl) {
1168 0 : rv = labeledEl->GetLabel(label);
1169 : }
1170 : else {
1171 0 : nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl(do_QueryInterface(mContent));
1172 0 : if (itemEl) {
1173 0 : rv = itemEl->GetLabel(label);
1174 : }
1175 : else {
1176 0 : nsCOMPtr<nsIDOMXULSelectControlElement> select(do_QueryInterface(mContent));
1177 : // Use label if this is not a select control element which
1178 : // uses label attribute to indicate which option is selected
1179 0 : if (!select) {
1180 0 : nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(mContent));
1181 0 : if (xulEl) {
1182 0 : rv = xulEl->GetAttribute(NS_LITERAL_STRING("label"), label);
1183 : }
1184 : }
1185 : }
1186 : }
1187 :
1188 : // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
1189 0 : if (NS_FAILED(rv) || label.IsEmpty()) {
1190 0 : label.Truncate();
1191 :
1192 0 : nsAccessible* labelAcc = nsnull;
1193 0 : XULLabelIterator iter(Document(), mContent);
1194 0 : while ((labelAcc = iter.Next())) {
1195 : nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
1196 0 : do_QueryInterface(labelAcc->GetContent());
1197 : // Check if label's value attribute is used
1198 0 : if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(label)) && label.IsEmpty()) {
1199 : // If no value attribute, a non-empty label must contain
1200 : // children that define its text -- possibly using HTML
1201 : nsTextEquivUtils::
1202 0 : AppendTextEquivFromContent(this, labelAcc->GetContent(), &label);
1203 : }
1204 : }
1205 : }
1206 :
1207 : // XXX If CompressWhiteSpace worked on nsAString we could avoid a copy
1208 0 : label.CompressWhitespace();
1209 0 : if (!label.IsEmpty()) {
1210 0 : aLabel = label;
1211 0 : return NS_OK;
1212 : }
1213 :
1214 : // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
1215 0 : nsIContent *bindingParent = mContent->GetBindingParent();
1216 0 : nsIContent *parent = bindingParent? bindingParent->GetParent() :
1217 0 : mContent->GetParent();
1218 0 : while (parent) {
1219 0 : if (parent->Tag() == nsGkAtoms::toolbaritem &&
1220 0 : parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, label)) {
1221 0 : label.CompressWhitespace();
1222 0 : aLabel = label;
1223 0 : return NS_OK;
1224 : }
1225 0 : parent = parent->GetParent();
1226 : }
1227 :
1228 0 : return nsTextEquivUtils::GetNameFromSubtree(this, aLabel);
1229 : }
1230 :
1231 : nsresult
1232 0 : nsAccessible::HandleAccEvent(AccEvent* aEvent)
1233 : {
1234 0 : NS_ENSURE_ARG_POINTER(aEvent);
1235 :
1236 : nsCOMPtr<nsIObserverService> obsService =
1237 0 : mozilla::services::GetObserverService();
1238 0 : NS_ENSURE_TRUE(obsService, NS_ERROR_FAILURE);
1239 :
1240 0 : nsCOMPtr<nsISimpleEnumerator> observers;
1241 0 : obsService->EnumerateObservers(NS_ACCESSIBLE_EVENT_TOPIC,
1242 0 : getter_AddRefs(observers));
1243 :
1244 0 : NS_ENSURE_STATE(observers);
1245 :
1246 0 : bool hasObservers = false;
1247 0 : observers->HasMoreElements(&hasObservers);
1248 0 : if (hasObservers) {
1249 0 : nsRefPtr<nsAccEvent> evnt(aEvent->CreateXPCOMObject());
1250 0 : return obsService->NotifyObservers(evnt, NS_ACCESSIBLE_EVENT_TOPIC, nsnull);
1251 : }
1252 :
1253 0 : return NS_OK;
1254 : }
1255 :
1256 : NS_IMETHODIMP
1257 0 : nsAccessible::GetRole(PRUint32 *aRole)
1258 : {
1259 0 : NS_ENSURE_ARG_POINTER(aRole);
1260 0 : *aRole = nsIAccessibleRole::ROLE_NOTHING;
1261 :
1262 0 : if (IsDefunct())
1263 0 : return NS_ERROR_FAILURE;
1264 :
1265 0 : *aRole = Role();
1266 0 : return NS_OK;
1267 : }
1268 :
1269 : NS_IMETHODIMP
1270 0 : nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
1271 : {
1272 0 : NS_ENSURE_ARG_POINTER(aAttributes); // In/out param. Created if necessary.
1273 :
1274 0 : if (IsDefunct())
1275 0 : return NS_ERROR_FAILURE;
1276 :
1277 0 : nsCOMPtr<nsIPersistentProperties> attributes = *aAttributes;
1278 0 : if (!attributes) {
1279 : // Create only if an array wasn't already passed in
1280 0 : attributes = do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
1281 0 : NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY);
1282 0 : NS_ADDREF(*aAttributes = attributes);
1283 : }
1284 :
1285 0 : nsresult rv = GetAttributesInternal(attributes);
1286 0 : NS_ENSURE_SUCCESS(rv, rv);
1287 :
1288 0 : nsAutoString id;
1289 0 : nsAutoString oldValueUnused;
1290 0 : if (nsCoreUtils::GetID(mContent, id)) {
1291 : // Expose ID. If an <iframe id> exists override the one on the <body> of the source doc,
1292 : // because the specific instance is what makes the ID useful for scripts
1293 0 : attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, oldValueUnused);
1294 : }
1295 :
1296 0 : nsAutoString xmlRoles;
1297 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles)) {
1298 0 : attributes->SetStringProperty(NS_LITERAL_CSTRING("xml-roles"), xmlRoles, oldValueUnused);
1299 : }
1300 :
1301 0 : nsCOMPtr<nsIAccessibleValue> supportsValue = do_QueryInterface(static_cast<nsIAccessible*>(this));
1302 0 : if (supportsValue) {
1303 : // We support values, so expose the string value as well, via the valuetext object attribute
1304 : // We test for the value interface because we don't want to expose traditional get_accValue()
1305 : // information such as URL's on links and documents, or text in an input
1306 0 : nsAutoString valuetext;
1307 0 : GetValue(valuetext);
1308 0 : attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext, oldValueUnused);
1309 : }
1310 :
1311 : // Expose checkable object attribute if the accessible has checkable state
1312 0 : if (State() & states::CHECKABLE)
1313 0 : nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable, NS_LITERAL_STRING("true"));
1314 :
1315 : // Group attributes (level/setsize/posinset)
1316 0 : PRInt32 level = 0, posInSet = 0, setSize = 0;
1317 0 : rv = GroupPosition(&level, &setSize, &posInSet);
1318 0 : if (NS_SUCCEEDED(rv))
1319 0 : nsAccUtils::SetAccGroupAttrs(attributes, level, setSize, posInSet);
1320 :
1321 : // Expose object attributes from ARIA attributes.
1322 0 : PRUint32 numAttrs = mContent->GetAttrCount();
1323 0 : for (PRUint32 count = 0; count < numAttrs; count ++) {
1324 0 : const nsAttrName *attr = mContent->GetAttrNameAt(count);
1325 0 : if (attr && attr->NamespaceEquals(kNameSpaceID_None)) {
1326 0 : nsIAtom *attrAtom = attr->Atom();
1327 0 : nsDependentAtomString attrStr(attrAtom);
1328 0 : if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
1329 0 : continue; // Not ARIA
1330 0 : PRUint8 attrFlags = nsAccUtils::GetAttributeCharacteristics(attrAtom);
1331 0 : if (attrFlags & ATTR_BYPASSOBJ)
1332 0 : continue; // No need to handle exposing as obj attribute here
1333 0 : if ((attrFlags & ATTR_VALTOKEN) &&
1334 0 : !nsAccUtils::HasDefinedARIAToken(mContent, attrAtom))
1335 0 : continue; // only expose token based attributes if they are defined
1336 0 : nsAutoString value;
1337 0 : if (mContent->GetAttr(kNameSpaceID_None, attrAtom, value)) {
1338 0 : attributes->SetStringProperty(NS_ConvertUTF16toUTF8(Substring(attrStr, 5)), value, oldValueUnused);
1339 : }
1340 : }
1341 : }
1342 :
1343 : // If there is no aria-live attribute then expose default value of 'live'
1344 : // object attribute used for ARIA role of this accessible.
1345 0 : if (mRoleMapEntry) {
1346 0 : nsAutoString live;
1347 0 : nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
1348 0 : if (live.IsEmpty()) {
1349 0 : if (nsAccUtils::GetLiveAttrValue(mRoleMapEntry->liveAttRule, live))
1350 0 : nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
1351 : }
1352 : }
1353 :
1354 0 : return NS_OK;
1355 : }
1356 :
1357 : nsresult
1358 0 : nsAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
1359 : {
1360 : // Attributes set by this method will not be used to override attributes on a sub-document accessible
1361 : // when there is a <frame>/<iframe> element that spawned the sub-document
1362 0 : nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mContent));
1363 :
1364 0 : nsAutoString tagName;
1365 0 : element->GetTagName(tagName);
1366 0 : if (!tagName.IsEmpty()) {
1367 0 : nsAutoString oldValueUnused;
1368 0 : aAttributes->SetStringProperty(NS_LITERAL_CSTRING("tag"), tagName,
1369 0 : oldValueUnused);
1370 : }
1371 :
1372 0 : nsEventShell::GetEventAttributes(GetNode(), aAttributes);
1373 :
1374 : // Expose class because it may have useful microformat information
1375 : // Let the class from an iframe's document be exposed, don't override from <iframe class>
1376 0 : nsAutoString _class;
1377 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
1378 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::_class, _class);
1379 :
1380 : // Get container-foo computed live region properties based on the closest container with
1381 : // the live region attribute.
1382 : // Inner nodes override outer nodes within the same document --
1383 : // The inner nodes can be used to override live region behavior on more general outer nodes
1384 : // However, nodes in outer documents override nodes in inner documents:
1385 : // Outer doc author may want to override properties on a widget they used in an iframe
1386 0 : nsIContent *startContent = mContent;
1387 0 : while (true) {
1388 0 : NS_ENSURE_STATE(startContent);
1389 0 : nsIDocument *doc = startContent->GetDocument();
1390 0 : nsIContent* rootContent = nsCoreUtils::GetRoleContent(doc);
1391 0 : NS_ENSURE_STATE(rootContent);
1392 : nsAccUtils::SetLiveContainerAttributes(aAttributes, startContent,
1393 0 : rootContent);
1394 :
1395 : // Allow ARIA live region markup from outer documents to override
1396 0 : nsCOMPtr<nsISupports> container = doc->GetContainer();
1397 : nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
1398 0 : do_QueryInterface(container);
1399 0 : if (!docShellTreeItem)
1400 : break;
1401 :
1402 0 : nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
1403 0 : docShellTreeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
1404 0 : if (!sameTypeParent || sameTypeParent == docShellTreeItem)
1405 : break;
1406 :
1407 0 : nsIDocument *parentDoc = doc->GetParentDocument();
1408 0 : if (!parentDoc)
1409 : break;
1410 :
1411 0 : startContent = parentDoc->FindContentForSubDocument(doc);
1412 : }
1413 :
1414 0 : if (!mContent->IsElement())
1415 0 : return NS_OK;
1416 :
1417 : // Expose draggable object attribute?
1418 0 : nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
1419 0 : if (htmlElement) {
1420 0 : bool draggable = false;
1421 0 : htmlElement->GetDraggable(&draggable);
1422 0 : if (draggable) {
1423 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::draggable,
1424 0 : NS_LITERAL_STRING("true"));
1425 : }
1426 : }
1427 :
1428 : // Don't calculate CSS-based object attributes when no frame (i.e.
1429 : // the accessible is not unattached form three) or when the accessible is not
1430 : // primary for node (like list bullet or XUL tree items).
1431 0 : if (!mContent->GetPrimaryFrame() || !IsPrimaryForNode())
1432 0 : return NS_OK;
1433 :
1434 : // CSS style based object attributes.
1435 0 : nsAutoString value;
1436 0 : StyleInfo styleInfo(mContent->AsElement(), mDoc->PresShell());
1437 :
1438 : // Expose 'display' attribute.
1439 0 : styleInfo.Display(value);
1440 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::display, value);
1441 :
1442 : // Expose 'text-align' attribute.
1443 0 : styleInfo.TextAlign(value);
1444 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textAlign, value);
1445 :
1446 : // Expose 'text-indent' attribute.
1447 0 : styleInfo.TextIndent(value);
1448 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textIndent, value);
1449 :
1450 : // Expose 'margin-left' attribute.
1451 0 : styleInfo.MarginLeft(value);
1452 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginLeft, value);
1453 :
1454 : // Expose 'margin-right' attribute.
1455 0 : styleInfo.MarginRight(value);
1456 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginRight, value);
1457 :
1458 : // Expose 'margin-top' attribute.
1459 0 : styleInfo.MarginTop(value);
1460 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginTop, value);
1461 :
1462 : // Expose 'margin-bottom' attribute.
1463 0 : styleInfo.MarginBottom(value);
1464 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::marginBottom, value);
1465 :
1466 0 : return NS_OK;
1467 : }
1468 :
1469 : NS_IMETHODIMP
1470 0 : nsAccessible::GroupPosition(PRInt32 *aGroupLevel,
1471 : PRInt32 *aSimilarItemsInGroup,
1472 : PRInt32 *aPositionInGroup)
1473 : {
1474 0 : NS_ENSURE_ARG_POINTER(aGroupLevel);
1475 0 : *aGroupLevel = 0;
1476 :
1477 0 : NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup);
1478 0 : *aSimilarItemsInGroup = 0;
1479 :
1480 0 : NS_ENSURE_ARG_POINTER(aPositionInGroup);
1481 0 : *aPositionInGroup = 0;
1482 :
1483 0 : if (IsDefunct())
1484 0 : return NS_ERROR_FAILURE;
1485 :
1486 : // Get group position from ARIA attributes.
1487 : nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_level,
1488 0 : aGroupLevel);
1489 : nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_posinset,
1490 0 : aPositionInGroup);
1491 : nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_setsize,
1492 0 : aSimilarItemsInGroup);
1493 :
1494 : // If ARIA is missed and the accessible is visible then calculate group
1495 : // position from hierarchy.
1496 0 : if (State() & states::INVISIBLE)
1497 0 : return NS_OK;
1498 :
1499 : // Calculate group level if ARIA is missed.
1500 0 : if (*aGroupLevel == 0) {
1501 0 : PRInt32 level = GetLevelInternal();
1502 0 : if (level != 0)
1503 0 : *aGroupLevel = level;
1504 : }
1505 :
1506 : // Calculate position in group and group size if ARIA is missed.
1507 0 : if (*aSimilarItemsInGroup == 0 || *aPositionInGroup == 0) {
1508 0 : PRInt32 posInSet = 0, setSize = 0;
1509 0 : GetPositionAndSizeInternal(&posInSet, &setSize);
1510 0 : if (posInSet != 0 && setSize != 0) {
1511 0 : if (*aPositionInGroup == 0)
1512 0 : *aPositionInGroup = posInSet;
1513 :
1514 0 : if (*aSimilarItemsInGroup == 0)
1515 0 : *aSimilarItemsInGroup = setSize;
1516 : }
1517 : }
1518 :
1519 0 : return NS_OK;
1520 : }
1521 :
1522 : NS_IMETHODIMP
1523 0 : nsAccessible::GetState(PRUint32* aState, PRUint32* aExtraState)
1524 : {
1525 0 : NS_ENSURE_ARG_POINTER(aState);
1526 :
1527 0 : nsAccUtils::To32States(State(), aState, aExtraState);
1528 0 : return NS_OK;
1529 : }
1530 :
1531 : PRUint64
1532 0 : nsAccessible::State()
1533 : {
1534 0 : if (IsDefunct())
1535 0 : return states::DEFUNCT;
1536 :
1537 0 : PRUint64 state = NativeState();
1538 : // Apply ARIA states to be sure accessible states will be overridden.
1539 0 : ApplyARIAState(&state);
1540 :
1541 : // If this is an ARIA item of the selectable widget and if it's focused and
1542 : // not marked unselected explicitly (i.e. aria-selected="false") then expose
1543 : // it as selected to make ARIA widget authors life easier.
1544 0 : if (mRoleMapEntry && !(state & states::SELECTED) &&
1545 0 : !mContent->AttrValueIs(kNameSpaceID_None,
1546 : nsGkAtoms::aria_selected,
1547 0 : nsGkAtoms::_false, eCaseMatters)) {
1548 : // Special case for tabs: focused tab or focus inside related tab panel
1549 : // implies selected state.
1550 0 : if (mRoleMapEntry->role == roles::PAGETAB) {
1551 0 : if (state & states::FOCUSED) {
1552 0 : state |= states::SELECTED;
1553 : } else {
1554 : // If focus is in a child of the tab panel surely the tab is selected!
1555 0 : Relation rel = RelationByType(nsIAccessibleRelation::RELATION_LABEL_FOR);
1556 0 : nsAccessible* relTarget = nsnull;
1557 0 : while ((relTarget = rel.Next())) {
1558 0 : if (relTarget->Role() == roles::PROPERTYPAGE &&
1559 0 : FocusMgr()->IsFocusWithin(relTarget))
1560 0 : state |= states::SELECTED;
1561 : }
1562 : }
1563 0 : } else if (state & states::FOCUSED) {
1564 0 : nsAccessible* container = nsAccUtils::GetSelectableContainer(this, state);
1565 0 : if (container &&
1566 : !nsAccUtils::HasDefinedARIAToken(container->GetContent(),
1567 0 : nsGkAtoms::aria_multiselectable)) {
1568 0 : state |= states::SELECTED;
1569 : }
1570 : }
1571 : }
1572 :
1573 0 : const PRUint32 kExpandCollapseStates = states::COLLAPSED | states::EXPANDED;
1574 0 : if ((state & kExpandCollapseStates) == kExpandCollapseStates) {
1575 : // Cannot be both expanded and collapsed -- this happens in ARIA expanded
1576 : // combobox because of limitation of nsARIAMap.
1577 : // XXX: Perhaps we will be able to make this less hacky if we support
1578 : // extended states in nsARIAMap, e.g. derive COLLAPSED from
1579 : // EXPANDABLE && !EXPANDED.
1580 0 : state &= ~states::COLLAPSED;
1581 : }
1582 :
1583 0 : if (!(state & states::UNAVAILABLE)) {
1584 0 : state |= states::ENABLED | states::SENSITIVE;
1585 :
1586 : // If the object is a current item of container widget then mark it as
1587 : // ACTIVE. This allows screen reader virtual buffer modes to know which
1588 : // descendant is the current one that would get focus if the user navigates
1589 : // to the container widget.
1590 0 : nsAccessible* widget = ContainerWidget();
1591 0 : if (widget && widget->CurrentItem() == this)
1592 0 : state |= states::ACTIVE;
1593 : }
1594 :
1595 0 : if ((state & states::COLLAPSED) || (state & states::EXPANDED))
1596 0 : state |= states::EXPANDABLE;
1597 :
1598 : // For some reasons DOM node may have not a frame. We tract such accessibles
1599 : // as invisible.
1600 0 : nsIFrame *frame = GetFrame();
1601 0 : if (!frame)
1602 0 : return state;
1603 :
1604 0 : const nsStyleDisplay* display = frame->GetStyleDisplay();
1605 0 : if (display && display->mOpacity == 1.0f &&
1606 0 : !(state & states::INVISIBLE)) {
1607 0 : state |= states::OPAQUE1;
1608 : }
1609 :
1610 0 : const nsStyleXUL *xulStyle = frame->GetStyleXUL();
1611 0 : if (xulStyle) {
1612 : // In XUL all boxes are either vertical or horizontal
1613 0 : if (xulStyle->mBoxOrient == NS_STYLE_BOX_ORIENT_VERTICAL) {
1614 0 : state |= states::VERTICAL;
1615 : }
1616 : else {
1617 0 : state |= states::HORIZONTAL;
1618 : }
1619 : }
1620 :
1621 : // If we are editable, force readonly bit off
1622 0 : if (state & states::EDITABLE)
1623 0 : state &= ~states::READONLY;
1624 :
1625 0 : return state;
1626 : }
1627 :
1628 : void
1629 0 : nsAccessible::ApplyARIAState(PRUint64* aState)
1630 : {
1631 : // Test for universal states first
1632 0 : *aState |= nsARIAMap::UniversalStatesFor(mContent);
1633 :
1634 0 : if (mRoleMapEntry) {
1635 :
1636 : // We only force the readonly bit off if we have a real mapping for the aria
1637 : // role. This preserves the ability for screen readers to use readonly
1638 : // (primarily on the document) as the hint for creating a virtual buffer.
1639 0 : if (mRoleMapEntry->role != roles::NOTHING)
1640 0 : *aState &= ~states::READONLY;
1641 :
1642 0 : if (mContent->HasAttr(kNameSpaceID_None, mContent->GetIDAttributeName())) {
1643 : // If has a role & ID and aria-activedescendant on the container, assume focusable
1644 0 : nsIContent *ancestorContent = mContent;
1645 0 : while ((ancestorContent = ancestorContent->GetParent()) != nsnull) {
1646 0 : if (ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant)) {
1647 : // ancestor has activedescendant property, this content could be active
1648 0 : *aState |= states::FOCUSABLE;
1649 0 : break;
1650 : }
1651 : }
1652 : }
1653 : }
1654 :
1655 0 : if (*aState & states::FOCUSABLE) {
1656 : // Special case: aria-disabled propagates from ancestors down to any focusable descendant
1657 0 : nsIContent *ancestorContent = mContent;
1658 0 : while ((ancestorContent = ancestorContent->GetParent()) != nsnull) {
1659 0 : if (ancestorContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
1660 0 : nsGkAtoms::_true, eCaseMatters)) {
1661 : // ancestor has aria-disabled property, this is disabled
1662 0 : *aState |= states::UNAVAILABLE;
1663 0 : break;
1664 : }
1665 : }
1666 : }
1667 :
1668 0 : if (!mRoleMapEntry)
1669 0 : return;
1670 :
1671 : // Note: the readonly bitflag will be overridden later if content is editable
1672 0 : *aState |= mRoleMapEntry->state;
1673 0 : if (nsStateMapEntry::MapToStates(mContent, aState,
1674 0 : mRoleMapEntry->attributeMap1) &&
1675 : nsStateMapEntry::MapToStates(mContent, aState,
1676 0 : mRoleMapEntry->attributeMap2)) {
1677 : nsStateMapEntry::MapToStates(mContent, aState,
1678 0 : mRoleMapEntry->attributeMap3);
1679 : }
1680 :
1681 : }
1682 :
1683 : // Not implemented by this class
1684 :
1685 : /* DOMString getValue (); */
1686 : NS_IMETHODIMP
1687 0 : nsAccessible::GetValue(nsAString& aValue)
1688 : {
1689 0 : if (IsDefunct())
1690 0 : return NS_ERROR_FAILURE;
1691 :
1692 0 : if (mRoleMapEntry) {
1693 0 : if (mRoleMapEntry->valueRule == eNoValue) {
1694 0 : return NS_OK;
1695 : }
1696 :
1697 : // aria-valuenow is a number, and aria-valuetext is the optional text equivalent
1698 : // For the string value, we will try the optional text equivalent first
1699 0 : if (!mContent->GetAttr(kNameSpaceID_None,
1700 0 : nsGkAtoms::aria_valuetext, aValue)) {
1701 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
1702 0 : aValue);
1703 : }
1704 : }
1705 :
1706 0 : if (!aValue.IsEmpty())
1707 0 : return NS_OK;
1708 :
1709 : // Check if it's a simple xlink.
1710 0 : if (nsCoreUtils::IsXLink(mContent)) {
1711 0 : nsIPresShell* presShell = mDoc->PresShell();
1712 0 : if (presShell) {
1713 0 : nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
1714 0 : return presShell->GetLinkLocation(DOMNode, aValue);
1715 : }
1716 : }
1717 :
1718 0 : return NS_OK;
1719 : }
1720 :
1721 : // nsIAccessibleValue
1722 : NS_IMETHODIMP
1723 0 : nsAccessible::GetMaximumValue(double *aMaximumValue)
1724 : {
1725 0 : return GetAttrValue(nsGkAtoms::aria_valuemax, aMaximumValue);
1726 : }
1727 :
1728 : NS_IMETHODIMP
1729 0 : nsAccessible::GetMinimumValue(double *aMinimumValue)
1730 : {
1731 0 : return GetAttrValue(nsGkAtoms::aria_valuemin, aMinimumValue);
1732 : }
1733 :
1734 : NS_IMETHODIMP
1735 0 : nsAccessible::GetMinimumIncrement(double *aMinIncrement)
1736 : {
1737 0 : NS_ENSURE_ARG_POINTER(aMinIncrement);
1738 0 : *aMinIncrement = 0;
1739 :
1740 : // No mimimum increment in dynamic content spec right now
1741 0 : return NS_OK_NO_ARIA_VALUE;
1742 : }
1743 :
1744 : NS_IMETHODIMP
1745 0 : nsAccessible::GetCurrentValue(double *aValue)
1746 : {
1747 0 : return GetAttrValue(nsGkAtoms::aria_valuenow, aValue);
1748 : }
1749 :
1750 : NS_IMETHODIMP
1751 0 : nsAccessible::SetCurrentValue(double aValue)
1752 : {
1753 0 : if (IsDefunct())
1754 0 : return NS_ERROR_FAILURE;
1755 :
1756 0 : if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
1757 0 : return NS_OK_NO_ARIA_VALUE;
1758 :
1759 0 : const PRUint32 kValueCannotChange = states::READONLY | states::UNAVAILABLE;
1760 :
1761 0 : if (State() & kValueCannotChange)
1762 0 : return NS_ERROR_FAILURE;
1763 :
1764 0 : double minValue = 0;
1765 0 : if (NS_SUCCEEDED(GetMinimumValue(&minValue)) && aValue < minValue)
1766 0 : return NS_ERROR_INVALID_ARG;
1767 :
1768 0 : double maxValue = 0;
1769 0 : if (NS_SUCCEEDED(GetMaximumValue(&maxValue)) && aValue > maxValue)
1770 0 : return NS_ERROR_INVALID_ARG;
1771 :
1772 0 : nsAutoString newValue;
1773 0 : newValue.AppendFloat(aValue);
1774 : return mContent->SetAttr(kNameSpaceID_None,
1775 0 : nsGkAtoms::aria_valuenow, newValue, true);
1776 : }
1777 :
1778 : /* void setName (in DOMString name); */
1779 0 : NS_IMETHODIMP nsAccessible::SetName(const nsAString& name)
1780 : {
1781 0 : return NS_ERROR_NOT_IMPLEMENTED;
1782 : }
1783 :
1784 : NS_IMETHODIMP
1785 0 : nsAccessible::GetDefaultKeyBinding(nsAString& aKeyBinding)
1786 : {
1787 0 : aKeyBinding.Truncate();
1788 0 : if (IsDefunct())
1789 0 : return NS_ERROR_FAILURE;
1790 :
1791 0 : KeyboardShortcut().ToString(aKeyBinding);
1792 0 : return NS_OK;
1793 : }
1794 :
1795 : NS_IMETHODIMP
1796 0 : nsAccessible::GetKeyBindings(PRUint8 aActionIndex,
1797 : nsIDOMDOMStringList **aKeyBindings)
1798 : {
1799 : // Currently we support only unique key binding on element for default action.
1800 0 : NS_ENSURE_TRUE(aActionIndex == 0, NS_ERROR_INVALID_ARG);
1801 :
1802 0 : nsAccessibleDOMStringList *keyBindings = new nsAccessibleDOMStringList();
1803 0 : NS_ENSURE_TRUE(keyBindings, NS_ERROR_OUT_OF_MEMORY);
1804 :
1805 0 : nsAutoString defaultKey;
1806 0 : nsresult rv = GetDefaultKeyBinding(defaultKey);
1807 0 : NS_ENSURE_SUCCESS(rv, rv);
1808 :
1809 0 : if (!defaultKey.IsEmpty())
1810 0 : keyBindings->Add(defaultKey);
1811 :
1812 0 : NS_ADDREF(*aKeyBindings = keyBindings);
1813 0 : return NS_OK;
1814 : }
1815 :
1816 : role
1817 0 : nsAccessible::ARIARoleInternal()
1818 : {
1819 0 : NS_PRECONDITION(mRoleMapEntry && mRoleMapEntry->roleRule == kUseMapRole,
1820 : "ARIARoleInternal should only be called when ARIA role overrides!");
1821 :
1822 : // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
1823 : // where the accessible role depends on both the role and ARIA state.
1824 0 : if (mRoleMapEntry->role == roles::PUSHBUTTON) {
1825 0 : if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
1826 : // For simplicity, any existing pressed attribute except "" or "undefined"
1827 : // indicates a toggle.
1828 0 : return roles::TOGGLE_BUTTON;
1829 : }
1830 :
1831 0 : if (mContent->AttrValueIs(kNameSpaceID_None,
1832 : nsGkAtoms::aria_haspopup,
1833 : nsGkAtoms::_true,
1834 0 : eCaseMatters)) {
1835 : // For button with aria-haspopup="true".
1836 0 : return roles::BUTTONMENU;
1837 : }
1838 :
1839 0 : } else if (mRoleMapEntry->role == roles::LISTBOX) {
1840 : // A listbox inside of a combobox needs a special role because of ATK
1841 : // mapping to menu.
1842 0 : if (mParent && mParent->Role() == roles::COMBOBOX) {
1843 0 : return roles::COMBOBOX_LIST;
1844 :
1845 : Relation rel = RelationByType(nsIAccessibleRelation::RELATION_NODE_CHILD_OF);
1846 : nsAccessible* targetAcc = nsnull;
1847 : while ((targetAcc = rel.Next()))
1848 : if (targetAcc->Role() == roles::COMBOBOX)
1849 : return roles::COMBOBOX_LIST;
1850 : }
1851 :
1852 0 : } else if (mRoleMapEntry->role == roles::OPTION) {
1853 0 : if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
1854 0 : return roles::COMBOBOX_OPTION;
1855 : }
1856 :
1857 0 : return mRoleMapEntry->role;
1858 : }
1859 :
1860 : role
1861 0 : nsAccessible::NativeRole()
1862 : {
1863 0 : return nsCoreUtils::IsXLink(mContent) ? roles::LINK : roles::NOTHING;
1864 : }
1865 :
1866 : // readonly attribute PRUint8 numActions
1867 : NS_IMETHODIMP
1868 0 : nsAccessible::GetNumActions(PRUint8* aActionCount)
1869 : {
1870 0 : NS_ENSURE_ARG_POINTER(aActionCount);
1871 0 : *aActionCount = 0;
1872 0 : if (IsDefunct())
1873 0 : return NS_ERROR_FAILURE;
1874 :
1875 0 : *aActionCount = ActionCount();
1876 0 : return NS_OK;
1877 : }
1878 :
1879 : PRUint8
1880 0 : nsAccessible::ActionCount()
1881 : {
1882 0 : return GetActionRule(State()) == eNoAction ? 0 : 1;
1883 : }
1884 :
1885 : /* DOMString getAccActionName (in PRUint8 index); */
1886 : NS_IMETHODIMP
1887 0 : nsAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
1888 : {
1889 0 : aName.Truncate();
1890 :
1891 0 : if (aIndex != 0)
1892 0 : return NS_ERROR_INVALID_ARG;
1893 :
1894 0 : if (IsDefunct())
1895 0 : return NS_ERROR_FAILURE;
1896 :
1897 0 : PRUint64 states = State();
1898 0 : PRUint32 actionRule = GetActionRule(states);
1899 :
1900 0 : switch (actionRule) {
1901 : case eActivateAction:
1902 0 : aName.AssignLiteral("activate");
1903 0 : return NS_OK;
1904 :
1905 : case eClickAction:
1906 0 : aName.AssignLiteral("click");
1907 0 : return NS_OK;
1908 :
1909 : case ePressAction:
1910 0 : aName.AssignLiteral("press");
1911 0 : return NS_OK;
1912 :
1913 : case eCheckUncheckAction:
1914 0 : if (states & states::CHECKED)
1915 0 : aName.AssignLiteral("uncheck");
1916 0 : else if (states & states::MIXED)
1917 0 : aName.AssignLiteral("cycle");
1918 : else
1919 0 : aName.AssignLiteral("check");
1920 0 : return NS_OK;
1921 :
1922 : case eJumpAction:
1923 0 : aName.AssignLiteral("jump");
1924 0 : return NS_OK;
1925 :
1926 : case eOpenCloseAction:
1927 0 : if (states & states::COLLAPSED)
1928 0 : aName.AssignLiteral("open");
1929 : else
1930 0 : aName.AssignLiteral("close");
1931 0 : return NS_OK;
1932 :
1933 : case eSelectAction:
1934 0 : aName.AssignLiteral("select");
1935 0 : return NS_OK;
1936 :
1937 : case eSwitchAction:
1938 0 : aName.AssignLiteral("switch");
1939 0 : return NS_OK;
1940 :
1941 : case eSortAction:
1942 0 : aName.AssignLiteral("sort");
1943 0 : return NS_OK;
1944 :
1945 : case eExpandAction:
1946 0 : if (states & states::COLLAPSED)
1947 0 : aName.AssignLiteral("expand");
1948 : else
1949 0 : aName.AssignLiteral("collapse");
1950 0 : return NS_OK;
1951 : }
1952 :
1953 0 : return NS_ERROR_INVALID_ARG;
1954 : }
1955 :
1956 : // AString getActionDescription(in PRUint8 index)
1957 : NS_IMETHODIMP
1958 0 : nsAccessible::GetActionDescription(PRUint8 aIndex, nsAString& aDescription)
1959 : {
1960 : // default to localized action name.
1961 0 : nsAutoString name;
1962 0 : nsresult rv = GetActionName(aIndex, name);
1963 0 : NS_ENSURE_SUCCESS(rv, rv);
1964 :
1965 0 : TranslateString(name, aDescription);
1966 0 : return NS_OK;
1967 : }
1968 :
1969 : // void doAction(in PRUint8 index)
1970 : NS_IMETHODIMP
1971 0 : nsAccessible::DoAction(PRUint8 aIndex)
1972 : {
1973 0 : if (aIndex != 0)
1974 0 : return NS_ERROR_INVALID_ARG;
1975 :
1976 0 : if (IsDefunct())
1977 0 : return NS_ERROR_FAILURE;
1978 :
1979 0 : if (GetActionRule(State()) != eNoAction) {
1980 0 : DoCommand();
1981 0 : return NS_OK;
1982 : }
1983 :
1984 0 : return NS_ERROR_INVALID_ARG;
1985 : }
1986 :
1987 : /* DOMString getHelp (); */
1988 0 : NS_IMETHODIMP nsAccessible::GetHelp(nsAString& _retval)
1989 : {
1990 0 : return NS_ERROR_NOT_IMPLEMENTED;
1991 : }
1992 :
1993 : nsIContent*
1994 0 : nsAccessible::GetAtomicRegion() const
1995 : {
1996 0 : nsIContent *loopContent = mContent;
1997 0 : nsAutoString atomic;
1998 0 : while (loopContent && !loopContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
1999 0 : loopContent = loopContent->GetParent();
2000 :
2001 0 : return atomic.EqualsLiteral("true") ? loopContent : nsnull;
2002 : }
2003 :
2004 : // nsIAccessible getRelationByType()
2005 : NS_IMETHODIMP
2006 0 : nsAccessible::GetRelationByType(PRUint32 aType,
2007 : nsIAccessibleRelation** aRelation)
2008 : {
2009 0 : NS_ENSURE_ARG_POINTER(aRelation);
2010 0 : *aRelation = nsnull;
2011 0 : if (IsDefunct())
2012 0 : return NS_ERROR_FAILURE;
2013 :
2014 0 : Relation rel = RelationByType(aType);
2015 0 : NS_ADDREF(*aRelation = new nsAccessibleRelation(aType, &rel));
2016 0 : return *aRelation ? NS_OK : NS_ERROR_FAILURE;
2017 : }
2018 :
2019 : Relation
2020 0 : nsAccessible::RelationByType(PRUint32 aType)
2021 : {
2022 : // Relationships are defined on the same content node that the role would be
2023 : // defined on.
2024 0 : switch (aType) {
2025 : case nsIAccessibleRelation::RELATION_LABEL_FOR: {
2026 0 : Relation rel(new RelatedAccIterator(Document(), mContent,
2027 0 : nsGkAtoms::aria_labelledby));
2028 0 : if (mContent->Tag() == nsGkAtoms::label)
2029 0 : rel.AppendIter(new IDRefsIterator(mContent, mContent->IsHTML() ?
2030 : nsGkAtoms::_for :
2031 0 : nsGkAtoms::control));
2032 :
2033 0 : return rel;
2034 : }
2035 : case nsIAccessibleRelation::RELATION_LABELLED_BY: {
2036 : Relation rel(new IDRefsIterator(mContent,
2037 0 : nsGkAtoms::aria_labelledby));
2038 0 : if (mContent->IsHTML()) {
2039 0 : rel.AppendIter(new HTMLLabelIterator(Document(), this));
2040 0 : } else if (mContent->IsXUL()) {
2041 0 : rel.AppendIter(new XULLabelIterator(Document(), mContent));
2042 : }
2043 :
2044 0 : return rel;
2045 : }
2046 : case nsIAccessibleRelation::RELATION_DESCRIBED_BY: {
2047 : Relation rel(new IDRefsIterator(mContent,
2048 0 : nsGkAtoms::aria_describedby));
2049 0 : if (mContent->IsXUL())
2050 0 : rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
2051 :
2052 0 : return rel;
2053 : }
2054 : case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR: {
2055 0 : Relation rel(new RelatedAccIterator(Document(), mContent,
2056 0 : nsGkAtoms::aria_describedby));
2057 :
2058 : // This affectively adds an optional control attribute to xul:description,
2059 : // which only affects accessibility, by allowing the description to be
2060 : // tied to a control.
2061 0 : if (mContent->Tag() == nsGkAtoms::description &&
2062 0 : mContent->IsXUL())
2063 : rel.AppendIter(new IDRefsIterator(mContent,
2064 0 : nsGkAtoms::control));
2065 :
2066 0 : return rel;
2067 : }
2068 : case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: {
2069 0 : Relation rel(new RelatedAccIterator(Document(), mContent,
2070 0 : nsGkAtoms::aria_owns));
2071 :
2072 : // This is an ARIA tree or treegrid that doesn't use owns, so we need to
2073 : // get the parent the hard way.
2074 0 : if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
2075 : mRoleMapEntry->role == roles::ROW)) {
2076 0 : AccGroupInfo* groupInfo = GetGroupInfo();
2077 0 : if (!groupInfo)
2078 0 : return rel;
2079 :
2080 0 : rel.AppendTarget(groupInfo->ConceptualParent());
2081 : }
2082 :
2083 : // If accessible is in its own Window, or is the root of a document,
2084 : // then we should provide NODE_CHILD_OF relation so that MSAA clients
2085 : // can easily get to true parent instead of getting to oleacc's
2086 : // ROLE_WINDOW accessible which will prevent us from going up further
2087 : // (because it is system generated and has no idea about the hierarchy
2088 : // above it).
2089 0 : nsIFrame *frame = GetFrame();
2090 0 : if (frame) {
2091 0 : nsIView *view = frame->GetViewExternal();
2092 0 : if (view) {
2093 0 : nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
2094 0 : if (scrollFrame || view->GetWidget() || !frame->GetParent())
2095 0 : rel.AppendTarget(Parent());
2096 : }
2097 : }
2098 :
2099 0 : return rel;
2100 : }
2101 : case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
2102 0 : return Relation(new RelatedAccIterator(Document(), mContent,
2103 0 : nsGkAtoms::aria_controls));
2104 : case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: {
2105 : Relation rel(new IDRefsIterator(mContent,
2106 0 : nsGkAtoms::aria_controls));
2107 0 : rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
2108 0 : return rel;
2109 : }
2110 : case nsIAccessibleRelation::RELATION_FLOWS_TO:
2111 : return Relation(new IDRefsIterator(mContent,
2112 0 : nsGkAtoms::aria_flowto));
2113 : case nsIAccessibleRelation::RELATION_FLOWS_FROM:
2114 0 : return Relation(new RelatedAccIterator(Document(), mContent,
2115 0 : nsGkAtoms::aria_flowto));
2116 : case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON: {
2117 0 : if (mContent->IsHTML()) {
2118 : // HTML form controls implements nsIFormControl interface.
2119 0 : nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
2120 0 : if (control) {
2121 0 : nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
2122 0 : if (form) {
2123 : nsCOMPtr<nsIContent> formContent =
2124 0 : do_QueryInterface(form->GetDefaultSubmitElement());
2125 0 : return Relation(formContent);
2126 : }
2127 : }
2128 : } else {
2129 : // In XUL, use first <button default="true" .../> in the document
2130 : nsCOMPtr<nsIDOMXULDocument> xulDoc =
2131 0 : do_QueryInterface(mContent->OwnerDoc());
2132 0 : nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
2133 0 : if (xulDoc) {
2134 0 : nsCOMPtr<nsIDOMNodeList> possibleDefaultButtons;
2135 0 : xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
2136 0 : NS_LITERAL_STRING("true"),
2137 0 : getter_AddRefs(possibleDefaultButtons));
2138 0 : if (possibleDefaultButtons) {
2139 : PRUint32 length;
2140 0 : possibleDefaultButtons->GetLength(&length);
2141 0 : nsCOMPtr<nsIDOMNode> possibleButton;
2142 : // Check for button in list of default="true" elements
2143 0 : for (PRUint32 count = 0; count < length && !buttonEl; count ++) {
2144 0 : possibleDefaultButtons->Item(count, getter_AddRefs(possibleButton));
2145 0 : buttonEl = do_QueryInterface(possibleButton);
2146 : }
2147 : }
2148 0 : if (!buttonEl) { // Check for anonymous accept button in <dialog>
2149 0 : nsCOMPtr<nsIDOMDocumentXBL> xblDoc(do_QueryInterface(xulDoc));
2150 0 : if (xblDoc) {
2151 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(xulDoc);
2152 0 : NS_ASSERTION(domDoc, "No DOM document");
2153 0 : nsCOMPtr<nsIDOMElement> rootEl;
2154 0 : domDoc->GetDocumentElement(getter_AddRefs(rootEl));
2155 0 : if (rootEl) {
2156 0 : nsCOMPtr<nsIDOMElement> possibleButtonEl;
2157 0 : xblDoc->GetAnonymousElementByAttribute(rootEl,
2158 0 : NS_LITERAL_STRING("default"),
2159 0 : NS_LITERAL_STRING("true"),
2160 0 : getter_AddRefs(possibleButtonEl));
2161 0 : buttonEl = do_QueryInterface(possibleButtonEl);
2162 : }
2163 : }
2164 : }
2165 0 : nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
2166 0 : return Relation(relatedContent);
2167 : }
2168 : }
2169 0 : return Relation();
2170 : }
2171 : case nsIAccessibleRelation::RELATION_MEMBER_OF:
2172 0 : return Relation(GetAtomicRegion());
2173 : case nsIAccessibleRelation::RELATION_SUBWINDOW_OF:
2174 : case nsIAccessibleRelation::RELATION_EMBEDS:
2175 : case nsIAccessibleRelation::RELATION_EMBEDDED_BY:
2176 : case nsIAccessibleRelation::RELATION_POPUP_FOR:
2177 : case nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF:
2178 : default:
2179 0 : return Relation();
2180 : }
2181 : }
2182 :
2183 : NS_IMETHODIMP
2184 0 : nsAccessible::GetRelations(nsIArray **aRelations)
2185 : {
2186 0 : NS_ENSURE_ARG_POINTER(aRelations);
2187 0 : *aRelations = nsnull;
2188 :
2189 0 : if (IsDefunct())
2190 0 : return NS_ERROR_FAILURE;
2191 :
2192 0 : nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID);
2193 0 : NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY);
2194 :
2195 0 : for (PRUint32 relType = nsIAccessibleRelation::RELATION_FIRST;
2196 : relType < nsIAccessibleRelation::RELATION_LAST;
2197 : ++relType) {
2198 :
2199 0 : nsCOMPtr<nsIAccessibleRelation> relation;
2200 0 : nsresult rv = GetRelationByType(relType, getter_AddRefs(relation));
2201 :
2202 0 : if (NS_SUCCEEDED(rv) && relation) {
2203 0 : PRUint32 targets = 0;
2204 0 : relation->GetTargetsCount(&targets);
2205 0 : if (targets)
2206 0 : relations->AppendElement(relation, false);
2207 : }
2208 : }
2209 :
2210 0 : NS_ADDREF(*aRelations = relations);
2211 0 : return NS_OK;
2212 : }
2213 :
2214 : /* void extendSelection (); */
2215 0 : NS_IMETHODIMP nsAccessible::ExtendSelection()
2216 : {
2217 : // XXX Should be implemented, but not high priority
2218 0 : return NS_ERROR_NOT_IMPLEMENTED;
2219 : }
2220 :
2221 : /* [noscript] void getNativeInterface(out voidPtr aOutAccessible); */
2222 0 : NS_IMETHODIMP nsAccessible::GetNativeInterface(void **aOutAccessible)
2223 : {
2224 0 : return NS_ERROR_NOT_IMPLEMENTED;
2225 : }
2226 :
2227 : void
2228 0 : nsAccessible::DoCommand(nsIContent *aContent, PRUint32 aActionIndex)
2229 : {
2230 0 : nsIContent* content = aContent ? aContent : mContent.get();
2231 0 : NS_DISPATCH_RUNNABLEMETHOD_ARG2(DispatchClickEvent, this, content,
2232 : aActionIndex);
2233 0 : }
2234 :
2235 : void
2236 0 : nsAccessible::DispatchClickEvent(nsIContent *aContent, PRUint32 aActionIndex)
2237 : {
2238 0 : if (IsDefunct())
2239 0 : return;
2240 :
2241 0 : nsIPresShell* presShell = mDoc->PresShell();
2242 :
2243 : // Scroll into view.
2244 : presShell->ScrollContentIntoView(aContent, NS_PRESSHELL_SCROLL_ANYWHERE,
2245 : NS_PRESSHELL_SCROLL_ANYWHERE,
2246 0 : nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
2247 :
2248 : // Fire mouse down and mouse up events.
2249 : bool res = nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, presShell,
2250 0 : aContent);
2251 0 : if (!res)
2252 0 : return;
2253 :
2254 0 : nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_UP, presShell, aContent);
2255 : }
2256 :
2257 : NS_IMETHODIMP
2258 0 : nsAccessible::ScrollTo(PRUint32 aHow)
2259 : {
2260 0 : nsAccessNode::ScrollTo(aHow);
2261 0 : return NS_OK;
2262 : }
2263 :
2264 : NS_IMETHODIMP
2265 0 : nsAccessible::ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY)
2266 : {
2267 0 : nsIFrame *frame = GetFrame();
2268 0 : if (!frame)
2269 0 : return NS_ERROR_FAILURE;
2270 :
2271 0 : nsIntPoint coords;
2272 : nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
2273 0 : this, &coords);
2274 0 : NS_ENSURE_SUCCESS(rv, rv);
2275 :
2276 0 : nsIFrame *parentFrame = frame;
2277 0 : while ((parentFrame = parentFrame->GetParent()))
2278 0 : nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
2279 :
2280 0 : return NS_OK;
2281 : }
2282 :
2283 : // nsIAccessibleSelectable
2284 0 : NS_IMETHODIMP nsAccessible::GetSelectedChildren(nsIArray **aSelectedAccessibles)
2285 : {
2286 0 : NS_ENSURE_ARG_POINTER(aSelectedAccessibles);
2287 0 : *aSelectedAccessibles = nsnull;
2288 :
2289 0 : if (IsDefunct() || !IsSelect())
2290 0 : return NS_ERROR_FAILURE;
2291 :
2292 0 : nsCOMPtr<nsIArray> items = SelectedItems();
2293 0 : if (items) {
2294 0 : PRUint32 length = 0;
2295 0 : items->GetLength(&length);
2296 0 : if (length)
2297 0 : items.swap(*aSelectedAccessibles);
2298 : }
2299 :
2300 0 : return NS_OK;
2301 : }
2302 :
2303 : // return the nth selected descendant nsIAccessible object
2304 0 : NS_IMETHODIMP nsAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **aSelected)
2305 : {
2306 0 : NS_ENSURE_ARG_POINTER(aSelected);
2307 0 : *aSelected = nsnull;
2308 :
2309 0 : if (IsDefunct() || !IsSelect())
2310 0 : return NS_ERROR_FAILURE;
2311 :
2312 0 : if (aIndex < 0) {
2313 0 : return NS_ERROR_INVALID_ARG;
2314 : }
2315 :
2316 0 : *aSelected = GetSelectedItem(aIndex);
2317 0 : if (*aSelected) {
2318 0 : NS_ADDREF(*aSelected);
2319 0 : return NS_OK;
2320 : }
2321 :
2322 0 : return NS_ERROR_INVALID_ARG;
2323 : }
2324 :
2325 0 : NS_IMETHODIMP nsAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
2326 : {
2327 0 : NS_ENSURE_ARG_POINTER(aSelectionCount);
2328 0 : *aSelectionCount = 0;
2329 :
2330 0 : if (IsDefunct() || !IsSelect())
2331 0 : return NS_ERROR_FAILURE;
2332 :
2333 0 : *aSelectionCount = SelectedItemCount();
2334 0 : return NS_OK;
2335 : }
2336 :
2337 0 : NS_IMETHODIMP nsAccessible::AddChildToSelection(PRInt32 aIndex)
2338 : {
2339 0 : if (IsDefunct() || !IsSelect())
2340 0 : return NS_ERROR_FAILURE;
2341 :
2342 0 : return aIndex >= 0 && AddItemToSelection(aIndex) ?
2343 0 : NS_OK : NS_ERROR_INVALID_ARG;
2344 : }
2345 :
2346 0 : NS_IMETHODIMP nsAccessible::RemoveChildFromSelection(PRInt32 aIndex)
2347 : {
2348 0 : if (IsDefunct() || !IsSelect())
2349 0 : return NS_ERROR_FAILURE;
2350 :
2351 0 : return aIndex >=0 && RemoveItemFromSelection(aIndex) ?
2352 0 : NS_OK : NS_ERROR_INVALID_ARG;
2353 : }
2354 :
2355 0 : NS_IMETHODIMP nsAccessible::IsChildSelected(PRInt32 aIndex, bool *aIsSelected)
2356 : {
2357 0 : NS_ENSURE_ARG_POINTER(aIsSelected);
2358 0 : *aIsSelected = false;
2359 :
2360 0 : if (IsDefunct() || !IsSelect())
2361 0 : return NS_ERROR_FAILURE;
2362 :
2363 0 : NS_ENSURE_TRUE(aIndex >= 0, NS_ERROR_FAILURE);
2364 :
2365 0 : *aIsSelected = IsItemSelected(aIndex);
2366 0 : return NS_OK;
2367 : }
2368 :
2369 : NS_IMETHODIMP
2370 0 : nsAccessible::ClearSelection()
2371 : {
2372 0 : if (IsDefunct() || !IsSelect())
2373 0 : return NS_ERROR_FAILURE;
2374 :
2375 0 : UnselectAll();
2376 0 : return NS_OK;
2377 : }
2378 :
2379 : NS_IMETHODIMP
2380 0 : nsAccessible::SelectAllSelection(bool* aIsMultiSelect)
2381 : {
2382 0 : NS_ENSURE_ARG_POINTER(aIsMultiSelect);
2383 0 : *aIsMultiSelect = false;
2384 :
2385 0 : if (IsDefunct() || !IsSelect())
2386 0 : return NS_ERROR_FAILURE;
2387 :
2388 0 : *aIsMultiSelect = SelectAll();
2389 0 : return NS_OK;
2390 : }
2391 :
2392 : // nsIAccessibleHyperLink
2393 : // Because of new-atk design, any embedded object in text can implement
2394 : // nsIAccessibleHyperLink, which helps determine where it is located
2395 : // within containing text
2396 :
2397 : // readonly attribute long nsIAccessibleHyperLink::anchorCount
2398 : NS_IMETHODIMP
2399 0 : nsAccessible::GetAnchorCount(PRInt32 *aAnchorCount)
2400 : {
2401 0 : NS_ENSURE_ARG_POINTER(aAnchorCount);
2402 0 : *aAnchorCount = 0;
2403 :
2404 0 : if (IsDefunct())
2405 0 : return NS_ERROR_FAILURE;
2406 :
2407 0 : *aAnchorCount = AnchorCount();
2408 0 : return NS_OK;
2409 : }
2410 :
2411 : // readonly attribute long nsIAccessibleHyperLink::startIndex
2412 : NS_IMETHODIMP
2413 0 : nsAccessible::GetStartIndex(PRInt32 *aStartIndex)
2414 : {
2415 0 : NS_ENSURE_ARG_POINTER(aStartIndex);
2416 0 : *aStartIndex = 0;
2417 :
2418 0 : if (IsDefunct())
2419 0 : return NS_ERROR_FAILURE;
2420 :
2421 0 : *aStartIndex = StartOffset();
2422 0 : return NS_OK;
2423 : }
2424 :
2425 : // readonly attribute long nsIAccessibleHyperLink::endIndex
2426 : NS_IMETHODIMP
2427 0 : nsAccessible::GetEndIndex(PRInt32 *aEndIndex)
2428 : {
2429 0 : NS_ENSURE_ARG_POINTER(aEndIndex);
2430 0 : *aEndIndex = 0;
2431 :
2432 0 : if (IsDefunct())
2433 0 : return NS_ERROR_FAILURE;
2434 :
2435 0 : *aEndIndex = EndOffset();
2436 0 : return NS_OK;
2437 : }
2438 :
2439 : NS_IMETHODIMP
2440 0 : nsAccessible::GetURI(PRInt32 aIndex, nsIURI **aURI)
2441 : {
2442 0 : NS_ENSURE_ARG_POINTER(aURI);
2443 :
2444 0 : if (IsDefunct())
2445 0 : return NS_ERROR_FAILURE;
2446 :
2447 0 : if (aIndex < 0 || aIndex >= static_cast<PRInt32>(AnchorCount()))
2448 0 : return NS_ERROR_INVALID_ARG;
2449 :
2450 0 : nsRefPtr<nsIURI>(AnchorURIAt(aIndex)).forget(aURI);
2451 0 : return NS_OK;
2452 : }
2453 :
2454 :
2455 : NS_IMETHODIMP
2456 0 : nsAccessible::GetAnchor(PRInt32 aIndex, nsIAccessible** aAccessible)
2457 : {
2458 0 : NS_ENSURE_ARG_POINTER(aAccessible);
2459 0 : *aAccessible = nsnull;
2460 :
2461 0 : if (IsDefunct())
2462 0 : return NS_ERROR_FAILURE;
2463 :
2464 0 : if (aIndex < 0 || aIndex >= static_cast<PRInt32>(AnchorCount()))
2465 0 : return NS_ERROR_INVALID_ARG;
2466 :
2467 0 : NS_IF_ADDREF(*aAccessible = AnchorAt(aIndex));
2468 0 : return NS_OK;
2469 : }
2470 :
2471 : // readonly attribute boolean nsIAccessibleHyperLink::valid
2472 : NS_IMETHODIMP
2473 0 : nsAccessible::GetValid(bool *aValid)
2474 : {
2475 0 : NS_ENSURE_ARG_POINTER(aValid);
2476 0 : *aValid = false;
2477 :
2478 0 : if (IsDefunct())
2479 0 : return NS_ERROR_FAILURE;
2480 :
2481 0 : *aValid = IsLinkValid();
2482 0 : return NS_OK;
2483 : }
2484 :
2485 : // readonly attribute boolean nsIAccessibleHyperLink::selected
2486 : NS_IMETHODIMP
2487 0 : nsAccessible::GetSelected(bool *aSelected)
2488 : {
2489 0 : NS_ENSURE_ARG_POINTER(aSelected);
2490 0 : *aSelected = false;
2491 :
2492 0 : if (IsDefunct())
2493 0 : return NS_ERROR_FAILURE;
2494 :
2495 0 : *aSelected = IsLinkSelected();
2496 0 : return NS_OK;
2497 :
2498 : }
2499 :
2500 : void
2501 0 : nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset,
2502 : PRUint32 aLength)
2503 : {
2504 : // Return text representation of non-text accessible within hypertext
2505 : // accessible. Text accessible overrides this method to return enclosed text.
2506 0 : if (aStartOffset != 0 || aLength == 0)
2507 0 : return;
2508 :
2509 0 : nsIFrame *frame = GetFrame();
2510 0 : if (!frame)
2511 0 : return;
2512 :
2513 0 : if (frame->GetType() == nsGkAtoms::brFrame) {
2514 0 : aText += kForcedNewLineChar;
2515 0 : } else if (nsAccUtils::MustPrune(Parent())) {
2516 : // Expose the embedded object accessible as imaginary embedded object
2517 : // character if its parent hypertext accessible doesn't expose children to
2518 : // AT.
2519 0 : aText += kImaginaryEmbeddedObjectChar;
2520 : } else {
2521 0 : aText += kEmbeddedObjectChar;
2522 : }
2523 : }
2524 :
2525 : ////////////////////////////////////////////////////////////////////////////////
2526 : // nsAccessNode public methods
2527 :
2528 : void
2529 0 : nsAccessible::Shutdown()
2530 : {
2531 : // Invalidate the child count and pointers to other accessibles, also make
2532 : // sure none of its children point to this parent
2533 0 : InvalidateChildren();
2534 0 : if (mParent)
2535 0 : mParent->RemoveChild(this);
2536 :
2537 0 : nsAccessNodeWrap::Shutdown();
2538 0 : }
2539 :
2540 : ////////////////////////////////////////////////////////////////////////////////
2541 : // nsAccessible public methods
2542 :
2543 : nsresult
2544 0 : nsAccessible::GetARIAName(nsAString& aName)
2545 : {
2546 0 : nsAutoString label;
2547 :
2548 : // aria-labelledby now takes precedence over aria-label
2549 : nsresult rv = nsTextEquivUtils::
2550 0 : GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, label);
2551 0 : if (NS_SUCCEEDED(rv)) {
2552 0 : label.CompressWhitespace();
2553 0 : aName = label;
2554 : }
2555 :
2556 0 : if (label.IsEmpty() &&
2557 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label,
2558 0 : label)) {
2559 0 : label.CompressWhitespace();
2560 0 : aName = label;
2561 : }
2562 :
2563 0 : return NS_OK;
2564 : }
2565 :
2566 : nsresult
2567 0 : nsAccessible::GetNameInternal(nsAString& aName)
2568 : {
2569 0 : if (mContent->IsHTML())
2570 0 : return GetHTMLName(aName);
2571 :
2572 0 : if (mContent->IsXUL())
2573 0 : return GetXULName(aName);
2574 :
2575 0 : return NS_OK;
2576 : }
2577 :
2578 : // nsAccessible protected
2579 : void
2580 0 : nsAccessible::BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent)
2581 : {
2582 0 : NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
2583 :
2584 0 : if (mParent) {
2585 0 : if (mParent != aParent) {
2586 0 : NS_ERROR("Adopting child!");
2587 0 : mParent->RemoveChild(this);
2588 : } else {
2589 0 : NS_ERROR("Binding to the same parent!");
2590 0 : return;
2591 : }
2592 : }
2593 :
2594 0 : mParent = aParent;
2595 0 : mIndexInParent = aIndexInParent;
2596 : }
2597 :
2598 : void
2599 0 : nsAccessible::UnbindFromParent()
2600 : {
2601 0 : mParent = nsnull;
2602 0 : mIndexInParent = -1;
2603 0 : mIndexOfEmbeddedChild = -1;
2604 0 : mGroupInfo = nsnull;
2605 0 : }
2606 :
2607 : void
2608 0 : nsAccessible::InvalidateChildren()
2609 : {
2610 0 : PRInt32 childCount = mChildren.Length();
2611 0 : for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
2612 0 : nsAccessible* child = mChildren.ElementAt(childIdx);
2613 0 : child->UnbindFromParent();
2614 : }
2615 :
2616 0 : mEmbeddedObjCollector = nsnull;
2617 0 : mChildren.Clear();
2618 0 : SetChildrenFlag(eChildrenUninitialized);
2619 0 : }
2620 :
2621 : bool
2622 0 : nsAccessible::AppendChild(nsAccessible* aChild)
2623 : {
2624 0 : if (!aChild)
2625 0 : return false;
2626 :
2627 0 : if (!mChildren.AppendElement(aChild))
2628 0 : return false;
2629 :
2630 0 : if (!nsAccUtils::IsEmbeddedObject(aChild))
2631 0 : SetChildrenFlag(eMixedChildren);
2632 :
2633 0 : aChild->BindToParent(this, mChildren.Length() - 1);
2634 0 : return true;
2635 : }
2636 :
2637 : bool
2638 0 : nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
2639 : {
2640 0 : if (!aChild)
2641 0 : return false;
2642 :
2643 0 : if (!mChildren.InsertElementAt(aIndex, aChild))
2644 0 : return false;
2645 :
2646 0 : for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++) {
2647 0 : NS_ASSERTION(mChildren[idx]->mIndexInParent == idx - 1, "Accessible child index doesn't match");
2648 0 : mChildren[idx]->mIndexInParent = idx;
2649 : }
2650 :
2651 0 : if (nsAccUtils::IsText(aChild))
2652 0 : SetChildrenFlag(eMixedChildren);
2653 :
2654 0 : mEmbeddedObjCollector = nsnull;
2655 :
2656 0 : aChild->BindToParent(this, aIndex);
2657 0 : return true;
2658 : }
2659 :
2660 : bool
2661 0 : nsAccessible::RemoveChild(nsAccessible* aChild)
2662 : {
2663 0 : if (!aChild)
2664 0 : return false;
2665 :
2666 0 : if (aChild->mParent != this || aChild->mIndexInParent == -1)
2667 0 : return false;
2668 :
2669 0 : PRUint32 index = static_cast<PRUint32>(aChild->mIndexInParent);
2670 0 : if (index >= mChildren.Length() || mChildren[index] != aChild) {
2671 0 : NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
2672 0 : aChild->UnbindFromParent();
2673 0 : return false;
2674 : }
2675 :
2676 0 : for (PRUint32 idx = index + 1; idx < mChildren.Length(); idx++) {
2677 0 : NS_ASSERTION(mChildren[idx]->mIndexInParent == idx, "Accessible child index doesn't match");
2678 0 : mChildren[idx]->mIndexInParent = idx - 1;
2679 : }
2680 :
2681 0 : aChild->UnbindFromParent();
2682 0 : mChildren.RemoveElementAt(index);
2683 0 : mEmbeddedObjCollector = nsnull;
2684 :
2685 0 : return true;
2686 : }
2687 :
2688 : nsAccessible*
2689 0 : nsAccessible::GetChildAt(PRUint32 aIndex)
2690 : {
2691 0 : nsAccessible *child = mChildren.SafeElementAt(aIndex, nsnull);
2692 0 : if (!child)
2693 0 : return nsnull;
2694 :
2695 : #ifdef DEBUG
2696 0 : nsAccessible* realParent = child->mParent;
2697 0 : NS_ASSERTION(!realParent || realParent == this,
2698 : "Two accessibles have the same first child accessible!");
2699 : #endif
2700 :
2701 0 : return child;
2702 : }
2703 :
2704 : PRInt32
2705 0 : nsAccessible::GetChildCount()
2706 : {
2707 0 : return mChildren.Length();
2708 : }
2709 :
2710 : PRInt32
2711 0 : nsAccessible::GetIndexOf(nsAccessible* aChild)
2712 : {
2713 0 : return (aChild->mParent != this) ? -1 : aChild->IndexInParent();
2714 : }
2715 :
2716 : PRInt32
2717 0 : nsAccessible::IndexInParent() const
2718 : {
2719 0 : return mIndexInParent;
2720 : }
2721 :
2722 : PRInt32
2723 0 : nsAccessible::GetEmbeddedChildCount()
2724 : {
2725 0 : if (IsChildrenFlag(eMixedChildren)) {
2726 0 : if (!mEmbeddedObjCollector)
2727 0 : mEmbeddedObjCollector = new EmbeddedObjCollector(this);
2728 0 : return mEmbeddedObjCollector ? mEmbeddedObjCollector->Count() : -1;
2729 : }
2730 :
2731 0 : return GetChildCount();
2732 : }
2733 :
2734 : nsAccessible*
2735 0 : nsAccessible::GetEmbeddedChildAt(PRUint32 aIndex)
2736 : {
2737 0 : if (IsChildrenFlag(eMixedChildren)) {
2738 0 : if (!mEmbeddedObjCollector)
2739 0 : mEmbeddedObjCollector = new EmbeddedObjCollector(this);
2740 : return mEmbeddedObjCollector ?
2741 0 : mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nsnull;
2742 : }
2743 :
2744 0 : return GetChildAt(aIndex);
2745 : }
2746 :
2747 : PRInt32
2748 0 : nsAccessible::GetIndexOfEmbeddedChild(nsAccessible* aChild)
2749 : {
2750 0 : if (IsChildrenFlag(eMixedChildren)) {
2751 0 : if (!mEmbeddedObjCollector)
2752 0 : mEmbeddedObjCollector = new EmbeddedObjCollector(this);
2753 : return mEmbeddedObjCollector ?
2754 0 : mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
2755 : }
2756 :
2757 0 : return GetIndexOf(aChild);
2758 : }
2759 :
2760 : ////////////////////////////////////////////////////////////////////////////////
2761 : // HyperLinkAccessible methods
2762 :
2763 : bool
2764 0 : nsAccessible::IsLink()
2765 : {
2766 : // Every embedded accessible within hypertext accessible implements
2767 : // hyperlink interface.
2768 0 : return mParent && mParent->IsHyperText() && nsAccUtils::IsEmbeddedObject(this);
2769 : }
2770 :
2771 : PRUint32
2772 0 : nsAccessible::StartOffset()
2773 : {
2774 0 : NS_PRECONDITION(IsLink(), "StartOffset is called not on hyper link!");
2775 :
2776 0 : nsHyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nsnull;
2777 0 : return hyperText ? hyperText->GetChildOffset(this) : 0;
2778 : }
2779 :
2780 : PRUint32
2781 0 : nsAccessible::EndOffset()
2782 : {
2783 0 : NS_PRECONDITION(IsLink(), "EndOffset is called on not hyper link!");
2784 :
2785 0 : nsHyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nsnull;
2786 0 : return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
2787 : }
2788 :
2789 : bool
2790 0 : nsAccessible::IsLinkSelected()
2791 : {
2792 0 : NS_PRECONDITION(IsLink(),
2793 : "IsLinkSelected() called on something that is not a hyper link!");
2794 0 : return FocusMgr()->IsFocused(this);
2795 : }
2796 :
2797 : PRUint32
2798 0 : nsAccessible::AnchorCount()
2799 : {
2800 0 : NS_PRECONDITION(IsLink(), "AnchorCount is called on not hyper link!");
2801 0 : return 1;
2802 : }
2803 :
2804 : nsAccessible*
2805 0 : nsAccessible::AnchorAt(PRUint32 aAnchorIndex)
2806 : {
2807 0 : NS_PRECONDITION(IsLink(), "GetAnchor is called on not hyper link!");
2808 0 : return aAnchorIndex == 0 ? this : nsnull;
2809 : }
2810 :
2811 : already_AddRefed<nsIURI>
2812 0 : nsAccessible::AnchorURIAt(PRUint32 aAnchorIndex)
2813 : {
2814 0 : NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!");
2815 :
2816 0 : if (aAnchorIndex != 0)
2817 0 : return nsnull;
2818 :
2819 : // Check if it's a simple xlink.
2820 0 : if (nsCoreUtils::IsXLink(mContent)) {
2821 0 : nsAutoString href;
2822 0 : mContent->GetAttr(kNameSpaceID_XLink, nsGkAtoms::href, href);
2823 :
2824 0 : nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
2825 0 : nsCOMPtr<nsIDocument> document = mContent->OwnerDoc();
2826 0 : nsIURI* anchorURI = nsnull;
2827 : NS_NewURI(&anchorURI, href,
2828 0 : document ? document->GetDocumentCharacterSet().get() : nsnull,
2829 0 : baseURI);
2830 0 : return anchorURI;
2831 : }
2832 :
2833 0 : return nsnull;
2834 : }
2835 :
2836 :
2837 : ////////////////////////////////////////////////////////////////////////////////
2838 : // SelectAccessible
2839 :
2840 : bool
2841 0 : nsAccessible::IsSelect()
2842 : {
2843 : // If we have an ARIA role attribute present and the role allows multi
2844 : // selectable state, then we need to support SelectAccessible interface. If
2845 : // either attribute (role or multiselectable) change, then we'll destroy this
2846 : // accessible so that we can follow COM identity rules.
2847 :
2848 : return mRoleMapEntry &&
2849 : (mRoleMapEntry->attributeMap1 == eARIAMultiSelectable ||
2850 : mRoleMapEntry->attributeMap2 == eARIAMultiSelectable ||
2851 0 : mRoleMapEntry->attributeMap3 == eARIAMultiSelectable);
2852 : }
2853 :
2854 : already_AddRefed<nsIArray>
2855 0 : nsAccessible::SelectedItems()
2856 : {
2857 0 : nsCOMPtr<nsIMutableArray> selectedItems = do_CreateInstance(NS_ARRAY_CONTRACTID);
2858 0 : if (!selectedItems)
2859 0 : return nsnull;
2860 :
2861 0 : AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
2862 0 : nsIAccessible* selected = nsnull;
2863 0 : while ((selected = iter.Next()))
2864 0 : selectedItems->AppendElement(selected, false);
2865 :
2866 0 : nsIMutableArray* items = nsnull;
2867 0 : selectedItems.forget(&items);
2868 0 : return items;
2869 : }
2870 :
2871 : PRUint32
2872 0 : nsAccessible::SelectedItemCount()
2873 : {
2874 0 : PRUint32 count = 0;
2875 0 : AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
2876 0 : nsAccessible* selected = nsnull;
2877 0 : while ((selected = iter.Next()))
2878 0 : ++count;
2879 :
2880 0 : return count;
2881 : }
2882 :
2883 : nsAccessible*
2884 0 : nsAccessible::GetSelectedItem(PRUint32 aIndex)
2885 : {
2886 0 : AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
2887 0 : nsAccessible* selected = nsnull;
2888 :
2889 0 : PRUint32 index = 0;
2890 0 : while ((selected = iter.Next()) && index < aIndex)
2891 0 : index++;
2892 :
2893 0 : return selected;
2894 : }
2895 :
2896 : bool
2897 0 : nsAccessible::IsItemSelected(PRUint32 aIndex)
2898 : {
2899 0 : PRUint32 index = 0;
2900 0 : AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
2901 0 : nsAccessible* selected = nsnull;
2902 0 : while ((selected = iter.Next()) && index < aIndex)
2903 0 : index++;
2904 :
2905 : return selected &&
2906 0 : selected->State() & states::SELECTED;
2907 : }
2908 :
2909 : bool
2910 0 : nsAccessible::AddItemToSelection(PRUint32 aIndex)
2911 : {
2912 0 : PRUint32 index = 0;
2913 0 : AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
2914 0 : nsAccessible* selected = nsnull;
2915 0 : while ((selected = iter.Next()) && index < aIndex)
2916 0 : index++;
2917 :
2918 0 : if (selected)
2919 0 : selected->SetSelected(true);
2920 :
2921 0 : return static_cast<bool>(selected);
2922 : }
2923 :
2924 : bool
2925 0 : nsAccessible::RemoveItemFromSelection(PRUint32 aIndex)
2926 : {
2927 0 : PRUint32 index = 0;
2928 0 : AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
2929 0 : nsAccessible* selected = nsnull;
2930 0 : while ((selected = iter.Next()) && index < aIndex)
2931 0 : index++;
2932 :
2933 0 : if (selected)
2934 0 : selected->SetSelected(false);
2935 :
2936 0 : return static_cast<bool>(selected);
2937 : }
2938 :
2939 : bool
2940 0 : nsAccessible::SelectAll()
2941 : {
2942 0 : bool success = false;
2943 0 : nsAccessible* selectable = nsnull;
2944 :
2945 0 : AccIterator iter(this, filters::GetSelectable, AccIterator::eTreeNav);
2946 0 : while((selectable = iter.Next())) {
2947 0 : success = true;
2948 0 : selectable->SetSelected(true);
2949 : }
2950 0 : return success;
2951 : }
2952 :
2953 : bool
2954 0 : nsAccessible::UnselectAll()
2955 : {
2956 0 : bool success = false;
2957 0 : nsAccessible* selected = nsnull;
2958 :
2959 0 : AccIterator iter(this, filters::GetSelected, AccIterator::eTreeNav);
2960 0 : while ((selected = iter.Next())) {
2961 0 : success = true;
2962 0 : selected->SetSelected(false);
2963 : }
2964 0 : return success;
2965 : }
2966 :
2967 : ////////////////////////////////////////////////////////////////////////////////
2968 : // Widgets
2969 :
2970 : bool
2971 0 : nsAccessible::IsWidget() const
2972 : {
2973 0 : return false;
2974 : }
2975 :
2976 : bool
2977 0 : nsAccessible::IsActiveWidget() const
2978 : {
2979 0 : return FocusMgr()->IsFocused(this);
2980 : }
2981 :
2982 : bool
2983 0 : nsAccessible::AreItemsOperable() const
2984 : {
2985 0 : return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
2986 : }
2987 :
2988 : nsAccessible*
2989 0 : nsAccessible::CurrentItem()
2990 : {
2991 : // Check for aria-activedescendant, which changes which element has focus.
2992 : // For activedescendant, the ARIA spec does not require that the user agent
2993 : // checks whether pointed node is actually a DOM descendant of the element
2994 : // with the aria-activedescendant attribute.
2995 0 : nsAutoString id;
2996 0 : if (mContent->GetAttr(kNameSpaceID_None,
2997 0 : nsGkAtoms::aria_activedescendant, id)) {
2998 0 : nsIDocument* DOMDoc = mContent->OwnerDoc();
2999 0 : dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
3000 0 : if (activeDescendantElm) {
3001 0 : nsDocAccessible* document = Document();
3002 0 : if (document)
3003 0 : return document->GetAccessible(activeDescendantElm);
3004 : }
3005 : }
3006 0 : return nsnull;
3007 : }
3008 :
3009 : void
3010 0 : nsAccessible::SetCurrentItem(nsAccessible* aItem)
3011 : {
3012 0 : nsIAtom* id = aItem->GetContent()->GetID();
3013 0 : if (id) {
3014 0 : nsAutoString idStr;
3015 0 : id->ToString(idStr);
3016 : mContent->SetAttr(kNameSpaceID_None,
3017 0 : nsGkAtoms::aria_activedescendant, idStr, true);
3018 : }
3019 0 : }
3020 :
3021 : nsAccessible*
3022 0 : nsAccessible::ContainerWidget() const
3023 : {
3024 0 : if (HasARIARole() && mContent->HasID()) {
3025 0 : for (nsAccessible* parent = Parent(); parent; parent = parent->Parent()) {
3026 0 : nsIContent* parentContent = parent->GetContent();
3027 0 : if (parentContent &&
3028 : parentContent->HasAttr(kNameSpaceID_None,
3029 0 : nsGkAtoms::aria_activedescendant)) {
3030 0 : return parent;
3031 : }
3032 :
3033 : // Don't cross DOM document boundaries.
3034 0 : if (parent->IsDocumentNode())
3035 0 : break;
3036 : }
3037 : }
3038 0 : return nsnull;
3039 : }
3040 :
3041 : ////////////////////////////////////////////////////////////////////////////////
3042 : // nsAccessible protected methods
3043 :
3044 : void
3045 0 : nsAccessible::CacheChildren()
3046 : {
3047 0 : nsDocAccessible* doc = Document();
3048 0 : NS_ENSURE_TRUE(doc,);
3049 :
3050 0 : nsAccTreeWalker walker(doc, mContent, CanHaveAnonChildren());
3051 :
3052 0 : nsAccessible* child = nsnull;
3053 0 : while ((child = walker.NextChild()) && AppendChild(child));
3054 : }
3055 :
3056 : void
3057 0 : nsAccessible::TestChildCache(nsAccessible* aCachedChild) const
3058 : {
3059 : #ifdef DEBUG
3060 0 : PRInt32 childCount = mChildren.Length();
3061 0 : if (childCount == 0) {
3062 0 : NS_ASSERTION(IsChildrenFlag(eChildrenUninitialized),
3063 : "No children but initialized!");
3064 0 : return;
3065 : }
3066 :
3067 0 : nsAccessible *child = nsnull;
3068 0 : for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
3069 0 : child = mChildren[childIdx];
3070 0 : if (child == aCachedChild)
3071 0 : break;
3072 : }
3073 :
3074 0 : NS_ASSERTION(child == aCachedChild,
3075 : "[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");
3076 : #endif
3077 : }
3078 :
3079 : // nsAccessible public
3080 : bool
3081 0 : nsAccessible::EnsureChildren()
3082 : {
3083 0 : if (IsDefunct()) {
3084 0 : SetChildrenFlag(eChildrenUninitialized);
3085 0 : return true;
3086 : }
3087 :
3088 0 : if (!IsChildrenFlag(eChildrenUninitialized))
3089 0 : return false;
3090 :
3091 : // State is embedded children until text leaf accessible is appended.
3092 0 : SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
3093 :
3094 0 : CacheChildren();
3095 0 : return false;
3096 : }
3097 :
3098 : nsAccessible*
3099 0 : nsAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError) const
3100 : {
3101 0 : if (!mParent || mIndexInParent == -1) {
3102 0 : if (aError)
3103 0 : *aError = NS_ERROR_UNEXPECTED;
3104 :
3105 0 : return nsnull;
3106 : }
3107 :
3108 0 : if (aError && mIndexInParent + aOffset >= mParent->GetChildCount()) {
3109 0 : *aError = NS_OK; // fail peacefully
3110 0 : return nsnull;
3111 : }
3112 :
3113 0 : nsAccessible* child = mParent->GetChildAt(mIndexInParent + aOffset);
3114 0 : if (aError && !child)
3115 0 : *aError = NS_ERROR_UNEXPECTED;
3116 :
3117 0 : return child;
3118 : }
3119 :
3120 : nsAccessible *
3121 0 : nsAccessible::GetFirstAvailableAccessible(nsINode *aStartNode) const
3122 : {
3123 0 : nsAccessible* accessible = mDoc->GetAccessible(aStartNode);
3124 0 : if (accessible)
3125 0 : return accessible;
3126 :
3127 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aStartNode->OwnerDoc());
3128 0 : NS_ENSURE_TRUE(domDoc, nsnull);
3129 :
3130 0 : nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(aStartNode);
3131 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetNode());
3132 0 : nsCOMPtr<nsIDOMTreeWalker> walker;
3133 0 : domDoc->CreateTreeWalker(rootNode,
3134 : nsIDOMNodeFilter::SHOW_ELEMENT | nsIDOMNodeFilter::SHOW_TEXT,
3135 0 : nsnull, false, getter_AddRefs(walker));
3136 0 : NS_ENSURE_TRUE(walker, nsnull);
3137 :
3138 0 : walker->SetCurrentNode(currentNode);
3139 0 : while (true) {
3140 0 : walker->NextNode(getter_AddRefs(currentNode));
3141 0 : if (!currentNode)
3142 0 : return nsnull;
3143 :
3144 0 : nsCOMPtr<nsINode> node(do_QueryInterface(currentNode));
3145 0 : nsAccessible* accessible = mDoc->GetAccessible(node);
3146 0 : if (accessible)
3147 0 : return accessible;
3148 : }
3149 :
3150 : return nsnull;
3151 : }
3152 :
3153 : nsresult
3154 0 : nsAccessible::GetAttrValue(nsIAtom *aProperty, double *aValue)
3155 : {
3156 0 : NS_ENSURE_ARG_POINTER(aValue);
3157 0 : *aValue = 0;
3158 :
3159 0 : if (IsDefunct())
3160 0 : return NS_ERROR_FAILURE; // Node already shut down
3161 :
3162 0 : if (!mRoleMapEntry || mRoleMapEntry->valueRule == eNoValue)
3163 0 : return NS_OK_NO_ARIA_VALUE;
3164 :
3165 0 : nsAutoString attrValue;
3166 0 : mContent->GetAttr(kNameSpaceID_None, aProperty, attrValue);
3167 :
3168 : // Return zero value if there is no attribute or its value is empty.
3169 0 : if (attrValue.IsEmpty())
3170 0 : return NS_OK;
3171 :
3172 0 : PRInt32 error = NS_OK;
3173 0 : double value = attrValue.ToDouble(&error);
3174 0 : if (NS_SUCCEEDED(error))
3175 0 : *aValue = value;
3176 :
3177 0 : return NS_OK;
3178 : }
3179 :
3180 : PRUint32
3181 0 : nsAccessible::GetActionRule(PRUint64 aStates)
3182 : {
3183 0 : if (aStates & states::UNAVAILABLE)
3184 0 : return eNoAction;
3185 :
3186 : // Check if it's simple xlink.
3187 0 : if (nsCoreUtils::IsXLink(mContent))
3188 0 : return eJumpAction;
3189 :
3190 : // Return "click" action on elements that have an attached popup menu.
3191 0 : if (mContent->IsXUL())
3192 0 : if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
3193 0 : return eClickAction;
3194 :
3195 : // Has registered 'click' event handler.
3196 0 : bool isOnclick = nsCoreUtils::HasClickListener(mContent);
3197 :
3198 0 : if (isOnclick)
3199 0 : return eClickAction;
3200 :
3201 : // Get an action based on ARIA role.
3202 0 : if (mRoleMapEntry &&
3203 : mRoleMapEntry->actionRule != eNoAction)
3204 0 : return mRoleMapEntry->actionRule;
3205 :
3206 : // Get an action based on ARIA attribute.
3207 0 : if (nsAccUtils::HasDefinedARIAToken(mContent,
3208 0 : nsGkAtoms::aria_expanded))
3209 0 : return eExpandAction;
3210 :
3211 0 : return eNoAction;
3212 : }
3213 :
3214 : AccGroupInfo*
3215 0 : nsAccessible::GetGroupInfo()
3216 : {
3217 0 : if (mGroupInfo)
3218 0 : return mGroupInfo;
3219 :
3220 0 : mGroupInfo = AccGroupInfo::CreateGroupInfo(this);
3221 0 : return mGroupInfo;
3222 : }
3223 :
3224 : void
3225 0 : nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
3226 : {
3227 0 : AccGroupInfo* groupInfo = GetGroupInfo();
3228 0 : if (groupInfo) {
3229 0 : *aPosInSet = groupInfo->PosInSet();
3230 0 : *aSetSize = groupInfo->SetSize();
3231 : }
3232 0 : }
3233 :
3234 : PRInt32
3235 0 : nsAccessible::GetLevelInternal()
3236 : {
3237 0 : PRInt32 level = nsAccUtils::GetDefaultLevel(this);
3238 :
3239 0 : if (!IsBoundToParent())
3240 0 : return level;
3241 :
3242 0 : roles::Role role = Role();
3243 0 : if (role == roles::OUTLINEITEM) {
3244 : // Always expose 'level' attribute for 'outlineitem' accessible. The number
3245 : // of nested 'grouping' accessibles containing 'outlineitem' accessible is
3246 : // its level.
3247 0 : level = 1;
3248 :
3249 0 : nsAccessible* parent = this;
3250 0 : while ((parent = parent->Parent())) {
3251 0 : roles::Role parentRole = parent->Role();
3252 :
3253 0 : if (parentRole == roles::OUTLINE)
3254 0 : break;
3255 0 : if (parentRole == roles::GROUPING)
3256 0 : ++ level;
3257 :
3258 : }
3259 :
3260 0 : } else if (role == roles::LISTITEM) {
3261 : // Expose 'level' attribute on nested lists. We assume nested list is a last
3262 : // child of listitem of parent list. We don't handle the case when nested
3263 : // lists have more complex structure, for example when there are accessibles
3264 : // between parent listitem and nested list.
3265 :
3266 : // Calculate 'level' attribute based on number of parent listitems.
3267 0 : level = 0;
3268 0 : nsAccessible* parent = this;
3269 0 : while ((parent = parent->Parent())) {
3270 0 : roles::Role parentRole = parent->Role();
3271 :
3272 0 : if (parentRole == roles::LISTITEM)
3273 0 : ++ level;
3274 0 : else if (parentRole != roles::LIST)
3275 0 : break;
3276 :
3277 : }
3278 :
3279 0 : if (level == 0) {
3280 : // If this listitem is on top of nested lists then expose 'level'
3281 : // attribute.
3282 0 : parent = Parent();
3283 0 : PRInt32 siblingCount = parent->GetChildCount();
3284 0 : for (PRInt32 siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
3285 0 : nsAccessible* sibling = parent->GetChildAt(siblingIdx);
3286 :
3287 0 : nsAccessible* siblingChild = sibling->LastChild();
3288 0 : if (siblingChild && siblingChild->Role() == roles::LIST)
3289 0 : return 1;
3290 : }
3291 : } else {
3292 0 : ++ level; // level is 1-index based
3293 : }
3294 : }
3295 :
3296 0 : return level;
3297 : }
3298 :
3299 :
3300 : ////////////////////////////////////////////////////////////////////////////////
3301 : // KeyBinding class
3302 :
3303 : void
3304 0 : KeyBinding::ToPlatformFormat(nsAString& aValue) const
3305 : {
3306 0 : nsCOMPtr<nsIStringBundle> keyStringBundle;
3307 : nsCOMPtr<nsIStringBundleService> stringBundleService =
3308 0 : mozilla::services::GetStringBundleService();
3309 0 : if (stringBundleService)
3310 0 : stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL,
3311 0 : getter_AddRefs(keyStringBundle));
3312 :
3313 0 : if (!keyStringBundle)
3314 : return;
3315 :
3316 0 : nsAutoString separator;
3317 0 : keyStringBundle->GetStringFromName(NS_LITERAL_STRING("MODIFIER_SEPARATOR").get(),
3318 0 : getter_Copies(separator));
3319 :
3320 0 : nsAutoString modifierName;
3321 0 : if (mModifierMask & kControl) {
3322 0 : keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_CONTROL").get(),
3323 0 : getter_Copies(modifierName));
3324 :
3325 0 : aValue.Append(modifierName);
3326 0 : aValue.Append(separator);
3327 : }
3328 :
3329 0 : if (mModifierMask & kAlt) {
3330 0 : keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_ALT").get(),
3331 0 : getter_Copies(modifierName));
3332 :
3333 0 : aValue.Append(modifierName);
3334 0 : aValue.Append(separator);
3335 : }
3336 :
3337 0 : if (mModifierMask & kShift) {
3338 0 : keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_SHIFT").get(),
3339 0 : getter_Copies(modifierName));
3340 :
3341 0 : aValue.Append(modifierName);
3342 0 : aValue.Append(separator);
3343 : }
3344 :
3345 0 : if (mModifierMask & kMeta) {
3346 0 : keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_META").get(),
3347 0 : getter_Copies(modifierName));
3348 :
3349 0 : aValue.Append(modifierName);
3350 0 : aValue.Append(separator);
3351 : }
3352 :
3353 0 : aValue.Append(mKey);
3354 : }
3355 :
3356 : void
3357 0 : KeyBinding::ToAtkFormat(nsAString& aValue) const
3358 : {
3359 0 : nsAutoString modifierName;
3360 0 : if (mModifierMask & kControl)
3361 0 : aValue.Append(NS_LITERAL_STRING("<Control>"));
3362 :
3363 0 : if (mModifierMask & kAlt)
3364 0 : aValue.Append(NS_LITERAL_STRING("<Alt>"));
3365 :
3366 0 : if (mModifierMask & kShift)
3367 0 : aValue.Append(NS_LITERAL_STRING("<Shift>"));
3368 :
3369 0 : if (mModifierMask & kMeta)
3370 0 : aValue.Append(NS_LITERAL_STRING("<Meta>"));
3371 :
3372 0 : aValue.Append(mKey);
3373 4392 : }
|