Bug 1472076 - Introduce nsATimerAdjustmentObserver in nsRefreshDriver. r=birtles a=lizzard
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 03 Jul 2018 10:57:12 +0900
changeset 477863 ae6851be89984fe2a74d35862ad25c59d20193aa
parent 477862 1bff764743ffd981b1e6ddbe15072eeeb1d54e86
child 477864 76f1a92b24ca9b2a06d27da38eaf837dd762cfba
push id9448
push userarchaeopteryx@coole-files.de
push dateMon, 09 Jul 2018 17:22:41 +0000
treeherdermozilla-beta@32cde6cdb297 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles, lizzard
bugs1472076
milestone62.0
Bug 1472076 - Introduce nsATimerAdjustmentObserver in nsRefreshDriver. r=birtles a=lizzard mMostRecentRefresh is changed not only in Tick() but also in EnsureTimerStarted(). In the case where it happens in Tick() refresh observers can know it through WillRefresh(), but there is no way in the case of EnsureTimerStarted(). This patch introduces a new abstract class to be notified when mMostRecentRefresh was changed in EnsureTimerStarted() so that animations can use the *real* most recent refresh time until the next tick happens. The reason why we have another observer array in parallel with existing array (mObservers) is that the refresh driver should stop the timer if there is no normal observes but there are still any timer adjustment observes. MozReview-Commit-ID: FaDcl5GrvI3
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1242,16 +1242,33 @@ nsRefreshDriver::AddRefreshObserver(nsAR
 bool
 nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
                                        FlushType aFlushType)
 {
   ObserverArray& array = ArrayFor(aFlushType);
   return array.RemoveElement(aObserver);
 }
 
+bool
+nsRefreshDriver::AddTimerAdjustmentObserver(
+  nsATimerAdjustmentObserver *aObserver)
+{
+  MOZ_ASSERT(!mTimerAdjustmentObservers.Contains(aObserver));
+
+  return mTimerAdjustmentObservers.AppendElement(aObserver) != nullptr;
+}
+
+bool
+nsRefreshDriver::RemoveTimerAdjustmentObserver(
+  nsATimerAdjustmentObserver *aObserver)
+{
+  MOZ_ASSERT(mTimerAdjustmentObservers.Contains(aObserver));
+  return mTimerAdjustmentObservers.RemoveElement(aObserver);
+}
+
 void
 nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent)
 {
   mScrollEvents.AppendElement(aScrollEvent);
   EnsureTimerStarted();
 }
 
 void
@@ -1382,25 +1399,36 @@ nsRefreshDriver::EnsureTimerStarted(Ensu
   // 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 =
+  TimeStamp newMostRecentRefresh =
     aFlags & eAllowTimeToGoBackwards
     ? mActiveTimer->MostRecentRefresh()
     : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
   mMostRecentRefreshEpochTime =
     aFlags & eAllowTimeToGoBackwards
     ? mActiveTimer->MostRecentRefreshEpochTime()
     : std::max(mActiveTimer->MostRecentRefreshEpochTime(),
                mMostRecentRefreshEpochTime);
+
+  if (mMostRecentRefresh != newMostRecentRefresh) {
+    mMostRecentRefresh = newMostRecentRefresh;
+
+    nsTObserverArray<nsATimerAdjustmentObserver*>::EndLimitedIterator
+      iter(mTimerAdjustmentObservers);
+    while (iter.HasMore()) {
+      nsATimerAdjustmentObserver* obs = iter.GetNext();
+      obs->NotifyTimerAdjusted(mMostRecentRefresh);
+    }
+  }
 }
 
 void
 nsRefreshDriver::StopTimer()
 {
   if (!mActiveTimer)
     return;
 
@@ -1424,28 +1452,32 @@ nsRefreshDriver::ObserverCount() const
   sum += mResizeEventFlushObservers.Length();
   sum += mStyleFlushObservers.Length();
   sum += mLayoutFlushObservers.Length();
   sum += mPendingEvents.Length();
   sum += mFrameRequestCallbackDocs.Length();
   sum += mThrottledFrameRequestCallbackDocs.Length();
   sum += mViewManagerFlushIsPending;
   sum += mEarlyRunners.Length();
+  sum += mTimerAdjustmentObservers.Length();
   return sum;
 }
 
 bool
 nsRefreshDriver::HasObservers() const
 {
   for (const ObserverArray& array : mObservers) {
     if (!array.IsEmpty()) {
       return true;
     }
   }
 
+  // We should NOT count mTimerAdjustmentObservers here since this method is
+  // used to determine whether or not to stop the timer or re-start it and timer
+  // adjustment observers should not influence timer starting or stopping.
   return mViewManagerFlushIsPending ||
          !mStyleFlushObservers.IsEmpty() ||
          !mLayoutFlushObservers.IsEmpty() ||
          !mAnimationEventFlushObservers.IsEmpty() ||
          !mResizeEventFlushObservers.IsEmpty() ||
          !mPendingEvents.IsEmpty() ||
          !mFrameRequestCallbackDocs.IsEmpty() ||
          !mThrottledFrameRequestCallbackDocs.IsEmpty() ||
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -63,16 +63,27 @@ public:
   // except while it is notifying them.
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
 };
 
 /**
  * An abstract base class to be implemented by callers wanting to be notified
+ * when the observing refresh driver updated mMostRecentRefresh due to active
+ * timer changes. Callers must ensure an observer is removed before it is
+ * destroyed.
+ */
+class nsATimerAdjustmentObserver {
+public:
+  virtual void NotifyTimerAdjusted(mozilla::TimeStamp aTime) = 0;
+};
+
+/**
+ * An abstract base class to be implemented by callers wanting to be notified
  * that a refresh has occurred. Callers must ensure an observer is removed
  * before it is destroyed.
  */
 class nsAPostRefreshObserver {
 public:
   virtual void DidRefresh() = 0;
 };
 
