Bug 405414. nsDocAccessible is not destroyed when closing a tab. Patch by Ginn Chen. r=aaronlev, a=dsicore
authoraaronleventhal@moonset.net
Fri, 30 Nov 2007 11:47:16 -0800
changeset 8471 5ece9e87c41e4ae701a72def09deddd1c7d7eff7
parent 8470 4482f01e34976419c7d25b30cc26c4c11d5fce95
child 8472 12a280f537ef8f57177bf3728ee743c5745f8939
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaaronlev, dsicore
bugs405414
milestone1.9b2pre
Bug 405414. nsDocAccessible is not destroyed when closing a tab. Patch by Ginn Chen. r=aaronlev, a=dsicore
accessible/public/nsPIAccessibleDocument.idl
accessible/src/base/nsAccessNode.cpp
accessible/src/base/nsAccessNode.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/base/nsDocAccessible.cpp
accessible/src/base/nsRootAccessible.cpp
--- a/accessible/public/nsPIAccessibleDocument.idl
+++ b/accessible/public/nsPIAccessibleDocument.idl
@@ -36,17 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIAccessNode;
 interface nsIContent;
 
-[uuid(08811f23-1298-4882-9a68-6f1466c28007)]
+[uuid(fa9cafac-9562-49ad-afcf-911ab1e4e4fb)]
 
 interface nsPIAccessibleDocument : nsISupports
 {
    /**
     * Find the accessible object in the accessibility cache that 
     * corresponds to aStartNode or the first ancestor of aStartNode 
     * that has an accessible object associated with it.
     * Clear that accessible object's parent's cache of accessible children and
@@ -56,13 +56,12 @@ interface nsPIAccessibleDocument : nsISu
     * @param aEvent             The event from nsIAccessibleEvent that caused the change:
     *                           Must be one of: EVENT_REORDER (change),
     *                                           EVENT_SHOW (make visible or create) or 
     *                                           EVENT_HIDE (destroy or hide)
     */
   void invalidateCacheSubtree(in nsIContent aChangeContent,
                               in PRUint32 aChangeEvent);
   void cacheAccessNode(in voidPtr aUniqueID, in nsIAccessNode aAccessNode);
-  void destroy();
   void flushPendingEvents();
   void fireDocLoadEvents(in PRUint32 aEventType);
   void fireAnchorJumpEvent();
 };
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -650,37 +650,53 @@ void nsAccessNode::GetComputedStyleDecla
   nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
   viewCSS->GetComputedStyle(aElement, aPseudoElt, getter_AddRefs(cssDecl));
   NS_IF_ADDREF(*aCssDecl = cssDecl);
 }
 
 /***************** Hashtable of nsIAccessNode's *****************/
 
 already_AddRefed<nsIAccessibleDocument>
-nsAccessNode::GetDocAccessibleFor(nsIWeakReference *aPresShell)
+nsAccessNode::GetDocAccessibleFor(nsIDocument *aDocument)
 {
+  if (!aDocument) {
+    return nsnull;
+  }
+
   nsIAccessibleDocument *docAccessible = nsnull;
   nsCOMPtr<nsIAccessNode> accessNode;
-  gGlobalDocAccessibleCache.Get(static_cast<void*>(aPresShell), getter_AddRefs(accessNode));
+  gGlobalDocAccessibleCache.Get(static_cast<void*>(aDocument),
+                                getter_AddRefs(accessNode));
   if (accessNode) {
     CallQueryInterface(accessNode, &docAccessible);
   }
   return docAccessible;
 }
  
 already_AddRefed<nsIAccessibleDocument>
