Add interface for taking over the refresh driver in a test (i.e., mocking time). (Bug 435442, patch 9) r=bzbarsky
authorL. David Baron <dbaron@dbaron.org>
Mon, 11 Apr 2011 23:18:43 -0700
changeset 67983 618c5d784dace564d9fdb4dabba2a1582214c321
parent 67982 f4d2a9cb8e06bbf0936399684a67f693b70ff111
child 67984 6ab8e5df08ec2ed73b65c62a210014e8dea48d77
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)
reviewersbzbarsky
bugs435442
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Add interface for taking over the refresh driver in a test (i.e., mocking time). (Bug 435442, patch 9) r=bzbarsky
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -44,16 +44,17 @@
 #include "nsDOMWindowUtils.h"
 #include "nsQueryContentEventResult.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "nsFocusManager.h"
 #include "nsIEventStateManager.h"
 #include "nsEventStateManager.h"
 #include "nsFrameManager.h"
+#include "nsRefreshDriver.h"
 
 #include "nsIScrollableFrame.h"
 
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 
 #include "nsIFrame.h"
 #include "nsIWidget.h"
@@ -1640,16 +1641,40 @@ ComputeAnimationValue(nsCSSProperty aPro
     aOutput.SetIntValue(aOutput.GetIntValue(),
                         nsStyleAnimation::eUnit_Visibility);
   }
 
   return PR_TRUE;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::AdvanceTimeAndRefresh(PRInt64 aMilliseconds)
