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
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/
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
15 * The Original Code is mozilla.org code.
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.
23 * John Gaunt (jgaunt@netscape.com)
24 * Aaron Leventhal (aaronl@netscape.com)
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.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsAccessible.h"
41 #include "nsAccessibleRelation.h"
42 #include "nsHyperTextAccessibleWrap.h"
43 #include "nsNameUtils.h"
45 #include "nsIAccessibleDocument.h"
46 #include "nsIAccessibleHyperText.h"
47 #include "nsAccessibleTreeWalker.h"
49 #include "nsIDOMElement.h"
50 #include "nsIDOMDocument.h"
51 #include "nsIDOMDocumentXBL.h"
52 #include "nsIDOMDocumentTraversal.h"
53 #include "nsIDOMHTMLDocument.h"
54 #include "nsIDOMHTMLFormElement.h"
55 #include "nsIDOMNodeFilter.h"
56 #include "nsIDOMNSHTMLElement.h"
57 #include "nsIDOMTreeWalker.h"
58 #include "nsIDOMXULButtonElement.h"
59 #include "nsIDOMXULDocument.h"
60 #include "nsIDOMXULElement.h"
61 #include "nsIDOMXULLabelElement.h"
62 #include "nsIDOMXULSelectCntrlEl.h"
63 #include "nsIDOMXULSelectCntrlItemEl.h"
64 #include "nsPIDOMWindow.h"
66 #include "nsIDocument.h"
67 #include "nsIContent.h"
69 #include "nsIFormControl.h"
71 #include "nsIPresShell.h"
72 #include "nsPresContext.h"
74 #include "nsIViewManager.h"
75 #include "nsIDocShellTreeItem.h"
76 #include "nsIScrollableFrame.h"
78 #include "nsXPIDLString.h"
79 #include "nsUnicharUtils.h"
80 #include "nsReadableUtils.h"
83 #include "nsIPrefService.h"
84 #include "nsIPrefBranch.h"
87 #include "nsIMutableArray.h"
88 #include "nsIObserverService.h"
89 #include "nsIServiceManager.h"
90 #include "nsWhitespaceTokenizer.h"
91 #include "nsAttrName.h"
92 #include "nsNetUtil.h"
95 #include "nsIFrameDebug.h"
96 #include "nsIDOMCharacterData.h"
100 * nsAccessibleDOMStringList implementation
102 nsAccessibleDOMStringList::nsAccessibleDOMStringList()
106 nsAccessibleDOMStringList::~nsAccessibleDOMStringList()
110 NS_IMPL_ISUPPORTS1(nsAccessibleDOMStringList
, nsIDOMDOMStringList
)
113 nsAccessibleDOMStringList::Item(PRUint32 aIndex
, nsAString
& aResult
)
115 if (aIndex
>= (PRUint32
)mNames
.Count()) {
116 SetDOMStringToNull(aResult
);
118 mNames
.StringAt(aIndex
, aResult
);
125 nsAccessibleDOMStringList::GetLength(PRUint32
*aLength
)
127 *aLength
= (PRUint32
)mNames
.Count();
133 nsAccessibleDOMStringList::Contains(const nsAString
& aString
, PRBool
*aResult
)
135 *aResult
= mNames
.IndexOf(aString
) > -1;
144 ////////////////////////////////////////////////////////////////////////////////
145 // nsAccessible. nsISupports
147 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessible
)
149 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsAccessible
, nsAccessNode
)
150 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParent
)
151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstChild
)
152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNextSibling
)
153 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
155 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsAccessible
, nsAccessNode
)
156 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent
)
157 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstChild
)
158 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNextSibling
)
159 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
161 NS_IMPL_ADDREF_INHERITED(nsAccessible
, nsAccessNode
)
162 NS_IMPL_RELEASE_INHERITED(nsAccessible
, nsAccessNode
)
164 nsresult
nsAccessible::QueryInterface(REFNSIID aIID
, void** aInstancePtr
)
166 // Custom-built QueryInterface() knows when we support nsIAccessibleSelectable
167 // based on role attribute and aria-multiselectable
168 *aInstancePtr
= nsnull
;
170 if (aIID
.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant
))) {
171 *aInstancePtr
= &NS_CYCLE_COLLECTION_NAME(nsAccessible
);
175 if (aIID
.Equals(NS_GET_IID(nsIAccessible
))) {
176 *aInstancePtr
= static_cast<nsIAccessible
*>(this);
181 if(aIID
.Equals(NS_GET_IID(nsPIAccessible
))) {
182 *aInstancePtr
= static_cast<nsPIAccessible
*>(this);
187 if (aIID
.Equals(NS_GET_IID(nsAccessible
))) {
188 *aInstancePtr
= static_cast<nsAccessible
*>(this);
193 if (aIID
.Equals(NS_GET_IID(nsIAccessibleSelectable
))) {
194 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
196 return NS_ERROR_FAILURE
; // This accessible has been shut down
198 if (content
->HasAttr(kNameSpaceID_None
, nsAccessibilityAtoms::role
)) {
199 // If we have an XHTML role attribute present and the
200 // aria-multiselectable attribute is true, then we need
201 // to support nsIAccessibleSelectable
202 // If either attribute (role or multiselectable) change, then we'll
203 // destroy this accessible so that we can follow COM identity rules.
204 nsAutoString multiselectable
;
205 if (content
->AttrValueIs(kNameSpaceID_None
, nsAccessibilityAtoms::aria_multiselectable
,
206 nsAccessibilityAtoms::_true
, eCaseMatters
)) {
207 *aInstancePtr
= static_cast<nsIAccessibleSelectable
*>(this);
214 if (aIID
.Equals(NS_GET_IID(nsIAccessibleValue
))) {
215 if (mRoleMapEntry
&& mRoleMapEntry
->valueRule
!= eNoValue
) {
216 *aInstancePtr
= static_cast<nsIAccessibleValue
*>(this);
222 if (aIID
.Equals(NS_GET_IID(nsIAccessibleHyperLink
))) {
223 nsCOMPtr
<nsIAccessible
> parent(GetParent());
224 nsCOMPtr
<nsIAccessibleHyperText
> hyperTextParent(do_QueryInterface(parent
));
225 if (hyperTextParent
) {
226 *aInstancePtr
= static_cast<nsIAccessibleHyperLink
*>(this);
230 return NS_ERROR_NO_INTERFACE
;
233 return nsAccessNodeWrap::QueryInterface(aIID
, aInstancePtr
);
236 nsAccessible::nsAccessible(nsIDOMNode
* aNode
, nsIWeakReference
* aShell
): nsAccessNodeWrap(aNode
, aShell
),
237 mParent(nsnull
), mFirstChild(nsnull
), mNextSibling(nsnull
), mRoleMapEntry(nsnull
),
238 mAccChildCount(eChildCountUninitialized
)
242 nsCOMPtr
<nsIPresShell
> shell(do_QueryReferent(aShell
));
243 printf(">>> %p Created Acc - DOM: %p PS: %p",
244 (void*)static_cast<nsIAccessible
*>(this), (void*)aNode
,
246 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
249 if (content
->NodeInfo())
250 content
->NodeInfo()->GetQualifiedName(buf
);
251 printf(" Con: %s@%p", NS_ConvertUTF16toUTF8(buf
).get(), (void *)content
.get());
252 if (NS_SUCCEEDED(GetName(buf
))) {
253 printf(" Name:[%s]", NS_ConvertUTF16toUTF8(buf
).get());
261 //-----------------------------------------------------
263 //-----------------------------------------------------
264 nsAccessible::~nsAccessible()
268 NS_IMETHODIMP
nsAccessible::SetRoleMapEntry(nsRoleMapEntry
* aRoleMapEntry
)
270 mRoleMapEntry
= aRoleMapEntry
;
275 nsAccessible::GetName(nsAString
& aName
)
280 return NS_ERROR_FAILURE
;
283 if (!aName
.IsEmpty())
286 nsresult rv
= GetNameInternal(aName
);
287 NS_ENSURE_SUCCESS(rv
, rv
);
289 if (!aName
.IsEmpty())
292 // In the end get the name from tooltip.
293 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
297 nsIAtom
*tooltipAttr
= nsnull
;
299 if (content
->IsNodeOfType(nsINode::eHTML
))
300 tooltipAttr
= nsAccessibilityAtoms::title
;
301 else if (content
->IsNodeOfType(nsINode::eXUL
))
302 tooltipAttr
= nsAccessibilityAtoms::tooltiptext
;
306 // XXX: if CompressWhiteSpace worked on nsAString we could avoid a copy.
308 if (content
->GetAttr(kNameSpaceID_None
, tooltipAttr
, name
)) {
309 name
.CompressWhitespace();
312 aName
.SetIsVoid(PR_TRUE
);
318 NS_IMETHODIMP
nsAccessible::GetDescription(nsAString
& aDescription
)
320 // There are 4 conditions that make an accessible have no accDescription:
321 // 1. it's a text node; or
322 // 2. It has no DHTML describedby property
323 // 3. it doesn't have an accName; or
324 // 4. its title attribute already equals to its accName nsAutoString name;
325 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
327 return NS_ERROR_FAILURE
; // Node shut down
329 if (!content
->IsNodeOfType(nsINode::eTEXT
)) {
330 nsAutoString description
;
331 nsresult rv
= GetTextFromRelationID(nsAccessibilityAtoms::aria_describedby
, description
);
333 PRBool isXUL
= content
->IsNodeOfType(nsINode::eXUL
);
335 // Try XUL <description control="[id]">description text</description>
336 nsIContent
*descriptionContent
=
337 nsCoreUtils::FindNeighbourPointingToNode(content
,
338 nsAccessibilityAtoms::control
,
339 nsAccessibilityAtoms::description
);
341 if (descriptionContent
) {
342 // We have a description content node
343 AppendFlatStringFromSubtree(descriptionContent
, &description
);
346 if (description
.IsEmpty()) {
347 nsIAtom
*descAtom
= isXUL
? nsAccessibilityAtoms::tooltiptext
:
348 nsAccessibilityAtoms::title
;
349 if (content
->GetAttr(kNameSpaceID_None
, descAtom
, description
)) {
352 if (name
.IsEmpty() || description
== name
) {
353 // Don't use tooltip for a description if this object
354 // has no name or the tooltip is the same as the name
355 description
.Truncate();
360 description
.CompressWhitespace();
361 aDescription
= description
;
367 // mask values for ui.key.chromeAccess and ui.key.contentAccess
368 #define NS_MODIFIER_SHIFT 1
369 #define NS_MODIFIER_CONTROL 2
370 #define NS_MODIFIER_ALT 4
371 #define NS_MODIFIER_META 8
373 // returns the accesskey modifier mask used in the given node's context
374 // (i.e. chrome or content), or 0 if an error occurs
376 GetAccessModifierMask(nsIContent
* aContent
)
378 nsCOMPtr
<nsIPrefBranch
> prefBranch
=
379 do_GetService(NS_PREFSERVICE_CONTRACTID
);
383 // use ui.key.generalAccessKey (unless it is -1)
385 nsresult rv
= prefBranch
->GetIntPref("ui.key.generalAccessKey", &accessKey
);
386 if (NS_SUCCEEDED(rv
) && accessKey
!= -1) {
388 case nsIDOMKeyEvent::DOM_VK_SHIFT
: return NS_MODIFIER_SHIFT
;
389 case nsIDOMKeyEvent::DOM_VK_CONTROL
: return NS_MODIFIER_CONTROL
;
390 case nsIDOMKeyEvent::DOM_VK_ALT
: return NS_MODIFIER_ALT
;
391 case nsIDOMKeyEvent::DOM_VK_META
: return NS_MODIFIER_META
;
396 // get the docShell to this DOMNode, return 0 on failure
397 nsCOMPtr
<nsIDocument
> document
= aContent
->GetCurrentDoc();
400 nsCOMPtr
<nsISupports
> container
= document
->GetContainer();
403 nsCOMPtr
<nsIDocShellTreeItem
> treeItem(do_QueryInterface(container
));
407 // determine the access modifier used in this context
408 PRInt32 itemType
, accessModifierMask
= 0;
409 treeItem
->GetItemType(&itemType
);
412 case nsIDocShellTreeItem::typeChrome
:
413 rv
= prefBranch
->GetIntPref("ui.key.chromeAccess", &accessModifierMask
);
416 case nsIDocShellTreeItem::typeContent
:
417 rv
= prefBranch
->GetIntPref("ui.key.contentAccess", &accessModifierMask
);
421 return NS_SUCCEEDED(rv
) ? accessModifierMask
: 0;
425 nsAccessible::GetKeyboardShortcut(nsAString
& aAccessKey
)
427 aAccessKey
.Truncate();
429 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
431 return NS_ERROR_FAILURE
;
433 PRUint32 key
= nsCoreUtils::GetAccessKeyFor(content
);
434 if (!key
&& content
->IsNodeOfType(nsIContent::eELEMENT
)) {
435 // Copy access key from label node unless it is labeled
436 // via an ancestor <label>, in which case that would be redundant
437 nsCOMPtr
<nsIContent
> labelContent(nsCoreUtils::GetLabelContent(content
));
438 nsCOMPtr
<nsIDOMNode
> labelNode
= do_QueryInterface(labelContent
);
439 if (labelNode
&& !nsCoreUtils::IsAncestorOf(labelNode
, mDOMNode
))
440 key
= nsCoreUtils::GetAccessKeyFor(labelContent
);
446 nsAutoString
accesskey(key
);
448 // Append the modifiers in reverse order, result: Control+Alt+Shift+Meta+<key>
449 nsAutoString propertyKey
;
450 PRInt32 modifierMask
= GetAccessModifierMask(content
);
451 if (modifierMask
& NS_MODIFIER_META
) {
452 propertyKey
.AssignLiteral("VK_META");
453 nsAccessible::GetFullKeyName(propertyKey
, accesskey
, accesskey
);
455 if (modifierMask
& NS_MODIFIER_SHIFT
) {
456 propertyKey
.AssignLiteral("VK_SHIFT");
457 nsAccessible::GetFullKeyName(propertyKey
, accesskey
, accesskey
);
459 if (modifierMask
& NS_MODIFIER_ALT
) {
460 propertyKey
.AssignLiteral("VK_ALT");
461 nsAccessible::GetFullKeyName(propertyKey
, accesskey
, accesskey
);
463 if (modifierMask
& NS_MODIFIER_CONTROL
) {
464 propertyKey
.AssignLiteral("VK_CONTROL");
465 nsAccessible::GetFullKeyName(propertyKey
, accesskey
, accesskey
);
468 aAccessKey
= accesskey
;
472 NS_IMETHODIMP
nsAccessible::SetParent(nsIAccessible
*aParent
)
474 if (mParent
!= aParent
) {
475 // Adopt a child -- we allow this now. the new parent
476 // may be a dom node which wasn't previously accessible but now is.
477 // The old parent's children now need to be invalidated, since
478 // it no longer owns the child, the new parent does
479 nsCOMPtr
<nsPIAccessible
> privOldParent
= do_QueryInterface(mParent
);
481 privOldParent
->InvalidateChildren();
489 NS_IMETHODIMP
nsAccessible::SetFirstChild(nsIAccessible
*aFirstChild
)
491 mFirstChild
= aFirstChild
;
496 NS_IMETHODIMP
nsAccessible::SetNextSibling(nsIAccessible
*aNextSibling
)
498 mNextSibling
= aNextSibling
;
503 nsAccessible::Shutdown()
505 mNextSibling
= nsnull
;
507 // Invalidate the child count and pointers to other accessibles, also make
508 // sure none of its children point to this parent
509 InvalidateChildren();
511 nsCOMPtr
<nsPIAccessible
> privateParent(do_QueryInterface(mParent
));
512 privateParent
->InvalidateChildren();
516 return nsAccessNodeWrap::Shutdown();
519 NS_IMETHODIMP
nsAccessible::InvalidateChildren()
521 // Document has transformed, reset our invalid children and child count
523 // Reset the sibling pointers, they will be set up again the next time
524 // CacheChildren() is called.
525 // Note: we don't want to start creating accessibles at this point,
526 // so don't use GetNextSibling() here. (bug 387252)
527 nsAccessible
* child
= static_cast<nsAccessible
*>(mFirstChild
.get());
529 child
->mParent
= nsnull
;
531 nsCOMPtr
<nsIAccessible
> next
= child
->mNextSibling
;
532 child
->mNextSibling
= nsnull
;
533 child
= static_cast<nsAccessible
*>(next
.get());
536 mAccChildCount
= eChildCountUninitialized
;
537 mFirstChild
= nsnull
;
541 NS_IMETHODIMP
nsAccessible::GetParent(nsIAccessible
** aParent
)
543 nsresult rv
= GetCachedParent(aParent
);
544 if (NS_FAILED(rv
) || *aParent
) {
548 nsCOMPtr
<nsIAccessibleDocument
> docAccessible(GetDocAccessible());
549 NS_ENSURE_TRUE(docAccessible
, NS_ERROR_FAILURE
);
551 return docAccessible
->GetAccessibleInParentChain(mDOMNode
, PR_TRUE
, aParent
);
554 NS_IMETHODIMP
nsAccessible::GetCachedParent(nsIAccessible
** aParent
)
558 // This node has been shut down
559 return NS_ERROR_FAILURE
;
561 NS_IF_ADDREF(*aParent
= mParent
);
565 NS_IMETHODIMP
nsAccessible::GetCachedFirstChild(nsIAccessible
** aFirstChild
)
567 *aFirstChild
= nsnull
;
569 // This node has been shut down
570 return NS_ERROR_FAILURE
;
572 NS_IF_ADDREF(*aFirstChild
= mFirstChild
);
576 /* readonly attribute nsIAccessible nextSibling; */
577 NS_IMETHODIMP
nsAccessible::GetNextSibling(nsIAccessible
* *aNextSibling
)
579 *aNextSibling
= nsnull
;
581 // This node has been shut down
582 return NS_ERROR_FAILURE
;
585 nsCOMPtr
<nsIAccessible
> parent(GetParent());
588 parent
->GetChildCount(&numChildren
); // Make sure we cache all of the children
592 if (mNextSibling
|| !mParent
) {
593 // If no parent, don't try to calculate a new sibling
594 // It either means we're at the root or shutting down the parent
595 NS_IF_ADDREF(*aNextSibling
= mNextSibling
);
600 return NS_ERROR_FAILURE
;
603 /* readonly attribute nsIAccessible previousSibling; */
604 NS_IMETHODIMP
nsAccessible::GetPreviousSibling(nsIAccessible
* *aPreviousSibling
)
606 *aPreviousSibling
= nsnull
;
609 // This node has been shut down
610 return NS_ERROR_FAILURE
;
613 nsCOMPtr
<nsIAccessible
> parent
;
614 if (NS_FAILED(GetParent(getter_AddRefs(parent
))) || !parent
) {
615 return NS_ERROR_FAILURE
;
618 nsCOMPtr
<nsIAccessible
> testAccessible
, prevSibling
;
619 parent
->GetFirstChild(getter_AddRefs(testAccessible
));
620 while (testAccessible
&& this != testAccessible
) {
621 prevSibling
= testAccessible
;
622 prevSibling
->GetNextSibling(getter_AddRefs(testAccessible
));
626 return NS_ERROR_FAILURE
;
629 NS_ADDREF(*aPreviousSibling
= prevSibling
);
633 /* readonly attribute nsIAccessible firstChild; */
634 NS_IMETHODIMP
nsAccessible::GetFirstChild(nsIAccessible
* *aFirstChild
)
636 if (gIsCacheDisabled
) {
637 InvalidateChildren();
640 GetChildCount(&numChildren
); // Make sure we cache all of the children
643 nsCOMPtr
<nsPIAccessible
> firstChild(do_QueryInterface(mFirstChild
));
645 nsCOMPtr
<nsIAccessible
> realParent
;
646 firstChild
->GetCachedParent(getter_AddRefs(realParent
));
647 NS_ASSERTION(!realParent
|| realParent
== this,
648 "Two accessibles have the same first child accessible.");
652 NS_IF_ADDREF(*aFirstChild
= mFirstChild
);
657 /* readonly attribute nsIAccessible lastChild; */
658 NS_IMETHODIMP
nsAccessible::GetLastChild(nsIAccessible
* *aLastChild
)
660 GetChildAt(-1, aLastChild
);
664 NS_IMETHODIMP
nsAccessible::GetChildAt(PRInt32 aChildNum
, nsIAccessible
**aChild
)
666 // aChildNum is a zero-based index
669 GetChildCount(&numChildren
);
671 // If no children or aChildNum is larger than numChildren, return null
672 if (aChildNum
>= numChildren
|| numChildren
== 0 || !mWeakShell
) {
674 return NS_ERROR_FAILURE
;
675 // If aChildNum is less than zero, set aChild to last index
676 } else if (aChildNum
< 0) {
677 aChildNum
= numChildren
- 1;
680 nsCOMPtr
<nsIAccessible
> current(mFirstChild
), nextSibling
;
684 nextSibling
= current
;
685 if (++index
> aChildNum
) {
688 nextSibling
->GetNextSibling(getter_AddRefs(current
));
691 NS_IF_ADDREF(*aChild
= nextSibling
);
696 // readonly attribute nsIArray children;
697 NS_IMETHODIMP
nsAccessible::GetChildren(nsIArray
**aOutChildren
)
699 *aOutChildren
= nsnull
;
700 nsCOMPtr
<nsIMutableArray
> children
= do_CreateInstance(NS_ARRAY_CONTRACTID
);
702 return NS_ERROR_FAILURE
;
704 nsCOMPtr
<nsIAccessible
> curChild
;
705 while (NextChild(curChild
)) {
706 children
->AppendElement(curChild
, PR_FALSE
);
709 NS_ADDREF(*aOutChildren
= children
);
713 nsIAccessible
*nsAccessible::NextChild(nsCOMPtr
<nsIAccessible
>& aAccessible
)
715 nsCOMPtr
<nsIAccessible
> nextChild
;
717 GetFirstChild(getter_AddRefs(nextChild
));
720 aAccessible
->GetNextSibling(getter_AddRefs(nextChild
));
722 return (aAccessible
= nextChild
);
725 void nsAccessible::CacheChildren()
728 // This node has been shut down
729 mAccChildCount
= eChildCountUninitialized
;
733 if (mAccChildCount
== eChildCountUninitialized
) {
734 mAccChildCount
= 0;// Prevent reentry
735 PRBool allowsAnonChildren
= PR_FALSE
;
736 GetAllowsAnonChildAccessibles(&allowsAnonChildren
);
737 nsAccessibleTreeWalker
walker(mWeakShell
, mDOMNode
, allowsAnonChildren
);
738 // Seed the frame hint early while we're still on a container node.
739 // This is better than doing the GetPrimaryFrameFor() later on
740 // a text node, because text nodes aren't in the frame map.
741 walker
.mState
.frame
= GetFrame();
743 nsCOMPtr
<nsPIAccessible
> privatePrevAccessible
;
744 PRInt32 childCount
= 0;
745 walker
.GetFirstChild();
746 SetFirstChild(walker
.mState
.accessible
);
748 while (walker
.mState
.accessible
) {
750 privatePrevAccessible
= do_QueryInterface(walker
.mState
.accessible
);
751 privatePrevAccessible
->SetParent(this);
752 walker
.GetNextSibling();
753 privatePrevAccessible
->SetNextSibling(walker
.mState
.accessible
);
755 mAccChildCount
= childCount
;
759 NS_IMETHODIMP
nsAccessible::GetAllowsAnonChildAccessibles(PRBool
*aAllowsAnonChildren
)
761 *aAllowsAnonChildren
= PR_TRUE
;
765 /* readonly attribute long childCount; */
766 NS_IMETHODIMP
nsAccessible::GetChildCount(PRInt32
*aAccChildCount
)
769 *aAccChildCount
= mAccChildCount
;
773 /* readonly attribute long indexInParent; */
774 NS_IMETHODIMP
nsAccessible::GetIndexInParent(PRInt32
*aIndexInParent
)
776 *aIndexInParent
= -1;
778 return NS_ERROR_FAILURE
;
781 nsCOMPtr
<nsIAccessible
> parent
;
782 GetParent(getter_AddRefs(parent
));
784 return NS_ERROR_FAILURE
;
787 nsCOMPtr
<nsIAccessible
> sibling
;
788 parent
->GetFirstChild(getter_AddRefs(sibling
));
790 return NS_ERROR_FAILURE
;
794 while (sibling
!= this) {
795 NS_ASSERTION(sibling
, "Never ran into the same child that we started from");
798 return NS_ERROR_FAILURE
;
801 nsCOMPtr
<nsIAccessible
> tempAccessible
;
802 sibling
->GetNextSibling(getter_AddRefs(tempAccessible
));
803 sibling
= tempAccessible
;
809 NS_IMETHODIMP
nsAccessible::TestChildCache(nsIAccessible
*aCachedChild
)
814 // All cached accessible nodes should be in the parent
815 // It will assert if not all the children were created
816 // when they were first cached, and no invalidation
817 // ever corrected parent accessible's child cache.
818 if (mAccChildCount
<= 0) {
821 nsCOMPtr
<nsIAccessible
> sibling
= mFirstChild
;
823 while (sibling
!= aCachedChild
) {
824 NS_ASSERTION(sibling
, "[TestChildCache] Never ran into the same child that we started from");
826 return NS_ERROR_FAILURE
;
828 nsCOMPtr
<nsIAccessible
> tempAccessible
;
829 sibling
->GetNextSibling(getter_AddRefs(tempAccessible
));
830 sibling
= tempAccessible
;
836 nsresult
nsAccessible::GetTranslatedString(const nsAString
& aKey
, nsAString
& aStringOut
)
838 nsXPIDLString xsValue
;
840 if (!gStringBundle
||
841 NS_FAILED(gStringBundle
->GetStringFromName(PromiseFlatString(aKey
).get(), getter_Copies(xsValue
))))
842 return NS_ERROR_FAILURE
;
844 aStringOut
.Assign(xsValue
);
848 nsresult
nsAccessible::GetFullKeyName(const nsAString
& aModifierName
, const nsAString
& aKeyName
, nsAString
& aStringOut
)
850 nsXPIDLString modifierName
, separator
;
852 if (!gKeyStringBundle
||
853 NS_FAILED(gKeyStringBundle
->GetStringFromName(PromiseFlatString(aModifierName
).get(),
854 getter_Copies(modifierName
))) ||
855 NS_FAILED(gKeyStringBundle
->GetStringFromName(PromiseFlatString(NS_LITERAL_STRING("MODIFIER_SEPARATOR")).get(),
856 getter_Copies(separator
)))) {
857 return NS_ERROR_FAILURE
;
860 aStringOut
= modifierName
+ separator
+ aKeyName
;
864 PRBool
nsAccessible::IsVisible(PRBool
*aIsOffscreen
)
866 // We need to know if at least a kMinPixels around the object is visible
867 // Otherwise it will be marked nsIAccessibleStates::STATE_OFFSCREEN
868 // The STATE_INVISIBLE flag is for elements which are programmatically hidden
870 *aIsOffscreen
= PR_TRUE
;
872 return PR_FALSE
; // Defunct object
875 const PRUint16 kMinPixels
= 12;
876 // Set up the variables we need, return false if we can't get at them all
877 nsCOMPtr
<nsIPresShell
> shell(GetPresShell());
881 nsIViewManager
* viewManager
= shell
->GetViewManager();
885 nsIFrame
*frame
= GetFrame();
890 // If visibility:hidden or visibility:collapsed then mark with STATE_INVISIBLE
891 if (!frame
->GetStyleVisibility()->IsVisible())
896 nsPresContext
*presContext
= shell
->GetPresContext();
900 // Get the bounds of the current frame, relative to the current view.
901 // We don't use the more accurate GetBoundsRect, because that is more expensive
902 // and the STATE_OFFSCREEN flag that this is used for only needs to be a rough
905 nsRect relFrameRect
= frame
->GetRect();
906 nsIView
*containingView
= frame
->GetViewExternal();
907 if (containingView
) {
908 // When frame itself has a view, it has the same bounds as the view
909 relFrameRect
.x
= relFrameRect
.y
= 0;
913 frame
->GetOffsetFromView(frameOffset
, &containingView
);
915 return PR_FALSE
; // no view -- not visible
916 relFrameRect
.x
= frameOffset
.x
;
917 relFrameRect
.y
= frameOffset
.y
;
920 nsRectVisibility rectVisibility
;
921 viewManager
->GetRectVisibility(containingView
, relFrameRect
,
922 nsPresContext::CSSPixelsToAppUnits(kMinPixels
),
925 if (rectVisibility
== nsRectVisibility_kZeroAreaRect
) {
926 nsIAtom
*frameType
= frame
->GetType();
927 if (frameType
== nsAccessibilityAtoms::textFrame
) {
928 // Zero area rects can occur in the first frame of a multi-frame text flow,
929 // in which case the rendered text is not empty and the frame should not be marked invisible
930 nsAutoString renderedText
;
931 frame
->GetRenderedText (&renderedText
, nsnull
, nsnull
, 0, 1);
932 if (!renderedText
.IsEmpty()) {
933 rectVisibility
= nsRectVisibility_kVisible
;
936 else if (frameType
== nsAccessibilityAtoms::inlineFrame
) {
937 // Yuck. Unfortunately inline frames can contain larger frames inside of them,
938 // so we can't really believe this is a zero area rect without checking more deeply.
939 // GetBounds() will do that for us.
940 PRInt32 x
, y
, width
, height
;
941 GetBounds(&x
, &y
, &width
, &height
);
942 if (width
> 0 && height
> 0) {
943 rectVisibility
= nsRectVisibility_kVisible
;
948 if (rectVisibility
== nsRectVisibility_kZeroAreaRect
&& frame
&&
949 0 == (frame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
)) {
950 // Consider zero area objects hidden unless they are absoultely positioned
951 // or floating and may have descendants that have a non-zero size
956 // nsRectVisibility_kVisible,
957 // nsRectVisibility_kAboveViewport,
958 // nsRectVisibility_kBelowViewport,
959 // nsRectVisibility_kLeftOfViewport,
960 // nsRectVisibility_kRightOfViewport
961 // This view says it is visible, but we need to check the parent view chain :(
962 nsCOMPtr
<nsIDOMDocument
> domDoc
;
963 mDOMNode
->GetOwnerDocument(getter_AddRefs(domDoc
));
964 nsCOMPtr
<nsIDocument
> doc(do_QueryInterface(domDoc
));
969 PRBool isVisible
= CheckVisibilityInParentChain(doc
, containingView
);
970 if (isVisible
&& rectVisibility
== nsRectVisibility_kVisible
) {
971 *aIsOffscreen
= PR_FALSE
;
977 nsAccessible::GetStateInternal(PRUint32
*aState
, PRUint32
*aExtraState
)
983 *aExtraState
= nsIAccessibleStates::EXT_STATE_DEFUNCT
;
985 return NS_OK_DEFUNCT_OBJECT
;
991 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
993 return NS_OK
; // On document, this is not an error
996 // Set STATE_UNAVAILABLE state based on disabled attribute
997 // The disabled attribute is mostly used in XUL elements and HTML forms, but
998 // if someone sets it on another attribute,
999 // it seems reasonable to consider it unavailable
1001 if (content
->IsNodeOfType(nsINode::eHTML
)) {
1002 // In HTML, just the presence of the disabled attribute means it is disabled,
1003 // therefore disabled="false" indicates disabled!
1004 isDisabled
= content
->HasAttr(kNameSpaceID_None
, nsAccessibilityAtoms::disabled
);
1007 isDisabled
= content
->AttrValueIs(kNameSpaceID_None
,
1008 nsAccessibilityAtoms::disabled
,
1009 nsAccessibilityAtoms::_true
,
1013 *aState
|= nsIAccessibleStates::STATE_UNAVAILABLE
;
1015 else if (content
->IsNodeOfType(nsINode::eELEMENT
)) {
1016 nsIFrame
*frame
= GetFrame();
1017 if (frame
&& frame
->IsFocusable()) {
1018 *aState
|= nsIAccessibleStates::STATE_FOCUSABLE
;
1021 if (gLastFocusedNode
== mDOMNode
) {
1022 *aState
|= nsIAccessibleStates::STATE_FOCUSED
;
1026 // Check if nsIAccessibleStates::STATE_INVISIBLE and
1027 // STATE_OFFSCREEN flags should be turned on for this object.
1029 if (!IsVisible(&isOffscreen
)) {
1030 *aState
|= nsIAccessibleStates::STATE_INVISIBLE
;
1033 *aState
|= nsIAccessibleStates::STATE_OFFSCREEN
;
1036 nsIFrame
*frame
= GetFrame();
1037 if (frame
&& (frame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
))
1038 *aState
|= nsIAccessibleStates::STATE_FLOATING
;
1040 // Add 'linked' state for simple xlink.
1041 if (nsCoreUtils::IsXLink(content
))
1042 *aState
|= nsIAccessibleStates::STATE_LINKED
;
1047 /* readonly attribute boolean focusedChild; */
1048 NS_IMETHODIMP
nsAccessible::GetFocusedChild(nsIAccessible
**aFocusedChild
)
1050 nsCOMPtr
<nsIAccessible
> focusedChild
;
1051 if (gLastFocusedNode
== mDOMNode
) {
1052 focusedChild
= this;
1054 else if (gLastFocusedNode
) {
1055 nsCOMPtr
<nsIAccessibilityService
> accService
=
1056 do_GetService("@mozilla.org/accessibilityService;1");
1057 NS_ENSURE_TRUE(accService
, NS_ERROR_FAILURE
);
1059 accService
->GetAccessibleFor(gLastFocusedNode
,
1060 getter_AddRefs(focusedChild
));
1062 nsCOMPtr
<nsIAccessible
> focusedParentAccessible
;
1063 focusedChild
->GetParent(getter_AddRefs(focusedParentAccessible
));
1064 if (focusedParentAccessible
!= this) {
1065 focusedChild
= nsnull
;
1070 NS_IF_ADDREF(*aFocusedChild
= focusedChild
);
1074 // nsIAccessible getDeepestChildAtPoint(in long x, in long y)
1076 nsAccessible::GetDeepestChildAtPoint(PRInt32 aX
, PRInt32 aY
,
1077 nsIAccessible
**aAccessible
)
1079 NS_ENSURE_ARG_POINTER(aAccessible
);
1080 *aAccessible
= nsnull
;
1083 return NS_ERROR_FAILURE
; // Already shut down
1086 // If we can't find the point in a child, we will return the fallback answer:
1087 // we return |this| if the point is within it, otherwise nsnull
1088 nsCOMPtr
<nsIAccessible
> fallbackAnswer
;
1089 PRInt32 x
, y
, width
, height
;
1090 GetBounds(&x
, &y
, &width
, &height
);
1091 if (aX
>= x
&& aX
< x
+ width
&&
1092 aY
>= y
&& aY
< y
+ height
) {
1093 fallbackAnswer
= this;
1095 if (nsAccUtils::MustPrune(this)) { // Do not dig any further
1096 NS_IF_ADDREF(*aAccessible
= fallbackAnswer
);
1100 // Search an accessible at the given point starting from accessible document
1101 // because containing block (see CSS2) for out of flow element (for example,
1102 // absolutely positioned element) may be different from its DOM parent and
1103 // therefore accessible for containing block may be different from accessible
1104 // for DOM parent but GetFrameForPoint() should be called for containing block
1105 // to get an out of flow element.
1106 nsCOMPtr
<nsIAccessibleDocument
> accDocument
;
1107 nsresult rv
= GetAccessibleDocument(getter_AddRefs(accDocument
));
1108 NS_ENSURE_SUCCESS(rv
, rv
);
1109 NS_ENSURE_TRUE(accDocument
, NS_ERROR_FAILURE
);
1111 nsRefPtr
<nsAccessNode
> docAccessNode
=
1112 nsAccUtils::QueryAccessNode(accDocument
);
1114 nsIFrame
*frame
= docAccessNode
->GetFrame();
1115 NS_ENSURE_STATE(frame
);
1117 nsPresContext
*presContext
= frame
->PresContext();
1119 nsIntRect screenRect
= frame
->GetScreenRectExternal();
1120 nsPoint
offset(presContext
->DevPixelsToAppUnits(aX
- screenRect
.x
),
1121 presContext
->DevPixelsToAppUnits(aY
- screenRect
.y
));
1123 nsCOMPtr
<nsIPresShell
> presShell
= presContext
->PresShell();
1124 nsIFrame
*foundFrame
= presShell
->GetFrameForPoint(frame
, offset
);
1125 nsCOMPtr
<nsIContent
> content
;
1126 if (!foundFrame
|| !(content
= foundFrame
->GetContent())) {
1127 NS_IF_ADDREF(*aAccessible
= fallbackAnswer
);
1131 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(content
));
1132 nsCOMPtr
<nsIAccessibilityService
> accService
= GetAccService();
1134 nsCOMPtr
<nsIDOMNode
> relevantNode
;
1135 accService
->GetRelevantContentNodeFor(node
, getter_AddRefs(relevantNode
));
1136 if (!relevantNode
) {
1137 NS_IF_ADDREF(*aAccessible
= fallbackAnswer
);
1141 nsCOMPtr
<nsIAccessible
> accessible
;
1142 accService
->GetAccessibleFor(relevantNode
, getter_AddRefs(accessible
));
1144 // No accessible for the node with the point, so find the first
1145 // accessible in the DOM parent chain
1146 accDocument
->GetAccessibleInParentChain(relevantNode
, PR_TRUE
,
1147 getter_AddRefs(accessible
));
1149 NS_IF_ADDREF(*aAccessible
= fallbackAnswer
);
1154 if (accessible
== this) {
1155 // Manually walk through accessible children and see if
1156 // the are within this point.
1157 // This takes care of cases where layout won't walk into
1158 // things for us, such as image map areas and sub documents
1159 nsCOMPtr
<nsIAccessible
> child
;
1160 while (NextChild(child
)) {
1161 PRInt32 childX
, childY
, childWidth
, childHeight
;
1162 child
->GetBounds(&childX
, &childY
, &childWidth
, &childHeight
);
1163 if (aX
>= childX
&& aX
< childX
+ childWidth
&&
1164 aY
>= childY
&& aY
< childY
+ childHeight
&&
1165 (nsAccUtils::State(child
) & nsIAccessibleStates::STATE_INVISIBLE
) == 0) {
1166 // Don't walk into offscreen or invisible items
1167 NS_IF_ADDREF(*aAccessible
= child
);
1171 // Fall through -- the point is in this accessible but not in a child
1172 // We are allowed to return |this| as the answer
1175 NS_IF_ADDREF(*aAccessible
= accessible
);
1179 // nsIAccessible getChildAtPoint(in long x, in long y)
1181 nsAccessible::GetChildAtPoint(PRInt32 aX
, PRInt32 aY
,
1182 nsIAccessible
**aAccessible
)
1184 nsresult rv
= GetDeepestChildAtPoint(aX
, aY
, aAccessible
);
1185 NS_ENSURE_SUCCESS(rv
, rv
);
1187 if (!*aAccessible
|| *aAccessible
== this)
1190 // Get direct child containing the deepest child at the given point.
1191 nsCOMPtr
<nsIAccessible
> parent
, accessible(*aAccessible
);
1193 accessible
->GetParent(getter_AddRefs(parent
));
1195 NS_NOTREACHED("Obtained accessible isn't a child of this accessible.");
1197 // Reached the top of the hierarchy. These bounds were inside an
1198 // accessible that is not a descendant of this one.
1200 // If we can't find the point in a child, we will return the fallback
1201 // answer: we return |this| if the point is within it, otherwise nsnull.
1202 PRInt32 x
, y
, width
, height
;
1203 GetBounds(&x
, &y
, &width
, &height
);
1204 if (aX
>= x
&& aX
< x
+ width
&& aY
>= y
&& aY
< y
+ height
)
1205 NS_ADDREF(*aAccessible
= this);
1210 if (parent
== this) {
1211 // We reached |this|, so |accessible| is the child we want to return.
1212 NS_ADDREF(*aAccessible
= accessible
);
1215 accessible
.swap(parent
);
1221 void nsAccessible::GetBoundsRect(nsRect
& aTotalBounds
, nsIFrame
** aBoundingFrame
)
1224 * This method is used to determine the bounds of a content node.
1225 * Because HTML wraps and links are not always rectangular, this
1226 * method uses the following algorithm:
1228 * 1) Start with an empty rectangle
1229 * 2) Add the rect for the primary frame from for the DOM node.
1230 * 3) For each next frame at the same depth with the same DOM node, add that rect to total
1231 * 4) If that frame is an inline frame, search deeper at that point in the tree, adding all rects
1234 // Initialization area
1235 *aBoundingFrame
= nsnull
;
1236 nsIFrame
*firstFrame
= GetBoundsFrame();
1240 // Find common relative parent
1241 // This is an ancestor frame that will incompass all frames for this content node.
1242 // We need the relative parent so we can get absolute screen coordinates
1243 nsIFrame
*ancestorFrame
= firstFrame
;
1245 while (ancestorFrame
) {
1246 *aBoundingFrame
= ancestorFrame
;
1247 // If any other frame type, we only need to deal with the primary frame
1248 // Otherwise, there may be more frames attached to the same content node
1249 if (!nsCoreUtils::IsCorrectFrameType(ancestorFrame
,
1250 nsAccessibilityAtoms::inlineFrame
) &&
1251 !nsCoreUtils::IsCorrectFrameType(ancestorFrame
,
1252 nsAccessibilityAtoms::textFrame
))
1254 ancestorFrame
= ancestorFrame
->GetParent();
1257 nsIFrame
*iterFrame
= firstFrame
;
1258 nsCOMPtr
<nsIContent
> firstContent(do_QueryInterface(mDOMNode
));
1259 nsIContent
* iterContent
= firstContent
;
1262 // Look only at frames below this depth, or at this depth (if we're still on the content node we started with)
1263 while (iterContent
== firstContent
|| depth
> 0) {
1264 // Coordinates will come back relative to parent frame
1265 nsRect currFrameBounds
= iterFrame
->GetRect();
1267 // Make this frame's bounds relative to common parent frame
1269 iterFrame
->GetParent()->GetOffsetToExternal(*aBoundingFrame
);
1271 // Add this frame's bounds to total
1272 aTotalBounds
.UnionRect(aTotalBounds
, currFrameBounds
);
1274 nsIFrame
*iterNextFrame
= nsnull
;
1276 if (nsCoreUtils::IsCorrectFrameType(iterFrame
,
1277 nsAccessibilityAtoms::inlineFrame
)) {
1278 // Only do deeper bounds search if we're on an inline frame
1279 // Inline frames can contain larger frames inside of them
1280 iterNextFrame
= iterFrame
->GetFirstChild(nsnull
);
1284 ++depth
; // Child was found in code above this: We are going deeper in this iteration of the loop
1286 // Use next sibling if it exists, or go back up the tree to get the first next-in-flow or next-sibling
1287 // within our search
1289 iterNextFrame
= iterFrame
->GetNextContinuation();
1291 iterNextFrame
= iterFrame
->GetNextSibling();
1292 if (iterNextFrame
|| --depth
< 0)
1294 iterFrame
= iterFrame
->GetParent();
1298 // Get ready for the next round of our loop
1299 iterFrame
= iterNextFrame
;
1300 if (iterFrame
== nsnull
)
1302 iterContent
= nsnull
;
1304 iterContent
= iterFrame
->GetContent();
1309 /* void getBounds (out long x, out long y, out long width, out long height); */
1310 NS_IMETHODIMP
nsAccessible::GetBounds(PRInt32
*x
, PRInt32
*y
, PRInt32
*width
, PRInt32
*height
)
1312 // This routine will get the entire rectange for all the frames in this node
1313 // -------------------------------------------------------------------------
1314 // Primary Frame for node
1315 // Another frame, same node <- Example
1316 // Another frame, same node
1318 nsPresContext
*presContext
= GetPresContext();
1321 *x
= *y
= *width
= *height
= 0;
1322 return NS_ERROR_FAILURE
;
1325 nsRect unionRectTwips
;
1326 nsIFrame
* aBoundingFrame
= nsnull
;
1327 GetBoundsRect(unionRectTwips
, &aBoundingFrame
); // Unions up all primary frames for this node and all siblings after it
1328 if (!aBoundingFrame
) {
1329 *x
= *y
= *width
= *height
= 0;
1330 return NS_ERROR_FAILURE
;
1333 *x
= presContext
->AppUnitsToDevPixels(unionRectTwips
.x
);
1334 *y
= presContext
->AppUnitsToDevPixels(unionRectTwips
.y
);
1335 *width
= presContext
->AppUnitsToDevPixels(unionRectTwips
.width
);
1336 *height
= presContext
->AppUnitsToDevPixels(unionRectTwips
.height
);
1338 // We have the union of the rectangle, now we need to put it in absolute screen coords
1340 nsRect orgRectPixels
= aBoundingFrame
->GetScreenRectExternal();
1341 *x
+= orgRectPixels
.x
;
1342 *y
+= orgRectPixels
.y
;
1349 nsIFrame
* nsAccessible::GetBoundsFrame()
1354 /* void removeSelection (); */
1355 NS_IMETHODIMP
nsAccessible::SetSelected(PRBool aSelect
)
1357 // Add or remove selection
1359 return NS_ERROR_FAILURE
;
1362 PRUint32 state
= nsAccUtils::State(this);
1363 if (state
& nsIAccessibleStates::STATE_SELECTABLE
) {
1364 nsCOMPtr
<nsIAccessible
> multiSelect
=
1365 nsAccUtils::GetMultiSelectFor(mDOMNode
);
1367 return aSelect
? TakeFocus() : NS_ERROR_FAILURE
;
1369 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
1370 NS_ASSERTION(content
, "Called for dead accessible");
1372 if (mRoleMapEntry
) {
1374 return content
->SetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_selected
,
1375 NS_LITERAL_STRING("true"), PR_TRUE
);
1377 return content
->UnsetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_selected
, PR_TRUE
);
1381 return NS_ERROR_FAILURE
;
1384 /* void takeSelection (); */
1385 NS_IMETHODIMP
nsAccessible::TakeSelection()
1387 // Select only this item
1389 return NS_ERROR_FAILURE
;
1392 PRUint32 state
= nsAccUtils::State(this);
1393 if (state
& nsIAccessibleStates::STATE_SELECTABLE
) {
1394 nsCOMPtr
<nsIAccessible
> multiSelect
=
1395 nsAccUtils::GetMultiSelectFor(mDOMNode
);
1397 nsCOMPtr
<nsIAccessibleSelectable
> selectable
= do_QueryInterface(multiSelect
);
1398 selectable
->ClearSelection();
1400 return SetSelected(PR_TRUE
);
1403 return NS_ERROR_FAILURE
;
1406 /* void takeFocus (); */
1408 nsAccessible::TakeFocus()
1411 return NS_ERROR_FAILURE
;
1413 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
1415 nsIFrame
*frame
= GetFrame();
1416 NS_ENSURE_STATE(frame
);
1418 // If the current element can't take real DOM focus and if it has an ID and
1419 // ancestor with a the aria-activedescendant attribute present, then set DOM
1420 // focus to that ancestor and set aria-activedescendant on the ancestor to
1421 // the ID of the desired element.
1422 if (!frame
->IsFocusable()) {
1424 if (content
&& nsCoreUtils::GetID(content
, id
)) {
1426 nsCOMPtr
<nsIContent
> ancestorContent
= content
;
1427 while ((ancestorContent
= ancestorContent
->GetParent()) &&
1428 !ancestorContent
->HasAttr(kNameSpaceID_None
,
1429 nsAccessibilityAtoms::aria_activedescendant
));
1431 if (ancestorContent
) {
1432 nsCOMPtr
<nsIPresShell
> presShell(do_QueryReferent(mWeakShell
));
1434 nsIFrame
*frame
= presShell
->GetPrimaryFrameFor(ancestorContent
);
1435 if (frame
&& frame
->IsFocusable()) {
1437 content
= ancestorContent
;
1438 content
->SetAttr(kNameSpaceID_None
,
1439 nsAccessibilityAtoms::aria_activedescendant
,
1447 nsCOMPtr
<nsIDOMNSHTMLElement
> htmlElement(do_QueryInterface(content
));
1449 // HTML Elements also set the caret position
1450 // in order to affect tabbing order
1451 return htmlElement
->Focus();
1454 content
->SetFocus(GetPresContext());
1458 nsresult
nsAccessible::AppendStringWithSpaces(nsAString
*aFlatString
, const nsAString
& textEquivalent
)
1460 // Insert spaces to insure that words from controls aren't jammed together
1461 if (!textEquivalent
.IsEmpty()) {
1462 if (!aFlatString
->IsEmpty())
1463 aFlatString
->Append(PRUnichar(' '));
1464 aFlatString
->Append(textEquivalent
);
1465 aFlatString
->Append(PRUnichar(' '));
1470 nsresult
nsAccessible::AppendNameFromAccessibleFor(nsIContent
*aContent
,
1471 nsAString
*aFlatString
,
1474 nsAutoString textEquivalent
, value
;
1476 nsCOMPtr
<nsIDOMNode
> domNode(do_QueryInterface(aContent
));
1477 nsCOMPtr
<nsIAccessible
> accessible
;
1478 if (domNode
== mDOMNode
) {
1481 // prevent recursive call GetName()
1486 nsCOMPtr
<nsIAccessibilityService
> accService
=
1487 do_GetService("@mozilla.org/accessibilityService;1");
1488 NS_ENSURE_TRUE(accService
, NS_ERROR_FAILURE
);
1489 accService
->GetAccessibleInWeakShell(domNode
, mWeakShell
, getter_AddRefs(accessible
));
1493 accessible
->GetValue(textEquivalent
);
1496 accessible
->GetName(textEquivalent
);
1500 textEquivalent
.CompressWhitespace();
1501 return AppendStringWithSpaces(aFlatString
, textEquivalent
);
1505 * AppendFlatStringFromContentNode and AppendFlatStringFromSubtree
1507 * This method will glean useful text, in whatever form it exists, from any content node given to it.
1508 * It is used by any decendant of nsAccessible that needs to get text from a single node, as
1509 * well as by nsAccessible::AppendFlatStringFromSubtree, which gleans and concatenates text from any node and
1510 * that node's decendants.
1513 nsresult
nsAccessible::AppendFlatStringFromContentNode(nsIContent
*aContent
, nsAString
*aFlatString
)
1515 if (aContent
->IsNodeOfType(nsINode::eTEXT
)) {
1516 // If it's a text node, append the text
1517 PRBool isHTMLBlock
= PR_FALSE
;
1518 nsCOMPtr
<nsIPresShell
> shell
= GetPresShell();
1520 return NS_ERROR_FAILURE
;
1523 nsIContent
*parentContent
= aContent
->GetParent();
1524 nsCOMPtr
<nsIContent
> appendedSubtreeStart(do_QueryInterface(mDOMNode
));
1525 if (parentContent
&& parentContent
!= appendedSubtreeStart
) {
1526 nsIFrame
*frame
= shell
->GetPrimaryFrameFor(parentContent
);
1528 // If this text is inside a block level frame (as opposed to span level), we need to add spaces around that
1529 // block's text, so we don't get words jammed together in final name
1530 // Extra spaces will be trimmed out later
1531 const nsStyleDisplay
* display
= frame
->GetStyleDisplay();
1532 if (display
->IsBlockOutside() ||
1533 display
->mDisplay
== NS_STYLE_DISPLAY_TABLE_CELL
) {
1534 isHTMLBlock
= PR_TRUE
;
1535 if (!aFlatString
->IsEmpty()) {
1536 aFlatString
->Append(PRUnichar(' '));
1541 if (aContent
->TextLength() > 0) {
1542 nsIFrame
*frame
= shell
->GetPrimaryFrameFor(aContent
);
1544 nsresult rv
= frame
->GetRenderedText(aFlatString
);
1545 NS_ENSURE_SUCCESS(rv
, rv
);
1547 //if aContent is an object that is display: none, we have no a frame
1548 aContent
->AppendTextTo(*aFlatString
);
1550 if (isHTMLBlock
&& !aFlatString
->IsEmpty()) {
1551 aFlatString
->Append(PRUnichar(' '));
1557 nsAutoString textEquivalent
;
1558 if (!aContent
->IsNodeOfType(nsINode::eHTML
)) {
1559 if (aContent
->IsNodeOfType(nsINode::eXUL
)) {
1560 nsCOMPtr
<nsIDOMXULLabeledControlElement
> labeledEl(do_QueryInterface(aContent
));
1562 labeledEl
->GetLabel(textEquivalent
);
1565 if (aContent
->NodeInfo()->Equals(nsAccessibilityAtoms::label
, kNameSpaceID_XUL
)) {
1566 aContent
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::value
, textEquivalent
);
1568 if (textEquivalent
.IsEmpty()) {
1569 aContent
->GetAttr(kNameSpaceID_None
,
1570 nsAccessibilityAtoms::tooltiptext
, textEquivalent
);
1573 AppendNameFromAccessibleFor(aContent
, &textEquivalent
, PR_TRUE
/* use value */);
1575 return AppendStringWithSpaces(aFlatString
, textEquivalent
);
1577 return NS_OK
; // Not HTML and not XUL -- we don't handle it yet
1580 nsCOMPtr
<nsIAtom
> tag
= aContent
->Tag();
1581 if (tag
== nsAccessibilityAtoms::img
) {
1582 return AppendNameFromAccessibleFor(aContent
, aFlatString
);
1585 if (tag
== nsAccessibilityAtoms::input
) {
1586 static nsIContent::AttrValuesArray strings
[] =
1587 {&nsAccessibilityAtoms::button
, &nsAccessibilityAtoms::submit
,
1588 &nsAccessibilityAtoms::reset
, &nsAccessibilityAtoms::image
, nsnull
};
1589 if (aContent
->FindAttrValueIn(kNameSpaceID_None
, nsAccessibilityAtoms::type
,
1590 strings
, eIgnoreCase
) >= 0) {
1591 return AppendNameFromAccessibleFor(aContent
, aFlatString
);
1595 if (tag
== nsAccessibilityAtoms::object
&& !aContent
->GetChildCount()) {
1596 // If object has no alternative content children, try title
1597 aContent
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::title
, textEquivalent
);
1599 else if (tag
== nsAccessibilityAtoms::br
) {
1600 // If it's a line break, insert a space so that words aren't jammed together
1601 aFlatString
->AppendLiteral("\r\n");
1604 else if (tag
!= nsAccessibilityAtoms::a
&& tag
!= nsAccessibilityAtoms::area
) {
1605 AppendNameFromAccessibleFor(aContent
, aFlatString
, PR_TRUE
/* use value */);
1608 textEquivalent
.CompressWhitespace();
1609 return AppendStringWithSpaces(aFlatString
, textEquivalent
);
1613 nsresult
nsAccessible::AppendFlatStringFromSubtree(nsIContent
*aContent
, nsAString
*aFlatString
)
1615 static PRBool isAlreadyHere
; // Prevent recursion which can cause infinite loops
1616 if (isAlreadyHere
) {
1620 isAlreadyHere
= PR_TRUE
;
1622 nsCOMPtr
<nsIPresShell
> shell
= GetPresShell();
1623 NS_ENSURE_TRUE(shell
, NS_ERROR_FAILURE
);
1625 nsIFrame
*frame
= shell
->GetPrimaryFrameFor(aContent
);
1626 PRBool isHidden
= (!frame
|| !frame
->GetStyleVisibility()->IsVisible());
1627 nsresult rv
= AppendFlatStringFromSubtreeRecurse(aContent
, aFlatString
,
1630 isAlreadyHere
= PR_FALSE
;
1632 if (NS_SUCCEEDED(rv
) && !aFlatString
->IsEmpty()) {
1633 nsAString::const_iterator start
, end
;
1634 aFlatString
->BeginReading(start
);
1635 aFlatString
->EndReading(end
);
1637 PRInt32 spacesToTruncate
= 0;
1638 while (-- end
!= start
&& *end
== ' ')
1639 ++ spacesToTruncate
;
1641 if (spacesToTruncate
> 0)
1642 aFlatString
->Truncate(aFlatString
->Length() - spacesToTruncate
);
1649 nsAccessible::AppendFlatStringFromSubtreeRecurse(nsIContent
*aContent
,
1650 nsAString
*aFlatString
,
1651 PRBool aIsRootHidden
)
1653 // Depth first search for all text nodes that are decendants of content node.
1654 // Append all the text into one flat string
1655 PRUint32 numChildren
= 0;
1656 nsCOMPtr
<nsIDOMXULSelectControlElement
> selectControlEl(do_QueryInterface(aContent
));
1657 nsCOMPtr
<nsIAtom
> tag
= aContent
->Tag();
1659 if (!selectControlEl
&&
1660 tag
!= nsAccessibilityAtoms::textarea
&&
1661 tag
!= nsAccessibilityAtoms::select
) {
1662 // Don't walk children of elements with options, just get label directly.
1663 // Don't traverse the children of a textarea, we want the value, not the
1664 // static text node.
1665 // Don't traverse the children of a select element, we only want the
1667 numChildren
= aContent
->GetChildCount();
1670 if (numChildren
== 0) {
1671 // There are no children or they are irrelvant: get the text from the current node
1672 AppendFlatStringFromContentNode(aContent
, aFlatString
);
1676 // There are relevant children: use them to get the text.
1677 nsCOMPtr
<nsIPresShell
> shell
= GetPresShell();
1678 NS_ENSURE_TRUE(shell
, NS_ERROR_FAILURE
);
1681 for (index
= 0; index
< numChildren
; index
++) {
1682 nsCOMPtr
<nsIContent
> childContent
= aContent
->GetChildAt(index
);
1684 // Walk into hidden subtree if the the root parent is also hidden. This
1685 // happens when the author explictly uses a hidden label or description.
1686 if (!aIsRootHidden
) {
1687 nsIFrame
*childFrame
= shell
->GetPrimaryFrameFor(childContent
);
1688 if (!childFrame
|| !childFrame
->GetStyleVisibility()->IsVisible())
1692 AppendFlatStringFromSubtreeRecurse(childContent
, aFlatString
,
1699 nsresult
nsAccessible::GetTextFromRelationID(nsIAtom
*aIDProperty
, nsString
&aName
)
1701 // Get DHTML name from content subtree pointed to by ID attribute
1703 NS_ASSERTION(mDOMNode
, "Called from shutdown accessible");
1704 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
1709 if (!content
->GetAttr(kNameSpaceID_None
, aIDProperty
, ids
))
1712 ids
.CompressWhitespace(PR_TRUE
, PR_TRUE
);
1714 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(content
->GetOwnerDoc());
1715 NS_ENSURE_TRUE(domDoc
, NS_ERROR_FAILURE
);
1717 // Support idlist as in aria-labelledby="id1 id2 id3"
1718 while (!ids
.IsEmpty()) {
1720 PRInt32 idLength
= ids
.FindChar(' ');
1721 NS_ASSERTION(idLength
!= 0, "Should not be 0 because of CompressWhitespace() call above");
1722 if (idLength
== kNotFound
) {
1726 id
= Substring(ids
, 0, idLength
);
1727 ids
.Cut(0, idLength
+ 1);
1730 if (!aName
.IsEmpty()) {
1731 aName
+= ' '; // Need whitespace between multiple labels or descriptions
1733 nsCOMPtr
<nsIDOMElement
> labelElement
;
1734 domDoc
->GetElementById(id
, getter_AddRefs(labelElement
));
1735 content
= do_QueryInterface(labelElement
);
1739 // We have a label content
1740 nsresult rv
= AppendFlatStringFromSubtree(content
, &aName
);
1741 if (NS_SUCCEEDED(rv
)) {
1742 aName
.CompressWhitespace();
1750 nsAccessible::GetHTMLName(nsAString
& aLabel
)
1752 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
1754 aLabel
.SetIsVoid(PR_TRUE
);
1758 nsIContent
*labelContent
= nsCoreUtils::GetHTMLLabelContent(content
);
1761 nsresult rv
= AppendFlatStringFromSubtree(labelContent
, &label
);
1762 NS_ENSURE_SUCCESS(rv
, rv
);
1764 label
.CompressWhitespace();
1765 if (!label
.IsEmpty()) {
1771 PRUint32 role
= nsAccUtils::Role(this);
1772 PRUint32 canAggregateName
=
1773 nsNameUtils::gRoleToNameRulesMap
[role
] & eFromSubtree
;
1775 if (canAggregateName
) {
1776 // Don't use AppendFlatStringFromSubtree for container widgets like menulist
1777 nsresult rv
= AppendFlatStringFromSubtree(content
, &aLabel
);
1778 NS_ENSURE_SUCCESS(rv
, rv
);
1780 if (!aLabel
.IsEmpty())
1788 * 3 main cases for XUL Controls to be labeled
1789 * 1 - control contains label="foo"
1790 * 2 - control has, as a child, a label element
1791 * - label has either value="foo" or children
1792 * 3 - non-child label contains control="controlID"
1793 * - label has either value="foo" or children
1794 * Once a label is found, the search is discontinued, so a control
1795 * that has a label child as well as having a label external to
1796 * the control that uses the control="controlID" syntax will use
1797 * the child label for its Name.
1800 nsAccessible::GetXULName(nsAString
& aLabel
)
1802 // CASE #1 (via label attribute) -- great majority of the cases
1803 nsresult rv
= NS_OK
;
1806 nsCOMPtr
<nsIDOMXULLabeledControlElement
> labeledEl(do_QueryInterface(mDOMNode
));
1808 rv
= labeledEl
->GetLabel(label
);
1811 nsCOMPtr
<nsIDOMXULSelectControlItemElement
> itemEl(do_QueryInterface(mDOMNode
));
1813 rv
= itemEl
->GetLabel(label
);
1816 nsCOMPtr
<nsIDOMXULSelectControlElement
> select(do_QueryInterface(mDOMNode
));
1817 // Use label if this is not a select control element which
1818 // uses label attribute to indicate which option is selected
1820 nsCOMPtr
<nsIDOMXULElement
> xulEl(do_QueryInterface(mDOMNode
));
1822 rv
= xulEl
->GetAttribute(NS_LITERAL_STRING("label"), label
);
1828 // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
1829 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
1833 if (NS_FAILED(rv
) || label
.IsEmpty()) {
1835 nsIContent
*labelContent
=
1836 nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::control
,
1837 nsAccessibilityAtoms::label
);
1839 nsCOMPtr
<nsIDOMXULLabelElement
> xulLabel(do_QueryInterface(labelContent
));
1840 // Check if label's value attribute is used
1841 if (xulLabel
&& NS_SUCCEEDED(xulLabel
->GetValue(label
)) && label
.IsEmpty()) {
1842 // If no value attribute, a non-empty label must contain
1843 // children that define its text -- possibly using HTML
1844 AppendFlatStringFromSubtree(labelContent
, &label
);
1848 // XXX If CompressWhiteSpace worked on nsAString we could avoid a copy
1849 label
.CompressWhitespace();
1850 if (!label
.IsEmpty()) {
1855 // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
1856 nsIContent
*bindingParent
= content
->GetBindingParent();
1857 nsIContent
*parent
= bindingParent
? bindingParent
->GetParent() :
1858 content
->GetParent();
1860 if (parent
->Tag() == nsAccessibilityAtoms::toolbaritem
&&
1861 parent
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::title
, label
)) {
1862 label
.CompressWhitespace();
1866 parent
= parent
->GetParent();
1869 PRUint32 role
= nsAccUtils::Role(this);
1870 PRUint32 canAggregateName
=
1871 nsNameUtils::gRoleToNameRulesMap
[role
] & eFromSubtree
;
1873 return canAggregateName
?
1874 AppendFlatStringFromSubtree(content
, &aLabel
) : NS_OK
;
1878 nsAccessible::FireAccessibleEvent(nsIAccessibleEvent
*aEvent
)
1880 NS_ENSURE_ARG_POINTER(aEvent
);
1881 nsCOMPtr
<nsIDOMNode
> eventNode
;
1882 aEvent
->GetDOMNode(getter_AddRefs(eventNode
));
1883 NS_ENSURE_TRUE(nsAccUtils::IsNodeRelevant(eventNode
), NS_ERROR_FAILURE
);
1885 nsCOMPtr
<nsIObserverService
> obsService
=
1886 do_GetService("@mozilla.org/observer-service;1");
1887 NS_ENSURE_TRUE(obsService
, NS_ERROR_FAILURE
);
1889 return obsService
->NotifyObservers(aEvent
, NS_ACCESSIBLE_EVENT_TOPIC
, nsnull
);
1892 NS_IMETHODIMP
nsAccessible::GetFinalRole(PRUint32
*aRole
)
1894 NS_ENSURE_ARG_POINTER(aRole
);
1895 *aRole
= nsIAccessibleRole::ROLE_NOTHING
;
1897 if (mRoleMapEntry
) {
1898 *aRole
= mRoleMapEntry
->role
;
1900 // These unfortunate exceptions don't fit into the ARIA table
1901 // This is where the nsIAccessible role depends on both the role and ARIA state
1902 if (*aRole
== nsIAccessibleRole::ROLE_PUSHBUTTON
) {
1903 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mDOMNode
);
1905 if (content
->HasAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_pressed
)) {
1906 // For aria-pressed="false" or aria-pressed="true"
1907 // For simplicity, any pressed attribute indicates it's a toggle button
1908 *aRole
= nsIAccessibleRole::ROLE_TOGGLE_BUTTON
;
1910 else if (content
->AttrValueIs(kNameSpaceID_None
, nsAccessibilityAtoms::aria_haspopup
,
1911 nsAccessibilityAtoms::_true
, eCaseMatters
)) {
1912 // For button with aria-haspopup="true"
1913 *aRole
= nsIAccessibleRole::ROLE_BUTTONMENU
;
1917 else if (*aRole
== nsIAccessibleRole::ROLE_LISTBOX
) {
1918 // A listbox inside of a combo box needs a special role because of ATK mapping to menu
1919 nsCOMPtr
<nsIAccessible
> possibleCombo
;
1920 GetParent(getter_AddRefs(possibleCombo
));
1921 if (nsAccUtils::Role(possibleCombo
) == nsIAccessibleRole::ROLE_COMBOBOX
) {
1922 *aRole
= nsIAccessibleRole::ROLE_COMBOBOX_LIST
;
1924 else { // Check to see if combo owns the listbox instead
1925 GetAccessibleRelated(nsIAccessibleRelation::RELATION_NODE_CHILD_OF
, getter_AddRefs(possibleCombo
));
1926 if (nsAccUtils::Role(possibleCombo
) == nsIAccessibleRole::ROLE_COMBOBOX
)
1927 *aRole
= nsIAccessibleRole::ROLE_COMBOBOX_LIST
;
1930 else if (*aRole
== nsIAccessibleRole::ROLE_OPTION
) {
1931 nsCOMPtr
<nsIAccessible
> parent
;
1932 GetParent(getter_AddRefs(parent
));
1933 if (nsAccUtils::Role(parent
) == nsIAccessibleRole::ROLE_COMBOBOX_LIST
)
1934 *aRole
= nsIAccessibleRole::ROLE_COMBOBOX_OPTION
;
1937 // gLandmarkRoleMap: can use role of accessible class impl
1938 // gEmptyRoleMap and all others: cannot use role of accessible class impl
1939 if (mRoleMapEntry
!= &nsARIAMap::gLandmarkRoleMap
) {
1940 // We can now expose ROLE_NOTHING when there is a role map entry, which
1941 // will cause ATK to use ROLE_UNKNOWN and MSAA to use a BSTR role with
1942 // the ARIA role or element's tag. In either case the AT can also use
1943 // the object attributes tag and xml-roles to find out more.
1947 return mDOMNode
? GetRole(aRole
) : NS_ERROR_FAILURE
; // Node already shut down
1951 nsAccessible::GetAttributes(nsIPersistentProperties
**aAttributes
)
1953 NS_ENSURE_ARG_POINTER(aAttributes
); // In/out param. Created if necessary.
1956 return NS_ERROR_FAILURE
;
1958 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
1960 return NS_ERROR_FAILURE
;
1963 nsCOMPtr
<nsIPersistentProperties
> attributes
= *aAttributes
;
1965 // Create only if an array wasn't already passed in
1966 attributes
= do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID
);
1967 NS_ENSURE_TRUE(attributes
, NS_ERROR_OUT_OF_MEMORY
);
1968 NS_ADDREF(*aAttributes
= attributes
);
1971 nsresult rv
= GetAttributesInternal(attributes
);
1972 NS_ENSURE_SUCCESS(rv
, rv
);
1975 nsAutoString oldValueUnused
;
1976 if (nsCoreUtils::GetID(content
, id
)) {
1977 // Expose ID. If an <iframe id> exists override the one on the <body> of the source doc,
1978 // because the specific instance is what makes the ID useful for scripts
1979 attributes
->SetStringProperty(NS_LITERAL_CSTRING("id"), id
, oldValueUnused
);
1982 nsAutoString xmlRoles
;
1983 if (content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::role
, xmlRoles
)) {
1984 attributes
->SetStringProperty(NS_LITERAL_CSTRING("xml-roles"), xmlRoles
, oldValueUnused
);
1987 nsCOMPtr
<nsIAccessibleValue
> supportsValue
= do_QueryInterface(static_cast<nsIAccessible
*>(this));
1988 if (supportsValue
) {
1989 // We support values, so expose the string value as well, via the valuetext object attribute
1990 // We test for the value interface because we don't want to expose traditional get_accValue()
1991 // information such as URL's on links and documents, or text in an input
1992 nsAutoString valuetext
;
1993 GetValue(valuetext
);
1994 attributes
->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext
, oldValueUnused
);
1998 PRUint32 role
= nsAccUtils::Role(this);
1999 if (role
== nsIAccessibleRole::ROLE_CHECKBUTTON
||
2000 role
== nsIAccessibleRole::ROLE_PUSHBUTTON
||
2001 role
== nsIAccessibleRole::ROLE_MENUITEM
||
2002 role
== nsIAccessibleRole::ROLE_LISTITEM
||
2003 role
== nsIAccessibleRole::ROLE_OPTION
||
2004 role
== nsIAccessibleRole::ROLE_RADIOBUTTON
||
2005 role
== nsIAccessibleRole::ROLE_RICH_OPTION
||
2006 role
== nsIAccessibleRole::ROLE_OUTLINEITEM
||
2007 content
->HasAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_checked
)) {
2008 // Might be checkable -- checking role & ARIA attribute first is faster than getting state
2010 GetState(&state
, nsnull
);
2011 if (state
& nsIAccessibleStates::STATE_CHECKABLE
) {
2012 // No official state for checkable, so use object attribute to expose that
2013 attributes
->SetStringProperty(NS_LITERAL_CSTRING("checkable"), NS_LITERAL_STRING("true"),
2018 // Level/setsize/posinset
2019 if (!nsAccUtils::HasAccGroupAttrs(attributes
)) {
2020 // The role of an accessible can be pointed by ARIA attribute but ARIA
2021 // posinset, level, setsize may be skipped. Therefore we calculate here
2022 // these properties to map them into description.
2024 // If accessible is invisible we don't want to calculate group ARIA
2025 // attributes for it.
2026 if ((role
== nsIAccessibleRole::ROLE_LISTITEM
||
2027 role
== nsIAccessibleRole::ROLE_MENUITEM
||
2028 role
== nsIAccessibleRole::ROLE_CHECK_MENU_ITEM
||
2029 role
== nsIAccessibleRole::ROLE_RADIO_MENU_ITEM
||
2030 role
== nsIAccessibleRole::ROLE_RADIOBUTTON
||
2031 role
== nsIAccessibleRole::ROLE_PAGETAB
||
2032 role
== nsIAccessibleRole::ROLE_OPTION
||
2033 role
== nsIAccessibleRole::ROLE_RADIOBUTTON
||
2034 role
== nsIAccessibleRole::ROLE_OUTLINEITEM
) &&
2035 0 == (nsAccUtils::State(this) & nsIAccessibleStates::STATE_INVISIBLE
)) {
2037 PRUint32 baseRole
= role
;
2038 if (role
== nsIAccessibleRole::ROLE_CHECK_MENU_ITEM
||
2039 role
== nsIAccessibleRole::ROLE_RADIO_MENU_ITEM
)
2040 baseRole
= nsIAccessibleRole::ROLE_MENUITEM
;
2042 nsCOMPtr
<nsIAccessible
> parent
= GetParent();
2043 NS_ENSURE_TRUE(parent
, NS_ERROR_FAILURE
);
2045 PRInt32 positionInGroup
= 0;
2046 PRInt32 setSize
= 0;
2048 nsCOMPtr
<nsIAccessible
> sibling
, nextSibling
;
2049 parent
->GetFirstChild(getter_AddRefs(sibling
));
2050 NS_ENSURE_TRUE(sibling
, NS_ERROR_FAILURE
);
2052 PRBool foundCurrent
= PR_FALSE
;
2053 PRUint32 siblingRole
, siblingBaseRole
;
2055 sibling
->GetFinalRole(&siblingRole
);
2057 siblingBaseRole
= siblingRole
;
2058 if (siblingRole
== nsIAccessibleRole::ROLE_CHECK_MENU_ITEM
||
2059 siblingRole
== nsIAccessibleRole::ROLE_RADIO_MENU_ITEM
)
2060 siblingBaseRole
= nsIAccessibleRole::ROLE_MENUITEM
;
2062 // If sibling is visible and has the same base role.
2063 if (siblingBaseRole
== baseRole
&&
2064 !(nsAccUtils::State(sibling
) & nsIAccessibleStates::STATE_INVISIBLE
)) {
2066 if (!foundCurrent
) {
2068 if (sibling
== this)
2069 foundCurrent
= PR_TRUE
;
2073 // If the sibling is separator
2074 if (siblingRole
== nsIAccessibleRole::ROLE_SEPARATOR
) {
2075 if (foundCurrent
) // the our group is ended
2078 // not our group, continue the searching
2079 positionInGroup
= 0;
2083 sibling
->GetNextSibling(getter_AddRefs(nextSibling
));
2084 sibling
= nextSibling
;
2087 PRInt32 groupLevel
= 0;
2088 if (role
== nsIAccessibleRole::ROLE_OUTLINEITEM
) {
2090 nsCOMPtr
<nsIAccessible
> nextParent
;
2092 parent
->GetFinalRole(&role
);
2094 if (role
== nsIAccessibleRole::ROLE_OUTLINE
)
2096 if (role
== nsIAccessibleRole::ROLE_GROUPING
)
2099 parent
->GetParent(getter_AddRefs(nextParent
));
2100 parent
.swap(nextParent
);
2104 nsAccUtils::SetAccGroupAttrs(attributes
, groupLevel
, positionInGroup
,
2109 // Expose all ARIA attributes
2110 PRUint32 numAttrs
= content
->GetAttrCount();
2111 for (PRUint32 count
= 0; count
< numAttrs
; count
++) {
2112 const nsAttrName
*attr
= content
->GetAttrNameAt(count
);
2113 if (attr
&& attr
->NamespaceEquals(kNameSpaceID_None
)) {
2114 nsIAtom
*attrAtom
= attr
->Atom();
2115 const char *attrStr
;
2116 attrAtom
->GetUTF8String(&attrStr
);
2117 if (PL_strncmp(attrStr
, "aria-", 5))
2118 continue; // Not ARIA
2119 if (!nsAccUtils::IsARIAPropForObjectAttr(attrAtom
))
2120 continue; // No need to expose obj attribute -- will be exposed some other way
2122 if (content
->GetAttr(kNameSpaceID_None
, attrAtom
, value
)) {
2123 attributes
->SetStringProperty(nsDependentCString(attrStr
+ 5), value
, oldValueUnused
);
2132 nsAccessible::GetAttributesInternal(nsIPersistentProperties
*aAttributes
)
2134 // Attributes set by this method will not be used to override attributes on a sub-document accessible
2135 // when there is a <frame>/<iframe> element that spawned the sub-document
2136 nsIContent
*content
= nsCoreUtils::GetRoleContent(mDOMNode
);
2137 nsCOMPtr
<nsIDOMElement
> element(do_QueryInterface(content
));
2138 NS_ENSURE_TRUE(element
, NS_ERROR_UNEXPECTED
);
2140 nsAutoString tagName
;
2141 element
->GetTagName(tagName
);
2142 if (!tagName
.IsEmpty()) {
2143 nsAutoString oldValueUnused
;
2144 aAttributes
->SetStringProperty(NS_LITERAL_CSTRING("tag"), tagName
,
2148 nsAccEvent::GetLastEventAttributes(mDOMNode
, aAttributes
);
2150 // Expose class because it may have useful microformat information
2151 // Let the class from an iframe's document be exposed, don't override from <iframe class>
2152 nsAutoString _class
;
2153 if (content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::_class
, _class
))
2154 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::_class
, _class
);
2156 // Get container-foo computed live region properties based on the closest container with
2157 // the live region attribute.
2158 // Inner nodes override outer nodes within the same document --
2159 // The inner nodes can be used to override live region behavior on more general outer nodes
2160 // However, nodes in outer documents override nodes in inner documents:
2161 // Outer doc author may want to override properties on a widget they used in an iframe
2162 nsCOMPtr
<nsIDOMNode
> startNode
= mDOMNode
;
2163 nsIContent
*startContent
= content
;
2165 NS_ENSURE_STATE(startContent
);
2166 nsIDocument
*doc
= startContent
->GetDocument();
2167 nsCOMPtr
<nsIDOMNode
> docNode
= do_QueryInterface(doc
);
2168 NS_ENSURE_STATE(docNode
);
2169 nsIContent
*topContent
= nsCoreUtils::GetRoleContent(docNode
);
2170 NS_ENSURE_STATE(topContent
);
2171 nsAccUtils::SetLiveContainerAttributes(aAttributes
, startContent
,
2174 // Allow ARIA live region markup from outer documents to override
2175 nsCOMPtr
<nsISupports
> container
= doc
->GetContainer();
2176 nsCOMPtr
<nsIDocShellTreeItem
> docShellTreeItem
=
2177 do_QueryInterface(container
);
2178 if (!docShellTreeItem
)
2181 nsCOMPtr
<nsIDocShellTreeItem
> sameTypeParent
;
2182 docShellTreeItem
->GetSameTypeParent(getter_AddRefs(sameTypeParent
));
2183 if (!sameTypeParent
|| sameTypeParent
== docShellTreeItem
)
2186 nsIDocument
*parentDoc
= doc
->GetParentDocument();
2190 startContent
= parentDoc
->FindContentForSubDocument(doc
);
2193 // Expose 'display' attribute.
2195 nsresult rv
= GetComputedStyleValue(EmptyString(),
2196 NS_LITERAL_STRING("display"),
2198 if (NS_SUCCEEDED(rv
))
2199 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::display
,
2202 // Expose 'text-align' attribute.
2203 rv
= GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("text-align"),
2205 if (NS_SUCCEEDED(rv
))
2206 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::textAlign
,
2209 // Expose 'text-indent' attribute.
2210 rv
= GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("text-indent"),
2212 if (NS_SUCCEEDED(rv
))
2213 nsAccUtils::SetAccAttr(aAttributes
, nsAccessibilityAtoms::textIndent
,
2220 nsAccessible::GroupPosition(PRInt32
*aGroupLevel
,
2221 PRInt32
*aSimilarItemsInGroup
,
2222 PRInt32
*aPositionInGroup
)
2224 // Every element exposes level/posinset/sizeset for IAccessdible::attributes
2225 // if they make sense for it. These attributes are mapped into groupPosition.
2226 // If 'level' attribute doesn't make sense element then it isn't represented
2227 // via IAccessible::attributes and groupLevel of groupPosition method is 0.
2228 // Elements that expose 'level' attribute only (like html headings elements)
2229 // don't support this method and all arguments are equalled 0.
2231 NS_ENSURE_ARG_POINTER(aGroupLevel
);
2232 NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup
);
2233 NS_ENSURE_ARG_POINTER(aPositionInGroup
);
2236 *aSimilarItemsInGroup
= 0;
2237 *aPositionInGroup
= 0;
2239 nsCOMPtr
<nsIPersistentProperties
> attributes
;
2240 nsresult rv
= GetAttributes(getter_AddRefs(attributes
));
2241 NS_ENSURE_SUCCESS(rv
, rv
);
2243 return NS_ERROR_FAILURE
;
2245 PRInt32 level
, posInSet
, setSize
;
2246 nsAccUtils::GetAccGroupAttrs(attributes
, &level
, &posInSet
, &setSize
);
2248 if (!posInSet
&& !setSize
)
2251 *aGroupLevel
= level
;
2253 *aPositionInGroup
= posInSet
;
2254 *aSimilarItemsInGroup
= setSize
;
2259 PRBool
nsAccessible::MappedAttrState(nsIContent
*aContent
, PRUint32
*aStateInOut
,
2260 nsStateMapEntry
*aStateMapEntry
)
2262 // Return true if we should continue
2263 if (!aStateMapEntry
->attributeName
) {
2264 return PR_FALSE
; // Stop looking -- no more states
2267 nsAutoString attribValue
;
2268 if (aContent
->GetAttr(kNameSpaceID_None
, *aStateMapEntry
->attributeName
, attribValue
)) {
2269 if (aStateMapEntry
->attributeValue
== kBoolState
) {
2270 // No attribute value map specified in state map entry indicates state cleared
2271 if (attribValue
.EqualsLiteral("false")) {
2272 *aStateInOut
&= ~aStateMapEntry
->state
;
2275 *aStateInOut
|= aStateMapEntry
->state
;
2278 else if (NS_ConvertUTF16toUTF8(attribValue
).Equals(aStateMapEntry
->attributeValue
)) {
2279 *aStateInOut
|= aStateMapEntry
->state
;
2287 nsAccessible::GetState(PRUint32
*aState
, PRUint32
*aExtraState
)
2289 NS_ENSURE_ARG_POINTER(aState
);
2291 nsresult rv
= GetStateInternal(aState
, aExtraState
);
2292 NS_ENSURE_A11Y_SUCCESS(rv
, rv
);
2294 // Apply ARIA states to be sure accessible states will be overriden.
2295 GetARIAState(aState
);
2297 if (mRoleMapEntry
&& mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_PAGETAB
) {
2298 if (*aState
& nsIAccessibleStates::STATE_FOCUSED
) {
2299 *aState
|= nsIAccessibleStates::STATE_SELECTED
;
2301 // Expose 'selected' state on ARIA tab if the focus is on internal element
2302 // of related tabpanel.
2303 nsCOMPtr
<nsIAccessible
> tabPanel
;
2304 rv
= GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABEL_FOR
,
2305 getter_AddRefs(tabPanel
));
2306 NS_ENSURE_SUCCESS(rv
, rv
);
2308 if (nsAccUtils::Role(tabPanel
) == nsIAccessibleRole::ROLE_PROPERTYPAGE
) {
2309 nsCOMPtr
<nsIAccessNode
> tabPanelAccessNode(do_QueryInterface(tabPanel
));
2310 nsCOMPtr
<nsIDOMNode
> tabPanelNode
;
2311 tabPanelAccessNode
->GetDOMNode(getter_AddRefs(tabPanelNode
));
2312 NS_ENSURE_STATE(tabPanelNode
);
2314 if (nsCoreUtils::IsAncestorOf(tabPanelNode
, gLastFocusedNode
))
2315 *aState
|= nsIAccessibleStates::STATE_SELECTED
;
2320 const PRUint32 kExpandCollapseStates
=
2321 nsIAccessibleStates::STATE_COLLAPSED
| nsIAccessibleStates::STATE_EXPANDED
;
2322 if ((*aState
& kExpandCollapseStates
) == kExpandCollapseStates
) {
2323 // Cannot be both expanded and collapsed -- this happens in ARIA expanded
2324 // combobox because of limitation of nsARIAMap.
2325 // XXX: Perhaps we will be able to make this less hacky if we support
2326 // extended states in nsARIAMap, e.g. derive COLLAPSED from
2327 // EXPANDABLE && !EXPANDED.
2328 *aState
&= ~nsIAccessibleStates::STATE_COLLAPSED
;
2331 // Set additional states which presence depends on another states.
2335 if (!(*aState
& nsIAccessibleStates::STATE_UNAVAILABLE
)) {
2336 *aExtraState
|= nsIAccessibleStates::EXT_STATE_ENABLED
|
2337 nsIAccessibleStates::EXT_STATE_SENSITIVE
;
2340 if ((*aState
& nsIAccessibleStates::STATE_COLLAPSED
) ||
2341 (*aState
& nsIAccessibleStates::STATE_EXPANDED
))
2342 *aExtraState
|= nsIAccessibleStates::EXT_STATE_EXPANDABLE
;
2344 if (mRoleMapEntry
) {
2345 // If an object has an ancestor with the activedescendant property
2346 // pointing at it, we mark it as ACTIVE even if it's not currently focused.
2347 // This allows screen reader virtual buffer modes to know which descendant
2348 // is the current one that would get focus if the user navigates to the container widget.
2349 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mDOMNode
);
2351 if (content
&& nsCoreUtils::GetID(content
, id
)) {
2352 nsIContent
*ancestorContent
= content
;
2353 nsAutoString activeID
;
2354 while ((ancestorContent
= ancestorContent
->GetParent()) != nsnull
) {
2355 if (ancestorContent
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_activedescendant
, activeID
)) {
2356 if (id
== activeID
) {
2357 *aExtraState
|= nsIAccessibleStates::EXT_STATE_ACTIVE
;
2366 rv
= GetFinalRole(&role
);
2367 NS_ENSURE_SUCCESS(rv
, rv
);
2369 if (role
== nsIAccessibleRole::ROLE_ENTRY
||
2370 role
== nsIAccessibleRole::ROLE_COMBOBOX
) {
2372 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
2373 NS_ENSURE_STATE(content
);
2375 nsAutoString autocomplete
;
2376 if (content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_autocomplete
, autocomplete
) &&
2377 (autocomplete
.EqualsIgnoreCase("inline") ||
2378 autocomplete
.EqualsIgnoreCase("list") ||
2379 autocomplete
.EqualsIgnoreCase("both"))) {
2380 *aExtraState
|= nsIAccessibleStates::EXT_STATE_SUPPORTS_AUTOCOMPLETION
;
2383 // XXX We can remove this hack once we support RDF-based role & state maps
2384 if (mRoleMapEntry
&& mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_ENTRY
) {
2385 PRBool isMultiLine
= content
->AttrValueIs(kNameSpaceID_None
, nsAccessibilityAtoms::aria_multiline
,
2386 nsAccessibilityAtoms::_true
, eCaseMatters
);
2387 *aExtraState
|= isMultiLine
? nsIAccessibleStates::EXT_STATE_MULTI_LINE
:
2388 nsIAccessibleStates::EXT_STATE_SINGLE_LINE
;
2389 if (0 == (*aState
& nsIAccessibleStates::STATE_READONLY
))
2390 *aExtraState
|= nsIAccessibleStates::EXT_STATE_EDITABLE
; // Not readonly
2391 else // We're readonly: make sure editable state wasn't set by impl class
2392 *aExtraState
&= ~nsIAccessibleStates::EXT_STATE_EDITABLE
;
2396 // For some reasons DOM node may have not a frame. We tract such accessibles
2398 nsIFrame
*frame
= GetFrame();
2402 const nsStyleDisplay
* display
= frame
->GetStyleDisplay();
2403 if (display
&& display
->mOpacity
== 1.0f
&&
2404 !(*aState
& nsIAccessibleStates::STATE_INVISIBLE
)) {
2405 *aExtraState
|= nsIAccessibleStates::EXT_STATE_OPAQUE
;
2408 const nsStyleXUL
*xulStyle
= frame
->GetStyleXUL();
2410 // In XUL all boxes are either vertical or horizontal
2411 if (xulStyle
->mBoxOrient
== NS_STYLE_BOX_ORIENT_VERTICAL
) {
2412 *aExtraState
|= nsIAccessibleStates::EXT_STATE_VERTICAL
;
2415 *aExtraState
|= nsIAccessibleStates::EXT_STATE_HORIZONTAL
;
2423 nsAccessible::GetARIAState(PRUint32
*aState
)
2425 // Test for universal states first
2426 nsIContent
*content
= nsCoreUtils::GetRoleContent(mDOMNode
);
2432 while (MappedAttrState(content
, aState
, &nsARIAMap::gWAIUnivStateMap
[index
])) {
2436 if (mRoleMapEntry
) {
2437 // Once DHTML role is used, we're only readonly if DHTML readonly used
2438 *aState
&= ~nsIAccessibleStates::STATE_READONLY
;
2440 if (content
->HasAttr(kNameSpaceID_None
, content
->GetIDAttributeName())) {
2441 // If has a role & ID and aria-activedescendant on the container, assume focusable
2442 nsIContent
*ancestorContent
= content
;
2443 while ((ancestorContent
= ancestorContent
->GetParent()) != nsnull
) {
2444 if (ancestorContent
->HasAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_activedescendant
)) {
2445 // ancestor has activedescendant property, this content could be active
2446 *aState
|= nsIAccessibleStates::STATE_FOCUSABLE
;
2453 if (*aState
& nsIAccessibleStates::STATE_FOCUSABLE
) {
2454 // Special case: aria-disabled propagates from ancestors down to any focusable descendant
2455 nsIContent
*ancestorContent
= content
;
2456 while ((ancestorContent
= ancestorContent
->GetParent()) != nsnull
) {
2457 if (ancestorContent
->AttrValueIs(kNameSpaceID_None
, nsAccessibilityAtoms::aria_disabled
,
2458 nsAccessibilityAtoms::_true
, eCaseMatters
)) {
2459 // ancestor has aria-disabled property, this is disabled
2460 *aState
|= nsIAccessibleStates::STATE_UNAVAILABLE
;
2469 *aState
|= mRoleMapEntry
->state
;
2470 if (MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap1
) &&
2471 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap2
) &&
2472 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap3
) &&
2473 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap4
) &&
2474 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap5
) &&
2475 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap6
) &&
2476 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap7
)) {
2477 MappedAttrState(content
, aState
, &mRoleMapEntry
->attributeMap8
);
2483 // Not implemented by this class
2485 /* DOMString getValue (); */
2487 nsAccessible::GetValue(nsAString
& aValue
)
2490 return NS_ERROR_FAILURE
;
2492 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
2496 if (mRoleMapEntry
) {
2497 if (mRoleMapEntry
->valueRule
== eNoValue
) {
2501 // aria-valuenow is a number, and aria-valuetext is the optional text equivalent
2502 // For the string value, we will try the optional text equivalent first
2503 if (!content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_valuetext
, aValue
)) {
2504 content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_valuenow
, aValue
);
2508 if (!aValue
.IsEmpty())
2511 // Check if it's a simple xlink.
2512 if (nsCoreUtils::IsXLink(content
)) {
2513 nsCOMPtr
<nsIPresShell
> presShell(do_QueryReferent(mWeakShell
));
2515 return presShell
->GetLinkLocation(mDOMNode
, aValue
);
2521 // nsIAccessibleValue
2523 nsAccessible::GetMaximumValue(double *aMaximumValue
)
2525 return GetAttrValue(nsAccessibilityAtoms::aria_valuemax
, aMaximumValue
);
2529 nsAccessible::GetMinimumValue(double *aMinimumValue
)
2531 return GetAttrValue(nsAccessibilityAtoms::aria_valuemin
, aMinimumValue
);
2535 nsAccessible::GetMinimumIncrement(double *aMinIncrement
)
2537 NS_ENSURE_ARG_POINTER(aMinIncrement
);
2540 // No mimimum increment in dynamic content spec right now
2541 return NS_OK_NO_ARIA_VALUE
;
2545 nsAccessible::GetCurrentValue(double *aValue
)
2547 return GetAttrValue(nsAccessibilityAtoms::aria_valuenow
, aValue
);
2551 nsAccessible::SetCurrentValue(double aValue
)
2554 return NS_ERROR_FAILURE
; // Node already shut down
2556 if (!mRoleMapEntry
|| mRoleMapEntry
->valueRule
== eNoValue
)
2557 return NS_OK_NO_ARIA_VALUE
;
2559 const PRUint32 kValueCannotChange
= nsIAccessibleStates::STATE_READONLY
|
2560 nsIAccessibleStates::STATE_UNAVAILABLE
;
2562 if (nsAccUtils::State(this) & kValueCannotChange
)
2563 return NS_ERROR_FAILURE
;
2565 double minValue
= 0;
2566 if (NS_SUCCEEDED(GetMinimumValue(&minValue
)) && aValue
< minValue
)
2567 return NS_ERROR_INVALID_ARG
;
2569 double maxValue
= 0;
2570 if (NS_SUCCEEDED(GetMaximumValue(&maxValue
)) && aValue
> maxValue
)
2571 return NS_ERROR_INVALID_ARG
;
2573 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
2574 NS_ENSURE_STATE(content
);
2576 nsAutoString newValue
;
2577 newValue
.AppendFloat(aValue
);
2578 return content
->SetAttr(kNameSpaceID_None
,
2579 nsAccessibilityAtoms::aria_valuenow
, newValue
, PR_TRUE
);
2582 /* void setName (in DOMString name); */
2583 NS_IMETHODIMP
nsAccessible::SetName(const nsAString
& name
)
2585 return NS_ERROR_NOT_IMPLEMENTED
;
2589 nsAccessible::GetDefaultKeyBinding(nsAString
& aKeyBinding
)
2591 aKeyBinding
.Truncate();
2596 nsAccessible::GetKeyBindings(PRUint8 aActionIndex
,
2597 nsIDOMDOMStringList
**aKeyBindings
)
2599 // Currently we support only unique key binding on element for default action.
2600 NS_ENSURE_TRUE(aActionIndex
== 0, NS_ERROR_INVALID_ARG
);
2602 nsAccessibleDOMStringList
*keyBindings
= new nsAccessibleDOMStringList();
2603 NS_ENSURE_TRUE(keyBindings
, NS_ERROR_OUT_OF_MEMORY
);
2605 nsAutoString defaultKey
;
2606 nsresult rv
= GetDefaultKeyBinding(defaultKey
);
2607 NS_ENSURE_SUCCESS(rv
, rv
);
2609 if (!defaultKey
.IsEmpty())
2610 keyBindings
->Add(defaultKey
);
2612 NS_ADDREF(*aKeyBindings
= keyBindings
);
2616 /* unsigned long getRole (); */
2617 NS_IMETHODIMP
nsAccessible::GetRole(PRUint32
*aRole
)
2619 NS_ENSURE_ARG_POINTER(aRole
);
2620 *aRole
= nsIAccessibleRole::ROLE_NOTHING
;
2623 return NS_ERROR_FAILURE
;
2625 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
2626 if (nsCoreUtils::IsXLink(content
))
2627 *aRole
= nsIAccessibleRole::ROLE_LINK
;
2632 // readonly attribute PRUint8 numActions
2634 nsAccessible::GetNumActions(PRUint8
*aNumActions
)
2636 NS_ENSURE_ARG_POINTER(aNumActions
);
2640 return NS_ERROR_FAILURE
;
2642 PRUint32 actionRule
= GetActionRule(nsAccUtils::State(this));
2643 if (actionRule
== eNoAction
)
2650 /* DOMString getAccActionName (in PRUint8 index); */
2652 nsAccessible::GetActionName(PRUint8 aIndex
, nsAString
& aName
)
2657 return NS_ERROR_INVALID_ARG
;
2660 return NS_ERROR_FAILURE
;
2662 PRUint32 states
= nsAccUtils::State(this);
2663 PRUint32 actionRule
= GetActionRule(states
);
2665 switch (actionRule
) {
2666 case eActivateAction
:
2667 aName
.AssignLiteral("activate");
2671 aName
.AssignLiteral("click");
2674 case eCheckUncheckAction
:
2675 if (states
& nsIAccessibleStates::STATE_CHECKED
)
2676 aName
.AssignLiteral("uncheck");
2678 aName
.AssignLiteral("check");
2682 aName
.AssignLiteral("jump");
2685 case eOpenCloseAction
:
2686 if (states
& nsIAccessibleStates::STATE_COLLAPSED
)
2687 aName
.AssignLiteral("open");
2689 aName
.AssignLiteral("close");
2693 aName
.AssignLiteral("select");
2697 aName
.AssignLiteral("switch");
2701 return NS_ERROR_INVALID_ARG
;
2704 // AString getActionDescription(in PRUint8 index)
2706 nsAccessible::GetActionDescription(PRUint8 aIndex
, nsAString
& aDescription
)
2708 // default to localized action name.
2710 nsresult rv
= GetActionName(aIndex
, name
);
2711 NS_ENSURE_SUCCESS(rv
, rv
);
2713 return GetTranslatedString(name
, aDescription
);
2716 // void doAction(in PRUint8 index)
2718 nsAccessible::DoAction(PRUint8 aIndex
)
2721 return NS_ERROR_INVALID_ARG
;
2724 return NS_ERROR_FAILURE
;
2726 if (GetActionRule(nsAccUtils::State(this)) != eNoAction
) {
2727 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
2728 return DoCommand(content
);
2731 return NS_ERROR_INVALID_ARG
;
2734 /* DOMString getHelp (); */
2735 NS_IMETHODIMP
nsAccessible::GetHelp(nsAString
& _retval
)
2737 return NS_ERROR_NOT_IMPLEMENTED
;
2740 /* nsIAccessible getAccessibleToRight(); */
2741 NS_IMETHODIMP
nsAccessible::GetAccessibleToRight(nsIAccessible
**_retval
)
2743 return NS_ERROR_NOT_IMPLEMENTED
;
2746 /* nsIAccessible getAccessibleToLeft(); */
2747 NS_IMETHODIMP
nsAccessible::GetAccessibleToLeft(nsIAccessible
**_retval
)
2749 return NS_ERROR_NOT_IMPLEMENTED
;
2752 /* nsIAccessible getAccessibleAbove(); */
2753 NS_IMETHODIMP
nsAccessible::GetAccessibleAbove(nsIAccessible
**_retval
)
2755 return NS_ERROR_NOT_IMPLEMENTED
;
2758 /* nsIAccessible getAccessibleBelow(); */
2759 NS_IMETHODIMP
nsAccessible::GetAccessibleBelow(nsIAccessible
**_retval
)
2761 return NS_ERROR_NOT_IMPLEMENTED
;
2764 nsIDOMNode
* nsAccessible::GetAtomicRegion()
2766 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
2767 nsIContent
*loopContent
= content
;
2768 nsAutoString atomic
;
2769 while (loopContent
&& !loopContent
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_atomic
, atomic
)) {
2770 loopContent
= loopContent
->GetParent();
2773 nsCOMPtr
<nsIDOMNode
> atomicRegion
;
2774 if (atomic
.EqualsLiteral("true")) {
2775 atomicRegion
= do_QueryInterface(loopContent
);
2777 return atomicRegion
;
2780 /* nsIAccessible getAccessibleRelated(); */
2781 NS_IMETHODIMP
nsAccessible::GetAccessibleRelated(PRUint32 aRelationType
, nsIAccessible
**aRelated
)
2783 // When adding support for relations, make sure to add them to
2784 // appropriate places in nsAccessibleWrap implementations
2787 // Relationships are defined on the same content node
2788 // that the role would be defined on
2789 nsIContent
*content
= nsCoreUtils::GetRoleContent(mDOMNode
);
2791 return NS_ERROR_FAILURE
; // Node already shut down
2794 nsCOMPtr
<nsIDOMNode
> relatedNode
;
2795 nsAutoString relatedID
;
2797 // Search for the related DOM node according to the specified "relation type"
2798 switch (aRelationType
)
2800 case nsIAccessibleRelation::RELATION_LABEL_FOR
:
2802 if (content
->Tag() == nsAccessibilityAtoms::label
) {
2803 nsIAtom
*relatedIDAttr
= content
->IsNodeOfType(nsINode::eHTML
) ?
2804 nsAccessibilityAtoms::_for
: nsAccessibilityAtoms::control
;
2805 content
->GetAttr(kNameSpaceID_None
, relatedIDAttr
, relatedID
);
2807 if (relatedID
.IsEmpty()) {
2809 do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::aria_labelledby
));
2813 case nsIAccessibleRelation::RELATION_LABELLED_BY
:
2815 if (!content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_labelledby
, relatedID
)) {
2816 relatedNode
= do_QueryInterface(nsCoreUtils::GetLabelContent(content
));
2820 case nsIAccessibleRelation::RELATION_DESCRIBED_BY
:
2822 if (!content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_describedby
, relatedID
)) {
2823 relatedNode
= do_QueryInterface(
2824 nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::control
, nsAccessibilityAtoms::description
));
2828 case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR
:
2831 do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::aria_describedby
));
2833 if (!relatedNode
&& content
->Tag() == nsAccessibilityAtoms::description
&&
2834 content
->IsNodeOfType(nsINode::eXUL
)) {
2835 // This affectively adds an optional control attribute to xul:description,
2836 // which only affects accessibility, by allowing the description to be
2837 // tied to a control.
2838 content
->GetAttr(kNameSpaceID_None
,
2839 nsAccessibilityAtoms::control
, relatedID
);
2843 case nsIAccessibleRelation::RELATION_NODE_CHILD_OF
:
2846 do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::aria_owns
));
2847 if (!relatedNode
&& mRoleMapEntry
&& mRoleMapEntry
->role
== nsIAccessibleRole::ROLE_OUTLINEITEM
) {
2848 // This is an ARIA tree that doesn't use owns, so we need to get the parent the hard way
2849 nsAccUtils::GetARIATreeItemParent(this, content
, aRelated
);
2852 // If accessible is in its own Window then we should provide NODE_CHILD_OF relation
2853 // so that MSAA clients can easily get to true parent instead of getting to oleacc's
2854 // ROLE_WINDOW accessible which will prevent us from going up further (because it is
2855 // system generated and has no idea about the hierarchy above it).
2856 nsIFrame
*frame
= GetFrame();
2858 nsIView
*view
= frame
->GetViewExternal();
2860 nsIScrollableFrame
*scrollFrame
= nsnull
;
2861 CallQueryInterface(frame
, &scrollFrame
);
2862 if (scrollFrame
|| view
->GetWidget()) {
2863 return GetParent(aRelated
);
2869 case nsIAccessibleRelation::RELATION_CONTROLLED_BY
:
2872 do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::aria_controls
));
2875 case nsIAccessibleRelation::RELATION_CONTROLLER_FOR
:
2877 content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_controls
, relatedID
);
2880 case nsIAccessibleRelation::RELATION_FLOWS_TO
:
2882 content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_flowto
, relatedID
);
2885 case nsIAccessibleRelation::RELATION_FLOWS_FROM
:
2888 do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content
, nsAccessibilityAtoms::aria_flowto
));
2891 case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON
:
2893 if (content
->IsNodeOfType(nsINode::eHTML
)) {
2894 // HTML form controls implements nsIFormControl interface.
2895 nsCOMPtr
<nsIFormControl
> control(do_QueryInterface(content
));
2897 nsCOMPtr
<nsIDOMHTMLFormElement
> htmlform
;
2898 control
->GetForm(getter_AddRefs(htmlform
));
2899 nsCOMPtr
<nsIForm
> form(do_QueryInterface(htmlform
));
2901 relatedNode
= do_QueryInterface(form
->GetDefaultSubmitElement());
2905 // In XUL, use first <button default="true" .../> in the document
2906 nsCOMPtr
<nsIDOMXULDocument
> xulDoc
= do_QueryInterface(content
->GetDocument());
2907 nsCOMPtr
<nsIDOMXULButtonElement
> buttonEl
;
2909 nsCOMPtr
<nsIDOMNodeList
> possibleDefaultButtons
;
2910 xulDoc
->GetElementsByAttribute(NS_LITERAL_STRING("default"),
2911 NS_LITERAL_STRING("true"),
2912 getter_AddRefs(possibleDefaultButtons
));
2913 if (possibleDefaultButtons
) {
2915 possibleDefaultButtons
->GetLength(&length
);
2916 nsCOMPtr
<nsIDOMNode
> possibleButton
;
2917 // Check for button in list of default="true" elements
2918 for (PRUint32 count
= 0; count
< length
&& !buttonEl
; count
++) {
2919 possibleDefaultButtons
->Item(count
, getter_AddRefs(possibleButton
));
2920 buttonEl
= do_QueryInterface(possibleButton
);
2923 if (!buttonEl
) { // Check for anonymous accept button in <dialog>
2924 nsCOMPtr
<nsIDOMDocumentXBL
> xblDoc(do_QueryInterface(xulDoc
));
2926 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(xulDoc
);
2927 NS_ASSERTION(domDoc
, "No DOM document");
2928 nsCOMPtr
<nsIDOMElement
> rootEl
;
2929 domDoc
->GetDocumentElement(getter_AddRefs(rootEl
));
2931 nsCOMPtr
<nsIDOMElement
> possibleButtonEl
;
2932 xblDoc
->GetAnonymousElementByAttribute(rootEl
,
2933 NS_LITERAL_STRING("default"),
2934 NS_LITERAL_STRING("true"),
2935 getter_AddRefs(possibleButtonEl
));
2936 buttonEl
= do_QueryInterface(possibleButtonEl
);
2940 relatedNode
= do_QueryInterface(buttonEl
);
2945 case nsIAccessibleRelation::RELATION_MEMBER_OF
:
2947 relatedNode
= GetAtomicRegion();
2951 return NS_ERROR_NOT_IMPLEMENTED
;
2954 if (!relatedID
.IsEmpty()) {
2955 // In some cases we need to get the relatedNode from an ID-style attribute
2956 nsCOMPtr
<nsIDOMDocument
> domDoc
;
2957 mDOMNode
->GetOwnerDocument(getter_AddRefs(domDoc
));
2958 NS_ENSURE_TRUE(domDoc
, NS_ERROR_FAILURE
);
2959 nsCOMPtr
<nsIDOMElement
> relatedEl
;
2960 domDoc
->GetElementById(relatedID
, getter_AddRefs(relatedEl
));
2961 relatedNode
= do_QueryInterface(relatedEl
);
2964 // Return the corresponding accessible if the related DOM node is found
2966 nsCOMPtr
<nsIAccessibilityService
> accService
= GetAccService();
2967 NS_ENSURE_TRUE(accService
, NS_ERROR_FAILURE
);
2968 accService
->GetAccessibleInWeakShell(relatedNode
, mWeakShell
, aRelated
);
2974 nsAccessible::GetRelationsCount(PRUint32
*aCount
)
2976 NS_ENSURE_ARG_POINTER(aCount
);
2979 nsCOMPtr
<nsIArray
> relations
;
2980 nsresult rv
= GetRelations(getter_AddRefs(relations
));
2981 NS_ENSURE_SUCCESS(rv
, rv
);
2983 return relations
->GetLength(aCount
);
2987 nsAccessible::GetRelation(PRUint32 aIndex
, nsIAccessibleRelation
**aRelation
)
2989 NS_ENSURE_ARG_POINTER(aRelation
);
2990 *aRelation
= nsnull
;
2992 nsCOMPtr
<nsIArray
> relations
;
2993 nsresult rv
= GetRelations(getter_AddRefs(relations
));
2994 NS_ENSURE_SUCCESS(rv
, rv
);
2996 nsCOMPtr
<nsIAccessibleRelation
> relation
;
2997 rv
= relations
->QueryElementAt(aIndex
, NS_GET_IID(nsIAccessibleRelation
),
2998 getter_AddRefs(relation
));
3000 // nsIArray::QueryElementAt() returns NS_ERROR_ILLEGAL_VALUE on invalid index.
3001 if (rv
== NS_ERROR_ILLEGAL_VALUE
)
3002 return NS_ERROR_INVALID_ARG
;
3004 NS_ENSURE_SUCCESS(rv
, rv
);
3006 NS_IF_ADDREF(*aRelation
= relation
);
3011 nsAccessible::GetRelations(nsIArray
**aRelations
)
3013 NS_ENSURE_ARG_POINTER(aRelations
);
3015 nsCOMPtr
<nsIMutableArray
> relations
= do_CreateInstance(NS_ARRAY_CONTRACTID
);
3016 NS_ENSURE_TRUE(relations
, NS_ERROR_OUT_OF_MEMORY
);
3018 for (PRUint32 relType
= nsIAccessibleRelation::RELATION_FIRST
;
3019 relType
< nsIAccessibleRelation::RELATION_LAST
;
3021 nsCOMPtr
<nsIAccessible
> accessible
;
3022 GetAccessibleRelated(relType
, getter_AddRefs(accessible
));
3025 nsCOMPtr
<nsIAccessibleRelation
> relation
=
3026 new nsAccessibleRelationWrap(relType
, accessible
);
3027 NS_ENSURE_TRUE(relation
, NS_ERROR_OUT_OF_MEMORY
);
3029 relations
->AppendElement(relation
, PR_FALSE
);
3033 NS_ADDREF(*aRelations
= relations
);
3037 /* void extendSelection (); */
3038 NS_IMETHODIMP
nsAccessible::ExtendSelection()
3040 // XXX Should be implemented, but not high priority
3041 return NS_ERROR_NOT_IMPLEMENTED
;
3044 /* [noscript] void getNativeInterface(out voidPtr aOutAccessible); */
3045 NS_IMETHODIMP
nsAccessible::GetNativeInterface(void **aOutAccessible
)
3047 return NS_ERROR_NOT_IMPLEMENTED
;
3050 void nsAccessible::DoCommandCallback(nsITimer
*aTimer
, void *aClosure
)
3052 NS_ASSERTION(gDoCommandTimer
,
3053 "How did we get here if there was no gDoCommandTimer?");
3054 NS_RELEASE(gDoCommandTimer
);
3056 nsCOMPtr
<nsIContent
> content
=
3057 reinterpret_cast<nsIContent
*>(aClosure
);
3059 nsIDocument
*doc
= content
->GetDocument();
3063 nsCOMPtr
<nsIPresShell
> presShell
= doc
->GetPrimaryShell();
3065 // Scroll into view.
3066 presShell
->ScrollContentIntoView(content
, NS_PRESSHELL_SCROLL_ANYWHERE
,
3067 NS_PRESSHELL_SCROLL_ANYWHERE
);
3069 // Fire mouse down and mouse up events.
3070 PRBool res
= nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN
, presShell
,
3075 nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_UP
, presShell
, content
);
3079 * Use Timer to execute "Click" command of XUL/HTML element (e.g. menuitem, button...).
3081 * When "Click" is to open a "modal" dialog/window, it won't return untill the
3082 * dialog/window is closed. If executing "Click" command directly in
3083 * nsXXXAccessible::DoAction, it will block AT-Tools(e.g. GOK) that invoke
3084 * "action" of mozilla accessibles direclty.
3086 nsresult
nsAccessible::DoCommand(nsIContent
*aContent
)
3088 nsCOMPtr
<nsIContent
> content
= aContent
;
3090 content
= do_QueryInterface(mDOMNode
);
3092 if (gDoCommandTimer
) {
3093 // Already have timer going for another command
3094 NS_WARNING("Doubling up on do command timers doesn't work. This wasn't expected.");
3095 return NS_ERROR_FAILURE
;
3098 nsCOMPtr
<nsITimer
> timer
= do_CreateInstance("@mozilla.org/timer;1");
3100 return NS_ERROR_OUT_OF_MEMORY
;
3103 NS_ADDREF(gDoCommandTimer
= timer
);
3104 return gDoCommandTimer
->InitWithFuncCallback(DoCommandCallback
,
3106 nsITimer::TYPE_ONE_SHOT
);
3109 already_AddRefed
<nsIAccessible
>
3110 nsAccessible::GetNextWithState(nsIAccessible
*aStart
, PRUint32 matchState
)
3112 // Return the next descendant that matches one of the states in matchState
3113 // Uses depth first search
3114 NS_ASSERTION(matchState
, "GetNextWithState() not called with a state to match");
3115 NS_ASSERTION(aStart
, "GetNextWithState() not called with an accessible to start with");
3116 nsCOMPtr
<nsIAccessible
> look
, current
= aStart
;
3118 while (0 == (state
& matchState
)) {
3119 current
->GetFirstChild(getter_AddRefs(look
));
3121 if (current
== this) {
3122 return nsnull
; // At top of subtree
3124 current
->GetNextSibling(getter_AddRefs(look
));
3126 current
->GetParent(getter_AddRefs(look
));
3133 state
= nsAccUtils::State(current
);
3136 nsIAccessible
*returnAccessible
= nsnull
;
3137 current
.swap(returnAccessible
);
3139 return returnAccessible
;
3142 // nsIAccessibleSelectable
3143 NS_IMETHODIMP
nsAccessible::GetSelectedChildren(nsIArray
**aSelectedAccessibles
)
3145 *aSelectedAccessibles
= nsnull
;
3147 nsCOMPtr
<nsIMutableArray
> selectedAccessibles
=
3148 do_CreateInstance(NS_ARRAY_CONTRACTID
);
3149 NS_ENSURE_STATE(selectedAccessibles
);
3151 nsCOMPtr
<nsIAccessible
> selected
= this;
3152 while ((selected
= GetNextWithState(selected
, nsIAccessibleStates::STATE_SELECTED
)) != nsnull
) {
3153 selectedAccessibles
->AppendElement(selected
, PR_FALSE
);
3156 PRUint32 length
= 0;
3157 selectedAccessibles
->GetLength(&length
);
3158 if (length
) { // length of nsIArray containing selected options
3159 *aSelectedAccessibles
= selectedAccessibles
;
3160 NS_ADDREF(*aSelectedAccessibles
);
3166 // return the nth selected descendant nsIAccessible object
3167 NS_IMETHODIMP
nsAccessible::RefSelection(PRInt32 aIndex
, nsIAccessible
**aSelected
)
3169 *aSelected
= nsnull
;
3171 return NS_ERROR_FAILURE
;
3173 nsCOMPtr
<nsIAccessible
> selected
= this;
3175 while (count
++ <= aIndex
) {
3176 selected
= GetNextWithState(selected
, nsIAccessibleStates::STATE_SELECTED
);
3178 return NS_ERROR_FAILURE
; // aIndex out of range
3181 NS_IF_ADDREF(*aSelected
= selected
);
3185 NS_IMETHODIMP
nsAccessible::GetSelectionCount(PRInt32
*aSelectionCount
)
3187 *aSelectionCount
= 0;
3188 nsCOMPtr
<nsIAccessible
> selected
= this;
3189 while ((selected
= GetNextWithState(selected
, nsIAccessibleStates::STATE_SELECTED
)) != nsnull
) {
3190 ++ *aSelectionCount
;
3196 NS_IMETHODIMP
nsAccessible::AddChildToSelection(PRInt32 aIndex
)
3198 // Tree views and other container widgets which may have grandchildren should
3199 // implement a selection methods for their specific interfaces, because being
3200 // able to deal with selection on a per-child basis would not be enough.
3202 NS_ENSURE_TRUE(aIndex
>= 0, NS_ERROR_FAILURE
);
3204 nsCOMPtr
<nsIAccessible
> child
;
3205 GetChildAt(aIndex
, getter_AddRefs(child
));
3207 PRUint32 state
= nsAccUtils::State(child
);
3208 if (!(state
& nsIAccessibleStates::STATE_SELECTABLE
)) {
3212 return child
->SetSelected(PR_TRUE
);
3215 NS_IMETHODIMP
nsAccessible::RemoveChildFromSelection(PRInt32 aIndex
)
3217 // Tree views and other container widgets which may have grandchildren should
3218 // implement a selection methods for their specific interfaces, because being
3219 // able to deal with selection on a per-child basis would not be enough.
3221 NS_ENSURE_TRUE(aIndex
>= 0, NS_ERROR_FAILURE
);
3223 nsCOMPtr
<nsIAccessible
> child
;
3224 GetChildAt(aIndex
, getter_AddRefs(child
));
3226 PRUint32 state
= nsAccUtils::State(child
);
3227 if (!(state
& nsIAccessibleStates::STATE_SELECTED
)) {
3231 return child
->SetSelected(PR_FALSE
);
3234 NS_IMETHODIMP
nsAccessible::IsChildSelected(PRInt32 aIndex
, PRBool
*aIsSelected
)
3236 // Tree views and other container widgets which may have grandchildren should
3237 // implement a selection methods for their specific interfaces, because being
3238 // able to deal with selection on a per-child basis would not be enough.
3240 *aIsSelected
= PR_FALSE
;
3241 NS_ENSURE_TRUE(aIndex
>= 0, NS_ERROR_FAILURE
);
3243 nsCOMPtr
<nsIAccessible
> child
;
3244 GetChildAt(aIndex
, getter_AddRefs(child
));
3246 PRUint32 state
= nsAccUtils::State(child
);
3247 if (state
& nsIAccessibleStates::STATE_SELECTED
) {
3248 *aIsSelected
= PR_TRUE
;
3253 NS_IMETHODIMP
nsAccessible::ClearSelection()
3255 nsCOMPtr
<nsIAccessible
> selected
= this;
3256 while ((selected
= GetNextWithState(selected
, nsIAccessibleStates::STATE_SELECTED
)) != nsnull
) {
3257 selected
->SetSelected(PR_FALSE
);
3262 NS_IMETHODIMP
nsAccessible::SelectAllSelection(PRBool
*_retval
)
3264 nsCOMPtr
<nsIAccessible
> selectable
= this;
3265 while ((selectable
= GetNextWithState(selectable
, nsIAccessibleStates::STATE_SELECTED
)) != nsnull
) {
3266 selectable
->SetSelected(PR_TRUE
);
3271 // nsIAccessibleHyperLink
3272 // Because of new-atk design, any embedded object in text can implement
3273 // nsIAccessibleHyperLink, which helps determine where it is located
3274 // within containing text
3276 // readonly attribute long nsIAccessibleHyperLink::anchorCount
3278 nsAccessible::GetAnchorCount(PRInt32
*aAnchorCount
)
3280 NS_ENSURE_ARG_POINTER(aAnchorCount
);
3285 // readonly attribute long nsIAccessibleHyperLink::startIndex
3287 nsAccessible::GetStartIndex(PRInt32
*aStartIndex
)
3289 NS_ENSURE_ARG_POINTER(aStartIndex
);
3292 return GetLinkOffset(aStartIndex
, &endIndex
);
3295 // readonly attribute long nsIAccessibleHyperLink::endIndex
3297 nsAccessible::GetEndIndex(PRInt32
*aEndIndex
)
3299 NS_ENSURE_ARG_POINTER(aEndIndex
);
3302 return GetLinkOffset(&startIndex
, aEndIndex
);
3306 nsAccessible::GetURI(PRInt32 aIndex
, nsIURI
**aURI
)
3308 NS_ENSURE_ARG_POINTER(aURI
);
3312 return NS_ERROR_INVALID_ARG
;
3314 // Check if it's a simple xlink.
3315 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
3316 if (nsCoreUtils::IsXLink(content
)) {
3318 content
->GetAttr(kNameSpaceID_XLink
, nsAccessibilityAtoms::href
, href
);
3320 nsCOMPtr
<nsIURI
> baseURI
= content
->GetBaseURI();
3321 nsCOMPtr
<nsIDocument
> document
= content
->GetOwnerDoc();
3322 return NS_NewURI(aURI
, href
,
3323 document
? document
->GetDocumentCharacterSet().get() : nsnull
,
3332 nsAccessible::GetAnchor(PRInt32 aIndex
,
3333 nsIAccessible
**aAccessible
)
3335 NS_ENSURE_ARG_POINTER(aAccessible
);
3336 *aAccessible
= nsnull
;
3339 return NS_ERROR_INVALID_ARG
;
3341 *aAccessible
= this;
3346 // readonly attribute boolean nsIAccessibleHyperLink::valid
3348 nsAccessible::GetValid(PRBool
*aValid
)
3350 NS_ENSURE_ARG_POINTER(aValid
);
3351 PRUint32 state
= nsAccUtils::State(this);
3352 *aValid
= (0 == (state
& nsIAccessibleStates::STATE_INVALID
));
3353 // XXX In order to implement this we would need to follow every link
3354 // Perhaps we can get information about invalid links from the cache
3355 // In the mean time authors can use role="link" aria-invalid="true"
3356 // to force it for links they internally know to be invalid
3360 // readonly attribute boolean nsIAccessibleHyperLink::selected
3362 nsAccessible::GetSelected(PRBool
*aSelected
)
3364 NS_ENSURE_ARG_POINTER(aSelected
);
3365 *aSelected
= (gLastFocusedNode
== mDOMNode
);
3369 nsresult
nsAccessible::GetLinkOffset(PRInt32
* aStartOffset
, PRInt32
* aEndOffset
)
3371 *aStartOffset
= *aEndOffset
= 0;
3372 nsCOMPtr
<nsIAccessible
> parent(GetParent());
3374 return NS_ERROR_FAILURE
;
3377 nsCOMPtr
<nsIAccessible
> accessible
, nextSibling
;
3378 PRInt32 characterCount
= 0;
3379 parent
->GetFirstChild(getter_AddRefs(accessible
));
3381 while (accessible
) {
3382 if (nsAccUtils::IsText(accessible
))
3383 characterCount
+= nsAccUtils::TextLength(accessible
);
3385 else if (accessible
== this) {
3386 *aStartOffset
= characterCount
;
3387 *aEndOffset
= characterCount
+ 1;
3393 accessible
->GetNextSibling(getter_AddRefs(nextSibling
));
3394 accessible
.swap(nextSibling
);
3397 return NS_ERROR_FAILURE
;
3401 nsAccessible::AppendTextTo(nsAString
& aText
, PRUint32 aStartOffset
, PRUint32 aLength
)
3406 ////////////////////////////////////////////////////////////////////////////////
3407 // nsAccessible public methods
3410 nsAccessible::GetARIAName(nsAString
& aName
)
3412 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
3416 // First check for label override via aria-label property
3418 if (content
->GetAttr(kNameSpaceID_None
, nsAccessibilityAtoms::aria_label
, label
)) {
3423 // Second check for label override via aria-labelledby relationship
3424 nsresult rv
= GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby
, label
);
3425 if (NS_SUCCEEDED(rv
))
3432 nsAccessible::GetNameInternal(nsAString
& aName
)
3434 nsCOMPtr
<nsIContent
> content
= nsCoreUtils::GetRoleContent(mDOMNode
);
3438 if (content
->IsNodeOfType(nsINode::eHTML
))
3439 return GetHTMLName(aName
);
3441 if (content
->IsNodeOfType(nsINode::eXUL
))
3442 return GetXULName(aName
);
3447 ////////////////////////////////////////////////////////////////////////////////
3448 // nsAccessible private methods
3450 already_AddRefed
<nsIAccessible
>
3451 nsAccessible::GetFirstAvailableAccessible(nsIDOMNode
*aStartNode
, PRBool aRequireLeaf
)
3453 nsIAccessibilityService
*accService
= GetAccService();
3454 nsCOMPtr
<nsIAccessible
> accessible
;
3455 nsCOMPtr
<nsIDOMTreeWalker
> walker
;
3456 nsCOMPtr
<nsIDOMNode
> currentNode(aStartNode
);
3458 while (currentNode
) {
3459 accService
->GetAccessibleInWeakShell(currentNode
, mWeakShell
, getter_AddRefs(accessible
)); // AddRef'd
3460 if (accessible
&& (!aRequireLeaf
|| nsAccUtils::IsLeaf(accessible
))) {
3461 nsIAccessible
*retAccessible
= accessible
;
3462 NS_ADDREF(retAccessible
);
3463 return retAccessible
;
3466 // Instantiate walker lazily since we won't need it in 90% of the cases
3467 // where the first DOM node we're given provides an accessible
3468 nsCOMPtr
<nsIDOMDocument
> document
;
3469 currentNode
->GetOwnerDocument(getter_AddRefs(document
));
3470 nsCOMPtr
<nsIDOMDocumentTraversal
> trav
= do_QueryInterface(document
);
3471 NS_ASSERTION(trav
, "No DOM document traversal for document");
3472 NS_ENSURE_TRUE(trav
, nsnull
);
3473 trav
->CreateTreeWalker(mDOMNode
, nsIDOMNodeFilter::SHOW_ELEMENT
| nsIDOMNodeFilter::SHOW_TEXT
,
3474 nsnull
, PR_FALSE
, getter_AddRefs(walker
));
3475 NS_ENSURE_TRUE(walker
, nsnull
);
3476 walker
->SetCurrentNode(currentNode
);
3479 walker
->NextNode(getter_AddRefs(currentNode
));
3485 PRBool
nsAccessible::CheckVisibilityInParentChain(nsIDocument
* aDocument
, nsIView
* aView
)
3487 nsIDocument
* document
= aDocument
;
3488 nsIView
* view
= aView
;
3489 // both view chain and widget chain are broken between chrome and content
3490 while (document
!= nsnull
) {
3491 while (view
!= nsnull
) {
3492 if (view
->GetVisibility() == nsViewVisibility_kHide
) {
3495 view
= view
->GetParent();
3498 nsIDocument
* parentDoc
= document
->GetParentDocument();
3499 if (parentDoc
!= nsnull
) {
3500 nsIContent
* content
= parentDoc
->FindContentForSubDocument(document
);
3501 if (content
!= nsnull
) {
3502 nsIPresShell
* shell
= parentDoc
->GetPrimaryShell();
3506 nsIFrame
* frame
= shell
->GetPrimaryFrameFor(content
);
3507 while (frame
!= nsnull
&& !frame
->HasView()) {
3508 frame
= frame
->GetParent();
3511 if (frame
!= nsnull
) {
3512 view
= frame
->GetViewExternal();
3517 document
= parentDoc
;
3524 nsAccessible::GetAttrValue(nsIAtom
*aProperty
, double *aValue
)
3526 NS_ENSURE_ARG_POINTER(aValue
);
3530 return NS_ERROR_FAILURE
; // Node already shut down
3532 if (!mRoleMapEntry
|| mRoleMapEntry
->valueRule
== eNoValue
)
3533 return NS_OK_NO_ARIA_VALUE
;
3535 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
3536 NS_ENSURE_STATE(content
);
3538 PRInt32 result
= NS_OK
;
3540 if (content
->GetAttr(kNameSpaceID_None
, aProperty
, value
))
3541 *aValue
= value
.ToFloat(&result
);
3547 nsAccessible::GetActionRule(PRUint32 aStates
)
3549 if (aStates
& nsIAccessibleStates::STATE_UNAVAILABLE
)
3552 // Check if it's simple xlink.
3553 nsCOMPtr
<nsIContent
> content(do_QueryInterface(mDOMNode
));
3554 if (nsCoreUtils::IsXLink(content
))
3557 // Has registered 'click' event handler.
3558 PRBool isOnclick
= nsCoreUtils::HasListener(content
,
3559 NS_LITERAL_STRING("click"));
3562 return eClickAction
;
3564 // Get an action based on ARIA role.
3566 return mRoleMapEntry
->actionRule
;