Make nsRefreshDriver reference-counted instead of being a sub-object of the pres context. (Bug 531585) r=bzbarsky
authorL. David Baron <dbaron@dbaron.org>
Wed, 23 Dec 2009 06:35:45 -0500
changeset 36590 188c2ab04c3dbbb6a7d06461de65751f551b2f08
parent 36589 3b14d01ce9607971d218cfa5ed307131c3bbc237
child 36591 b9a8430469bcacdafc0b99ce4e8442f086f4431f
push idunknown
push userunknown
push dateunknown
reviewersbzbarsky
bugs531585
milestone1.9.3a1pre
Make nsRefreshDriver reference-counted instead of being a sub-object of the pres context. (Bug 531585) r=bzbarsky
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -256,16 +256,22 @@ nsPresContext::~nsPresContext()
   for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
     mImageLoaders[i].Enumerate(destroy_loads, nsnull);
 
   NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer");
   SetShell(nsnull);
 
   delete mTransitionManager;
 
+  // Disconnect the refresh driver *after* the transition manager, which
+  // needs it.
+  if (mRefreshDriver) {
+    mRefreshDriver->Disconnect();
+  }
+
   if (mEventManager) {
     // unclear if these are needed, but can't hurt
     mEventManager->NotifyDestroyPresContext(this);
     mEventManager->SetPresContext(nsnull);
 
     NS_RELEASE(mEventManager);
   }
 
