Bug 571459 - shutdown document accessible when presshell goes away, patch=bz, surkov, r=surkov, davidb, sr=roc, bz, f=marcoz
authorAlexander Surkov <surkov.alexander@gmail.com>
Fri, 18 Jun 2010 11:44:09 +0900
changeset 43765 279ce7f9b1e31683679d11168ef5905508bfe0d7
parent 43764 31d4d880d0a81e4a2cbf920670a17a24be272923
child 43766 598c5f8d295cb5e1456e55cd512758d6c3498df2
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov, davidb, roc, bz
bugs571459
milestone1.9.3a6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 571459 - shutdown document accessible when presshell goes away, patch=bz, surkov, r=surkov, davidb, sr=roc, bz, f=marcoz
accessible/public/nsIAccessibilityService.h
accessible/src/base/nsAccDocManager.cpp
accessible/src/base/nsAccDocManager.h
accessible/src/base/nsAccessNode.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/base/nsAccessibilityService.h
accessible/src/base/nsDocAccessible.cpp
accessible/src/base/nsDocAccessible.h
accessible/src/base/nsOuterDocAccessible.cpp
accessible/tests/mochitest/events.js
layout/base/nsPresShell.cpp
--- a/accessible/public/nsIAccessibilityService.h
+++ b/accessible/public/nsIAccessibilityService.h
@@ -46,20 +46,20 @@
 
 class nsAccessible;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
 class nsIPresShell;
 class nsObjectFrame;
 
-// 9f43b315-53c6-4d46-9818-9c8593e91984
+// 10ff6dca-b219-4b64-9a4c-67a62b86edce
 #define NS_IACCESSIBILITYSERVICE_IID \
-{0x9f43b315, 0x53c6, 0x4d46,         \
-  {0x98, 0x18, 0x9c, 0x85, 0x93, 0xe9, 0x19, 0x84} }
+{ 0x10ff6dca, 0xb219, 0x4b64, \
+ { 0x9a, 0x4c, 0x67, 0xa6, 0x2b, 0x86, 0xed, 0xce } }
 
 class nsIAccessibilityService : public nsIAccessibleRetrieval
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IACCESSIBILITYSERVICE_IID)
 
   /**
    * Return an accessible object for a DOM node in the given pres shell.
@@ -162,16 +162,22 @@ public:
 
   /**
    * Notify accessibility that anchor jump has been accomplished to the given
    * target. Used by layout.
    */
   virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget) = 0;
 
   /**
+   * Notify the accessibility service that the given presshell is
+   * being destroyed.
+   */
+  virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0;
+
+  /**
    * Fire accessible event of the given type for the given target.
    *
    * @param aEvent   [in] accessible event type
    * @param aTarget  [in] target of accessible event
    */
   virtual nsresult FireAccessibleEvent(PRUint32 aEvent,
                                        nsIAccessible *aTarget) = 0;
 };
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -91,16 +91,17 @@ nsAccDocManager::FindAccessibleInCache(v
 void
 nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocument *aDocument)
 {
   nsCOMPtr<nsISupports> container = aDocument->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
   ShutdownDocAccessiblesInTree(treeItem, aDocument);
 }
 
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccDocManager protected
 
 PRBool
 nsAccDocManager::Init()
 {
   mDocAccessibleCache.Init(4);
 
@@ -123,16 +124,32 @@ nsAccDocManager::Shutdown()
     do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
 
   if (progress)
     progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
 
   ClearDocCache();
 }
 
+void
+nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
+{
+  nsDocAccessible* docAccessible =
+    mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
+  if (!docAccessible)
+    return;
+
+  // We're allowed to not remove listeners when accessible document is shutdown
+  // since we don't keep strong reference on chrome event target and listeners
+  // are removed automatically when chrome event target goes away.
+
+  docAccessible->Shutdown();
+  mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
 NS_IMPL_THREADSAFE_ISUPPORTS3(nsAccDocManager,
                               nsIWebProgressListener,
                               nsIDOMEventListener,
                               nsISupportsWeakReference)
 
@@ -408,40 +425,16 @@ nsAccDocManager::AddListeners(nsIDocumen
 
   if (aAddDOMContentLoadedListener) {
     elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
                                 NS_EVENT_FLAG_CAPTURE, nsnull);
     NS_LOG_ACCDOCCREATE_TEXT("  added 'DOMContentLoaded' listener")
   }
 }
 
