forked from mirrors/gecko-dev
		
	 ae94ae81a4
			
		
	
	
		ae94ae81a4
		
	
	
	
	
		
			
			MozReview-Commit-ID: FQs2zsIU2zO --HG-- extra : rebase_source : 166617f5966cbad18401bc590a266ce00d858bab
		
			
				
	
	
		
			500 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * Code to notify things that animate before a refresh, at an appropriate
 | |
|  * refresh rate.  (Perhaps temporary, until replaced by compositor.)
 | |
|  */
 | |
| 
 | |
| #ifndef nsRefreshDriver_h_
 | |
| #define nsRefreshDriver_h_
 | |
| 
 | |
| #include "mozilla/FlushType.h"
 | |
| #include "mozilla/TimeStamp.h"
 | |
| #include "mozilla/Vector.h"
 | |
| #include "mozilla/WeakPtr.h"
 | |
| #include "nsTObserverArray.h"
 | |
| #include "nsTArray.h"
 | |
| #include "nsTHashtable.h"
 | |
| #include "nsTObserverArray.h"
 | |
| #include "nsClassHashtable.h"
 | |
| #include "nsHashKeys.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/Maybe.h"
 | |
| #include "mozilla/layers/TransactionIdAllocator.h"
 | |
| 
 | |
| class nsPresContext;
 | |
| class nsIPresShell;
 | |
| class nsIDocument;
 | |
| class imgIRequest;
 | |
| class nsIDOMEvent;
 | |
| class nsINode;
 | |
| class nsIRunnable;
 | |
| 
 | |
| namespace mozilla {
 | |
| class RefreshDriverTimer;
 | |
| class Runnable;
 | |
| namespace layout {
 | |
| class VsyncChild;
 | |
| } // namespace layout
 | |
| } // namespace mozilla
 | |
| 
 | |
| /**
 | |
|  * 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:
 | |
|   // AddRef and Release signatures that match nsISupports.  Implementors
 | |
|   // must implement reference counting, and those that do implement
 | |
|   // nsISupports will already have methods with the correct signature.
 | |
|   //
 | |
|   // The refresh driver does NOT hold references to refresh observers
 | |
|   // 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
 | |
|  * that a refresh has occurred. Callers must ensure an observer is removed
 | |
|  * before it is destroyed.
 | |
|  */
 | |
| class nsAPostRefreshObserver {
 | |
| public:
 | |
|   virtual void DidRefresh() = 0;
 | |
| };
 | |
| 
 | |
| class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
 | |
|                               public nsARefreshObserver
 | |
| {
 | |
| public:
 | |
|   explicit nsRefreshDriver(nsPresContext *aPresContext);
 | |
|   ~nsRefreshDriver();
 | |
| 
 | |
|   /**
 | |
|    * Methods for testing, exposed via nsIDOMWindowUtils.  See
 | |
|    * nsIDOMWindowUtils.advanceTimeAndRefresh for description.
 | |
|    */
 | |
|   void AdvanceTimeAndRefresh(int64_t aMilliseconds);
 | |
|   void RestoreNormalRefresh();
 | |
|   void DoTick();
 | |
|   bool IsTestControllingRefreshesEnabled() const
 | |
|   {
 | |
|     return mTestControllingRefreshes;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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;
 | |
|   /**
 | |
|    * Same thing, but in microseconds since the epoch.
 | |
|    */
 | |
|   int64_t MostRecentRefreshEpochTime() const;
 | |
| 
 | |
|   /**
 | |
|    * Add / remove refresh observers.  Returns whether the operation
 | |
|    * succeeded.
 | |
|    *
 | |
|    * The flush type affects:
 | |
|    *   + the order in which the observers are notified (lowest flush
 | |
|    *     type to highest, in order registered)
 | |
|    *   + (in the future) which observers are suppressed when the display
 | |
|    *     doesn't require current position data or isn't currently
 | |
|    *     painting, and, correspondingly, which get notified when there
 | |
|    *     is a flush during such suppression
 | |
|    * and it must be FlushType::Style, FlushType::Layout, or FlushType::Display.
 | |
|    *
 | |
|    * The refresh driver does NOT own a reference to these observers;
 | |
|    * 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);
 | |
| 
 | |
|   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.
 | |
|    */
 | |
|   void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver);
 | |
|   void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver);
 | |