+{
+  if (!IsUniversalXPConnectCapable()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  GetPresContext()->RefreshDriver()->AdvanceTimeAndRefresh(aMilliseconds);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::RestoreNormalRefresh()
+{
+  if (!IsUniversalXPConnectCapable()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  GetPresContext()->RefreshDriver()->RestoreNormalRefresh();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement,
                                            const nsAString& aProperty,
                                            const nsAString& aValue1,
                                            const nsAString& aValue2,
                                            double* aResult)
 {
   if (!IsUniversalXPConnectCapable()) {
     return NS_ERROR_DOM_SECURITY_ERR;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -61,17 +61,17 @@ interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 
-[scriptable, uuid(da529d62-ba27-480c-bb37-766ad083179a)]
+[scriptable, uuid(3828e648-af61-47e1-b9bc-89ca51bc19f2)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -820,16 +820,42 @@ interface nsIDOMWindowUtils : nsISupport
   nsIDOMWindow getOuterWindowWithId(in unsigned long long aOuterWindowID);
 
   [noscript] void RenderDocument(in nsConstRect aRect,
                                  in PRUint32 aFlags,
                                  in nscolor aBackgroundColor,
                                  in gfxContext aThebesContext);
 
   /**
+   * advanceTimeAndRefresh allows the caller to take over the refresh
+   * driver timing for a window.  A call to advanceTimeAndRefresh does
+   * three things:
+   *  (1) It marks the refresh driver for this presentation so that it
+   *      no longer refreshes on its own, but is instead driven entirely
+   *      by the caller (except for the refresh that happens when a
+   *      document comes out of the bfcache).
+   *  (2) It advances the refresh driver's current refresh time by the
+   *      argument given.  Negative advances are permitted.
+   *  (3) It does a refresh (i.e., notifies refresh observers) at that
+   *      new time.
+   *
+   * Note that this affects other connected docshells of the same type
+   * in the same docshell tree, such as parent frames.
+   *
+   * When callers have completed their use of advanceTimeAndRefresh,
+   * they must call restoreNormalRefresh.
+   */
+  void advanceTimeAndRefresh(in long long aMilliseconds);
+
+  /**
+   * Undoes the effects of advanceTimeAndRefresh.
+   */
+  void restoreNormalRefresh();
+
+  /**
    * Method for testing nsStyleAnimation::ComputeDistance.
    *
    * Returns the distance between the two values as reported by
    * nsStyleAnimation::ComputeDistance for the given element and
    * property.
    */
   double computeAnimationDistance(in nsIDOMElement element,
                                   in AString property,
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -49,16 +49,17 @@
 #include "nsCSSFrameConstructor.h"
 #include "nsIDocument.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "jsapi.h"
 #include "nsContentUtils.h"
 
 using mozilla::TimeStamp;
+using mozilla::TimeDuration;
 
 #define DEFAULT_FRAME_RATE 60
 #define DEFAULT_THROTTLED_FRAME_RATE 1
 
 static PRBool sPrecisePref;
 
 /* static */ void
 nsRefreshDriver::InitializeStatics()
@@ -99,28 +100,47 @@ nsRefreshDriver::GetRefreshTimerType() c
   }
   return nsITimer::TYPE_REPEATING_SLACK;
 }
 
 nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
   : mPresContext(aPresContext),
     mFrozen(false),
     mThrottled(false),
+    mTestControllingRefreshes(false),
     mTimerIsPrecise(false),
     mLastTimerInterval(0)
 {
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   NS_ABORT_IF_FALSE(ObserverCount() == 0,
                     "observers should have unregistered");
   NS_ABORT_IF_FALSE(!mTimer, "timer should be gone");
 }
 
+// Method for testing.  See nsIDOMWindowUtils.advanceTimeAndRefresh
+// for description.
+void
+nsRefreshDriver::AdvanceTimeAndRefresh(PRInt64 aMilliseconds)
+{
+  mTestControllingRefreshes = true;
+  mMostRecentRefreshEpochTime += aMilliseconds * 1000;
+  mMostRecentRefresh += TimeDuration::FromMilliseconds(aMilliseconds);
+  Notify(nsnull);
+}
+
+void
+nsRefreshDriver::RestoreNormalRefresh()
+{
+  mTestControllingRefreshes = false;
+  Notify(nsnull); // will call UpdateMostRecentRefresh()
+}
+
 TimeStamp
 nsRefreshDriver::MostRecentRefresh() const
 {
   const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
 
   return mMostRecentRefresh;
 }
 
@@ -206,16 +226,20 @@ nsRefreshDriver::ObserverCount() const
   sum += mBeforePaintTargets.Length();
   sum += mAnimationFrameListenerDocs.Length();
   return sum;
 }
 
 void
 nsRefreshDriver::UpdateMostRecentRefresh()
 {
+  if (mTestControllingRefreshes) {
+    return;
+  }
+
   // Call JS_Now first, since that can have nonzero latency in some rare cases.
   mMostRecentRefreshEpochTime = JS_Now();
   mMostRecentRefresh = TimeStamp::Now();
 }
 
 nsRefreshDriver::ObserverArray&
 nsRefreshDriver::ArrayFor(mozFlushType aFlushType)
 {
@@ -238,21 +262,26 @@ nsRefreshDriver::ArrayFor(mozFlushType a
 
 NS_IMPL_ISUPPORTS1(nsRefreshDriver, nsITimerCallback)
 
 /*
  * nsITimerCallback implementation
  */
 
 NS_IMETHODIMP
-nsRefreshDriver::Notify(nsITimer * /* unused */)
+nsRefreshDriver::Notify(nsITimer *aTimer)
 {
   NS_PRECONDITION(!mFrozen, "Why are we notified while frozen?");
   NS_PRECONDITION(mPresContext, "Why are we notified after disconnection?");
 
+  if (mTestControllingRefreshes && aTimer) {
+    // Ignore real refreshes from our timer (but honor the others).
+    return NS_OK;
+  }
+
   UpdateMostRecentRefresh();
 
   nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
   if (!presShell || ObserverCount() == 0) {
     // Things are being destroyed, or we no longer have any observers.
     // We don't want to stop the timer when observers are initially
     // removed, because sometimes observers can be added and removed
     // often depending on what other things are going on and in that
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -83,16 +83,23 @@ public:
 
   // nsISupports implementation
   NS_DECL_ISUPPORTS
 
   // nsITimerCallback implementation
   NS_DECL_NSITIMERCALLBACK
 
   /**
+   * Methods for testing, exposed via nsIDOMWindowUtils.  See
+   * nsIDOMWindowUtils.advanceTimeAndRefresh for description.
+   */
+  void AdvanceTimeAndRefresh(PRInt64 aMilliseconds);
+  void RestoreNormalRefresh();
+
+  /**
    * 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;
   /**
@@ -232,16 +239,17 @@ private:
   PRInt64 mMostRecentRefreshEpochTime;   // same thing as mMostRecentRefresh,
                                          // but in microseconds since the epoch.
 
   nsPresContext *mPresContext; // weak; pres context passed in constructor
                                // and unset in Disconnect
 
   bool mFrozen;
   bool mThrottled;
+  bool mTestControllingRefreshes;
   /* If mTimer is non-null, this boolean indicates whether the timer is
      a precise timer.  If mTimer is null, this boolean's value can be
      anything.  */
   bool mTimerIsPrecise;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
   nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;