-void
-nsAccDocManager::RemoveListeners(nsIDocument *aDocument)
-{
-  // Document has no window when application shuts down. The document can still
-  // exist because we didn't receive a "pagehide" event.
-  nsPIDOMWindow *window = aDocument->GetWindow();
-  if (!window)
-    return;
-
-  nsPIDOMEventTarget *target = window->GetChromeEventHandler();
-  nsIEventListenerManager* elm = target->GetListenerManager(PR_TRUE);
-  elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
-                                 NS_EVENT_FLAG_CAPTURE, nsnull);
-
-  NS_LOG_ACCDOCDESTROY("removed 'pagehide' listener", aDocument)
-
-  if (nsCoreUtils::IsRootDocument(aDocument)) {
-    elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
-                                     NS_EVENT_FLAG_CAPTURE, nsnull);
-
-    NS_LOG_ACCDOCDESTROY("removed 'DOMContentLoaded' listener", aDocument)
-  }
-}
-
 nsDocAccessible*
 nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
 {
   // Ignore temporary, hiding and svg resource documents.
   if (aDocument->IsInitialDocument() || !aDocument->IsVisible() ||
       aDocument->GetDisplayDocument())
     return nsnull;
 
@@ -497,27 +490,26 @@ nsAccDocManager::CreateDocOrRootAccessib
     return nsnull;
   }
 
   // XXX: ideally we should initialize an accessible and then put it into tree,
   // also this code should be shared between doc and root accessibles.
   if (outerDocAcc) {
     // Root document accessible doesn't have associated outerdoc accessible, it
     // adds itself to application accessible instead.
-    NS_LOG_ACCDOCCREATE("append document to outerdoc", aDocument)
     outerDocAcc->AppendChild(docAcc);
   }
 
   if (!GetAccService()->InitAccessible(docAcc,
                                        nsAccUtils::GetRoleMapEntry(aDocument))) {
     mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
     return nsnull;
   }
 
-  NS_LOG_ACCDOCCREATE("document created", aDocument)
+  NS_LOG_ACCDOCCREATE("document creation finished", aDocument)
 
   AddListeners(aDocument, isRootDoc);
   return docAcc;
 }
 
 void
 nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
                                               nsIDocument *aDocument)
@@ -542,50 +534,31 @@ nsAccDocManager::ShutdownDocAccessiblesI
 
       ShutdownDocAccessiblesInTree(treeItemChild, contentViewer->GetDocument());
     }
   }
 
   ShutdownDocAccessible(aDocument);
 }
 
