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:40:54 +0200
changeset 118313 0e76f133a7108f1161398016a04e122d80b5bca2
parent 118312 715e2e7ce60aa0429ba871fe08587097bdb45618
child 118314 b47f6f5f1fe616799b1788ec95eff879dfee4ea5
push id344
push useropettay@mozilla.com
push dateThu, 24 Jan 2013 18:58:38 +0000
reviewersmccr8, lsblakk
bugs830975
milestone18.0
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
@@ -1705,18 +1705,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 (nsGenericElement::CanSkip(tmp, aRemovingAllowed)) {
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
       elm->UnmarkGrayJSListeners();
     }
     return true;
@@ -6450,16 +6469,18 @@ nsDocument::CreateEvent(const nsAString&
   // Create event even without presContext.
   return nsEventDispatcher::CreateEvent(presContext, nullptr,
                                         aEventType, aReturn);
 }
 
 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
@@ -89,16 +89,17 @@ class nsOnloadBlocker;
 class nsUnblockOnloadEvent;
 class nsChildContentList;
 class nsXMLEventsManager;
 class nsHTMLStyleSheet;
 class nsHTMLCSSStyleSheet;
 class nsDOMNavigationTiming;
 class nsWindowSizes;
 class nsHtml5TreeOpExecutor;
+class nsDocumentOnStack;
 
 /**
  * Right now our identifier map entries contain information for 'name'
  * and 'id' mappings of a given string. This is so that
  * nsHTMLDocument::ResolveName only has to do one hash lookup instead
  * of two. It's not clear whether this still matters for performance.
  * 
  * We also store the document.all result list here. This is mainly so that
@@ -1004,16 +1005,30 @@ public:
   virtual void PostVisibilityUpdateEvent();
 
   virtual void DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const;
   // DocSizeOfIncludingThis is inherited from nsIDocument.
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
 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.
@@ -1362,21 +1377,39 @@ private:
   // Tracking for images in the document.
   nsDataHashtable< nsPtrHashKey<imgIRequest>, uint32_t> mImageTracker;
 
   // Tracking for plugins in the document.
   nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins;
 
   VisibilityState mVisibilityState;
 
+  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___ */