Bug 402982, r=jst, sr=sicking
authorOlli.Pettay@helsinki.fi
Tue, 26 Feb 2008 06:47:51 -0800
changeset 12256 c85cd6fba5dae6bfd69d9f62383c7093be2f9822
parent 12255 5829028a931bcc92ebca9a8bcf9a5be46a75a0f6
child 12257 4cfa3f1ffdbf3a170c4280f113a3fe63f7b5cfe2
push idunknown
push userunknown
push dateunknown
reviewersjst, sicking
bugs402982
milestone1.9b4pre
Bug 402982, r=jst, sr=sicking
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -88,21 +88,22 @@ class nsILayoutHistoryState;
 class nsIVariant;
 class nsIDOMUserDataHandler;
 template<class E> class nsCOMArray;
 class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
+class nsFrameLoader;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0x626d86d2, 0x615f, 0x4a12, \
- { 0x94, 0xd8, 0xe3, 0xdb, 0x3a, 0x29, 0x83, 0x72 } }
+{ 0x3c441ae9, 0xaa82, 0x4102, \
+  { 0xa3, 0x16, 0x3b, 0x97, 0x2b, 0x60, 0x2b, 0x05 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -946,16 +947,17 @@ public:
   virtual already_AddRefed<nsIParser> GetFragmentParser() {
     return nsnull;
   }
 
   virtual void SetFragmentParser(nsIParser* aParser) {
     // Do nothing.
   }
 
+  virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) = 0;
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
     // XXX Same thing applies to mBindingManager
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -153,16 +153,18 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsDOMCID.h"
 
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 #include "nsCycleCollector.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsIContentPolicy.h"
 
+#include "nsFrameLoader.h"
+
 #ifdef MOZ_LOGGING
 // so we can get logging even in release builds
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
@@ -2714,16 +2716,27 @@ nsDocument::EndUpdate(nsUpdateType aUpda
     // This set of updates may have created XBL bindings.  Let the
     // binding manager know we're done.
     mBindingManager->EndOutermostUpdate();
   }
 
   if (mScriptLoader) {
     mScriptLoader->RemoveExecuteBlocker();
   }
+
+  if (mUpdateNestLevel == 0) {
+    PRUint32 length = mFinalizableFrameLoaders.Length();
+    if (length > 0) {
+      nsTArray<nsRefPtr<nsFrameLoader> > loaders;
+      mFinalizableFrameLoaders.SwapElements(loaders);
+      for (PRInt32 i = 0; i < length; ++i) {
+        loaders[i]->Finalize();
+      }
+    }
+  }
 }
 
 void
 nsDocument::BeginLoad()
 {
   // Block onload here to prevent having to deal with blocking and
   // unblocking it while we know the document is loading.
   BlockOnload();
@@ -3820,16 +3833,31 @@ nsDocument::GetContentListFor(nsIContent
 }
 
 void
 nsDocument::FlushSkinBindings()
 {
   mBindingManager->FlushSkinBindings();
 }
 
+nsresult
+nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
+{
+  if (mInDestructor) {
+    return NS_ERROR_FAILURE;
+  }
+  if (mUpdateNestLevel == 0) {
+    nsRefPtr<nsFrameLoader> loader = aLoader;
+    loader->Finalize();
+  } else {
+    mFinalizableFrameLoaders.AppendElement(aLoader);
+  }
+  return NS_OK;
+}
+
 struct DirTable {
   const char* mName;
   PRUint8     mValue;
 };
 
 static const DirTable dirAttributes[] = {
   {"ltr", IBMBIDI_TEXTDIRECTION_LTR},
   {"rtl", IBMBIDI_TEXTDIRECTION_RTL},
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -642,16 +642,18 @@ public:
 
   NS_HIDDEN_(void) ClearBoxObjectFor(nsIContent* aContent);
 
   virtual NS_HIDDEN_(nsresult) GetXBLChildNodesFor(nsIContent* aContent,
                                                    nsIDOMNodeList** aResult);
   virtual NS_HIDDEN_(nsresult) GetContentListFor(nsIContent* aContent,
                                                  nsIDOMNodeList** aResult);
   virtual NS_HIDDEN_(void) FlushSkinBindings();
+  
+  virtual NS_HIDDEN_(nsresult) FinalizeFrameLoader(nsFrameLoader* aLoader);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocument, nsIDocument)
 
   /**
    * Utility method for getElementsByClassName.  aRootNode is the node (either
    * document or element), which getElementsByClassName was called on.
    */
   static nsresult GetElementsByClassNameHelper(nsINode* aRootNode,
@@ -834,12 +836,14 @@ private:
   
   // A map from unvisited URI hashes to content elements
   nsTHashtable<nsUint32ToContentHashEntry> mLinkMap;
   // URIs whose visitedness has changed while we were hidden
   nsCOMArray<nsIURI> mVisitednessChangedURIs;
 
   // Member to store out last-selected stylesheet set.
   nsString mLastStyleSheetSet;
+
+  nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
 };
 
 
 #endif /* nsDocument_h___ */
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -68,16 +68,36 @@
 
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 
 #include "nsGkAtoms.h"
 #include "nsINameSpaceManager.h"
 
+#include "nsThreadUtils.h"
+
+class nsAsyncDocShellDestroyer : public nsRunnable
+{
+public:
+  nsAsyncDocShellDestroyer(nsIDocShell* aDocShell)
+    : mDocShell(aDocShell)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
+    if (base_win) {
+      base_win->Destroy();
+    }
+  }
+  nsRefPtr<nsIDocShell> mDocShell;
+};
+
 // Bug 136580: Limit to the number of nested content frames that can have the
 //             same URL. This is to stop content that is recursively loading
 //             itself.  Note that "#foo" on the end of URL doesn't affect
 //             whether it's considered identical, but "?foo" or ";foo" are
 //             considered and compared.
 // Bug 228829: Limit this to 1, like IE does.
 #define MAX_SAME_URL_CONTENT_FRAMES 1
 