-void
-nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
-{
-  RemoveListeners(aDocument);
-
-  nsDocAccessible *docAccessible =
-    mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
-  if (docAccessible)
-    docAccessible->Shutdown();
-
-  mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccDocManager static
 
 PLDHashOperator
 nsAccDocManager::ClearDocCacheEntry(const void* aKey,
                                     nsRefPtr<nsDocAccessible>& aDocAccessible,
                                     void* aUserArg)
 {
   nsAccDocManager *accDocMgr = static_cast<nsAccDocManager*>(aUserArg);
 
   NS_ASSERTION(aDocAccessible,
                "Calling ClearDocCacheEntry with a NULL pointer!");
 
-  if (aDocAccessible) {
-    nsCOMPtr<nsIDocument> document = aDocAccessible->GetDOMDocument();
-    NS_ASSERTION(document, "Document accessible was shutdown already!");
-    if (document)
-      accDocMgr->RemoveListeners(document);
-
+  if (aDocAccessible)
     aDocAccessible->Shutdown();
-  }
 
   return PL_DHASH_REMOVE;
 }
 
 PLDHashOperator
 nsAccDocManager::SearchAccessibleInDocCache(const void* aKey,
                                             nsDocAccessible* aDocAccessible,
                                             void* aUserArg)
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -43,16 +43,18 @@
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
 #include "nsWeakReference.h"
 
 class nsDocAccessible;
 
+//#define DEBUG_ACCDOCMGR
+
 /**
  * Manage the document accessible life cycle.
  */
 class nsAccDocManager : public nsIWebProgressListener,
                         public nsIDOMEventListener,
                         public nsSupportsWeakReference
 {
 public:
@@ -75,33 +77,41 @@ public:
 
   /**
    * Shutdown document accessibles in the tree starting from the given one.
    *
    * @param  aDocument  [in] the DOM document of start document accessible
    */
   void ShutdownDocAccessiblesInTree(nsIDocument *aDocument);
 
+  /**
+   * Return document accessible from the cache. Convenient method for testing.
+   */
+  inline nsDocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const
+  {
+    return mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
+  }
+
 protected:
   nsAccDocManager() { };
 
   /**
    * Initialize the manager.
    */
   PRBool Init();
 
   /**
    * Shutdown the manager.
    */
   void Shutdown();
 
-  inline nsDocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const
-  {
-    return mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
-  }
+  /**
+   * Shutdown the document accessible.
+   */
+  void ShutdownDocAccessible(nsIDocument* aDocument);
 
 private:
   nsAccDocManager(const nsAccDocManager&);
   nsAccDocManager& operator =(const nsAccDocManager&);
 
 private:
   /**
    * Create an accessible document if it was't created and fire accessibility
@@ -132,37 +142,31 @@ private:
    * them have separate processing of iframe documents on the page and therefore
    * they need a way to distinguish sub documents from page document). Ideally
    * we should make events firing for any loaded document and provide additional
    * info AT are needing.
    */
   PRBool IsEventTargetDocument(nsIDocument *aDocument) const;
 
   /**
-   * Add/remove 'pagehide' and 'DOMContentLoaded' event listeners.
+   * Add 'pagehide' and 'DOMContentLoaded' event listeners.
    */
   void AddListeners(nsIDocument *aDocument, PRBool aAddPageShowListener);
-  void RemoveListeners(nsIDocument *aDocument);
 
   /**
    * Create document or root accessible.
    */
   nsDocAccessible *CreateDocOrRootAccessible(nsIDocument *aDocument);
 
   /**
    * Shutdown document accessibles in the tree starting from given tree item.
    */
   void ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
                                     nsIDocument *aDocument);
 
-  /**
-   * Shutdown the document accessible.
-   */
-  void ShutdownDocAccessible(nsIDocument *aDocument);
-
   typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible>
     nsDocAccessibleHashtable;
 
   /**
    * Shutdown and remove the document accessible from cache.
    */
   static PLDHashOperator
     ClearDocCacheEntry(const void* aKey,
@@ -189,30 +193,26 @@ private:
                                void* aUserArg);
 
   nsDocAccessibleHashtable mDocAccessibleCache;
 };
 
 /**
  * nsAccDocManager debugging macros.
  */
-//#define DEBUG_ACCDOCMGR
-
 #ifdef DEBUG_ACCDOCMGR
 
 // Enable these to log accessible document loading, creation or destruction.
 #define DEBUG_ACCDOCMGR_DOCLOAD
 #define DEBUG_ACCDOCMGR_DOCCREATE
 #define DEBUG_ACCDOCMGR_DOCDESTROY
 
 // Common macros, do not use directly.
 #define NS_LOG_ACCDOC_ADDRESS(aDocument, aDocAcc)                              \
-  printf("DOM id: 0x%x, acc id: 0x%x",                                         \
-         reinterpret_cast<PRInt32>(static_cast<void*>(aDocument)),             \
-         reinterpret_cast<PRInt32>(aDocAcc));
+  printf("DOM id: %p, acc id: %p", aDocument, aDocAcc);
 
 #define NS_LOG_ACCDOC_URI(aDocument)                                           \
   nsIURI *uri = aDocument->GetDocumentURI();                                   \
   nsCAutoString spec;                                                          \
   uri->GetSpec(spec);                                                          \
   printf("uri: %s", spec);
 
 #define NS_LOG_ACCDOC_TYPE(aDocument)                                          \
