Bug 1472076 - Introduce nsATimerAdjustmentObserver in nsRefreshDriver. r?birtles
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
--- 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;