Bug 1113413 - Make refresh driver go back immediately when restored from test control; r=vlad
authorBrian Birtles <birtles@gmail.com>
Tue, 06 Jan 2015 10:56:02 +0900
changeset 222169 3b5b53b73e1531425a366c408873d152ed3086cd
parent 222168 b2c5a67125d6457a50d3fed0df8681d7c359ca8c
child 222170 78d67ab239d260e9d05b8718603c3bac4d1174b2
push id28059
push userryanvm@gmail.com
push dateTue, 06 Jan 2015 15:53:01 +0000
treeherdermozilla-central@4d91c33b351c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad
bugs1113413
milestone37.0a1
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
Bug 1113413 - Make refresh driver go back immediately when restored from test control; r=vlad
dom/animation/AnimationTimeline.cpp
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/dom/animation/AnimationTimeline.cpp
+++ b/dom/animation/AnimationTimeline.cpp
@@ -55,24 +55,18 @@ AnimationTimeline::FastForward(const Tim
   // nsDOMWindowUtils::AdvanceTimeAndRefresh automatically starts any
   // pending animation players so we don't need to fast-forward the timeline
   // anyway.
   nsRefreshDriver* refreshDriver = GetRefreshDriver();
   if (refreshDriver && refreshDriver->IsTestControllingRefreshesEnabled()) {
     return;
   }
 
-  // Bug 1113413: If the refresh driver has just been restored from test
-  // control it's possible that aTimeStamp could be before the most recent
-  // refresh.
-  if (refreshDriver &&
-      aTimeStamp < refreshDriver->MostRecentRefresh()) {
-    mFastForwardTime = refreshDriver->MostRecentRefresh();
-    return;
-  }
+  MOZ_ASSERT(!refreshDriver || aTimeStamp >= refreshDriver->MostRecentRefresh(),
+             "aTimeStamp must be >= the refresh driver time");
 
   // FIXME: For all animations attached to this timeline, we should mark
   // their target elements as needing restyling. Otherwise, tasks that run
   // in between now and the next refresh driver tick might see inconsistencies
   // between the timing of an animation and the computed style of its target.
 
   mFastForwardTime = aTimeStamp;
 }
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -756,43 +756,43 @@ nsRefreshDriver::AdvanceTimeAndRefresh(i
   mozilla::dom::AutoNoJSAPI nojsapi;
   DoTick();
 }
 
 void
 nsRefreshDriver::RestoreNormalRefresh()
 {
   mTestControllingRefreshes = false;
-  EnsureTimerStarted(false);
+  EnsureTimerStarted(eAllowTimeToGoBackwards);
   mCompletedTransaction = mPendingTransaction;
 }
 
 TimeStamp
 nsRefreshDriver::MostRecentRefresh() const
 {
-  const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted(false);
+  const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
 
   return mMostRecentRefresh;
 }
 
 int64_t
 nsRefreshDriver::MostRecentRefreshEpochTime() const
 {
-  const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted(false);
+  const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
 
   return mMostRecentRefreshEpochTime;
 }
 
 bool
 nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
                                     mozFlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
   bool success = array.AppendElement(aObserver) != nullptr;
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
   return success;
 }
 
 bool
 nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
                                        mozFlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
@@ -823,17 +823,17 @@ nsRefreshDriver::AddImageRequest(imgIReq
     ImageStartData* start = mStartTable.Get(delay);
     if (!start) {
       start = new ImageStartData();
       mStartTable.Put(delay, start);
     }
     start->mEntries.PutEntry(aRequest);
   }
 
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
 
   return true;
 }
 
 void
 nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
 {
   // Try to remove from both places, just in case, because we can't tell
@@ -844,23 +844,23 @@ nsRefreshDriver::RemoveImageRequest(imgI
     ImageStartData* start = mStartTable.Get(delay);
     if (start) {
       start->mEntries.RemoveEntry(aRequest);
     }
   }
 }
 
 void
-nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
+nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
 {
   if (mTestControllingRefreshes)
     return;
 
   // will it already fire, and no other changes needed?
-  if (mActiveTimer && !aAdjustingTimer)
+  if (mActiveTimer && !(aFlags & eAdjustingTimer))
     return;
 
   if (IsFrozen() || !mPresContext) {
     // If we don't want to start it now, or we've been disconnected.
     StopTimer();
     return;
   }
 
@@ -886,21 +886,29 @@ nsRefreshDriver::EnsureTimerStarted(bool
     mActiveTimer = newTimer;
     mActiveTimer->AddRefreshDriver(this);
   }
 
   // Since the different timers are sampled at different rates, when switching
   // timers, the most recent refresh of the new timer may be *before* the
   // most recent refresh of the old timer. However, the refresh driver time
   // should not go backwards so we clamp the most recent refresh time.
+  //
+  // The one exception to this is when we are restoring the refresh driver
+  // from test control in which case the time is expected to go backwards
+  // (see bug 1043078).
   mMostRecentRefresh =
-    std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
+    aFlags & eAllowTimeToGoBackwards
+    ? mActiveTimer->MostRecentRefresh()
+    : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
   mMostRecentRefreshEpochTime =
-    std::max(mActiveTimer->MostRecentRefreshEpochTime(),
-             mMostRecentRefreshEpochTime);
+    aFlags & eAllowTimeToGoBackwards
+    ? mActiveTimer->MostRecentRefreshEpochTime()
+    : std::max(mActiveTimer->MostRecentRefreshEpochTime(),
+               mMostRecentRefreshEpochTime);
 }
 
 void
 nsRefreshDriver::StopTimer()
 {
   if (!mActiveTimer)
     return;
 
@@ -1498,17 +1506,17 @@ nsRefreshDriver::Thaw()
 
   if (mFreezeCount == 0) {
     if (ObserverCount() || ImageRequestCount()) {
       // FIXME: This isn't quite right, since our EnsureTimerStarted call
       // updates our mMostRecentRefresh, but the DoRefresh call won't run
       // and notify our observers until we get back to the event loop.
       // Thus MostRecentRefresh() will lie between now and the DoRefresh.
       NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsRefreshDriver::DoRefresh));
-      EnsureTimerStarted(false);
+      EnsureTimerStarted();
     }
   }
 }
 
 void
 nsRefreshDriver::FinishedWaitingForTransaction()
 {
   mWaitingForTransaction = false;
@@ -1626,17 +1634,17 @@ nsRefreshDriver::IsWaitingForPaint(mozil
 void
 nsRefreshDriver::SetThrottled(bool aThrottled)
 {
   if (aThrottled != mThrottled) {
     mThrottled = aThrottled;
     if (mActiveTimer) {
       // We want to switch our timer type here, so just stop and
       // restart the timer.
-      EnsureTimerStarted(true);
+      EnsureTimerStarted(eAdjustingTimer);
     }
   }
 }
 
 void
 nsRefreshDriver::DoRefresh()
 {
   // Don't do a refresh unless we're in a state where we should be refreshing.
@@ -1656,30 +1664,30 @@ nsRefreshDriver::IsRefreshObserver(nsARe
 #endif
 
 void
 nsRefreshDriver::ScheduleViewManagerFlush()
 {
   NS_ASSERTION(mPresContext->IsRoot(),
                "Should only schedule view manager flush on root prescontexts");
   mViewManagerFlushIsPending = true;
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
 }
 
 void
 nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
 {
   NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
                mFrameRequestCallbackDocs.NoIndex,
                "Don't schedule the same document multiple times");
   mFrameRequestCallbackDocs.AppendElement(aDocument);
 
   // make sure that the timer is running
   ConfigureHighPrecision();
-  EnsureTimerStarted(false);
+  EnsureTimerStarted();
 }
 
 void
 nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
 {
   mFrameRequestCallbackDocs.RemoveElement(aDocument);
   ConfigureHighPrecision();
   // No need to worry about restarting our timer in slack mode if it's already
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -154,17 +154,17 @@ public:
     // We only get the cause for the first observer each frame because capturing
     // a stack is expensive. This is still useful if (1) you're trying to remove
     // all flushes for a particial frame or (2) the costly flush is triggered
     // near the call site where the first observer is triggered.
     if (!mStyleCause) {
       mStyleCause = profiler_get_backtrace();
     }
     bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr;
-    EnsureTimerStarted(false);
+    EnsureTimerStarted();
 
     return appended;
   }
   void RemoveStyleFlushObserver(nsIPresShell* aShell) {
     mStyleFlushObservers.RemoveElement(aShell);
   }
   bool AddLayoutFlushObserver(nsIPresShell* aShell) {
     NS_ASSERTION(!IsLayoutFlushObserver(aShell),
@@ -172,30 +172,30 @@ public:
     // We only get the cause for the first observer each frame because capturing
     // a stack is expensive. This is still useful if (1) you're trying to remove
     // all flushes for a particial frame or (2) the costly flush is triggered
     // near the call site where the first observer is triggered.
     if (!mReflowCause) {
       mReflowCause = profiler_get_backtrace();
     }
     bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr;
-    EnsureTimerStarted(false);
+    EnsureTimerStarted();
     return appended;
   }
   void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
     mLayoutFlushObservers.RemoveElement(aShell);
   }
   bool IsLayoutFlushObserver(nsIPresShell* aShell) {
     return mLayoutFlushObservers.Contains(aShell);
   }
   bool AddPresShellToInvalidateIfHidden(nsIPresShell* aShell) {
     NS_ASSERTION(!mPresShellsToInvalidateIfHidden.Contains(aShell),
 		 "Double-adding style flush observer");
     bool appended = mPresShellsToInvalidateIfHidden.AppendElement(aShell) != nullptr;
-    EnsureTimerStarted(false);
+    EnsureTimerStarted();
     return appended;
   }
   void RemovePresShellToInvalidateIfHidden(nsIPresShell* aShell) {
     mPresShellsToInvalidateIfHidden.RemoveElement(aShell);
   }
 
   /**
    * Remember whether our presshell's view manager needs a flush
@@ -293,17 +293,22 @@ private:
 
     mozilla::Maybe<mozilla::TimeStamp> mStartTime;
     RequestTable mEntries;
   };
   typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
 
   void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
 
-  void EnsureTimerStarted(bool aAdjustingTimer);
+  enum EnsureTimerStartedFlags {
+    eNone = 0,
+    eAdjustingTimer = 1 << 0,
+    eAllowTimeToGoBackwards = 1 << 1
+  };
+  void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
   void StopTimer();
 
   uint32_t ObserverCount() const;
   uint32_t ImageRequestCount() const;
   static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry,
                                                 void* aUserArg);
   static PLDHashOperator StartTableRequestCounter(const uint32_t& aKey,
                                                   ImageStartData* aEntry,