Bug 830975, make sure to flush all the documents, yet keep the no-flush-needed case fast, r=mccr8,f=bz,a=lsblakk
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 24 Jan 2013 20:39:06 +0200
changeset 123780 932c91fda1cf3d4fda6f22f78a770622dfc3ccdb
parent 123779 5bc3b1121d9a9b9e576b6c66d109f4399cb7acba
child 123781 b1c12aa591e049ea9256e1ffdf403e75a2611d6c
push idunknown
push userunknown
push dateunknown
reviewersmccr8, lsblakk
bugs830975
milestone20.0a2
Bug 830975, make sure to flush all the documents, yet keep the no-flush-needed case fast, r=mccr8,f=bz,a=lsblakk
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1507,18 +1507,37 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
 
     return mXPathEvaluatorTearoff->QueryInterface(aIID, aInstancePtr);
   }
   else
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
-NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument,
-                                              nsNodeUtils::LastRelease(this))
+NS_IMETHODIMP_(nsrefcnt)
+nsDocument::Release()
+{
+  NS_PRECONDITION(0 != mRefCnt, "dup release");
+  NS_ASSERT_OWNINGTHREAD_AND_NOT_CCTHREAD(nsDocument);
+  nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
+  nsrefcnt count = mRefCnt.decr(base);
+  NS_LOG_RELEASE(this, count, "nsDocument");
+  if (count == 0) {
+    if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
+      mNeedsReleaseAfterStackRefCntRelease = true;
+      NS_ADDREF_THIS();
+      return mRefCnt.get();
+    }
+    NS_ASSERT_OWNINGTHREAD(nsDocument);
+    mRefCnt.stabilizeForDeletion();
+    nsNodeUtils::LastRelease(this);
+    return 0;
+  }
+  return count;
+}
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
   if (Element::CanSkip(tmp, aRemovingAllowed)) {
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
       elm->MarkForCC();
     }
     return true;
@@ -6429,16 +6448,18 @@ nsIDocument::CreateEvent(const nsAString
   rv = nsEventDispatcher::CreateEvent(presContext, nullptr,
                                       aEventType, getter_AddRefs(ev));
   return ev.forget();
 }
 
 void
 nsDocument::FlushPendingNotifications(mozFlushType aType)
 {
+  nsDocumentOnStack dos(this);
+
   // We need to flush the sink for non-HTML documents (because the XML
   // parser still does insertion with deferred notifications).  We
   // also need to flush the sink if this is a layout-related flush, to
   // make sure that layout is started as needed.  But we can skip that
   // part if we have no presshell or if it's already done an initial
   // reflow.
   if ((!IsHTML() ||
        (aType > Flush_ContentAndNotify && mPresShell &&
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -88,16 +88,17 @@ struct nsRadioGroupStruct;
 class nsOnloadBlocker;
 class nsUnblockOnloadEvent;
 class nsChildContentList;
 class nsHTMLStyleSheet;
 class nsHTMLCSSStyleSheet;
 class nsDOMNavigationTiming;
 class nsWindowSizes;
 class nsHtml5TreeOpExecutor;
+class nsDocumentOnStack;
 
 namespace mozilla {
 namespace dom {
 class UndoManager;
 }
 }
 
 /**
@@ -1011,16 +1012,30 @@ public:
   virtual nsIDOMStyleSheetList* StyleSheets();
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet);
   virtual void GetLastStyleSheetSet(nsString& aSheetSet);
   virtual nsIDOMDOMStringList* StyleSheetSets();
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet);
 
 protected:
   friend class nsNodeUtils;
+  friend class nsDocumentOnStack;
+
+  void IncreaseStackRefCnt()
+  {
+    ++mStackRefCnt;
+  }
+
+  void DecreaseStackRefCnt()
+  {
+    if (--mStackRefCnt == 0 && mNeedsReleaseAfterStackRefCntRelease) {
+      mNeedsReleaseAfterStackRefCntRelease = false;
+      NS_RELEASE_THIS();
+    }
+  }
 
   // Returns true if a request for DOM full-screen is currently enabled in
   // this document. This returns true if there are no windowed plugins in this
   // doc tree, and if the document is visible, and if the api is not
   // disabled by pref. aIsCallerChrome must contain the return value of
   // nsContentUtils::IsCallerChrome() from the context we're checking.
   // If aLogFailure is true, an appropriate warning message is logged to the
   // console, and a "mozfullscreenerror" event is dispatched to this document.
@@ -1354,21 +1369,39 @@ private:
   // Tracking for images in the document.
   nsDataHashtable< nsPtrHashKey<imgIRequest>, uint32_t> mImageTracker;
 
   // Tracking for plugins in the document.
   nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins;
 
   nsRefPtr<mozilla::dom::UndoManager> mUndoManager;
 
+  nsrefcnt mStackRefCnt;
+  bool mNeedsReleaseAfterStackRefCntRelease;
+
 #ifdef DEBUG
 protected:
   bool mWillReparent;
 #endif
 };
 
+class nsDocumentOnStack
+{
+public:
+  nsDocumentOnStack(nsDocument* aDoc) : mDoc(aDoc)
+  {
+    mDoc->IncreaseStackRefCnt();
+  }
+  ~nsDocumentOnStack()
+  {
+    mDoc->DecreaseStackRefCnt();
+  }
+private:
+  nsDocument* mDoc;
+};
+
 #define NS_DOCUMENT_INTERFACE_TABLE_BEGIN(_class)                             \
   NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class)                            \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMDocument, nsDocument)      \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMEventTarget, nsDocument)   \
   NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsIDOMNode, nsDocument)
 
 #endif /* nsDocument_h___ */