@@ -255,29 +255,28 @@ private:
   printf("doc state: %s", docState);                                           \
   printf(", %sinitial", aDocument->IsInitialDocument() ? "" : "not ");         \
   printf(", %sshowing", aDocument->IsShowing() ? "" : "not ");                 \
   printf(", %svisible", aDocument->IsVisible() ? "" : "not ");                 \
   printf(", %sactive", aDocument->IsActive() ? "" : "not ");
 
 #define NS_LOG_ACCDOC_DOCPRESSHELL(aDocument)                                  \
   nsIPresShell *ps = aDocument->GetPrimaryShell();                             \
-  printf("presshell: 0x%x", reinterpret_cast<PRInt32>(ps));                    \
+  printf("presshell: %p", ps);                                                 \
   nsIScrollableFrame *sf = ps ?                                                \
     ps->GetRootScrollFrameAsScrollableExternal() : nsnull;                     \
-  printf(", root scroll frame: 0x%x", reinterpret_cast<PRInt32>(sf));
+  printf(", root scroll frame: %p", sf);
 
 #define NS_LOG_ACCDOC_DOCLOADGROUP(aDocument)                                  \
   nsCOMPtr<nsILoadGroup> loadGroup = aDocument->GetDocumentLoadGroup();        \
-  printf("load group: 0x%x", reinterpret_cast<PRInt32>(loadGroup.get()));
+  printf("load group: %p", loadGroup);
 
 #define NS_LOG_ACCDOC_DOCPARENT(aDocument)                                     \
   nsIDocument *parentDoc = aDocument->GetParentDocument();                     \
-  printf("parent id: 0x%x",                                                    \
-         reinterpret_cast<PRInt32>(parentDoc));                                \
+  printf("parent id: %p", parentDoc);                                          \
   if (parentDoc) {                                                             \
     printf("\n    parent ");                                                   \
     NS_LOG_ACCDOC_URI(parentDoc)                                               \
     printf("\n");                                                              \
   }
 
 #define NS_LOG_ACCDOC_SHELLLOADTYPE(aDocShell)                                 \
   {                                                                            \
@@ -392,16 +391,31 @@ private:
       strEventType.AssignLiteral("busy ");                                     \
       if (event->IsStateEnabled())                                             \
         strEventType.AppendLiteral("true");                                    \
       else                                                                     \
         strEventType.AppendLiteral("false");                                   \
     }                                                                          \
   }
 
+#define NS_LOG_ACCDOC_ACCADDRESS(aName, aAcc)                                  \
+  {                                                                            \
+    nsINode* node = aAcc->GetNode();                                           \
+    nsIDocument* doc = aAcc->GetDocumentNode();                                \
+    nsDocAccessible *docacc = GetAccService()->GetDocAccessibleFromCache(doc); \
+    printf("  " aName " accessible: %p, node: %p\n", aAcc, node);              \
+    printf("  docacc for " aName " accessible: %p, node: %p\n", docacc, doc);  \
+    printf("  ");                                                              \
+    NS_LOG_ACCDOC_URI(doc)                                                     \
+    printf("\n");                                                              \
+  }
+
+#define NS_LOG_ACCDOC_MSG(aMsg)                                                \
+  printf("\n" aMsg "\n");                                                      \
+
 #define NS_LOG_ACCDOC_TEXT(aMsg)                                               \
   printf("  " aMsg "\n");
 
 // Accessible document loading macros.
 #ifdef DEBUG_ACCDOCMGR_DOCLOAD
 
 #define NS_LOG_ACCDOCLOAD_REQUEST(aRequest)                                    \
   if (aRequest) {                                                              \
@@ -426,27 +440,27 @@ private:
     if (loadFlags & nsIChannel::LOAD_CLASSIFY_URI)                             \
       printf("classify uri; ");                                                \
   } else {                                                                     \
     printf("    no request");                                                  \
   }
 
 #define NS_LOG_ACCDOCLOAD(aMsg, aWebProgress, aRequest, aStateFlags)           \
   {                                                                            \
-    printf("\nA11Y DOCLOAD: " aMsg "\n");                                      \
+    NS_LOG_ACCDOC_MSG("A11Y DOCLOAD: " aMsg);                                  \
                                                                                \
     nsCOMPtr<nsIDOMWindow> DOMWindow;                                          \
     aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow));                     \
     if (DOMWindow) {                                                           \
       nsCOMPtr<nsIDOMDocument> DOMDocument;                                    \
       DOMWindow->GetDocument(getter_AddRefs(DOMDocument));                     \
       if (DOMDocument) {                                                       \
         nsCOMPtr<nsIDocument> document(do_QueryInterface(DOMDocument));        \
         nsDocAccessible *docAcc =                                              \
-          mDocAccessibleCache.GetWeak(static_cast<void*>(document));           \
+          GetAccService()->GetDocAccessibleFromCache(document);                \
         NS_LOG_ACCDOC_DOCINFO(document, docAcc)                                \
                                                                                \
         printf("  {\n");                                                       \
         nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));         \
         nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));             \
         printf("    ");                                                        \
         NS_LOG_ACCDOC_SHELLLOADTYPE(docShell)                                  \
         printf("\n");                                                          \