| 
 | |
|   /**
 | |
|    * Add/Remove imgIRequest versions of observers.
 | |
|    *
 | |
|    * These are used for hooking into the refresh driver for
 | |
|    * controlling animated images.
 | |
|    *
 | |
|    * @note The refresh driver owns a reference to these listeners.
 | |
|    *
 | |
|    * @note Technically, imgIRequest objects are not nsARefreshObservers, but
 | |
|    * for controlling animated image repaint events, we subscribe the
 | |
|    * imgIRequests to the nsRefreshDriver for notification of paint events.
 | |
|    *
 | |
|    * @returns whether the operation succeeded, or void in the case of removal.
 | |
|    */
 | |
|   bool AddImageRequest(imgIRequest* aRequest);
 | |
|   void RemoveImageRequest(imgIRequest* aRequest);
 | |
| 
 | |
|   /**
 | |
|    * Add / remove presshells that we should flush style and layout on
 | |
|    */
 | |
|   bool AddStyleFlushObserver(nsIPresShell* aShell) {
 | |
|     NS_ASSERTION(!mStyleFlushObservers.Contains(aShell),
 | |
|                  "Double-adding style flush observer");
 | |
|     bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr;
 | |
|     EnsureTimerStarted();
 | |
| 
 | |
|     return appended;
 | |
|   }
 | |
|   void RemoveStyleFlushObserver(nsIPresShell* aShell) {
 | |
|     mStyleFlushObservers.RemoveElement(aShell);
 | |
|   }
 | |
|   bool AddLayoutFlushObserver(nsIPresShell* aShell) {
 | |
|     NS_ASSERTION(!IsLayoutFlushObserver(aShell),
 | |
|                  "Double-adding layout flush observer");
 | |
|     bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr;
 | |
|     EnsureTimerStarted();
 | |
|     return appended;
 | |
|   }
 | |
|   void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
 | |
|     mLayoutFlushObservers.RemoveElement(aShell);
 | |
|   }
 | |
