Bug 489050. Make sure to not notify on our content once we've disconnected from the document. r=bent, sr=sicking
authorBoris Zbarsky <bzbarsky@mit.edu>
Sun, 17 May 2009 10:22:55 -0400
changeset 28461 7eac29fbea15b0678e388b526265f18111f3d05c
parent 28460 cab8d8a075dab7a739b1d460c93d0679131034b2
child 28462 ce5e54260f1d9aa02d65df2e2b15ef85def4af9d
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)
reviewersbent, sicking
bugs489050
milestone1.9.2a1pre
Bug 489050. Make sure to not notify on our content once we've disconnected from the document. r=bent, sr=sicking
content/base/src/nsContentSink.h
content/html/document/src/nsHTMLContentSink.cpp
content/xml/document/src/nsXMLContentSink.cpp
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -345,16 +345,20 @@ protected:
   PRUint8 mDroppedTimer : 1;
   PRUint8 mChangeScrollPosWhenScrollingToRef : 1;
   // If true, we deferred starting layout until sheets load
   PRUint8 mDeferredLayoutStart : 1;
   // If true, we deferred notifications until sheets load
   PRUint8 mDeferredFlushTags : 1;
   // If true, we did get a ReadyToCallDidBuildModel call
   PRUint8 mDidGetReadyToCallDidBuildModelCall : 1;
+  // If false, we're not ourselves a document observer; that means we
+  // shouldn't be performing any more content model notifications,
+  // since we're not longer updating our child counts.
+  PRUint8 mIsDocumentObserver : 1;
   
   // -- Can interrupt parsing members --
   PRUint32 mDelayTimerStart;
 
   // Interrupt parsing during token procesing after # of microseconds
   PRInt32 mMaxTokenProcessingTime;
 
   // Switch between intervals when time is exceeded
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -1642,16 +1642,17 @@ HTMLContentSink::Init(nsIDocument* aDoc,
   nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
   if (NS_FAILED(rv)) {
     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
     MOZ_TIMER_STOP(mWatch);
     return rv;
   }
 
   aDoc->AddObserver(this);
+  mIsDocumentObserver = PR_TRUE;
   CallQueryInterface(aDoc, &mHTMLDocument);
 
   mObservers = nsnull;
   nsIParserService* service = nsContentUtils::GetParserService();
   if (!service) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
@@ -1823,18 +1824,20 @@ HTMLContentSink::DidBuildModel(void)
   ScrollToRef();
 
   mDocument->ScriptLoader()->RemoveObserver(this);
 
   // Make sure we no longer respond to document mutations.  We've flushed all
   // our notifications out, so there's no need to do anything else here.
 
   // XXXbz I wonder whether we could End() our contexts here too, or something,
-  // just to make sure we no longer notify...
+  // just to make sure we no longer notify...  Or is the mIsDocumentObserver
+  // thing sufficient?
   mDocument->RemoveObserver(this);
+  mIsDocumentObserver = PR_FALSE;
   
   mDocument->EndLoad();
 
   DropParserAndPerfHint();
 
   return NS_OK;
 }
 
@@ -3189,21 +3192,25 @@ HTMLContentSink::ProcessSTYLEEndTag(nsGe
 }
 
 void
 HTMLContentSink::FlushPendingNotifications(mozFlushType aType)
 {
   // Only flush tags if we're not doing the notification ourselves
   // (since we aren't reentrant)
   if (!mInNotification) {
-    if (aType >= Flush_ContentAndNotify) {
-      FlushTags();
-    }
-    else if (mCurrentContext) {
-      mCurrentContext->FlushText();
+    // Only flush if we're still a document observer (so that our child counts
+    // should be correct).
+    if (mIsDocumentObserver) {
+      if (aType >= Flush_ContentAndNotify) {
+        FlushTags();
+      }
+      else if (mCurrentContext) {
+        mCurrentContext->FlushText();
+      }
     }
     if (aType >= Flush_InterruptibleLayout) {
       // Make sure that layout has started so that the reflow flush
       // will actually happen.
       StartLayout(PR_TRUE);
     }
   }
 }
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -162,16 +162,17 @@ nsXMLContentSink::Init(nsIDocument* aDoc
                       this));
   MOZ_TIMER_RESET(mWatch);
   MOZ_TIMER_START(mWatch);
 	
   nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
 
   aDoc->AddObserver(this);