@@ -135,16 +155,17 @@ nsFrameLoader::LoadFrame()
   NS_ENSURE_SUCCESS(rv, rv);
   return LoadURI(uri);
 }
 
 NS_IMETHODIMP
 nsFrameLoader::LoadURI(nsIURI* aURI)
 {
   NS_PRECONDITION(aURI, "Null URI?");
+  NS_ENSURE_STATE(!mDestroyCalled);
   if (!aURI)
     return NS_ERROR_INVALID_POINTER;
 
   nsIDocument* doc = mOwnerContent->GetOwnerDoc();
   if (!doc) {
     return NS_OK;
   }
 
@@ -222,21 +243,37 @@ nsFrameLoader::GetDocShell(nsIDocShell *
   }
 
   *aDocShell = mDocShell;
   NS_IF_ADDREF(*aDocShell);
 
   return NS_OK;
 }
 
+void
+nsFrameLoader::Finalize()
+{
+  nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
+  if (base_win) {
+    base_win->Destroy();
+  }
+  mDocShell = nsnull;
+}
+
 NS_IMETHODIMP
 nsFrameLoader::Destroy()
 {
+  if (mDestroyCalled) {
+    return NS_OK;
+  }
+  mDestroyCalled = PR_TRUE;
+
+  nsCOMPtr<nsIDocument> doc;
   if (mOwnerContent) {
-    nsCOMPtr<nsIDocument> doc = mOwnerContent->GetDocument();
+    doc = mOwnerContent->GetOwnerDoc();
 
     if (doc) {
       doc->SetSubDocumentFor(mOwnerContent, nsnull);
     }
 
     mOwnerContent = nsnull;
   }
 
@@ -253,40 +290,48 @@ nsFrameLoader::Destroy()
     }
   }
   
   // Let our window know that we are gone
   nsCOMPtr<nsPIDOMWindow> win_private(do_GetInterface(mDocShell));
   if (win_private) {
     win_private->SetFrameElementInternal(nsnull);
   }
-  
-  nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
 
-  if (base_win) {
-    base_win->Destroy();
+  if ((mInDestructor || !doc ||
+       NS_FAILED(doc->FinalizeFrameLoader(this))) && mDocShell) {
+    nsCOMPtr<nsIRunnable> event = new nsAsyncDocShellDestroyer(mDocShell);
+    NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+    NS_DispatchToCurrentThread(event);
+
+    // Let go of our docshell now that the async destroyer holds on to
+    // the docshell.
+
+    mDocShell = nsnull;
   }
 
-  mDocShell = nsnull;
+  // NOTE: 'this' may very well be gone by now.
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetDepthTooGreat(PRBool* aDepthTooGreat)
 {
   *aDepthTooGreat = mDepthTooGreat;
   return NS_OK;
 }
 
 nsresult
 nsFrameLoader::EnsureDocShell()
 {
   if (mDocShell) {
     return NS_OK;
   }
+  NS_ENSURE_STATE(!mDestroyCalled);
 
   // Get our parent docshell off the document of mOwnerContent
   // XXXbz this is such a total hack.... We really need to have a
   // better setup for doing this.
   nsIDocument* doc = mOwnerContent->GetDocument();
   if (!doc) {
     return NS_ERROR_UNEXPECTED;
   }
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -52,29 +52,37 @@ class nsIContent;
 class nsIURI;
 
 class nsFrameLoader : public nsIFrameLoader
 {
 public:
   nsFrameLoader(nsIContent *aOwner) :
     mOwnerContent(aOwner),
     mDepthTooGreat(PR_FALSE),
-    mIsTopLevelContent(PR_FALSE)
+    mIsTopLevelContent(PR_FALSE),
+    mDestroyCalled(PR_FALSE),
+    mInDestructor(PR_FALSE)
   {}
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsFrameLoader)
   NS_DECL_NSIFRAMELOADER
   NS_HIDDEN_(nsresult) CheckForRecursiveLoad(nsIURI* aURI);
+  void Finalize();
 private:
-  ~nsFrameLoader() { nsFrameLoader::Destroy(); }
+  ~nsFrameLoader() {
+    mInDestructor = PR_TRUE;
+    nsFrameLoader::Destroy();
+  }
 
   NS_HIDDEN_(nsresult) EnsureDocShell();
   NS_HIDDEN_(void) GetURL(nsString& aURL);
 
   nsCOMPtr<nsIDocShell> mDocShell;
 
   nsIContent *mOwnerContent; // WEAK
   PRPackedBool mDepthTooGreat;
   PRPackedBool mIsTopLevelContent;
+  PRPackedBool mDestroyCalled;
+  PRPackedBool mInDestructor;
 };
 
 #endif