@@ -868,16 +874,22 @@ nsPresContext::Init(nsIDeviceContext* aD
 
   mEventManager = new nsEventStateManager();
   if (!mEventManager)
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ADDREF(mEventManager);
 
   mTransitionManager = new nsTransitionManager(this);
+  if (!mTransitionManager)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  mRefreshDriver = new nsRefreshDriver(this);
+  if (!mRefreshDriver)
+    return NS_ERROR_OUT_OF_MEMORY;
 
   mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
 
   // Register callbacks so we're notified when the preferences change
   nsContentUtils::RegisterPrefCallback("font.",
                                        nsPresContext::PrefChangedCallback,
                                        this);
   nsContentUtils::RegisterPrefCallback("browser.display.",
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -67,17 +67,16 @@
 #include "gfxRect.h"
 #include "nsRegion.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsIWidget.h"
 #include "mozilla/TimeStamp.h"
-#include "nsRefreshDriver.h"
 
 class nsImageLoader;
 #ifdef IBMBIDI
 class nsBidiPresUtils;
 #endif // IBMBIDI
 
 struct nsRect;
 
@@ -98,16 +97,17 @@ class nsIAtom;
 struct nsStyleBackground;
 struct nsStyleBorder;
 class nsIRunnable;
 class gfxUserFontSet;
 class nsUserFontSet;
 struct nsFontFaceRuleContainer;
 class nsObjectFrame;
 class nsTransitionManager;
+class nsRefreshDriver;
 class imgIContainer;
 
 #ifdef MOZ_REFLOW_PERF
 class nsIRenderingContext;
 #endif
 
 enum nsWidgetType {
   eWidgetType_Button  	= 1,
@@ -228,23 +228,17 @@ public:
 #ifdef _IMPL_NS_LAYOUT
   nsStyleSet* StyleSet() { return GetPresShell()->StyleSet(); }
 
   nsFrameManager* FrameManager()
     { return GetPresShell()->FrameManager(); } 
 
   nsTransitionManager* TransitionManager() { return mTransitionManager; }
 
-  nsRefreshDriver* RefreshDriver() { return &mRefreshDriver; }
-
-  static nsPresContext* FromRefreshDriver(nsRefreshDriver* aRefreshDriver) {
-    return reinterpret_cast<nsPresContext*>(
-             reinterpret_cast<char*>(aRefreshDriver) -
-             offsetof(nsPresContext, mRefreshDriver));
-  }
+  nsRefreshDriver* RefreshDriver() { return mRefreshDriver; }
 #endif
 
   /**
    * Rebuilds all style data by throwing out the old rule tree and
    * building a new one, and additionally applying aExtraHint (which
    * must not contain nsChangeHint_ReconstructFrame) to the root frame.
    * Also rebuild the user font set.
    */
@@ -952,17 +946,17 @@ protected:
   nsCOMPtr<nsIDocument> mDocument;
   nsIDeviceContext*     mDeviceContext; // [STRONG] could be weak, but
                                         // better safe than sorry.
                                         // Cannot reintroduce cycles
                                         // since there is no dependency
                                         // from gfx back to layout.
   nsIEventStateManager* mEventManager;  // [STRONG]
   nsILookAndFeel*       mLookAndFeel;   // [STRONG]
-  nsRefreshDriver       mRefreshDriver;
+  nsRefPtr<nsRefreshDriver> mRefreshDriver;
   nsTransitionManager*  mTransitionManager; // owns; it aggregates our refcount
   nsIAtom*              mMedium;        // initialized by subclass ctors;
                                         // weak pointer to static atom
 
   nsILinkHandler*       mLinkHandler;   // [WEAK]
   nsIAtom*              mLangGroup;     // [STRONG]
 
   nsRefPtrHashtable<nsVoidPtrHashKey, nsImageLoader>
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -51,17 +51,18 @@
  * is not visible, we need to hook it up to FlushPendingNotifications so
  * that we flush when necessary.
  */
 
 #define REFRESH_INTERVAL_MILLISECONDS 20
 
 using mozilla::TimeStamp;
 
-nsRefreshDriver::nsRefreshDriver()
+nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
+  : mPresContext(aPresContext)
 {
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   NS_ABORT_IF_FALSE(ObserverCount() == 0,
                     "observers should have unregistered");
   NS_ABORT_IF_FALSE(!mTimer, "timer should be gone");
@@ -165,33 +166,33 @@ nsRefreshDriver::ArrayFor(mozFlushType a
       return *static_cast<ObserverArray*>(nsnull);
   }
 }
 
 /*
  * nsISupports implementation
  */
 
-NS_IMPL_ADDREF_USING_AGGREGATOR(nsRefreshDriver,
-                                nsPresContext::FromRefreshDriver(this))
-NS_IMPL_RELEASE_USING_AGGREGATOR(nsRefreshDriver,
-                                 nsPresContext::FromRefreshDriver(this))
-NS_IMPL_QUERY_INTERFACE1(nsRefreshDriver, nsITimerCallback)
+NS_IMPL_ISUPPORTS1(nsRefreshDriver, nsITimerCallback)
 
 /*
  * nsITimerCallback implementation
  */
 
 NS_IMETHODIMP
 nsRefreshDriver::Notify(nsITimer *aTimer)
 {
   UpdateMostRecentRefresh();
 
-  nsPresContext *presContext = nsPresContext::FromRefreshDriver(this);
-  nsCOMPtr<nsIPresShell> presShell = presContext->GetPresShell();
+  if (!mPresContext) {
+    // Things are being destroyed.
+    NS_ABORT_IF_FALSE(!mTimer, "timer should have been stopped");
+    return NS_OK;
+  }
+  nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
   if (!presShell) {
     // Things are being destroyed.
     StopTimer();
     return NS_OK;
   }
 
   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mObservers); ++i) {
     ObserverArray::EndLimitedIterator etor(mObservers[i]);
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -44,36 +44,39 @@
 #define nsRefreshDriver_h_
 
 #include "mozilla/TimeStamp.h"
 #include "mozFlushType.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "nsTObserverArray.h"
 
+class nsPresContext;
+
 /**
  * An abstract base class to be implemented by callers wanting to be
  * notified at refresh times.  When nothing needs to be painted, callers
  * may not be notified.
  */
 class nsARefreshObserver {
 public:
   virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
 };
 
-/*
- * nsRefreshDriver MUST ONLY be constructed as a sub-object of
- * nsPresContext (since its reference counting methods forward to the
- * pres context of which it is an mRefreshDriver)
- */
-class nsRefreshDriver : private nsITimerCallback {
+class nsRefreshDriver : public nsITimerCallback {
 public:
-  nsRefreshDriver();
+  nsRefreshDriver(nsPresContext *aPresContext);
   ~nsRefreshDriver();
 
+  // nsISupports implementation
+  NS_DECL_ISUPPORTS
+
+  // nsITimerCallback implementation
+  NS_DECL_NSITIMERCALLBACK
+
   /**
    * Return the time of the most recent refresh.  This is intended to be
    * used by callers who want to start an animation now and want to know
    * what time to consider the start of the animation.  (This helps
    * ensure that multiple animations started during the same event off
    * the main event loop have the same start time.)
    */
   mozilla::TimeStamp MostRecentRefresh() const;
@@ -90,31 +93,39 @@ public:
    *     painting, and, correspondingly, which get notified when there
    *     is a flush during such suppression
    * and it must be either Flush_Style, Flush_Layout, or Flush_Display.
    */
   PRBool AddRefreshObserver(nsARefreshObserver *aObserver,
                             mozFlushType aFlushType);
   PRBool RemoveRefreshObserver(nsARefreshObserver *aObserver,
                                mozFlushType aFlushType);
-private:
-  // nsISupports implementation
-  NS_DECL_ISUPPORTS_INHERITED
 
-  // nsITimerCallback implementation
-  NS_IMETHOD Notify(nsITimer *aTimer);
+  /**
+   * Tell the refresh driver that it is done driving refreshes and
+   * should stop its timer and forget about its pres context.  This may
+   * be called from within a refresh.
+   */
+  void Disconnect() {
+    StopTimer();
+    mPresContext = nsnull;
+  }
 
+private:
   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
 
   void EnsureTimerStarted();
   void StopTimer();
   PRUint32 ObserverCount() const;
   void UpdateMostRecentRefresh();
   ObserverArray& ArrayFor(mozFlushType aFlushType);
 
   nsCOMPtr<nsITimer> mTimer;
   mozilla::TimeStamp mMostRecentRefresh; // only valid when mTimer non-null
 
+  nsPresContext *mPresContext; // weak; pres context passed in constructor
+                               // and unset in Disconnect
+
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
 };
 
 #endif /* !defined(nsRefreshDriver_h_) */