Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / accessible / src / base / nsAccessNode.cpp
blob2366fef533f66b0af795ff42f378ce168fa3e7c3
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
13 * License.
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) 2003
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Original Author: Aaron Leventhal (aaronl@netscape.com)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsAccessNode.h"
40 #include "nsIAccessible.h"
41 #include "nsAccessibilityAtoms.h"
42 #include "nsHashtable.h"
43 #include "nsIAccessibilityService.h"
44 #include "nsIAccessibleDocument.h"
45 #include "nsPIAccessibleDocument.h"
46 #include "nsIDocShell.h"
47 #include "nsIDocShellTreeItem.h"
48 #include "nsIDocument.h"
49 #include "nsIDOMCSSStyleDeclaration.h"
50 #include "nsIDOMCSSPrimitiveValue.h"
51 #include "nsIDOMDocument.h"
52 #include "nsIDOMElement.h"
53 #include "nsIDOMHTMLDocument.h"
54 #include "nsIDOMHTMLElement.h"
55 #include "nsIDOMNSDocument.h"
56 #include "nsIDOMNSHTMLElement.h"
57 #include "nsIDOMWindow.h"
58 #include "nsPIDOMWindow.h"
59 #include "nsIInterfaceRequestorUtils.h"
60 #include "nsIFrame.h"
61 #include "nsIPrefService.h"
62 #include "nsIPrefBranch.h"
63 #include "nsPresContext.h"
64 #include "nsIPresShell.h"
65 #include "nsIServiceManager.h"
66 #include "nsIStringBundle.h"
67 #include "nsITimer.h"
68 #include "nsRootAccessible.h"
69 #include "nsIFocusController.h"
70 #include "nsIObserverService.h"
72 #ifdef MOZ_ACCESSIBILITY_ATK
73 #include "nsAppRootAccessible.h"
74 #else
75 #include "nsApplicationAccessibleWrap.h"
76 #endif
78 /* For documentation of the accessibility architecture,
79 * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
82 nsIStringBundle *nsAccessNode::gStringBundle = 0;
83 nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
84 nsITimer *nsAccessNode::gDoCommandTimer = 0;
85 nsIDOMNode *nsAccessNode::gLastFocusedNode = 0;
86 PRBool nsAccessNode::gIsAccessibilityActive = PR_FALSE;
87 PRBool nsAccessNode::gIsShuttingDownApp = PR_FALSE;
88 PRBool nsAccessNode::gIsCacheDisabled = PR_FALSE;
89 PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
90 nsAccessNodeHashtable nsAccessNode::gGlobalDocAccessibleCache;
92 nsApplicationAccessibleWrap *nsAccessNode::gApplicationAccessible = nsnull;
94 nsIAccessibilityService *nsAccessNode::sAccService = nsnull;
95 nsIAccessibilityService *nsAccessNode::GetAccService()
97 if (!gIsAccessibilityActive)
98 return nsnull;
100 if (!sAccService) {
101 nsresult rv = CallGetService("@mozilla.org/accessibilityService;1",
102 &sAccService);
103 NS_ASSERTION(NS_SUCCEEDED(rv), "No accessibility service");
106 return sAccService;
110 * Class nsAccessNode
113 ////////////////////////////////////////////////////////////////////////////////
114 // nsAccessible. nsISupports
116 NS_IMPL_CYCLE_COLLECTION_0(nsAccessNode)
118 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessNode)
119 NS_INTERFACE_MAP_ENTRY(nsIAccessNode)
120 NS_INTERFACE_MAP_ENTRY(nsAccessNode)
121 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessNode)
122 NS_INTERFACE_MAP_END
124 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsAccessNode, nsIAccessNode)
125 NS_IMPL_CYCLE_COLLECTING_RELEASE_FULL(nsAccessNode, nsIAccessNode,
126 LastRelease())
128 ////////////////////////////////////////////////////////////////////////////////
129 // nsAccessible. Constructor
131 nsAccessNode::nsAccessNode(nsIDOMNode *aNode, nsIWeakReference* aShell):
132 mDOMNode(aNode), mWeakShell(aShell)
134 #ifdef DEBUG_A11Y
135 mIsInitialized = PR_FALSE;
136 #endif
139 //-----------------------------------------------------
140 // destruction
141 //-----------------------------------------------------
142 nsAccessNode::~nsAccessNode()
144 NS_ASSERTION(!mWeakShell, "LastRelease was never called!?!");
147 void nsAccessNode::LastRelease()
149 // First cleanup if needed...
150 if (mWeakShell) {
151 Shutdown();
152 NS_ASSERTION(!mWeakShell, "A Shutdown() impl forgot to call its parent's Shutdown?");
154 // ... then die.
155 NS_DELETEXPCOM(this);
158 nsresult
159 nsAccessNode::Init()
161 // We have to put this here, instead of constructor, otherwise
162 // we don't have the virtual GetUniqueID() method for the hash key.
163 // We need that for accessibles that don't have DOM nodes
165 #ifdef DEBUG_A11Y
166 NS_ASSERTION(!mIsInitialized, "Initialized twice!");
167 #endif
168 nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
169 if (!docAccessible) {
170 // No doc accessible yet for this node's document.
171 // There was probably an accessible event fired before the
172 // current document was ever asked for by the assistive technology.
173 // Create a doc accessible so we can cache this node
174 nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
175 if (presShell) {
176 nsCOMPtr<nsIDOMNode> docNode(do_QueryInterface(presShell->GetDocument()));
177 if (docNode) {
178 nsIAccessibilityService *accService = GetAccService();
179 if (accService) {
180 nsCOMPtr<nsIAccessible> accessible;
181 accService->GetAccessibleInShell(docNode, presShell,
182 getter_AddRefs(accessible));
183 docAccessible = do_QueryInterface(accessible);
187 NS_ASSERTION(docAccessible, "Cannot cache new nsAccessNode");
188 if (!docAccessible) {
189 return NS_ERROR_FAILURE;
193 void* uniqueID;
194 GetUniqueID(&uniqueID);
195 nsCOMPtr<nsPIAccessibleDocument> privateDocAccessible =
196 do_QueryInterface(docAccessible);
197 NS_ASSERTION(privateDocAccessible, "No private docaccessible for docaccessible");
198 privateDocAccessible->CacheAccessNode(uniqueID, this);
200 // Make sure an ancestor in real content is cached
201 // so that nsDocAccessible::RefreshNodes() can find the anonymous subtree to release when
202 // the root node goes away
203 nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
204 if (content && content->IsInAnonymousSubtree()) {
205 // Specific examples of where this is used: <input type="file"> and <xul:findbar>
206 nsCOMPtr<nsIAccessible> parentAccessible;
207 docAccessible->GetAccessibleInParentChain(mDOMNode, PR_TRUE, getter_AddRefs(parentAccessible));
208 if (parentAccessible) {
209 PRInt32 childCountUnused;
210 parentAccessible->GetChildCount(&childCountUnused);
214 #ifdef DEBUG_A11Y
215 mIsInitialized = PR_TRUE;
216 #endif
218 return NS_OK;
222 nsresult
223 nsAccessNode::Shutdown()
225 mDOMNode = nsnull;
226 mWeakShell = nsnull;
228 return NS_OK;
231 NS_IMETHODIMP nsAccessNode::GetUniqueID(void **aUniqueID)
233 *aUniqueID = static_cast<void*>(mDOMNode);
234 return NS_OK;
237 NS_IMETHODIMP nsAccessNode::GetOwnerWindow(void **aWindow)
239 *aWindow = nsnull;
240 nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
241 if (!docAccessible)
242 return NS_ERROR_FAILURE; // This node or doc accessible is shut down
243 return docAccessible->GetWindowHandle(aWindow);
246 already_AddRefed<nsApplicationAccessibleWrap>
247 nsAccessNode::GetApplicationAccessible()
249 if (!gIsAccessibilityActive) {
250 return nsnull;
253 if (!gApplicationAccessible) {
254 nsApplicationAccessibleWrap::PreCreate();
256 gApplicationAccessible = new nsApplicationAccessibleWrap();
257 if (!gApplicationAccessible)
258 return nsnull;
260 // Addref on create. Will Release in ShutdownXPAccessibility()
261 NS_ADDREF(gApplicationAccessible);
263 nsresult rv = gApplicationAccessible->Init();
264 if (NS_FAILED(rv)) {
265 NS_RELEASE(gApplicationAccessible);
266 gApplicationAccessible = nsnull;
267 return nsnull;
271 NS_ADDREF(gApplicationAccessible); // Addref because we're a getter
272 return gApplicationAccessible;
275 void nsAccessNode::InitXPAccessibility()
277 if (gIsAccessibilityActive) {
278 return;
281 nsCOMPtr<nsIStringBundleService> stringBundleService =
282 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
283 if (stringBundleService) {
284 // Static variables are released in ShutdownAllXPAccessibility();
285 stringBundleService->CreateBundle(ACCESSIBLE_BUNDLE_URL,
286 &gStringBundle);
287 stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL,
288 &gKeyStringBundle);
291 nsAccessibilityAtoms::AddRefAtoms();
293 gGlobalDocAccessibleCache.Init(4);
295 nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
296 if (prefBranch) {
297 prefBranch->GetBoolPref("accessibility.disablecache", &gIsCacheDisabled);
298 prefBranch->GetBoolPref("browser.formfill.enable", &gIsFormFillEnabled);
301 gIsAccessibilityActive = PR_TRUE;
302 NotifyA11yInitOrShutdown();
305 void nsAccessNode::NotifyA11yInitOrShutdown()
307 nsCOMPtr<nsIObserverService> obsService =
308 do_GetService("@mozilla.org/observer-service;1");
309 NS_ASSERTION(obsService, "No observer service to notify of a11y init/shutdown");
310 if (obsService) {
311 static const PRUnichar kInitIndicator[] = { '1', 0 };
312 static const PRUnichar kShutdownIndicator[] = { '0', 0 };
313 obsService->NotifyObservers(nsnull, "a11y-init-or-shutdown",
314 gIsAccessibilityActive ? kInitIndicator : kShutdownIndicator);
318 void nsAccessNode::ShutdownXPAccessibility()
320 // Called by nsAccessibilityService::Shutdown()
321 // which happens when xpcom is shutting down
322 // at exit of program
324 if (!gIsAccessibilityActive) {
325 return;
327 gIsShuttingDownApp = PR_TRUE;
329 NS_IF_RELEASE(gStringBundle);
330 NS_IF_RELEASE(gKeyStringBundle);
331 NS_IF_RELEASE(gDoCommandTimer);
332 NS_IF_RELEASE(gLastFocusedNode);
333 NS_IF_RELEASE(sAccService);
335 nsApplicationAccessibleWrap::Unload();
336 ClearCache(gGlobalDocAccessibleCache);
338 // Release gApplicationAccessible after everything else is shutdown
339 // so we don't accidently create it again while tearing down root accessibles
340 NS_IF_RELEASE(gApplicationAccessible);
341 gApplicationAccessible = nsnull;
343 gIsAccessibilityActive = PR_FALSE;
344 NotifyA11yInitOrShutdown();
347 already_AddRefed<nsIPresShell> nsAccessNode::GetPresShell()
349 nsIPresShell *presShell = nsnull;
350 if (mWeakShell)
351 CallQueryReferent(mWeakShell.get(), &presShell);
352 if (!presShell) {
353 if (mWeakShell) {
354 // If our pres shell has died, but we're still holding onto
355 // a weak reference, our accessibles are no longer relevant
356 // and should be shut down
357 Shutdown();
359 return nsnull;
361 return presShell;
364 nsPresContext* nsAccessNode::GetPresContext()
366 nsCOMPtr<nsIPresShell> presShell(GetPresShell());
367 if (!presShell) {
368 return nsnull;
370 return presShell->GetPresContext();
373 already_AddRefed<nsIAccessibleDocument> nsAccessNode::GetDocAccessible()
375 return GetDocAccessibleFor(mWeakShell); // Addref'd
378 already_AddRefed<nsRootAccessible> nsAccessNode::GetRootAccessible()
380 nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
381 nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
382 NS_ASSERTION(docShellTreeItem, "No docshell tree item for mDOMNode");
383 if (!docShellTreeItem) {
384 return nsnull;
386 nsCOMPtr<nsIDocShellTreeItem> root;
387 docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
388 NS_ASSERTION(root, "No root content tree item");
389 if (!root) {
390 return nsnull;
393 nsCOMPtr<nsIAccessibleDocument> accDoc = GetDocAccessibleFor(root);
394 if (!accDoc) {
395 return nsnull;
398 // nsRootAccessible has a special QI
399 // that let us get that concrete type directly.
400 nsRootAccessible* rootAccessible;
401 accDoc->QueryInterface(NS_GET_IID(nsRootAccessible), (void**)&rootAccessible); // addrefs
402 return rootAccessible;
405 nsIFrame*
406 nsAccessNode::GetFrame()
408 nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
409 if (!shell)
410 return nsnull;
412 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
413 return content ? shell->GetPrimaryFrameFor(content) : nsnull;
416 NS_IMETHODIMP
417 nsAccessNode::GetDOMNode(nsIDOMNode **aNode)
419 NS_IF_ADDREF(*aNode = mDOMNode);
420 return NS_OK;
423 NS_IMETHODIMP
424 nsAccessNode::GetNumChildren(PRInt32 *aNumChildren)
426 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
428 if (!content) {
429 *aNumChildren = 0;
431 return NS_ERROR_NULL_POINTER;
434 *aNumChildren = content->GetChildCount();
436 return NS_OK;
439 NS_IMETHODIMP
440 nsAccessNode::GetAccessibleDocument(nsIAccessibleDocument **aDocAccessible)
442 *aDocAccessible = GetDocAccessibleFor(mWeakShell).get();
443 return NS_OK;
446 NS_IMETHODIMP
447 nsAccessNode::GetInnerHTML(nsAString& aInnerHTML)
449 aInnerHTML.Truncate();
451 nsCOMPtr<nsIDOMNSHTMLElement> domNSElement(do_QueryInterface(mDOMNode));
452 NS_ENSURE_TRUE(domNSElement, NS_ERROR_NULL_POINTER);
454 return domNSElement->GetInnerHTML(aInnerHTML);
457 NS_IMETHODIMP
458 nsAccessNode::ScrollTo(PRUint32 aScrollType)
460 if (IsDefunct())
461 return NS_ERROR_FAILURE;
463 nsCOMPtr<nsIPresShell> shell(GetPresShell());
464 NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
466 nsIFrame *frame = GetFrame();
467 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
469 nsCOMPtr<nsIContent> content = frame->GetContent();
470 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
472 PRInt16 vPercent, hPercent;
473 nsCoreUtils::ConvertScrollTypeToPercents(aScrollType, &vPercent, &hPercent);
474 return shell->ScrollContentIntoView(content, vPercent, hPercent);
477 NS_IMETHODIMP
478 nsAccessNode::ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY)
480 nsIFrame *frame = GetFrame();
481 if (!frame)
482 return NS_ERROR_FAILURE;
484 nsIntPoint coords;
485 nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
486 this, &coords);
487 NS_ENSURE_SUCCESS(rv, rv);
489 nsIFrame *parentFrame = frame;
490 while ((parentFrame = parentFrame->GetParent()))
491 nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
493 return NS_OK;
496 nsresult
497 nsAccessNode::MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode)
499 *aAccessNode = nsnull;
501 nsIAccessibilityService *accService = GetAccService();
502 NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
504 nsCOMPtr<nsIAccessNode> accessNode;
505 accService->GetCachedAccessNode(aNode, mWeakShell, getter_AddRefs(accessNode));
507 if (!accessNode) {
508 nsCOMPtr<nsIAccessible> accessible;
509 accService->GetAccessibleInWeakShell(aNode, mWeakShell,
510 getter_AddRefs(accessible));
512 accessNode = do_QueryInterface(accessible);
515 if (accessNode) {
516 NS_ADDREF(*aAccessNode = accessNode);
517 return NS_OK;
520 nsAccessNode *newAccessNode = new nsAccessNode(aNode, mWeakShell);
521 if (!newAccessNode) {
522 return NS_ERROR_OUT_OF_MEMORY;
525 NS_ADDREF(*aAccessNode = newAccessNode);
526 newAccessNode->Init();
528 return NS_OK;
531 NS_IMETHODIMP
532 nsAccessNode::GetFirstChildNode(nsIAccessNode **aAccessNode)
534 NS_ENSURE_ARG_POINTER(aAccessNode);
535 *aAccessNode = nsnull;
536 NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
538 nsCOMPtr<nsIDOMNode> domNode;
539 mDOMNode->GetFirstChild(getter_AddRefs(domNode));
541 return domNode ? MakeAccessNode(domNode, aAccessNode) : NS_OK;
544 NS_IMETHODIMP
545 nsAccessNode::GetLastChildNode(nsIAccessNode **aAccessNode)
547 NS_ENSURE_ARG_POINTER(aAccessNode);
548 *aAccessNode = nsnull;
549 NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
551 nsCOMPtr<nsIDOMNode> domNode;
552 mDOMNode->GetLastChild(getter_AddRefs(domNode));
554 return domNode ? MakeAccessNode(domNode, aAccessNode) : NS_OK;
557 NS_IMETHODIMP
558 nsAccessNode::GetParentNode(nsIAccessNode **aAccessNode)
560 NS_ENSURE_ARG_POINTER(aAccessNode);
561 *aAccessNode = nsnull;
562 NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
564 nsCOMPtr<nsIDOMNode> domNode;
565 mDOMNode->GetParentNode(getter_AddRefs(domNode));
567 return domNode ? MakeAccessNode(domNode, aAccessNode) : NS_OK;
570 NS_IMETHODIMP
571 nsAccessNode::GetPreviousSiblingNode(nsIAccessNode **aAccessNode)
573 NS_ENSURE_ARG_POINTER(aAccessNode);
574 *aAccessNode = nsnull;
575 NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
577 nsCOMPtr<nsIDOMNode> domNode;
578 mDOMNode->GetPreviousSibling(getter_AddRefs(domNode));
580 return domNode ? MakeAccessNode(domNode, aAccessNode) : NS_OK;
583 NS_IMETHODIMP
584 nsAccessNode::GetNextSiblingNode(nsIAccessNode **aAccessNode)
586 NS_ENSURE_ARG_POINTER(aAccessNode);
587 *aAccessNode = nsnull;
588 NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
590 nsCOMPtr<nsIDOMNode> domNode;
591 mDOMNode->GetNextSibling(getter_AddRefs(domNode));
593 return domNode ? MakeAccessNode(domNode, aAccessNode) : NS_OK;
596 NS_IMETHODIMP
597 nsAccessNode::GetChildNodeAt(PRInt32 aChildNum, nsIAccessNode **aAccessNode)
599 NS_ENSURE_ARG_POINTER(aAccessNode);
600 *aAccessNode = nsnull;
602 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
603 NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
605 nsCOMPtr<nsIDOMNode> domNode =
606 do_QueryInterface(content->GetChildAt(aChildNum));
608 return domNode ? MakeAccessNode(domNode, aAccessNode) : NS_OK;
611 NS_IMETHODIMP
612 nsAccessNode::GetComputedStyleValue(const nsAString& aPseudoElt,
613 const nsAString& aPropertyName,
614 nsAString& aValue)
616 if (IsDefunct())
617 return NS_ERROR_FAILURE;
619 nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl;
620 nsCoreUtils::GetComputedStyleDeclaration(aPseudoElt, mDOMNode,
621 getter_AddRefs(styleDecl));
622 NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
624 return styleDecl->GetPropertyValue(aPropertyName, aValue);
627 NS_IMETHODIMP
628 nsAccessNode::GetComputedStyleCSSValue(const nsAString& aPseudoElt,
629 const nsAString& aPropertyName,
630 nsIDOMCSSPrimitiveValue **aCSSValue)
632 NS_ENSURE_ARG_POINTER(aCSSValue);
633 *aCSSValue = nsnull;
635 if (IsDefunct())
636 return NS_ERROR_FAILURE;
638 nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl;
639 nsCoreUtils::GetComputedStyleDeclaration(aPseudoElt, mDOMNode,
640 getter_AddRefs(styleDecl));
641 NS_ENSURE_STATE(styleDecl);
643 nsCOMPtr<nsIDOMCSSValue> cssValue;
644 styleDecl->GetPropertyCSSValue(aPropertyName, getter_AddRefs(cssValue));
645 NS_ENSURE_TRUE(cssValue, NS_ERROR_FAILURE);
647 return CallQueryInterface(cssValue, aCSSValue);
650 /***************** Hashtable of nsIAccessNode's *****************/
652 already_AddRefed<nsIAccessibleDocument>
653 nsAccessNode::GetDocAccessibleFor(nsIDocument *aDocument)
655 if (!aDocument) {
656 return nsnull;
659 nsIAccessibleDocument *docAccessible = nsnull;
660 nsCOMPtr<nsIAccessNode> accessNode;
661 gGlobalDocAccessibleCache.Get(static_cast<void*>(aDocument),
662 getter_AddRefs(accessNode));
663 if (accessNode) {
664 CallQueryInterface(accessNode, &docAccessible);
666 return docAccessible;
669 already_AddRefed<nsIAccessibleDocument>
670 nsAccessNode::GetDocAccessibleFor(nsIWeakReference *aWeakShell)
672 nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(aWeakShell));
673 if (!presShell) {
674 return nsnull;
677 return nsAccessNode::GetDocAccessibleFor(presShell->GetDocument());
680 already_AddRefed<nsIAccessibleDocument>
681 nsAccessNode::GetDocAccessibleFor(nsIDocShellTreeItem *aContainer,
682 PRBool aCanCreate)
684 if (!aCanCreate) {
685 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
686 NS_ASSERTION(docShell, "This method currently only supports docshells");
687 nsCOMPtr<nsIPresShell> presShell;
688 docShell->GetPresShell(getter_AddRefs(presShell));
689 return presShell ? GetDocAccessibleFor(presShell->GetDocument()) : nsnull;
692 nsCOMPtr<nsIDOMNode> node = nsCoreUtils::GetDOMNodeForContainer(aContainer);
693 if (!node) {
694 return nsnull;
697 nsCOMPtr<nsIAccessible> accessible;
698 GetAccService()->GetAccessibleFor(node, getter_AddRefs(accessible));
699 nsIAccessibleDocument *docAccessible = nsnull;
700 if (accessible) {
701 CallQueryInterface(accessible, &docAccessible);
703 return docAccessible;
706 already_AddRefed<nsIAccessibleDocument>
707 nsAccessNode::GetDocAccessibleFor(nsIDOMNode *aNode)
709 nsCOMPtr<nsIPresShell> eventShell = nsCoreUtils::GetPresShellFor(aNode);
710 if (eventShell) {
711 return GetDocAccessibleFor(eventShell->GetDocument());
714 nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
715 if (doc) {
716 return GetDocAccessibleFor(doc);
719 return nsnull;
722 void
723 nsAccessNode::PutCacheEntry(nsAccessNodeHashtable& aCache,
724 void* aUniqueID,
725 nsIAccessNode *aAccessNode)
727 #ifdef DEBUG_A11Y
728 nsCOMPtr<nsIAccessNode> oldAccessNode;
729 GetCacheEntry(aCache, aUniqueID, getter_AddRefs(oldAccessNode));
730 NS_ASSERTION(!oldAccessNode, "This cache entry shouldn't exist already");
731 #endif
732 aCache.Put(aUniqueID, aAccessNode);
735 void
736 nsAccessNode::GetCacheEntry(nsAccessNodeHashtable& aCache,
737 void* aUniqueID,
738 nsIAccessNode **aAccessNode)
740 aCache.Get(aUniqueID, aAccessNode); // AddRefs for us
743 PLDHashOperator nsAccessNode::ClearCacheEntry(const void* aKey, nsCOMPtr<nsIAccessNode>& aAccessNode, void* aUserArg)
745 NS_ASSERTION(aAccessNode, "Calling ClearCacheEntry with a NULL pointer!");
746 if (aAccessNode) {
747 nsRefPtr<nsAccessNode> accessNode =
748 nsAccUtils::QueryAccessNode(aAccessNode);
749 accessNode->Shutdown();
752 return PL_DHASH_REMOVE;
755 void
756 nsAccessNode::ClearCache(nsAccessNodeHashtable& aCache)
758 aCache.Enumerate(ClearCacheEntry, nsnull);
761 already_AddRefed<nsIDOMNode> nsAccessNode::GetCurrentFocus()
763 nsCOMPtr<nsIPresShell> shell = nsCoreUtils::GetPresShellFor(mDOMNode);
764 NS_ENSURE_TRUE(shell, nsnull);
765 nsCOMPtr<nsIDocument> doc = shell->GetDocument();
766 NS_ENSURE_TRUE(doc, nsnull);
768 nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(doc->GetWindow()));
769 if (!privateDOMWindow) {
770 return nsnull;
772 nsIFocusController *focusController = privateDOMWindow->GetRootFocusController();
773 if (!focusController) {
774 return nsnull;
776 nsCOMPtr<nsIDOMElement> focusedElement;
777 focusController->GetFocusedElement(getter_AddRefs(focusedElement));
778 nsIDOMNode *focusedNode = nsnull;
779 if (!focusedElement) {
780 // Document itself has focus
781 nsCOMPtr<nsIDOMWindowInternal> focusedWinInternal;
782 focusController->GetFocusedWindow(getter_AddRefs(focusedWinInternal));
783 if (!focusedWinInternal) {
784 return nsnull;
786 nsCOMPtr<nsIDOMDocument> focusedDOMDocument;
787 focusedWinInternal->GetDocument(getter_AddRefs(focusedDOMDocument));
788 if (!focusedDOMDocument) {
789 return nsnull;
791 focusedDOMDocument->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&focusedNode);
793 else {
794 focusedElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)&focusedNode);
797 return focusedNode;
800 NS_IMETHODIMP
801 nsAccessNode::GetLanguage(nsAString& aLanguage)
803 aLanguage.Truncate();
804 nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
805 if (!content) {
806 // For documents make sure we look for lang attribute on
807 // document element
808 nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDOMNode));
809 if (domDoc) {
810 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDOMNode));
811 if (htmlDoc) {
812 // Make sure we look for lang attribute on HTML <body>
813 nsCOMPtr<nsIDOMHTMLElement> bodyElement;
814 htmlDoc->GetBody(getter_AddRefs(bodyElement));
815 content = do_QueryInterface(bodyElement);
817 if (!content) {
818 nsCOMPtr<nsIDOMElement> docElement;
819 domDoc->GetDocumentElement(getter_AddRefs(docElement));
820 content = do_QueryInterface(docElement);
823 if (!content) {
824 return NS_ERROR_FAILURE;
828 nsCoreUtils::GetLanguageFor(content, nsnull, aLanguage);
830 if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
831 nsIDocument *doc = content->GetOwnerDoc();
832 if (doc) {
833 doc->GetHeaderData(nsAccessibilityAtoms::headerContentLanguage, aLanguage);
837 return NS_OK;