|   bool IsLayoutFlushObserver(nsIPresShell* aShell) {
 | |
|     return mLayoutFlushObservers.Contains(aShell);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * "Early Runner" runnables will be called as the first step when refresh
 | |
|    * driver tick is triggered. Runners shouldn't keep other objects alive,
 | |
|    * since it isn't guaranteed they will ever get called.
 | |
|    */
 | |
|   void AddEarlyRunner(nsIRunnable* aRunnable)
 | |
|   {
 | |
|     mEarlyRunners.AppendElement(aRunnable);
 | |
|     EnsureTimerStarted();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Remember whether our presshell's view manager needs a flush
 | |
|    */
 | |
|   void ScheduleViewManagerFlush();
 | |
|   void RevokeViewManagerFlush() {
 | |
|     mViewManagerFlushIsPending = false;
 | |
|   }
 | |
|   bool ViewManagerFlushIsPending() {
 | |
|     return mViewManagerFlushIsPending;
 | |
|   }
 | |
|   bool HasScheduleFlush() {
 | |
|     return mHasScheduleFlush;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Add a document for which we have FrameRequestCallbacks
 | |
|    */
 | |
|   void ScheduleFrameRequestCallbacks(nsIDocument* aDocument);
 | |
| 
 | |
|   /**
 | |
|    * Remove a document for which we have FrameRequestCallbacks
 | |
|    */
 | |
|   void RevokeFrameRequestCallbacks(nsIDocument* aDocument);
 | |
| 
 | |
|   /**
 | |
|    * Queue a new event to dispatch in next tick before the style flush
 | |
|    */
 | |
|   void ScheduleEventDispatch(nsINode* aTarget, nsIDOMEvent* aEvent);
 | |
| 
 | |
|   /**
 | |
|    * Cancel all pending events scheduled by ScheduleEventDispatch which
 | |
|    * targets any node in aDocument.
 | |
|    */
 | |
|   void CancelPendingEvents(nsIDocument* aDocument);
 | |
| 
 | |
|   /**
 | |
|    * Schedule a frame visibility update "soon", subject to the heuristics and
 | |
|    * throttling we apply to visibility updates.
 | |
|    */
 | |
|   void ScheduleFrameVisibilityUpdate() { mNeedToRecomputeVisibility = true; }
 | |
| 
 | |
|   /**
 | |
|    * 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();
 | |
| 
 | |
|   bool IsFrozen() { return mFreezeCount > 0; }
 | |
| 
 | |
|   /**
 | |
|    * Freeze the refresh driver.  It should stop delivering future
 | |
|    * refreshes until thawed. Note that the number of calls to Freeze() must
 | |
|    * match the number of calls to Thaw() in order for the refresh driver to
 | |
|    * be un-frozen.
 | |
|    */
 | |
|   void Freeze();
 | |
| 
 | |
|   /**
 | |
|    * Thaw the refresh driver.  If the number of calls to Freeze() matches the
 | |
|    * number of calls to this function, the refresh driver should start
 | |
|    * delivering refreshes again.
 | |
|    */
 | |
|   void Thaw();
 | |
| 
 | |
|   /**
 | |
|    * Throttle or unthrottle the refresh driver.  This is done if the
 | |
|    * corresponding presshell is hidden or shown.
 | |
|    */
 | |
|   void SetThrottled(bool aThrottled);
 | |
| 
 | |
|   /**
 | |
|    * Return the prescontext we were initialized with
 | |
|    */
 | |
|   nsPresContext* GetPresContext() const { return mPresContext; }
 | |
| 
 | |
|   /**
 | |
|    * PBackgroundChild actor is created asynchronously in content process.
 | |
|    * We can't create vsync-based timers during PBackground startup. This
 | |
|    * function will be called when PBackgroundChild actor is created. Then we can
 | |
|    * do the pending vsync-based timer creation.
 | |
|    */
 | |
|   static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild);
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   /**
 | |
|    * Check whether the given observer is an observer for the given flush type
 | |
|    */
 | |
|   bool IsRefreshObserver(nsARefreshObserver *aObserver,
 | |
|                          mozilla::FlushType aFlushType);
 | |
| #endif
 | |
| 
 | |
|   /**
 | |
|    * Default interval the refresh driver uses, in ms.
 | |
|    */
 | |
|   static int32_t DefaultInterval();
 | |
| 
 | |
|   bool IsInRefresh() { return mInRefresh; }
 | |
| 
 | |
|   void SetIsResizeSuppressed() { mResizeSuppressed = true; }
 | |
|   bool IsResizeSuppressed() const { return mResizeSuppressed; }
 | |
| 
 | |
|   /**
 | |
|    * The latest value of process-wide jank levels.
 | |
|    *
 | |
|    * For each i, sJankLevels[i] counts the number of times delivery of
 | |
|    * vsync to the main thread has been delayed by at least 2^i
 | |
|    * ms. This data structure has been designed to make it easy to
 | |
|    * determine how much jank has taken place between two instants in
 | |
|    * time.
 | |
|    *
 | |
|    * Return `false` if `aJank` needs to be grown to accomodate the
 | |
|    * data but we didn't have enough memory.
 | |
|    */
 | |
|   static bool GetJankLevels(mozilla::Vector<uint64_t>& aJank);
 | |
| 
 | |
|   // mozilla::layers::TransactionIdAllocator
 | |
|   uint64_t GetTransactionId(bool aThrottle) override;
 | |
|   uint64_t LastTransactionId() const override;
 | |
|   void NotifyTransactionCompleted(uint64_t aTransactionId) override;
 | |
|   void RevokeTransactionId(uint64_t aTransactionId) override;
 | |
|   void ClearPendingTransactions() override;
 | |
|   void ResetInitialTransactionId(uint64_t aTransactionId) override;
 | |
|   mozilla::TimeStamp GetTransactionStart() override;
 | |
| 
 | |
|   bool IsWaitingForPaint(mozilla::TimeStamp aTime);
 | |
| 
 | |
|   // nsARefreshObserver
 | |
|   NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); }
 | |
