LCOV - code coverage report
Current view: top level - layout/base - nsRefreshDriver.h (source / functions) Hit Total Coverage
Test: output.info Lines: 41 50 82.0 %
Date: 2017-07-14 16:53:18 Functions: 16 24 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :  * Code to notify things that animate before a refresh, at an appropriate
       9             :  * refresh rate.  (Perhaps temporary, until replaced by compositor.)
      10             :  */
      11             : 
      12             : #ifndef nsRefreshDriver_h_
      13             : #define nsRefreshDriver_h_
      14             : 
      15             : #include "mozilla/FlushType.h"
      16             : #include "mozilla/TimeStamp.h"
      17             : #include "mozilla/Vector.h"
      18             : #include "mozilla/WeakPtr.h"
      19             : #include "nsTObserverArray.h"
      20             : #include "nsTArray.h"
      21             : #include "nsTHashtable.h"
      22             : #include "nsTObserverArray.h"
      23             : #include "nsClassHashtable.h"
      24             : #include "nsHashKeys.h"
      25             : #include "mozilla/Attributes.h"
      26             : #include "mozilla/Maybe.h"
      27             : #include "GeckoProfiler.h"
      28             : #include "mozilla/layers/TransactionIdAllocator.h"
      29             : 
      30             : class nsPresContext;
      31             : class nsIPresShell;
      32             : class nsIDocument;
      33             : class imgIRequest;
      34             : class nsIDOMEvent;
      35             : class nsINode;
      36             : class nsIRunnable;
      37             : 
      38             : namespace mozilla {
      39             : class RefreshDriverTimer;
      40             : namespace layout {
      41             : class VsyncChild;
      42             : } // namespace layout
      43             : } // namespace mozilla
      44             : 
      45             : /**
      46             :  * An abstract base class to be implemented by callers wanting to be
      47             :  * notified at refresh times.  When nothing needs to be painted, callers
      48             :  * may not be notified.
      49             :  */
      50          73 : class nsARefreshObserver {
      51             : public:
      52             :   // AddRef and Release signatures that match nsISupports.  Implementors
      53             :   // must implement reference counting, and those that do implement
      54             :   // nsISupports will already have methods with the correct signature.
      55             :   //
      56             :   // The refresh driver does NOT hold references to refresh observers
      57             :   // except while it is notifying them.
      58             :   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
      59             : 
      60             :   virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
      61             : };
      62             : 
      63             : /**
      64             :  * An abstract base class to be implemented by callers wanting to be notified
      65             :  * that a refresh has occurred. Callers must ensure an observer is removed
      66             :  * before it is destroyed.
      67             :  */
      68           0 : class nsAPostRefreshObserver {
      69             : public:
      70             :   virtual void DidRefresh() = 0;
      71             : };
      72             : 
      73             : class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
      74             :                               public nsARefreshObserver
      75             : {
      76             : public:
      77             :   explicit nsRefreshDriver(nsPresContext *aPresContext);
      78             :   ~nsRefreshDriver();
      79             : 
      80             :   /**
      81             :    * Methods for testing, exposed via nsIDOMWindowUtils.  See
      82             :    * nsIDOMWindowUtils.advanceTimeAndRefresh for description.
      83             :    */
      84             :   void AdvanceTimeAndRefresh(int64_t aMilliseconds);
      85             :   void RestoreNormalRefresh();
      86             :   void DoTick();
      87          75 :   bool IsTestControllingRefreshesEnabled() const
      88             :   {
      89          75 :     return mTestControllingRefreshes;
      90             :   }
      91             : 
      92             :   /**
      93             :    * Return the time of the most recent refresh.  This is intended to be
      94             :    * used by callers who want to start an animation now and want to know
      95             :    * what time to consider the start of the animation.  (This helps
      96             :    * ensure that multiple animations started during the same event off
      97             :    * the main event loop have the same start time.)
      98             :    */
      99             :   mozilla::TimeStamp MostRecentRefresh() const;
     100             :   /**
     101             :    * Same thing, but in microseconds since the epoch.
     102             :    */
     103             :   int64_t MostRecentRefreshEpochTime() const;
     104             : 
     105             :   /**
     106             :    * Add / remove refresh observers.  Returns whether the operation
     107             :    * succeeded.
     108             :    *
     109             :    * The flush type affects:
     110             :    *   + the order in which the observers are notified (lowest flush
     111             :    *     type to highest, in order registered)
     112             :    *   + (in the future) which observers are suppressed when the display
     113             :    *     doesn't require current position data or isn't currently
     114             :    *     painting, and, correspondingly, which get notified when there
     115             :    *     is a flush during such suppression
     116             :    * and it must be FlushType::Style, FlushType::Layout, or FlushType::Display.
     117             :    *
     118             :    * The refresh driver does NOT own a reference to these observers;
     119             :    * they must remove themselves before they are destroyed.
     120             :    *
     121             :    * The observer will be called even if there is no other activity.
     122             :    */
     123             :   bool AddRefreshObserver(nsARefreshObserver *aObserver,
     124             :                           mozilla::FlushType aFlushType);
     125             :   bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
     126             :                              mozilla::FlushType aFlushType);
     127             : 
     128             :   /**
     129             :    * Add an observer that will be called after each refresh. The caller
     130             :    * must remove the observer before it is deleted. This does not trigger
     131             :    * refresh driver ticks.
     132             :    */
     133             :   void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver);
     134             :   void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver);
     135             : 
     136             :   /**
     137             :    * Add/Remove imgIRequest versions of observers.
     138             :    *
     139             :    * These are used for hooking into the refresh driver for
     140             :    * controlling animated images.
     141             :    *
     142             :    * @note The refresh driver owns a reference to these listeners.
     143             :    *
     144             :    * @note Technically, imgIRequest objects are not nsARefreshObservers, but
     145             :    * for controlling animated image repaint events, we subscribe the
     146             :    * imgIRequests to the nsRefreshDriver for notification of paint events.
     147             :    *
     148             :    * @returns whether the operation succeeded, or void in the case of removal.
     149             :    */
     150             :   bool AddImageRequest(imgIRequest* aRequest);
     151             :   void RemoveImageRequest(imgIRequest* aRequest);
     152             : 
     153             :   /**
     154             :    * Add / remove presshells that we should flush style and layout on
     155             :    */
     156          38 :   bool AddStyleFlushObserver(nsIPresShell* aShell) {
     157          38 :     NS_ASSERTION(!mStyleFlushObservers.Contains(aShell),
     158             :                  "Double-adding style flush observer");
     159             :     // We only get the cause for the first observer each frame because capturing
     160             :     // a stack is expensive. This is still useful if (1) you're trying to remove
     161             :     // all flushes for a particial frame or (2) the costly flush is triggered
     162             :     // near the call site where the first observer is triggered.
     163          38 :     if (!mStyleCause) {
     164          38 :       mStyleCause = profiler_get_backtrace();
     165             :     }
     166          38 :     bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr;
     167          38 :     EnsureTimerStarted();
     168             : 
     169          38 :     return appended;
     170             :   }
     171           4 :   void RemoveStyleFlushObserver(nsIPresShell* aShell) {
     172           4 :     mStyleFlushObservers.RemoveElement(aShell);
     173           4 :   }
     174          45 :   bool AddLayoutFlushObserver(nsIPresShell* aShell) {
     175          45 :     NS_ASSERTION(!IsLayoutFlushObserver(aShell),
     176             :                  "Double-adding layout flush observer");
     177             :     // We only get the cause for the first observer each frame because capturing
     178             :     // a stack is expensive. This is still useful if (1) you're trying to remove
     179             :     // all flushes for a particial frame or (2) the costly flush is triggered
     180             :     // near the call site where the first observer is triggered.
     181          45 :     if (!mReflowCause) {
     182          45 :       mReflowCause = profiler_get_backtrace();
     183             :     }
     184          45 :     bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr;
     185          45 :     EnsureTimerStarted();
     186          45 :     return appended;
     187             :   }
     188           4 :   void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
     189           4 :     mLayoutFlushObservers.RemoveElement(aShell);
     190           4 :   }
     191         342 :   bool IsLayoutFlushObserver(nsIPresShell* aShell) {
     192         342 :     return mLayoutFlushObservers.Contains(aShell);
     193             :   }
     194             : 
     195             :   /**
     196             :    * "Early Runner" runnables will be called as the first step when refresh
     197             :    * driver tick is triggered. Runners shouldn't keep other objects alive,
     198             :    * since it isn't guaranteed they will ever get called.
     199             :    */
     200           2 :   void AddEarlyRunner(nsIRunnable* aRunnable)
     201             :   {
     202           2 :     mEarlyRunners.AppendElement(aRunnable);
     203           2 :     EnsureTimerStarted();
     204           2 :   }
     205             : 
     206             :   /**
     207             :    * Remember whether our presshell's view manager needs a flush
     208             :    */
     209             :   void ScheduleViewManagerFlush();
     210          39 :   void RevokeViewManagerFlush() {
     211          39 :     mViewManagerFlushIsPending = false;
     212          39 :   }
     213           0 :   bool ViewManagerFlushIsPending() {
     214           0 :     return mViewManagerFlushIsPending;
     215             :   }
     216             : 
     217             :   /**
     218             :    * Add a document for which we have FrameRequestCallbacks
     219             :    */
     220             :   void ScheduleFrameRequestCallbacks(nsIDocument* aDocument);
     221             : 
     222             :   /**
     223             :    * Remove a document for which we have FrameRequestCallbacks
     224             :    */
     225             :   void RevokeFrameRequestCallbacks(nsIDocument* aDocument);
     226             : 
     227             :   /**
     228             :    * Queue a new event to dispatch in next tick before the style flush
     229             :    */
     230             :   void ScheduleEventDispatch(nsINode* aTarget, nsIDOMEvent* aEvent);
     231             : 
     232             :   /**
     233             :    * Cancel all pending events scheduled by ScheduleEventDispatch which
     234             :    * targets any node in aDocument.
     235             :    */
     236             :   void CancelPendingEvents(nsIDocument* aDocument);
     237             : 
     238             :   /**
     239             :    * Schedule a frame visibility update "soon", subject to the heuristics and
     240             :    * throttling we apply to visibility updates.
     241             :    */
     242           0 :   void ScheduleFrameVisibilityUpdate() { mNeedToRecomputeVisibility = true; }
     243             : 
     244             :   /**
     245             :    * Tell the refresh driver that it is done driving refreshes and
     246             :    * should stop its timer and forget about its pres context.  This may
     247             :    * be called from within a refresh.
     248             :    */
     249             :   void Disconnect();
     250             : 
     251         171 :   bool IsFrozen() { return mFreezeCount > 0; }
     252             : 
     253             :   /**
     254             :    * Freeze the refresh driver.  It should stop delivering future
     255             :    * refreshes until thawed. Note that the number of calls to Freeze() must
     256             :    * match the number of calls to Thaw() in order for the refresh driver to
     257             :    * be un-frozen.
     258             :    */
     259             :   void Freeze();
     260             : 
     261             :   /**
     262             :    * Thaw the refresh driver.  If the number of calls to Freeze() matches the
     263             :    * number of calls to this function, the refresh driver should start
     264             :    * delivering refreshes again.
     265             :    */
     266             :   void Thaw();
     267             : 
     268             :   /**
     269             :    * Throttle or unthrottle the refresh driver.  This is done if the
     270             :    * corresponding presshell is hidden or shown.
     271             :    */
     272             :   void SetThrottled(bool aThrottled);
     273             : 
     274             :   /**
     275             :    * Return the prescontext we were initialized with
     276             :    */
     277          81 :   nsPresContext* GetPresContext() const { return mPresContext; }
     278             : 
     279             :   /**
     280             :    * PBackgroundChild actor is created asynchronously in content process.
     281             :    * We can't create vsync-based timers during PBackground startup. This
     282             :    * function will be called when PBackgroundChild actor is created. Then we can
     283             :    * do the pending vsync-based timer creation.
     284             :    */
     285             :   static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild);
     286             : 
     287             : #ifdef DEBUG
     288             :   /**
     289             :    * Check whether the given observer is an observer for the given flush type
     290             :    */
     291             :   bool IsRefreshObserver(nsARefreshObserver *aObserver,
     292             :                          mozilla::FlushType aFlushType);
     293             : #endif
     294             : 
     295             :   /**
     296             :    * Default interval the refresh driver uses, in ms.
     297             :    */
     298             :   static int32_t DefaultInterval();
     299             : 
     300          20 :   bool IsInRefresh() { return mInRefresh; }
     301             : 
     302           0 :   void SetIsResizeSuppressed() { mResizeSuppressed = true; }
     303          56 :   bool IsResizeSuppressed() const { return mResizeSuppressed; }
     304             : 
     305             :   /**
     306             :    * The latest value of process-wide jank levels.
     307             :    *
     308             :    * For each i, sJankLevels[i] counts the number of times delivery of
     309             :    * vsync to the main thread has been delayed by at least 2^i
     310             :    * ms. This data structure has been designed to make it easy to
     311             :    * determine how much jank has taken place between two instants in
     312             :    * time.
     313             :    *
     314             :    * Return `false` if `aJank` needs to be grown to accomodate the
     315             :    * data but we didn't have enough memory.
     316             :    */
     317             :   static bool GetJankLevels(mozilla::Vector<uint64_t>& aJank);
     318             : 
     319             :   // mozilla::layers::TransactionIdAllocator
     320             :   uint64_t GetTransactionId(bool aThrottle) override;
     321             :   uint64_t LastTransactionId() const override;
     322             :   void NotifyTransactionCompleted(uint64_t aTransactionId) override;
     323             :   void RevokeTransactionId(uint64_t aTransactionId) override;
     324             :   void ClearPendingTransactions() override;
     325             :   void ResetInitialTransactionId(uint64_t aTransactionId) override;
     326             :   mozilla::TimeStamp GetTransactionStart() override;
     327             : 
     328             :   bool IsWaitingForPaint(mozilla::TimeStamp aTime);
     329             : 
     330             :   // nsARefreshObserver
     331         109 :   NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); }
     332          81 :   NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); }
     333             :   virtual void WillRefresh(mozilla::TimeStamp aTime) override;
     334             : 
     335             :   /**
     336             :    * Compute the time when the currently active refresh driver timer
     337             :    * will start its next tick.
     338             :    *
     339             :    * Expects a non-null default value that is the upper bound of the
     340             :    * expected deadline. If the next expected deadline is later than
     341             :    * the default value, the default value is returned.
     342             :    *
     343             :    * If we're animating and we have skipped paints a time in the past
     344             :    * is returned.
     345             :    */
     346             :   static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
     347             : 
     348             :   static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
     349             :                                             uint32_t aDelay);
     350             :   static void CancelIdleRunnable(nsIRunnable* aRunnable);
     351             : 
     352           0 :   bool SkippedPaints() const
     353             :   {
     354           0 :     return mSkippedPaints;
     355             :   }
     356             : 
     357             : private:
     358             :   typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
     359             :   typedef nsTHashtable<nsISupportsHashKey> RequestTable;
     360           0 :   struct ImageStartData {
     361           2 :     ImageStartData()
     362           2 :     {
     363           2 :     }
     364             : 
     365             :     mozilla::Maybe<mozilla::TimeStamp> mStartTime;
     366             :     RequestTable mEntries;
     367             :   };
     368             :   typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
     369             : 
     370             :   void DispatchPendingEvents();
     371             :   void DispatchAnimationEvents();
     372             :   void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
     373             :   void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
     374             : 
     375             :   enum EnsureTimerStartedFlags {
     376             :     eNone = 0,
     377             :     eForceAdjustTimer = 1 << 0,
     378             :     eAllowTimeToGoBackwards = 1 << 1,
     379             :     eNeverAdjustTimer = 1 << 2,
     380             :   };
     381             :   void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
     382             :   void StopTimer();
     383             : 
     384             :   uint32_t ObserverCount() const;
     385             :   uint32_t ImageRequestCount() const;
     386             :   ObserverArray& ArrayFor(mozilla::FlushType aFlushType);
     387             :   // Trigger a refresh immediately, if haven't been disconnected or frozen.
     388             :   void DoRefresh();
     389             : 
     390             :   double GetRefreshTimerInterval() const;
     391             :   double GetRegularTimerInterval(bool *outIsDefault = nullptr) const;
     392             :   static double GetThrottledTimerInterval();
     393             : 
     394             :   static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
     395             : 
     396             :   bool HaveFrameRequestCallbacks() const {
     397             :     return mFrameRequestCallbackDocs.Length() != 0;
     398             :   }
     399             : 
     400             :   void FinishedWaitingForTransaction();
     401             : 
     402             :   mozilla::RefreshDriverTimer* ChooseTimer() const;
     403             :   mozilla::RefreshDriverTimer* mActiveTimer;
     404             : 
     405             :   UniqueProfilerBacktrace mReflowCause;
     406             :   UniqueProfilerBacktrace mStyleCause;
     407             : 
     408             :   // nsPresContext passed in constructor and unset in Disconnect.
     409             :   mozilla::WeakPtr<nsPresContext> mPresContext;
     410             : 
     411             :   RefPtr<nsRefreshDriver> mRootRefresh;
     412             : 
     413             :   // The most recently allocated transaction id.
     414             :   uint64_t mPendingTransaction;
     415             :   // The most recently completed transaction id.
     416             :   uint64_t mCompletedTransaction;
     417             : 
     418             :   uint32_t mFreezeCount;
     419             : 
     420             :   // How long we wait between ticks for throttled (which generally means
     421             :   // non-visible) documents registered with a non-throttled refresh driver.
     422             :   const mozilla::TimeDuration mThrottledFrameRequestInterval;
     423             : 
     424             :   // How long we wait, at a minimum, before recomputing approximate frame
     425             :   // visibility information. This is a minimum because, regardless of this
     426             :   // interval, we only recompute visibility when we've seen a layout or style
     427             :   // flush since the last time we did it.
     428             :   const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
     429             : 
     430             :   bool mThrottled;
     431             :   bool mNeedToRecomputeVisibility;
     432             :   bool mTestControllingRefreshes;
     433             :   bool mViewManagerFlushIsPending;
     434             :   bool mInRefresh;
     435             : 
     436             :   // True if the refresh driver is suspended waiting for transaction
     437             :   // id's to be returned and shouldn't do any work during Tick().
     438             :   bool mWaitingForTransaction;
     439             :   // True if Tick() was skipped because of mWaitingForTransaction and
     440             :   // we should schedule a new Tick immediately when resumed instead
     441             :   // of waiting until the next interval.
     442             :   bool mSkippedPaints;
     443             : 
     444             :   // True if view managers should delay any resize request until the
     445             :   // next tick by the refresh driver. This flag will be reset at the
     446             :   // start of every tick.
     447             :   bool mResizeSuppressed;
     448             : 
     449             :   int64_t mMostRecentRefreshEpochTime;
     450             :   // Number of seconds that the refresh driver is blocked waiting for a compositor
     451             :   // transaction to be completed before we append a note to the gfx critical log.
     452             :   // The number is doubled every time the threshold is hit.
     453             :   uint64_t mWarningThreshold;
     454             :   mozilla::TimeStamp mMostRecentRefresh;
     455             :   mozilla::TimeStamp mMostRecentTick;
     456             :   mozilla::TimeStamp mTickStart;
     457             :   mozilla::TimeStamp mNextThrottledFrameRequestTick;
     458             :   mozilla::TimeStamp mNextRecomputeVisibilityTick;
     459             : 
     460             :   // separate arrays for each flush type we support
     461             :   ObserverArray mObservers[3];
     462             :   RequestTable mRequests;
     463             :   ImageStartTable mStartTable;
     464             :   AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
     465             : 
     466           0 :   struct PendingEvent {
     467             :     nsCOMPtr<nsINode> mTarget;
     468             :     nsCOMPtr<nsIDOMEvent> mEvent;
     469             :   };
     470             : 
     471             :   AutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
     472             :   AutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
     473             :   // nsTArray on purpose, because we want to be able to swap.
     474             :   nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
     475             :   nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
     476             :   nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
     477             :   nsTArray<PendingEvent> mPendingEvents;
     478             : 
     479             :   void BeginRefreshingImages(RequestTable& aEntries,
     480             :                              mozilla::TimeStamp aDesired);
     481             : 
     482             :   friend class mozilla::RefreshDriverTimer;
     483             : 
     484             :   static void Shutdown();
     485             : 
     486             :   // `true` if we are currently in jank-critical mode.
     487             :   //
     488             :   // In jank-critical mode, any iteration of the event loop that takes
     489             :   // more than 16ms to compute will cause an ongoing animation to miss
     490             :   // frames.
     491             :   //
     492             :   // For simplicity, the current implementation assumes that we are
     493             :   // in jank-critical mode if and only if the vsync driver has at least
     494             :   // one observer.
     495             :   static bool IsJankCritical();
     496             : };
     497             : 
     498             : #endif /* !defined(nsRefreshDriver_h_) */

Generated by: LCOV version 1.13