@@ -458,19 +472,19 @@ private:
         printf(", document is %sloading\n", (isDocLoading ? "" : "not "));     \
         printf("  }\n");                                                       \
       }                                                                        \
     }                                                                          \
   }
 
 #define NS_LOG_ACCDOCLOAD2(aMsg, aDocument)                                    \
   {                                                                            \
-    printf("\nA11Y DOCLOAD: " aMsg "\n");                                      \
+    NS_LOG_ACCDOC_MSG("A11Y DOCLOAD: " aMsg);                                  \
     nsDocAccessible *docAcc =                                                  \
-      mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));              \
+      GetAccService()->GetDocAccessibleFromCache(aDocument);                   \
     NS_LOG_ACCDOC_DOCINFO(aDocument, docAcc)                                   \
   }
 
 #define NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent)                                    \
   {                                                                            \
     NS_GET_ACCDOC_EVENTTYPE(aEvent)                                            \
     if (!strEventType.IsEmpty())                                               \
       printf("  fire: %s\n", strEventType.get());                              \
@@ -491,44 +505,55 @@ private:
 #define NS_LOG_ACCDOCLOAD_TEXT(aMsg)                                           \
     NS_LOG_ACCDOC_TEXT(aMsg)
 
 #endif // DEBUG_ACCDOCMGR_DOCLOAD
 
 // Accessible document creation macros.
 #ifdef DEBUG_ACCDOCMGR_DOCCREATE
 #define NS_LOG_ACCDOCCREATE_FOR(aMsg, aDocument, aDocAcc)                      \
-  printf("\nA11Y DOCCREATE: " aMsg "\n");                                      \
+  NS_LOG_ACCDOC_MSG("A11Y DOCCREATE: " aMsg);                                  \
   NS_LOG_ACCDOC_DOCINFO(aDocument, aDocAcc)
 
 #define NS_LOG_ACCDOCCREATE(aMsg, aDocument)                                   \
   {                                                                            \
     nsDocAccessible *docAcc =                                                  \
-      mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));              \
+      GetAccService()->GetDocAccessibleFromCache(aDocument);                   \
     NS_LOG_ACCDOCCREATE_FOR(aMsg, aDocument, docAcc)                           \
   }
 
+#define NS_LOG_ACCDOCCREATE_ACCADDRESS(aName, aAcc)                            \
+  NS_LOG_ACCDOC_ACCADDRESS(aName, aAcc)
+
 #define NS_LOG_ACCDOCCREATE_TEXT(aMsg)                                         \
     NS_LOG_ACCDOC_TEXT(aMsg)
 
 #endif // DEBUG_ACCDOCMGR_DOCCREATE
 
 // Accessible document destruction macros.
 #ifdef DEBUG_ACCDOCMGR_DOCDESTROY
 #define NS_LOG_ACCDOCDESTROY_FOR(aMsg, aDocument, aDocAcc)                     \