|   NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); }
 | |
|   virtual void WillRefresh(mozilla::TimeStamp aTime) override;
 | |
| 
 | |
|   /**
 | |
|    * Compute the time when the currently active refresh driver timer
 | |
|    * will start its next tick.
 | |
|    *
 | |
|    * Expects a non-null default value that is the upper bound of the
 | |
|    * expected deadline. If the next expected deadline is later than
 | |
|    * the default value, the default value is returned.
 | |
|    *
 | |
|    * If we're animating and we have skipped paints a time in the past
 | |
|    * is returned.
 | |
|    */
 | |
|   static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
 | |
| 
 | |
|   /**
 | |
|    * It returns the expected timestamp of the next tick or nothing if the next
 | |
|    * tick is missed.
 | |
|    */
 | |
|   static mozilla::Maybe<mozilla::TimeStamp> GetNextTickHint();
 | |
| 
 | |
|   static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
 | |
|                                             uint32_t aDelay);
 | |
|   static void CancelIdleRunnable(nsIRunnable* aRunnable);
 | |
| 
 | |
|   bool SkippedPaints() const
 | |
|   {
 | |
|     return mSkippedPaints;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
 | |
|   typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray;
 | |
|   typedef nsTHashtable<nsISupportsHashKey> RequestTable;
 | |
|   struct ImageStartData {
 | |
|     ImageStartData()
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     mozilla::Maybe<mozilla::TimeStamp> mStartTime;
 | |
|     RequestTable mEntries;
 | |
|   };
 | |
|   typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
 | |
| 
 | |
|   void DispatchPendingEvents();
 | |
|   void DispatchAnimationEvents();
 | |
|   void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
 | |
|   void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
 | |
| 
 | |
|   enum EnsureTimerStartedFlags {
 | |
|     eNone = 0,
 | |
|     eForceAdjustTimer = 1 << 0,
 | |
|     eAllowTimeToGoBackwards = 1 << 1,
 | |
|     eNeverAdjustTimer = 1 << 2,
 | |
|   };
 | |
|   void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
 | |
|   void StopTimer();
 | |
| 
 | |
|   uint32_t ObserverCount() const;
 | |
|   uint32_t ImageRequestCount() const;
 | |
|   ObserverArray& ArrayFor(mozilla::FlushType aFlushType);
 | |
|   // Trigger a refresh immediately, if haven't been disconnected or frozen.
 | |
|   void DoRefresh();
 | |
| 
 | |
|   double GetRefreshTimerInterval() const;
 | |
|   double GetRegularTimerInterval(bool *outIsDefault = nullptr) const;
 | |
|   static double GetThrottledTimerInterval();
 | |
| 
 | |
|   static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
 | |
| 
 | |
|   bool HaveFrameRequestCallbacks() const {
 | |
|     return mFrameRequestCallbackDocs.Length() != 0;
 | |
|   }
 | |
| 
 | |
|   void FinishedWaitingForTransaction();
 | |
| 
 | |
|   mozilla::RefreshDriverTimer* ChooseTimer() const;
 | |
|   mozilla::RefreshDriverTimer* mActiveTimer;
 | |
| 
 | |
|   // nsPresContext passed in constructor and unset in Disconnect.
 | |
|   mozilla::WeakPtr<nsPresContext> mPresContext;
 | |
| 
 | |
|   RefPtr<nsRefreshDriver> mRootRefresh;
 | |
| 
 | |
|   // The most recently allocated transaction id.
 | |
|   uint64_t mPendingTransaction;
 | |
|   // The most recently completed transaction id.
 | |
|   uint64_t mCompletedTransaction;
 | |
| 
 | |
|   uint32_t mFreezeCount;
 | |
| 
 | |
|   // How long we wait between ticks for throttled (which generally means
 | |
|   // non-visible) documents registered with a non-throttled refresh driver.
 | |
|   const mozilla::TimeDuration mThrottledFrameRequestInterval;
 | |
| 
 | |
|   // How long we wait, at a minimum, before recomputing approximate frame
 | |
|   // visibility information. This is a minimum because, regardless of this
 | |
|   // interval, we only recompute visibility when we've seen a layout or style
 | |
|   // flush since the last time we did it.
 | |
|   const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
 | |
| 
 | |
|   bool mThrottled;
 | |
|   bool mNeedToRecomputeVisibility;
 | |
|   bool mTestControllingRefreshes;
 | |
|   bool mViewManagerFlushIsPending;
 | |
| 
 | |
|   // True if the view manager needs a flush. Layers-free mode uses this value
 | |
|   // to know when to notify invalidation.
 | |
|   bool mHasScheduleFlush;
 | |
| 
 | |
|   bool mInRefresh;
 | |
| 
 | |
|   // True if the refresh driver is suspended waiting for transaction
 | |
|   // id's to be returned and shouldn't do any work during Tick().
 | |
|   bool mWaitingForTransaction;
 | |
|   // True if Tick() was skipped because of mWaitingForTransaction and
 | |
|   // we should schedule a new Tick immediately when resumed instead
 | |
|   // of waiting until the next interval.
 | |
|   bool mSkippedPaints;
 | |
| 
 | |
|   // True if view managers should delay any resize request until the
 | |
|   // next tick by the refresh driver. This flag will be reset at the
 | |
|   // start of every tick.
 | |
|   bool mResizeSuppressed;
 | |
| 
 | |
|   int64_t mMostRecentRefreshEpochTime;
 | |
|   // Number of seconds that the refresh driver is blocked waiting for a compositor
 | |
|   // transaction to be completed before we append a note to the gfx critical log.
 | |
|   // The number is doubled every time the threshold is hit.
 | |
|   uint64_t mWarningThreshold;
 | |
|   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];
 | |
