Back to index

lightning-sunbird  0.9+nobinonly
nsAccessNode.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Original Author: Aaron Leventhal (aaronl@netscape.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsAccessNode.h"
00040 #include "nsIAccessible.h"
00041 #include "nsAccessibilityAtoms.h"
00042 #include "nsHashtable.h"
00043 #include "nsIAccessibilityService.h"
00044 #include "nsIAccessibleDocument.h"
00045 #include "nsPIAccessibleDocument.h"
00046 #include "nsIDocShell.h"
00047 #include "nsIDocShellTreeItem.h"
00048 #include "nsIDocument.h"
00049 #include "nsIDOMCSSStyleDeclaration.h"
00050 #include "nsIDOMDocument.h"
00051 #include "nsIDOMElement.h"
00052 #include "nsIDOMNSHTMLElement.h"
00053 #include "nsIDOMViewCSS.h"
00054 #include "nsIDOMWindow.h"
00055 #include "nsIInterfaceRequestorUtils.h"
00056 #include "nsIFrame.h"
00057 #include "nsIPrefService.h"
00058 #include "nsIPrefBranch.h"
00059 #include "nsPresContext.h"
00060 #include "nsIPresShell.h"
00061 #include "nsIServiceManager.h"
00062 #include "nsIStringBundle.h"
00063 #include "nsITimer.h"
00064 
00065 /* For documentation of the accessibility architecture, 
00066  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
00067  */
00068 
00069 nsIStringBundle *nsAccessNode::gStringBundle = 0;
00070 nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
00071 nsITimer *nsAccessNode::gDoCommandTimer = 0;
00072 nsIDOMNode *nsAccessNode::gLastFocusedNode = 0;
00073 PRBool nsAccessNode::gIsAccessibilityActive = PR_FALSE;
00074 PRBool nsAccessNode::gIsCacheDisabled = PR_FALSE;
00075 nsInterfaceHashtable<nsVoidHashKey, nsIAccessNode> nsAccessNode::gGlobalDocAccessibleCache;
00076 
00077 /*
00078  * Class nsAccessNode
00079  */
00080 
00081 //-----------------------------------------------------
00082 // construction 
00083 //-----------------------------------------------------
00084 NS_IMPL_ISUPPORTS2(nsAccessNode, nsIAccessNode, nsPIAccessNode)
00085 
00086 nsAccessNode::nsAccessNode(nsIDOMNode *aNode, nsIWeakReference* aShell): 
00087   mDOMNode(aNode), mWeakShell(aShell), mRefCnt(0)
00088 {
00089 #ifdef DEBUG
00090   mIsInitialized = PR_FALSE;
00091 #endif
00092 }
00093 
00094 //-----------------------------------------------------
00095 // destruction
00096 //-----------------------------------------------------
00097 nsAccessNode::~nsAccessNode()
00098 {
00099   if (mWeakShell) {
00100     Shutdown();  // Otherwise virtual Shutdown() methods could get fired twice
00101   }
00102 }
00103 
00104 NS_IMETHODIMP nsAccessNode::Init()
00105 {
00106   // We have to put this here, instead of constructor, otherwise
00107   // we don't have the virtual GetUniqueID() method for the hash key.
00108   // We need that for accessibles that don't have DOM nodes
00109 
00110   NS_ASSERTION(!mIsInitialized, "Initialized twice!");
00111   nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
00112   if (!docAccessible) {
00113     // No doc accessible yet for this node's document. 
00114     // There was probably an accessible event fired before the 
00115     // current document was ever asked for by the assistive technology.
00116     // Create a doc accessible so we can cache this node
00117     nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
00118     if (presShell) {
00119       nsCOMPtr<nsIDOMNode> docNode(do_QueryInterface(presShell->GetDocument()));
00120       if (docNode) {
00121         nsCOMPtr<nsIAccessibilityService> accService = 
00122           do_GetService("@mozilla.org/accessibilityService;1");
00123         NS_ASSERTION(accService, "No accessibility service");
00124         if (accService) {
00125           nsCOMPtr<nsIAccessible> accessible;
00126           accService->GetAccessibleInShell(docNode, presShell, 
00127             getter_AddRefs(accessible));
00128           docAccessible = do_QueryInterface(accessible);
00129         }
00130       }
00131     }
00132     NS_ASSERTION(docAccessible, "Cannot cache new nsAccessNode");
00133     if (!docAccessible) {
00134       return NS_ERROR_FAILURE;
00135     }
00136   }
00137   void* uniqueID;
00138   GetUniqueID(&uniqueID);
00139   nsCOMPtr<nsPIAccessibleDocument> privateDocAccessible =
00140     do_QueryInterface(docAccessible);
00141   NS_ASSERTION(privateDocAccessible, "No private docaccessible for docaccessible");
00142   privateDocAccessible->CacheAccessNode(uniqueID, this);
00143 #ifdef DEBUG
00144   mIsInitialized = PR_TRUE;
00145 #endif
00146 
00147   return NS_OK;
00148 }
00149 
00150 
00151 NS_IMETHODIMP nsAccessNode::Shutdown()
00152 {
00153   mDOMNode = nsnull;
00154   mWeakShell = nsnull;
00155 
00156   return NS_OK;
00157 }
00158 
00159 NS_IMETHODIMP nsAccessNode::GetUniqueID(void **aUniqueID)
00160 {
00161   *aUniqueID = NS_STATIC_CAST(void*, mDOMNode);
00162   return NS_OK;
00163 }
00164 
00165 NS_IMETHODIMP nsAccessNode::GetOwnerWindow(void **aWindow)
00166 {
00167   nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
00168   NS_ASSERTION(docAccessible, "No root accessible pointer back, Init() not called.");
00169   return docAccessible->GetWindowHandle(aWindow);
00170 }
00171 
00172 void nsAccessNode::InitXPAccessibility()
00173 {
00174   if (gIsAccessibilityActive) {
00175     return;
00176   }
00177 
00178   nsCOMPtr<nsIStringBundleService> stringBundleService =
00179     do_GetService(NS_STRINGBUNDLE_CONTRACTID);
00180   if (stringBundleService) {
00181     // Static variables are released in ShutdownAllXPAccessibility();
00182     stringBundleService->CreateBundle(ACCESSIBLE_BUNDLE_URL, 
00183                                       &gStringBundle);
00184     stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL, 
00185                                       &gKeyStringBundle);
00186   }
00187 
00188   nsAccessibilityAtoms::AddRefAtoms();
00189 
00190   gGlobalDocAccessibleCache.Init(4);
00191 
00192   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00193   if (prefBranch) {
00194     prefBranch->GetBoolPref("accessibility.disablecache", &gIsCacheDisabled);
00195   }
00196 
00197   gIsAccessibilityActive = PR_TRUE;
00198 }
00199 
00200 void nsAccessNode::ShutdownXPAccessibility()
00201 {
00202   // Called by nsAccessibilityService::Shutdown()
00203   // which happens when xpcom is shutting down
00204   // at exit of program
00205 
00206   if (!gIsAccessibilityActive) {
00207     return;
00208   }
00209   NS_IF_RELEASE(gStringBundle);
00210   NS_IF_RELEASE(gKeyStringBundle);
00211   NS_IF_RELEASE(gDoCommandTimer);
00212   NS_IF_RELEASE(gLastFocusedNode);
00213 
00214   ClearCache(gGlobalDocAccessibleCache);
00215 
00216   gIsAccessibilityActive = PR_FALSE;
00217 }
00218 
00219 already_AddRefed<nsIPresShell> nsAccessNode::GetPresShell()
00220 {
00221   nsIPresShell *presShell = nsnull;
00222   if (mWeakShell)
00223     CallQueryReferent(mWeakShell.get(), &presShell);
00224   if (!presShell) {
00225     if (mWeakShell) {
00226       // If our pres shell has died, but we're still holding onto
00227       // a weak reference, our accessibles are no longer relevant
00228       // and should be shut down
00229       Shutdown();
00230     }
00231     return nsnull;
00232   }
00233   return presShell;
00234 }
00235 
00236 nsPresContext* nsAccessNode::GetPresContext()
00237 {
00238   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
00239   if (!presShell) {
00240     return nsnull;
00241   }
00242   return presShell->GetPresContext();
00243 }
00244 
00245 already_AddRefed<nsIAccessibleDocument> nsAccessNode::GetDocAccessible()
00246 {
00247   return GetDocAccessibleFor(mWeakShell); // Addref'd
00248 }
00249 
00250 nsIFrame* nsAccessNode::GetFrame()
00251 {
00252   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
00253   if (!shell) 
00254     return nsnull;  
00255 
00256   nsIFrame* frame;
00257   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
00258   while (content) {
00259     shell->GetPrimaryFrameFor(content, &frame);
00260     if (frame) {
00261       return frame;
00262     }
00263     nsCOMPtr<nsIContent> tempContent = content->GetParent();
00264     content = tempContent;
00265   }
00266 
00267   return nsnull;
00268 }
00269 
00270 NS_IMETHODIMP
00271 nsAccessNode::GetDOMNode(nsIDOMNode **aNode)
00272 {
00273   NS_IF_ADDREF(*aNode = mDOMNode);
00274   return NS_OK;
00275 }
00276 
00277 NS_IMETHODIMP
00278 nsAccessNode::GetNumChildren(PRInt32 *aNumChildren)
00279 {
00280   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
00281 
00282   if (!content) {
00283     *aNumChildren = 0;
00284 
00285     return NS_ERROR_NULL_POINTER;
00286   }
00287 
00288   *aNumChildren = content->GetChildCount();
00289 
00290   return NS_OK;
00291 }
00292 
00293 NS_IMETHODIMP
00294 nsAccessNode::GetAccessibleDocument(nsIAccessibleDocument **aDocAccessible)
00295 {
00296   *aDocAccessible = GetDocAccessibleFor(mWeakShell).get();
00297   return NS_OK;
00298 }
00299 
00300 NS_IMETHODIMP
00301 nsAccessNode::GetInnerHTML(nsAString& aInnerHTML)
00302 {
00303   aInnerHTML.Truncate();
00304 
00305   nsCOMPtr<nsIDOMNSHTMLElement> domNSElement(do_QueryInterface(mDOMNode));
00306   NS_ENSURE_TRUE(domNSElement, NS_ERROR_NULL_POINTER);
00307 
00308   return domNSElement->GetInnerHTML(aInnerHTML);
00309 }
00310 
00311 nsresult
00312 nsAccessNode::MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode)
00313 {
00314   nsCOMPtr<nsIAccessibilityService> accService = 
00315     do_GetService("@mozilla.org/accessibilityService;1");
00316   NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
00317 
00318   nsCOMPtr<nsIAccessNode> accessNode;
00319   accService->GetCachedAccessNode(aNode, mWeakShell, getter_AddRefs(accessNode));
00320 
00321   if (!accessNode) {
00322     nsCOMPtr<nsIAccessible> accessible;
00323     accService->GetAccessibleInWeakShell(aNode, mWeakShell, 
00324                                          getter_AddRefs(accessible));
00325     
00326     accessNode = do_QueryInterface(accessible);
00327   }
00328 
00329   if (accessNode) {
00330     NS_ADDREF(*aAccessNode = accessNode);
00331     return NS_OK;
00332   }
00333 
00334   nsAccessNode *newAccessNode = new nsAccessNode(aNode, mWeakShell);
00335   if (!newAccessNode) {
00336     return NS_ERROR_OUT_OF_MEMORY;
00337   }
00338 
00339   NS_ADDREF(*aAccessNode = newAccessNode);
00340   newAccessNode->Init();
00341 
00342   return NS_OK;
00343 }
00344 
00345 NS_IMETHODIMP
00346 nsAccessNode::GetFirstChildNode(nsIAccessNode **aAccessNode)
00347 {
00348   NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
00349 
00350   nsCOMPtr<nsIDOMNode> domNode;
00351   mDOMNode->GetFirstChild(getter_AddRefs(domNode));
00352   NS_ENSURE_TRUE(domNode, NS_ERROR_NULL_POINTER);
00353 
00354   return MakeAccessNode(domNode, aAccessNode);
00355 }
00356 
00357 NS_IMETHODIMP
00358 nsAccessNode::GetLastChildNode(nsIAccessNode **aAccessNode)
00359 {
00360   NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
00361 
00362   nsCOMPtr<nsIDOMNode> domNode;
00363   mDOMNode->GetLastChild(getter_AddRefs(domNode));
00364   NS_ENSURE_TRUE(domNode, NS_ERROR_NULL_POINTER);
00365 
00366   return MakeAccessNode(domNode, aAccessNode);
00367 }
00368 
00369 NS_IMETHODIMP
00370 nsAccessNode::GetParentNode(nsIAccessNode **aAccessNode)
00371 {
00372   NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
00373 
00374   nsCOMPtr<nsIDOMNode> domNode;
00375   mDOMNode->GetParentNode(getter_AddRefs(domNode));
00376   NS_ENSURE_TRUE(domNode, NS_ERROR_NULL_POINTER);
00377 
00378   return MakeAccessNode(domNode, aAccessNode);
00379 }
00380 
00381 NS_IMETHODIMP
00382 nsAccessNode::GetPreviousSiblingNode(nsIAccessNode **aAccessNode)
00383 {
00384   NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
00385 
00386   nsCOMPtr<nsIDOMNode> domNode;
00387   mDOMNode->GetPreviousSibling(getter_AddRefs(domNode));
00388   NS_ENSURE_TRUE(domNode, NS_ERROR_NULL_POINTER);
00389 
00390   return MakeAccessNode(domNode, aAccessNode);
00391 }
00392 
00393 NS_IMETHODIMP
00394 nsAccessNode::GetNextSiblingNode(nsIAccessNode **aAccessNode)
00395 {
00396   NS_ENSURE_TRUE(mDOMNode, NS_ERROR_NULL_POINTER);
00397 
00398   nsCOMPtr<nsIDOMNode> domNode;
00399   mDOMNode->GetNextSibling(getter_AddRefs(domNode));
00400   NS_ENSURE_TRUE(domNode, NS_ERROR_NULL_POINTER);
00401 
00402   return MakeAccessNode(domNode, aAccessNode);
00403 }
00404 
00405 NS_IMETHODIMP
00406 nsAccessNode::GetChildNodeAt(PRInt32 aChildNum, nsIAccessNode **aAccessNode)
00407 {
00408   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
00409   NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
00410 
00411   nsCOMPtr<nsIDOMNode> domNode =
00412     do_QueryInterface(content->GetChildAt(aChildNum));
00413   NS_ENSURE_TRUE(domNode, NS_ERROR_NULL_POINTER);
00414 
00415   return MakeAccessNode(domNode, aAccessNode);
00416 }
00417 
00418 NS_IMETHODIMP
00419 nsAccessNode::GetComputedStyleValue(const nsAString& aPseudoElt, const nsAString& aPropertyName, nsAString& aValue)
00420 {
00421   nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mDOMNode));
00422   nsPresContext *presContext = GetPresContext();
00423   NS_ENSURE_TRUE(domElement && presContext, NS_ERROR_FAILURE);
00424 
00425   nsCOMPtr<nsISupports> container = presContext->GetContainer();
00426   nsCOMPtr<nsIDOMWindow> domWin(do_GetInterface(container));
00427   nsCOMPtr<nsIDOMViewCSS> viewCSS(do_QueryInterface(domWin));
00428   NS_ENSURE_TRUE(viewCSS, NS_ERROR_FAILURE);
00429 
00430   nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl;
00431   viewCSS->GetComputedStyle(domElement, aPseudoElt, getter_AddRefs(styleDecl));
00432   NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
00433   
00434   return styleDecl->GetPropertyValue(aPropertyName, aValue);
00435 }
00436 
00437 /***************** Hashtable of nsIAccessNode's *****************/
00438 
00439 already_AddRefed<nsIAccessibleDocument>
00440 nsAccessNode::GetDocAccessibleFor(nsIWeakReference *aPresShell)
00441 {
00442   nsIAccessibleDocument *docAccessible = nsnull;
00443   nsCOMPtr<nsIAccessNode> accessNode;
00444   gGlobalDocAccessibleCache.Get(NS_STATIC_CAST(void*, aPresShell), getter_AddRefs(accessNode));
00445   if (accessNode) {
00446     CallQueryInterface(accessNode, &docAccessible);
00447   }
00448   return docAccessible;
00449 }
00450  
00451 already_AddRefed<nsIAccessibleDocument>
00452 nsAccessNode::GetDocAccessibleFor(nsISupports *aContainer)
00453 {
00454   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
00455   NS_ASSERTION(docShell, "This method currently only supports docshells");
00456   nsCOMPtr<nsIPresShell> presShell;
00457   docShell->GetPresShell(getter_AddRefs(presShell));
00458   nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
00459   return weakShell ? GetDocAccessibleFor(weakShell) : nsnull;
00460 }
00461  
00462 already_AddRefed<nsIAccessibleDocument>
00463 nsAccessNode::GetDocAccessibleFor(nsIDOMNode *aNode)
00464 {
00465   nsCOMPtr<nsIPresShell> eventShell = GetPresShellFor(aNode);
00466   nsCOMPtr<nsIWeakReference> weakEventShell(do_GetWeakReference(eventShell));
00467   return weakEventShell? GetDocAccessibleFor(weakEventShell) : nsnull;
00468 }
00469 
00470 already_AddRefed<nsIPresShell>
00471 nsAccessNode::GetPresShellFor(nsIDOMNode *aNode)
00472 {
00473   nsCOMPtr<nsIDOMDocument> domDocument;
00474   aNode->GetOwnerDocument(getter_AddRefs(domDocument));
00475   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDocument));
00476   if (!doc) {   // This is necessary when the node is the document node
00477     doc = do_QueryInterface(aNode);
00478   }
00479   nsIPresShell *presShell = nsnull;
00480   if (doc) {
00481     presShell = doc->GetShellAt(0);
00482     NS_IF_ADDREF(presShell);
00483   }
00484   return presShell;
00485 }
00486 
00487 
00488 already_AddRefed<nsIDocShellTreeItem>
00489 nsAccessNode::GetDocShellTreeItemFor(nsIDOMNode *aStartNode)
00490 {
00491   nsCOMPtr<nsIDOMDocument> domDoc;
00492   aStartNode->GetOwnerDocument(getter_AddRefs(domDoc));
00493   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00494   if (!doc) {
00495     doc = do_QueryInterface(aStartNode);
00496   }
00497   NS_ASSERTION(doc, "No document for node passed in");
00498   NS_ENSURE_TRUE(doc, nsnull);
00499   nsCOMPtr<nsISupports> container = doc->GetContainer();
00500   nsIDocShellTreeItem *docShellTreeItem = nsnull;
00501   if (container) {
00502     CallQueryInterface(container, &docShellTreeItem);
00503   }
00504 
00505   return docShellTreeItem;
00506 }
00507 
00508 void nsAccessNode::PutCacheEntry(nsInterfaceHashtable<nsVoidHashKey, nsIAccessNode> &aCache, 
00509                                  void* aUniqueID, 
00510                                  nsIAccessNode *aAccessNode)
00511 {
00512 #ifdef DEBUG
00513   nsCOMPtr<nsIAccessNode> oldAccessNode;
00514   GetCacheEntry(aCache, aUniqueID, getter_AddRefs(oldAccessNode));
00515   NS_ASSERTION(!oldAccessNode, "This cache entry shouldn't exist already");
00516 #endif
00517   aCache.Put(aUniqueID, aAccessNode);
00518 }
00519 
00520 void nsAccessNode::GetCacheEntry(nsInterfaceHashtable<nsVoidHashKey, nsIAccessNode> &aCache, 
00521                                  void* aUniqueID, 
00522                                  nsIAccessNode **aAccessNode)
00523 {
00524   aCache.Get(aUniqueID, aAccessNode);  // AddRefs for us
00525 }
00526 
00527 PLDHashOperator nsAccessNode::ClearCacheEntry(const void* aKey, nsCOMPtr<nsIAccessNode>& aAccessNode, void* aUserArg)
00528 {
00529   nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(aAccessNode));
00530   privateAccessNode->Shutdown();
00531 
00532   return PL_DHASH_REMOVE;
00533 }
00534 
00535 void nsAccessNode::ClearCache(nsInterfaceHashtable<nsVoidHashKey, nsIAccessNode> &aCache)
00536 {
00537   aCache.Enumerate(ClearCacheEntry, nsnull);
00538 }