-  printf("\nA11Y DOCDESTROY: " aMsg "\n");                                     \
+  NS_LOG_ACCDOC_MSG("A11Y DOCDESTROY: " aMsg);                                 \
   NS_LOG_ACCDOC_DOCINFO(aDocument, aDocAcc)
 
 #define NS_LOG_ACCDOCDESTROY(aMsg, aDocument)                                  \
-  nsDocAccessible *docAcc =                                                    \
-    mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));                \
-  NS_LOG_ACCDOCDESTROY_FOR(aMsg, aDocument, docAcc)
+  {                                                                            \
+    nsDocAccessible* docAcc =                                                  \
+      GetAccService()->GetDocAccessibleFromCache(aDocument);                   \
+    NS_LOG_ACCDOCDESTROY_FOR(aMsg, aDocument, docAcc)                          \
+  }
 
-#define NS_LOG_ACCDOCDESTROY_TEXT(aMsg)                                       \
-    NS_LOG_ACCDOC_TEXT(aMsg)
+#define NS_LOG_ACCDOCDESTROY_ACCADDRESS(aName, aAcc)                           \
+  NS_LOG_ACCDOC_ACCADDRESS(aName, aAcc)
+
+#define NS_LOG_ACCDOCDESTROY_MSG(aMsg)                                         \
+  NS_LOG_ACCDOC_MSG(aMsg)
+
+#define NS_LOG_ACCDOCDESTROY_TEXT(aMsg)                                        \
+  NS_LOG_ACCDOC_TEXT(aMsg)
 
 #endif // DEBUG_ACCDOCMGR_DOCDESTROY
 
 #endif // DEBUG_ACCDOCMGR
 
 #ifndef DEBUG_ACCDOCMGR_DOCLOAD
 #define NS_LOG_ACCDOCLOAD(aMsg, aWebProgress, aRequest, aStateFlags)
 #define NS_LOG_ACCDOCLOAD2(aMsg, aDocument)
@@ -536,18 +561,21 @@ private:
 #define NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent)
 #define NS_LOG_ACCDOCLOAD_HANDLEEVENT(aEvent)
 #define NS_LOG_ACCDOCLOAD_TEXT(aMsg)
 #endif
 
 #ifndef DEBUG_ACCDOCMGR_DOCCREATE
 #define NS_LOG_ACCDOCCREATE_FOR(aMsg, aDocument, aDocAcc)
 #define NS_LOG_ACCDOCCREATE(aMsg, aDocument)
+#define NS_LOG_ACCDOCCREATE_ACCADDRESS(aName, aAcc)
 #define NS_LOG_ACCDOCCREATE_TEXT(aMsg)
 #endif
 
 #ifndef DEBUG_ACCDOCMGR_DOCDESTROY
 #define NS_LOG_ACCDOCDESTROY_FOR(aMsg, aDocument, aDocAcc)
 #define NS_LOG_ACCDOCDESTROY(aMsg, aDocument)
+#define NS_LOG_ACCDOCDESTROY_MSG(aMsg)
+#define NS_LOG_ACCDOCDESTROY_ACCADDRESS(aName, aAcc)
 #define NS_LOG_ACCDOCDESTROY_TEXT(aMsg)
 #endif
 
 #endif // nsAccDocManager_h_
--- a/accessible/src/base/nsAccessNode.h
+++ b/accessible/src/base/nsAccessNode.h
@@ -152,16 +152,18 @@ public:
     return DOMNode;
   }
 
   /**
    * Return DOM node associated with the accessible.
    */
   virtual nsINode* GetNode() const { return mContent; }
   nsIContent* GetContent() const { return mContent; }
+  nsIDocument* GetDocumentNode() const
+    { return mContent ? mContent->GetOwnerDoc() : nsnull; }
 
   /**
    * Return node type information of DOM node associated with the accessible.
    */
   PRBool IsContent() const
   {
     return GetNode() && GetNode()->IsNodeOfType(nsINode::eCONTENT);
   }
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -775,16 +775,35 @@ nsAccessibilityService::CreateHTMLCaptio
   *aAccessible = new nsHTMLCaptionAccessible(content, weakShell);
   if (!*aAccessible)
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ADDREF(*aAccessible);
   return NS_OK;
 }
 