|   RequestTable mRequests;
 | |
|   ImageStartTable mStartTable;
 | |
|   AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
 | |
|   ScrollEventArray mScrollEvents;
 | |
| 
 | |
|   struct PendingEvent {
 | |
|     nsCOMPtr<nsINode> mTarget;
 | |
|     nsCOMPtr<nsIDOMEvent> mEvent;
 | |
|   };
 | |
| 
 | |
|   AutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
 | |
|   AutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
 | |
|   // nsTArray on purpose, because we want to be able to swap.
 | |
|   nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
 | |
|   nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
 | |
|   nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
 | |
|   nsTArray<PendingEvent> mPendingEvents;
 | |
| 
 | |
|   void BeginRefreshingImages(RequestTable& aEntries,
 | |
|                              mozilla::TimeStamp aDesired);
 | |
| 
 | |
|   friend class mozilla::RefreshDriverTimer;
 | |
| 
 | |
|   static void Shutdown();
 | |
| 
 | |
|   // `true` if we are currently in jank-critical mode.
 | |
|   //
 | |
|   // In jank-critical mode, any iteration of the event loop that takes
 | |
|   // more than 16ms to compute will cause an ongoing animation to miss
 | |
|   // frames.
 | |
|   //
 | |
|   // For simplicity, the current implementation assumes that we are
 | |
|   // in jank-critical mode if and only if the vsync driver has at least
 | |
|   // one observer.
 | |
|   static bool IsJankCritical();
 | |
| };
 | |
| 
 | |
| #endif /* !defined(nsRefreshDriver_h_) */
 |