@@ -127,16 +138,22 @@ public:
    * they must remove themselves before they are destroyed.
    *
    * The observer will be called even if there is no other activity.
    */
   bool AddRefreshObserver(nsARefreshObserver *aObserver,
                           mozilla::FlushType aFlushType);
   bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
                              mozilla::FlushType aFlushType);
+  /**
+   * Add / remove an observer wants to know the time when the refresh driver
+   * updated the most recent refresh time due to its active timer changes.
+   */
+  bool AddTimerAdjustmentObserver(nsATimerAdjustmentObserver *aObserver);
+  bool RemoveTimerAdjustmentObserver(nsATimerAdjustmentObserver *aObserver);
 
   void PostScrollEvent(mozilla::Runnable* aScrollEvent);
   void DispatchScrollEvents();
 
   /**
    * Add an observer that will be called after each refresh. The caller
    * must remove the observer before it is deleted. This does not trigger
    * refresh driver ticks.
@@ -420,16 +437,17 @@ private:
     eForceAdjustTimer = 1 << 0,
     eAllowTimeToGoBackwards = 1 << 1,
     eNeverAdjustTimer = 1 << 2,
   };
   void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
   void StopTimer();
 
   bool HasObservers() const;
+  // Note: This should only be called in the dtor of nsRefreshDriver.
   uint32_t ObserverCount() const;
   bool HasImageRequests() const;
   ObserverArray& ArrayFor(mozilla::FlushType aFlushType);
   // Trigger a refresh immediately, if haven't been disconnected or frozen.
   void DoRefresh();
 
   double GetRegularTimerInterval() const;
   static double GetThrottledTimerInterval();
@@ -502,16 +520,22 @@ private:
   mozilla::TimeStamp mMostRecentRefresh;
   mozilla::TimeStamp mMostRecentTick;
   mozilla::TimeStamp mTickStart;
   mozilla::TimeStamp mNextThrottledFrameRequestTick;
   mozilla::TimeStamp mNextRecomputeVisibilityTick;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[4];
+  // These observers should NOT be included in HasObservers() since that method
+  // is used to determine whether or not to stop the timer, or restore it when
+  // thawing the refresh driver. On the other hand these observers are intended
+  // to be called when the timer is re-started and should not influence its
+  // starting or stopping.
+  nsTObserverArray<nsATimerAdjustmentObserver*> mTimerAdjustmentObservers;
   RequestTable mRequests;
   ImageStartTable mStartTable;
   AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
   ScrollEventArray mScrollEvents;
 
   struct PendingEvent {
     nsCOMPtr<nsINode> mTarget;
     RefPtr<mozilla::dom::Event> mEvent;