+void
+nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
+{
+  // Presshell destruction will automatically destroy shells for descendant
+  // documents, so no need to worry about those. Just shut down the accessible
+  // for this one document. That keeps us from having bad behavior in case of
+  // deep bushy subtrees.
+  // When document subtree containing iframe is hidden then we don't get
+  // pagehide event for the iframe's underlying document and its presshell is
+  // destroyed before we're notified styles were changed. Shutdown the document
+  // accessible early.
+  nsIDocument* doc = aPresShell->GetDocument();
+  if (!doc)
+    return;
+
+  NS_LOG_ACCDOCDESTROY("presshell destroyed", doc)
+  ShutdownDocAccessible(doc);
+}
+
 // nsAccessibilityService protected
 nsAccessible *
 nsAccessibilityService::GetCachedAccessible(nsINode *aNode,
                                             nsIWeakReference *aWeakShell)
 {
   nsDocAccessible *docAccessible = GetDocAccessible(aNode->GetOwnerDoc());
   return docAccessible ?
     docAccessible->GetCachedAccessible(static_cast<void*>(aNode)) : nsnull;
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -119,16 +119,18 @@ public:
   virtual nsresult RemoveNativeRootAccessible(nsIAccessible *aRootAccessible);
 
   virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell,
                                         nsIContent *aContent,
                                         PRUint32 aChangeType);
 
   virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
 