-nsAccessNode::GetDocAccessibleFor(nsISupports *aContainer, PRBool aCanCreate)
+nsAccessNode::GetDocAccessibleFor(nsIWeakReference *aWeakShell)
+{
+  nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(aWeakShell));
+  if (!presShell) {
+    return nsnull;
+  }
+
+  return nsAccessNode::GetDocAccessibleFor(presShell->GetDocument());
+}
+
+already_AddRefed<nsIAccessibleDocument>
+nsAccessNode::GetDocAccessibleFor(nsIDocShellTreeItem *aContainer,
+                                  PRBool aCanCreate)
 {
   if (!aCanCreate) {
     nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
     NS_ASSERTION(docShell, "This method currently only supports docshells");
     nsCOMPtr<nsIPresShell> presShell;
     docShell->GetPresShell(getter_AddRefs(presShell));
-    nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
-    return weakShell ? GetDocAccessibleFor(weakShell) : nsnull;
+    return presShell ? GetDocAccessibleFor(presShell->GetDocument()) : nsnull;
   }
 
   nsCOMPtr<nsIDOMNode> node = GetDOMNodeForContainer(aContainer);
   if (!node) {
     return nsnull;
   }
 
   nsCOMPtr<nsIAccessible> accessible;
@@ -691,18 +707,26 @@ nsAccessNode::GetDocAccessibleFor(nsISup
   }
   return docAccessible;
 }
  
 already_AddRefed<nsIAccessibleDocument>
 nsAccessNode::GetDocAccessibleFor(nsIDOMNode *aNode)
 {
   nsCOMPtr<nsIPresShell> eventShell = GetPresShellFor(aNode);
-  nsCOMPtr<nsIWeakReference> weakEventShell(do_GetWeakReference(eventShell));
-  return weakEventShell? GetDocAccessibleFor(weakEventShell) : nsnull;
+  if (eventShell) {
+    return GetDocAccessibleFor(eventShell->GetDocument());
+  }
+
+  nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
+  if (doc) {
+    return GetDocAccessibleFor(doc);
+  }
+
+  return nsnull;
 }
 
 already_AddRefed<nsIPresShell>
 nsAccessNode::GetPresShellFor(nsIDOMNode *aNode)
 {
   nsCOMPtr<nsIDOMDocument> domDocument;
   aNode->GetOwnerDocument(getter_AddRefs(domDocument));
   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDocument));
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -59,16 +59,17 @@
 class nsIPresShell;
 class nsPresContext;
 class nsIAccessibleDocument;
 class nsIFrame;
 class nsIDOMNodeList;
 class nsITimer;
 class nsRootAccessible;
 class nsApplicationAccessibleWrap;