+  mIsDocumentObserver = PR_TRUE;
 
   if (!mDocShell) {
     mPrettyPrintXML = PR_FALSE;
   }
   
   mState = eXMLContentSinkState_InProlog;
   mDocElement = nsnull;
 
@@ -242,16 +243,17 @@ nsXMLContentSink::MaybePrettyPrint()
   if (!CanStillPrettyPrint()) {
     mPrettyPrintXML = PR_FALSE;
 
     return NS_OK;
   }
 
   // stop observing in order to avoid crashing when replacing content
   mDocument->RemoveObserver(this);
+  mIsDocumentObserver = PR_FALSE;
 
   // Reenable the CSSLoader so that the prettyprinting stylesheets can load
   if (mCSSLoader) {
     mCSSLoader->SetEnabled(PR_TRUE);
   }
   
   nsCOMPtr<nsXMLPrettyPrinter> printer;
   nsresult rv = NS_NewXMLPrettyPrinter(getter_AddRefs(printer));
@@ -310,16 +312,17 @@ CheckXSLTParamPI(nsIDOMProcessingInstruc
 NS_IMETHODIMP
 nsXMLContentSink::DidBuildModel()
 {
   DidBuildModelImpl();
 
   if (mXSLTProcessor) {
     // stop observing in order to avoid crashing when replacing content
     mDocument->RemoveObserver(this);
+    mIsDocumentObserver = PR_FALSE;
 
     // Check for xslt-param and xslt-param-namespace PIs
     PRUint32 i;
     nsIContent* child;
     for (i = 0; (child = mDocument->GetChildAt(i)); ++i) {
       if (child->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
         nsCOMPtr<nsIDOMProcessingInstruction> pi = do_QueryInterface(child);
         CheckXSLTParamPI(pi, mXSLTProcessor, mDocument);
@@ -368,16 +371,17 @@ nsXMLContentSink::DidBuildModel()
     
     if (startLayout) {
       StartLayout(PR_FALSE);
 
       ScrollToRef();
     }
 
     mDocument->RemoveObserver(this);
+    mIsDocumentObserver = PR_FALSE;
 
     mDocument->EndLoad();
   }
 
   DropParserAndPerfHint();
 
   return NS_OK;
 }
@@ -1480,16 +1484,17 @@ nsXMLContentSink::ReportError(const PRUn
   mPrettyPrintXML = PR_FALSE;
 
   mState = eXMLContentSinkState_InProlog;
 
   // XXX need to stop scripts here -- hsivonen
 
   // stop observing in order to avoid crashing when removing content
   mDocument->RemoveObserver(this);
+  mIsDocumentObserver = PR_FALSE;
 
   // Clear the current content and
   // prepare to set <parsererror> as the document root
   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mDocument));
   if (node) {
     for (;;) {
       nsCOMPtr<nsIDOMNode> child, dummy;
       node->GetLastChild(getter_AddRefs(child));
@@ -1642,21 +1647,25 @@ nsXMLContentSink::AddText(const PRUnicha
 }
 
 void
 nsXMLContentSink::FlushPendingNotifications(mozFlushType aType)
 {
   // Only flush tags if we're not doing the notification ourselves
   // (since we aren't reentrant)
   if (!mInNotification) {
-    if (aType >= Flush_ContentAndNotify) {
-      FlushTags();
-    }
-    else {
-      FlushText(PR_FALSE);
+    if (mIsDocumentObserver) {
+      // Only flush if we're still a document observer (so that our child
+      // counts should be correct).
+      if (aType >= Flush_ContentAndNotify) {
+        FlushTags();
+      }
+      else {
+        FlushText(PR_FALSE);
+      }
     }
     if (aType >= Flush_InterruptibleLayout) {
       // Make sure that layout has started so that the reflow flush
       // will actually happen.
       MaybeStartLayout(PR_TRUE);
     }
   }
 }