+  virtual void PresShellDestroyed(nsIPresShell* aPresShell);
+
   virtual nsresult FireAccessibleEvent(PRUint32 aEvent, nsIAccessible *aTarget);
 
   // nsAccessibiltiyService
 
   /**
    * Return true if accessibility service has been shutdown.
    */
   static PRBool IsShutdown() { return gIsShutdown; }
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -591,16 +591,18 @@ nsDocAccessible::RemoveAccessNodeFromCac
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode
 
 PRBool
 nsDocAccessible::Init()
 {
+  NS_LOG_ACCDOCCREATE_FOR("document initialize", mDocument, this)
+
   // Initialize event queue.
   mEventQueue = new nsAccEventQueue(this);
   if (!mEventQueue)
     return PR_FALSE;
 
   AddEventListeners();
 
   // Fire reorder event to notify new accessible document has been created and
@@ -625,18 +627,18 @@ nsDocAccessible::Shutdown()
   if (mEventQueue) {
     mEventQueue->Shutdown();
     mEventQueue = nsnull;
   }
 
   RemoveEventListeners();
 
   if (mParent) {
-    NS_LOG_ACCDOCDESTROY_FOR("remove document from outer doc", mDocument, this);
     mParent->RemoveChild(this);
+    mParent = nsnull;
   }
 
   mWeakShell = nsnull;  // Avoid reentrancy
 
   ClearCache(mAccessibleCache);
 
   nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
   mDocument = nsnull;
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -119,18 +119,16 @@ public:
   virtual nsresult HandleAccEvent(nsAccEvent *aAccEvent);
 #endif
 
   // nsIAccessibleText
   NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
 
   // nsDocAccessible
 
-  nsIDocument *GetDOMDocument() const { return mDocument; }
-
   /**
    * Return true if associated DOM document was loaded and isn't unloading.
    */
   PRBool IsContentLoaded() const
   {
     return mDocument && mDocument->IsVisible() &&
       (mDocument->IsShowing() || mIsLoaded);
   }
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -154,27 +154,30 @@ nsOuterDocAccessible::DoAction(PRUint8 a
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessNode public
 
 void
 nsOuterDocAccessible::Shutdown()
 {
-  // Shutdown child document if any.
+  // XXX: sometimes outerdoc accessible is shutdown because of layout style
+  // change however the presshell of underlying document isn't destroyed and
+  // the document doesn't get pagehide events. Shutdown underlying document if
+  // any to avoid hanging document accessible.
+  NS_LOG_ACCDOCDESTROY_MSG("A11y outerdoc shutdown")
+  NS_LOG_ACCDOCDESTROY_ACCADDRESS("outerdoc", this)
+
   nsAccessible *childAcc = mChildren.SafeElementAt(0, nsnull);
   if (childAcc) {
-    nsRefPtr<nsDocAccessible> docAcc(do_QueryObject(childAcc));
-    NS_LOG_ACCDOCDESTROY_FOR("outerdoc document shutdown",
-                             docAcc->GetDOMDocument(), docAcc.get())
-    GetAccService()->ShutdownDocAccessiblesInTree(docAcc->GetDOMDocument());
+    NS_LOG_ACCDOCDESTROY("outerdoc's child document shutdown",
+                         childAcc->GetDocumentNode())
+    GetAccService()->ShutdownDocAccessiblesInTree(childAcc->GetDocumentNode());
   }
 
-  nsAccessible::InvalidateChildren();
-
   nsAccessibleWrap::Shutdown();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible public
 
 void
 nsOuterDocAccessible::InvalidateChildren()
@@ -196,35 +199,46 @@ nsOuterDocAccessible::AppendChild(nsAcce
 {
   NS_ASSERTION(!mChildren.Length(),
                "Previous child document of outerdoc accessible wasn't removed!");
 
   if (!mChildren.AppendElement(aAccessible))
     return PR_FALSE;
 
   aAccessible->SetParent(this);
+
+  NS_LOG_ACCDOCCREATE("append document to outerdoc",
+                      aAccessible->GetDocumentNode())
+  NS_LOG_ACCDOCCREATE_ACCADDRESS("outerdoc", this)
+
   return PR_TRUE;
 }
 
 PRBool
 nsOuterDocAccessible::RemoveChild(nsAccessible *aAccessible)
 {
   nsAccessible *child = mChildren.SafeElementAt(0, nsnull);
   if (child != aAccessible) {
     NS_ERROR("Wrong child to remove!");
     return PR_FALSE;
   }
 
+  NS_LOG_ACCDOCDESTROY("remove document from outerdoc",
+                       child->GetDocumentNode())
+  NS_LOG_ACCDOCDESTROY_ACCADDRESS("outerdoc", this)
+
   mChildren.RemoveElement(child);
+
   NS_ASSERTION(!mChildren.Length(),
                "This child document of outerdoc accessible wasn't removed!");
 
   return PR_TRUE;
 }
 
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible protected
 
 void
 nsOuterDocAccessible::CacheChildren()
 {
   // Request document accessible for the content document to make sure it's
   // created because once it's created it appends itself as a child.
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -21,16 +21,21 @@ const EVENT_VALUE_CHANGE = nsIAccessible
 // General
 
 /**
  * Set up this variable to dump events into DOM.
  */
 var gA11yEventDumpID = "";
 
 /**
+ * Set up this variable to dump event processing into console.
+ */
+var gA11yEventDumpToConsole = false;
+
+/**
  * Executes the function when requested event is handled.
  *
  * @param aEventType  [in] event type
  * @param aTarget     [in] event target
  * @param aFunc       [in] function to call when event is handled
  * @param aContext    [in, optional] object in which context the function is
  *                    called
  * @param aArg1       [in, optional] argument passed into the function
@@ -257,16 +262,19 @@ function eventQueue(aEventType)
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
     this.setEventHandler(invoker);
 
+    if (gA11yEventDumpToConsole)
+      dump("\nEvent queue: \n  invoke: " + invoker.getID() + "\n");
+
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
     if (this.areAllEventsUnexpected())
       this.processNextInvokerInTimeout(true);
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1811,16 +1811,26 @@ PresShell::Destroy()
     delete mReflowCountMgr;
     mReflowCountMgr = nsnull;
   }
 #endif
 
   if (mHaveShutDown)
     return;
 
+#ifdef ACCESSIBILITY
+  if (gIsAccessibilityActive) {
+    nsCOMPtr<nsIAccessibilityService> accService =
+      do_GetService("@mozilla.org/accessibilityService;1");
+    if (accService) {
+      accService->PresShellDestroyed(this);
+    }
+  }
+#endif // ACCESSIBILITY
+
   MaybeReleaseCapturingContent();
 
   mContentToScrollTo = nsnull;
 
   if (mPresContext) {
     // We need to notify the destroying the nsPresContext to ESM for
     // suppressing to use from ESM.
     mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);