+class nsIDocShellTreeItem;
 
 #define ACCESSIBLE_BUNDLE_URL "chrome://global-platform/locale/accessible.properties"
 #define PLATFORM_KEYS_BUNDLE_URL "chrome://global-platform/locale/platformKeys.properties"
 
 typedef nsInterfaceHashtable<nsVoidPtrHashKey, nsIAccessNode>
         nsAccessNodeHashtable;
 
 /**
@@ -104,18 +105,19 @@ class nsAccessNode: public nsIAccessNode
                               void* aUniqueID, nsIAccessNode *aAccessNode);
     static void GetCacheEntry(nsAccessNodeHashtable& aCache,
                               void* aUniqueID, nsIAccessNode **aAccessNode);
     static void ClearCache(nsAccessNodeHashtable& aCache);
 
     static PLDHashOperator PR_CALLBACK ClearCacheEntry(const void* aKey, nsCOMPtr<nsIAccessNode>& aAccessNode, void* aUserArg);
 
     // Static cache methods for global document cache
-    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIWeakReference *aPresShell);
-    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsISupports *aContainer, PRBool aCanCreate = PR_FALSE);
+    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIDocument *aDocument);
+    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIWeakReference *aWeakShell);
+    static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIDocShellTreeItem *aContainer, PRBool aCanCreate = PR_FALSE);
     static already_AddRefed<nsIAccessibleDocument> GetDocAccessibleFor(nsIDOMNode *aNode);
 
     static already_AddRefed<nsIDOMNode> GetDOMNodeForContainer(nsISupports *aContainer);
     static already_AddRefed<nsIPresShell> GetPresShellFor(nsIDOMNode *aStartNode);
     
     // Return PR_TRUE if there is a role attribute
     static PRBool HasRoleAttribute(nsIContent *aContent)
     {
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1860,19 +1860,18 @@ NS_IMETHODIMP nsAccessibilityService::In
                aEvent == nsIAccessibleEvent::EVENT_ASYNCH_SHOW ||
                aEvent == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
                aEvent == nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE ||
                aEvent == nsIAccessibleEvent::EVENT_DOM_CREATE ||
                aEvent == nsIAccessibleEvent::EVENT_DOM_DESTROY,
                "Incorrect aEvent passed in");
 
   NS_ENSURE_ARG_POINTER(aShell);
-  nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aShell));
   nsCOMPtr<nsIAccessibleDocument> accessibleDoc =
-    nsAccessNode::GetDocAccessibleFor(weakShell);
+    nsAccessNode::GetDocAccessibleFor(aShell->GetDocument());
   nsCOMPtr<nsPIAccessibleDocument> privateAccessibleDoc =
     do_QueryInterface(accessibleDoc);
   if (!privateAccessibleDoc) {
     return NS_OK;
   }
   return privateAccessibleDoc->InvalidateCacheSubtree(aChangeContent, aEvent);
 }
 
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -516,17 +516,17 @@ nsDocAccessible::GetAttributes(nsIPersis
   NS_ENSURE_ARG_POINTER(aAttributes);
   *aAttributes = nsnull;
 
   return mDOMNode ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP nsDocAccessible::Init()
 {
-  PutCacheEntry(gGlobalDocAccessibleCache, mWeakShell, this);
+  PutCacheEntry(gGlobalDocAccessibleCache, mDocument, this);
 
   AddEventListeners();
 
   nsresult rv = nsHyperTextAccessibleWrap::Init();
 
   if (mRoleMapEntry && mRoleMapEntry->role != nsIAccessibleRole::ROLE_DIALOG &&
       mRoleMapEntry->role != nsIAccessibleRole::ROLE_APPLICATION &&
       mRoleMapEntry->role != nsIAccessibleRole::ROLE_ALERT &&
@@ -534,29 +534,16 @@ NS_IMETHODIMP nsDocAccessible::Init()
     // Document accessible can only have certain roles
     // This was set in nsAccessible::Init() based on dynamic role attribute
     mRoleMapEntry = nsnull; // role attribute is not valid for a document
   }
 
   return rv;
 }
 
-
-NS_IMETHODIMP nsDocAccessible::Destroy()
-{
-  nsresult rv = Shutdown();
-
-  if (mWeakShell) {
-    // Remove from the cache after Shutdown(), so that Shutdown() procedures
-    // can find the doc or root accessible in the cache if they need it
-    gGlobalDocAccessibleCache.Remove(static_cast<void*>(mWeakShell));
-  }
-  return rv;
-}
-
 NS_IMETHODIMP nsDocAccessible::Shutdown()
 {
   if (!mWeakShell) {
     return NS_OK;  // Already shutdown
   }
 
   nsCOMPtr<nsIDocShellTreeItem> treeItem =
     nsAccUtils::GetDocShellTreeItemFor(mDOMNode);
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -595,41 +595,43 @@ nsresult nsRootAccessible::HandleEventWi
   if (eventType.EqualsLiteral("AlertActive")) {
     printf("\ndebugging %s events for %s", NS_ConvertUTF16toUTF8(eventType).get(), NS_ConvertUTF16toUTF8(localName).get());
   }
   if (localName.LowerCaseEqualsLiteral("textbox")) {
     printf("\ndebugging %s events for %s", NS_ConvertUTF16toUTF8(eventType).get(), NS_ConvertUTF16toUTF8(localName).get());
   }
 #endif
 
-  nsCOMPtr<nsIPresShell> eventShell = GetPresShellFor(aTargetNode);
-  if (!eventShell) {
-    return NS_OK;
-  }
-      
   nsIAccessibilityService *accService = GetAccService();
   NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
 
   if (eventType.EqualsLiteral("pagehide")) {
     // pagehide event can be fired under several conditions, such as HTML
     // document going away, closing a window/dialog, and wizard page changing.
     // We only destroy the accessible object when it's a document accessible,
     // so that we don't destroy something still in use, like wizard page. 
     // And we only get cached document accessible to destroy, so that we don't
     // create it just to destroy it.
-    nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(eventShell));
-    nsCOMPtr<nsIAccessible> accessible;
-    accService->GetCachedAccessible(aTargetNode, weakShell, getter_AddRefs(accessible));
-    nsCOMPtr<nsPIAccessibleDocument> privateAccDoc = do_QueryInterface(accessible);
-    if (privateAccDoc) {
-      privateAccDoc->Destroy();
+    nsCOMPtr<nsIDocument> doc(do_QueryInterface(aTargetNode));
+    nsCOMPtr<nsIAccessibleDocument> accDoc = GetDocAccessibleFor(doc);
+    nsCOMPtr<nsPIAccessNode> privateAcc = do_QueryInterface(accDoc);
+    if (privateAcc) {
+      privateAcc->Shutdown();
+      // Remove from the cache after Shutdown(), so that Shutdown() procedures
+      // can find the doc or root accessible in the cache if they need it.
+      gGlobalDocAccessibleCache.Remove(static_cast<void*>(doc));
     }
     return NS_OK;
   }
 
+  nsCOMPtr<nsIPresShell> eventShell = GetPresShellFor(aTargetNode);
+  if (!eventShell) {
+    return NS_OK;
+  }
+
   if (eventType.EqualsLiteral("DOMContentLoaded")) {
     // Don't create the doc accessible until load scripts have a chance to set
     // role attribute for <body> or <html> element, because the value of 
     // role attribute will be cached when the doc accessible is Init()'d
     TryFireEarlyLoadEvent(aTargetNode);
     return NS_OK;
   }