LCOV - code coverage report
Current view: top level - layout/base - nsRefreshDriver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 704 1066 66.0 %
Date: 2017-07-14 16:53:18 Functions: 86 144 59.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             :  * Chrome and each tab have their own RefreshDriver, which in turn
      12             :  * hooks into one of a few global timer based on RefreshDriverTimer,
      13             :  * defined below.  There are two main global timers -- one for active
      14             :  * animations, and one for inactive ones.  These are implemented as
      15             :  * subclasses of RefreshDriverTimer; see below for a description of
      16             :  * their implementations.  In the future, additional timer types may
      17             :  * implement things like blocking on vsync.
      18             :  */
      19             : 
      20             : #ifdef XP_WIN
      21             : #include <windows.h>
      22             : // mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
      23             : // to manually include it
      24             : #include <mmsystem.h>
      25             : #include "WinUtils.h"
      26             : #endif
      27             : 
      28             : #include "mozilla/ArrayUtils.h"
      29             : #include "mozilla/AutoRestore.h"
      30             : #include "mozilla/IntegerRange.h"
      31             : #include "nsHostObjectProtocolHandler.h"
      32             : #include "nsRefreshDriver.h"
      33             : #include "nsITimer.h"
      34             : #include "nsLayoutUtils.h"
      35             : #include "nsPresContext.h"
      36             : #include "nsComponentManagerUtils.h"
      37             : #include "mozilla/Logging.h"
      38             : #include "nsAutoPtr.h"
      39             : #include "nsIDocument.h"
      40             : #include "nsIXULRuntime.h"
      41             : #include "jsapi.h"
      42             : #include "nsContentUtils.h"
      43             : #include "mozilla/PendingAnimationTracker.h"
      44             : #include "mozilla/Preferences.h"
      45             : #include "nsViewManager.h"
      46             : #include "GeckoProfiler.h"
      47             : #include "nsNPAPIPluginInstance.h"
      48             : #include "mozilla/dom/Performance.h"
      49             : #include "mozilla/dom/Selection.h"
      50             : #include "mozilla/dom/WindowBinding.h"
      51             : #include "mozilla/GeckoRestyleManager.h"
      52             : #include "mozilla/RestyleManager.h"
      53             : #include "mozilla/RestyleManagerInlines.h"
      54             : #include "Layers.h"
      55             : #include "imgIContainer.h"
      56             : #include "mozilla/dom/ScriptSettings.h"
      57             : #include "nsDocShell.h"
      58             : #include "nsISimpleEnumerator.h"
      59             : #include "nsJSEnvironment.h"
      60             : #include "mozilla/Telemetry.h"
      61             : #include "gfxPrefs.h"
      62             : #include "BackgroundChild.h"
      63             : #include "mozilla/ipc/PBackgroundChild.h"
      64             : #include "nsIIPCBackgroundChildCreateCallback.h"
      65             : #include "mozilla/layout/VsyncChild.h"
      66             : #include "VsyncSource.h"
      67             : #include "mozilla/VsyncDispatcher.h"
      68             : #include "nsThreadUtils.h"
      69             : #include "mozilla/Unused.h"
      70             : #include "mozilla/TimelineConsumers.h"
      71             : #include "nsAnimationManager.h"
      72             : #include "nsIDOMEvent.h"
      73             : #include "nsDisplayList.h"
      74             : 
      75             : #ifdef MOZ_XUL
      76             : #include "nsXULPopupManager.h"
      77             : #endif
      78             : 
      79             : using namespace mozilla;
      80             : using namespace mozilla::widget;
      81             : using namespace mozilla::ipc;
      82             : using namespace mozilla::layout;
      83             : 
      84             : static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
      85             : #define LOG(...) MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
      86             : 
      87             : #define DEFAULT_THROTTLED_FRAME_RATE 1
      88             : #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
      89             : #define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
      90             : // after 10 minutes, stop firing off inactive timers
      91             : #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
      92             : 
      93             : // The number of seconds spent skipping frames because we are waiting for the compositor
      94             : // before logging.
      95             : #if defined(MOZ_ASAN)
      96             : # define REFRESH_WAIT_WARNING 5
      97             : #elif defined(DEBUG) && !defined(MOZ_VALGRIND)
      98             : # define REFRESH_WAIT_WARNING 5
      99             : #elif defined(DEBUG) && defined(MOZ_VALGRIND)
     100             : # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
     101             : #elif defined(MOZ_VALGRIND)
     102             : # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
     103             : #else
     104             : # define REFRESH_WAIT_WARNING 1
     105             : #endif
     106             : 
     107             : namespace {
     108             :   // `true` if we are currently in jank-critical mode.
     109             :   //
     110             :   // In jank-critical mode, any iteration of the event loop that takes
     111             :   // more than 16ms to compute will cause an ongoing animation to miss
     112             :   // frames.
     113             :   //
     114             :   // For simplicity, the current implementation assumes that we are in
     115             :   // jank-critical mode if and only if at least one vsync driver has
     116             :   // at least one observer.
     117             :   static uint64_t sActiveVsyncTimers = 0;
     118             : 
     119             :   // The latest value of process-wide jank levels.
     120             :   //
     121             :   // For each i, sJankLevels[i] counts the number of times delivery of
     122             :   // vsync to the main thread has been delayed by at least 2^i ms. Use
     123             :   // GetJankLevels to grab a copy of this array.
     124             :   uint64_t sJankLevels[12];
     125             : 
     126             :   // The number outstanding nsRefreshDrivers (that have been created but not
     127             :   // disconnected). When this reaches zero we will call
     128             :   // nsRefreshDriver::Shutdown.
     129             :   static uint32_t sRefreshDriverCount = 0;
     130             : }
     131             : 
     132             : namespace mozilla {
     133             : 
     134             : /*
     135             :  * The base class for all global refresh driver timers.  It takes care
     136             :  * of managing the list of refresh drivers attached to them and
     137             :  * provides interfaces for querying/setting the rate and actually
     138             :  * running a timer 'Tick'.  Subclasses must implement StartTimer(),
     139             :  * StopTimer(), and ScheduleNextTick() -- the first two just
     140             :  * start/stop whatever timer mechanism is in use, and ScheduleNextTick
     141             :  * is called at the start of the Tick() implementation to set a time
     142             :  * for the next tick.
     143             :  */
     144             : class RefreshDriverTimer {
     145             : public:
     146           2 :   RefreshDriverTimer()
     147           2 :     : mLastFireEpoch(0)
     148           2 :     , mLastFireSkipped(false)
     149             :   {
     150           2 :   }
     151             : 
     152           0 :   virtual ~RefreshDriverTimer()
     153           0 :   {
     154           0 :     MOZ_ASSERT(mContentRefreshDrivers.Length() == 0, "Should have removed all content refresh drivers from here by now!");
     155           0 :     MOZ_ASSERT(mRootRefreshDrivers.Length() == 0, "Should have removed all root refresh drivers from here by now!");
     156           0 :   }
     157             : 
     158           8 :   virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
     159             :   {
     160           8 :     LOG("[%p] AddRefreshDriver %p", this, aDriver);
     161             : 
     162           8 :     bool startTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
     163           8 :     if (IsRootRefreshDriver(aDriver)) {
     164           8 :       NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver), "Adding a duplicate root refresh driver!");
     165           8 :       mRootRefreshDrivers.AppendElement(aDriver);
     166             :     } else {
     167           0 :       NS_ASSERTION(!mContentRefreshDrivers.Contains(aDriver), "Adding a duplicate content refresh driver!");
     168           0 :       mContentRefreshDrivers.AppendElement(aDriver);
     169             :     }
     170             : 
     171           8 :     if (startTimer) {
     172           6 :       StartTimer();
     173             :     }
     174           8 :   }
     175             : 
     176           8 :   virtual void RemoveRefreshDriver(nsRefreshDriver* aDriver)
     177             :   {
     178           8 :     LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
     179             : 
     180           8 :     if (IsRootRefreshDriver(aDriver)) {
     181           8 :       NS_ASSERTION(mRootRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a refresh driver that's not in the root refresh list!");
     182           8 :       mRootRefreshDrivers.RemoveElement(aDriver);
     183             :     } else {
     184           0 :       nsPresContext* pc = aDriver->GetPresContext();
     185           0 :       nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
     186             :       // During PresContext shutdown, we can't accurately detect
     187             :       // if a root refresh driver exists or not. Therefore, we have to
     188             :       // search and find out which list this driver exists in.
     189           0 :       if (!rootContext) {
     190           0 :         if (mRootRefreshDrivers.Contains(aDriver)) {
     191           0 :           mRootRefreshDrivers.RemoveElement(aDriver);
     192             :         } else {
     193           0 :           NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
     194             :                        "RemoveRefreshDriver without a display root for a driver that is not in the content refresh list");
     195           0 :           mContentRefreshDrivers.RemoveElement(aDriver);
     196             :         }
     197             :       } else {
     198           0 :         NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a driver that is not in the content refresh list");
     199           0 :         mContentRefreshDrivers.RemoveElement(aDriver);
     200             :       }
     201             :     }
     202             : 
     203           8 :     bool stopTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
     204           8 :     if (stopTimer) {
     205           6 :       StopTimer();
     206             :     }
     207           8 :   }
     208             : 
     209          45 :   TimeStamp MostRecentRefresh() const { return mLastFireTime; }
     210           4 :   int64_t MostRecentRefreshEpochTime() const { return mLastFireEpoch; }
     211             : 
     212           0 :   void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer)
     213             :   {
     214           0 :     MOZ_ASSERT(NS_IsMainThread());
     215             : 
     216           0 :     for (nsRefreshDriver* driver : mContentRefreshDrivers) {
     217           0 :       aNewTimer->AddRefreshDriver(driver);
     218           0 :       driver->mActiveTimer = aNewTimer;
     219             :     }
     220           0 :     mContentRefreshDrivers.Clear();
     221             : 
     222           0 :     for (nsRefreshDriver* driver : mRootRefreshDrivers) {
     223           0 :       aNewTimer->AddRefreshDriver(driver);
     224           0 :       driver->mActiveTimer = aNewTimer;
     225             :     }
     226           0 :     mRootRefreshDrivers.Clear();
     227             : 
     228           0 :     aNewTimer->mLastFireEpoch = mLastFireEpoch;
     229           0 :     aNewTimer->mLastFireTime = mLastFireTime;
     230           0 :   }
     231             : 
     232             :   virtual TimeDuration GetTimerRate() = 0;
     233             : 
     234             :   bool LastTickSkippedAnyPaints() const
     235             :   {
     236             :     return mLastFireSkipped;
     237             :   }
     238             : 
     239          41 :   TimeStamp GetIdleDeadlineHint(TimeStamp aDefault)
     240             :   {
     241          41 :     MOZ_ASSERT(NS_IsMainThread());
     242             : 
     243          41 :     TimeStamp mostRecentRefresh = MostRecentRefresh();
     244          41 :     TimeDuration refreshRate = GetTimerRate();
     245          41 :     TimeStamp idleEnd = mostRecentRefresh + refreshRate;
     246             : 
     247          82 :     if (idleEnd +
     248          82 :           refreshRate * nsLayoutUtils::QuiescentFramesBeforeIdlePeriod() <
     249          82 :         TimeStamp::Now()) {
     250          15 :       return aDefault;
     251             :     }
     252             : 
     253          52 :     idleEnd = idleEnd - TimeDuration::FromMilliseconds(
     254          52 :       nsLayoutUtils::IdlePeriodDeadlineLimit());
     255          26 :     return idleEnd < aDefault ? idleEnd : aDefault;
     256             :   }
     257             : 
     258             : protected:
     259             :   virtual void StartTimer() = 0;
     260             :   virtual void StopTimer() = 0;
     261             :   virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
     262             : 
     263          16 :   bool IsRootRefreshDriver(nsRefreshDriver* aDriver)
     264             :   {
     265          16 :     nsPresContext* pc = aDriver->GetPresContext();
     266          16 :     nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
     267          16 :     if (!rootContext) {
     268           0 :       return false;
     269             :     }
     270             : 
     271          16 :     return aDriver == rootContext->RefreshDriver();
     272             :   }
     273             : 
     274             :   /*
     275             :    * Actually runs a tick, poking all the attached RefreshDrivers.
     276             :    * Grabs the "now" time via JS_Now and TimeStamp::Now().
     277             :    */
     278           0 :   void Tick()
     279             :   {
     280           0 :     int64_t jsnow = JS_Now();
     281           0 :     TimeStamp now = TimeStamp::Now();
     282           0 :     Tick(jsnow, now);
     283           0 :   }
     284             : 
     285         142 :   void TickRefreshDrivers(int64_t aJsNow, TimeStamp aNow, nsTArray<RefPtr<nsRefreshDriver>>& aDrivers)
     286             :   {
     287         142 :     if (aDrivers.IsEmpty()) {
     288          71 :       return;
     289             :     }
     290             : 
     291         142 :     nsTArray<RefPtr<nsRefreshDriver> > drivers(aDrivers);
     292         144 :     for (nsRefreshDriver* driver : drivers) {
     293             :       // don't poke this driver if it's in test mode
     294          73 :       if (driver->IsTestControllingRefreshesEnabled()) {
     295           0 :         continue;
     296             :       }
     297             : 
     298          73 :       TickDriver(driver, aJsNow, aNow);
     299             : 
     300          73 :       mLastFireSkipped = mLastFireSkipped || driver->mSkippedPaints;
     301             :     }
     302             :   }
     303             : 
     304             :   /*
     305             :    * Tick the refresh drivers based on the given timestamp.
     306             :    */
     307          71 :   void Tick(int64_t jsnow, TimeStamp now)
     308             :   {
     309          71 :     ScheduleNextTick(now);
     310             : 
     311          71 :     mLastFireEpoch = jsnow;
     312          71 :     mLastFireTime = now;
     313          71 :     mLastFireSkipped = false;
     314             : 
     315          71 :     LOG("[%p] ticking drivers...", this);
     316             :     // RD is short for RefreshDriver
     317         142 :     AutoProfilerTracing tracing("Paint", "RefreshDriverTick");
     318             : 
     319          71 :     TickRefreshDrivers(jsnow, now, mContentRefreshDrivers);
     320          71 :     TickRefreshDrivers(jsnow, now, mRootRefreshDrivers);
     321             : 
     322          71 :     LOG("[%p] done.", this);
     323          71 :   }
     324             : 
     325          73 :   static void TickDriver(nsRefreshDriver* driver, int64_t jsnow, TimeStamp now)
     326             :   {
     327          73 :     LOG(">> TickDriver: %p (jsnow: %" PRId64 ")", driver, jsnow);
     328          73 :     driver->Tick(jsnow, now);
     329          73 :   }
     330             : 
     331             :   int64_t mLastFireEpoch;
     332             :   bool mLastFireSkipped;
     333             :   TimeStamp mLastFireTime;
     334             :   TimeStamp mTargetTime;
     335             : 
     336             :   nsTArray<RefPtr<nsRefreshDriver> > mContentRefreshDrivers;
     337             :   nsTArray<RefPtr<nsRefreshDriver> > mRootRefreshDrivers;
     338             : 
     339             :   // useful callback for nsITimer-based derived classes, here
     340             :   // bacause of c++ protected shenanigans
     341           0 :   static void TimerTick(nsITimer* aTimer, void* aClosure)
     342             :   {
     343           0 :     RefreshDriverTimer *timer = static_cast<RefreshDriverTimer*>(aClosure);
     344           0 :     timer->Tick();
     345           0 :   }
     346             : };
     347             : 
     348             : /*
     349             :  * A RefreshDriverTimer that uses a nsITimer as the underlying timer.  Note that
     350             :  * this is a ONE_SHOT timer, not a repeating one!  Subclasses are expected to
     351             :  * implement ScheduleNextTick and intelligently calculate the next time to tick,
     352             :  * and to reset mTimer.  Using a repeating nsITimer gets us into a lot of pain
     353             :  * with its attempt at intelligent slack removal and such, so we don't do it.
     354             :  */
     355             : class SimpleTimerBasedRefreshDriverTimer :
     356             :     public RefreshDriverTimer
     357             : {
     358             : public:
     359             :   /*
     360             :    * aRate -- the delay, in milliseconds, requested between timer firings
     361             :    */
     362           0 :   explicit SimpleTimerBasedRefreshDriverTimer(double aRate)
     363           0 :   {
     364           0 :     SetRate(aRate);
     365           0 :     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     366           0 :   }
     367             : 
     368           0 :   ~SimpleTimerBasedRefreshDriverTimer() override
     369           0 :   {
     370           0 :     StopTimer();
     371           0 :   }
     372             : 
     373             :   // will take effect at next timer tick
     374           0 :   virtual void SetRate(double aNewRate)
     375             :   {
     376           0 :     mRateMilliseconds = aNewRate;
     377           0 :     mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
     378           0 :   }
     379             : 
     380             :   double GetRate() const
     381             :   {
     382             :     return mRateMilliseconds;
     383             :   }
     384             : 
     385           0 :   TimeDuration GetTimerRate() override
     386             :   {
     387           0 :     return mRateDuration;
     388             :   }
     389             : 
     390             : protected:
     391             : 
     392           0 :   void StartTimer() override
     393             :   {
     394             :     // pretend we just fired, and we schedule the next tick normally
     395           0 :     mLastFireEpoch = JS_Now();
     396           0 :     mLastFireTime = TimeStamp::Now();
     397             : 
     398           0 :     mTargetTime = mLastFireTime + mRateDuration;
     399             : 
     400           0 :     uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
     401           0 :     mTimer->InitWithNamedFuncCallback(
     402             :       TimerTick,
     403             :       this,
     404             :       delay,
     405             :       nsITimer::TYPE_ONE_SHOT,
     406           0 :       "SimpleTimerBasedRefreshDriverTimer::StartTimer");
     407           0 :   }
     408             : 
     409           0 :   void StopTimer() override
     410             :   {
     411           0 :     mTimer->Cancel();
     412           0 :   }
     413             : 
     414             :   double mRateMilliseconds;
     415             :   TimeDuration mRateDuration;
     416             :   RefPtr<nsITimer> mTimer;
     417             : };
     418             : 
     419             : /*
     420             :  * A refresh driver that listens to vsync events and ticks the refresh driver
     421             :  * on vsync intervals. We throttle the refresh driver if we get too many
     422             :  * vsync events and wait to catch up again.
     423             :  */
     424             : class VsyncRefreshDriverTimer : public RefreshDriverTimer
     425             : {
     426             : public:
     427           1 :   VsyncRefreshDriverTimer()
     428           1 :     : mVsyncChild(nullptr)
     429             :   {
     430           1 :     MOZ_ASSERT(XRE_IsParentProcess());
     431           1 :     MOZ_ASSERT(NS_IsMainThread());
     432           1 :     mVsyncObserver = new RefreshDriverVsyncObserver(this);
     433           2 :     RefPtr<mozilla::gfx::VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
     434           1 :     MOZ_ALWAYS_TRUE(mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher());
     435           1 :     mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
     436           1 :     mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
     437           1 :   }
     438             : 
     439           1 :   explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
     440           1 :     : mVsyncChild(aVsyncChild)
     441             :   {
     442           1 :     MOZ_ASSERT(!XRE_IsParentProcess());
     443           1 :     MOZ_ASSERT(NS_IsMainThread());
     444           1 :     MOZ_ASSERT(mVsyncChild);
     445           1 :     mVsyncObserver = new RefreshDriverVsyncObserver(this);
     446           1 :     mVsyncChild->SetVsyncObserver(mVsyncObserver);
     447           1 :     mVsyncRate = mVsyncChild->GetVsyncRate();
     448           1 :   }
     449             : 
     450          41 :   TimeDuration GetTimerRate() override
     451             :   {
     452          41 :     if (mVsyncRate != TimeDuration::Forever()) {
     453          40 :       return mVsyncRate;
     454             :     }
     455             : 
     456           1 :     if (mVsyncChild) {
     457             :       // VsyncChild::VsyncRate() is a simple getter for the cached
     458             :       // hardware vsync rate. We depend on that
     459             :       // VsyncChild::GetVsyncRate() being called in the constructor
     460             :       // will result in a response with the actual vsync rate sooner
     461             :       // or later. Until that happens VsyncChild::VsyncRate() returns
     462             :       // TimeDuration::Forever() and we have to guess below.
     463           1 :       mVsyncRate = mVsyncChild->VsyncRate();
     464             :     }
     465             : 
     466             :     // If hardware queries fail / are unsupported, we have to just guess.
     467           2 :     return mVsyncRate != TimeDuration::Forever()
     468             :              ? mVsyncRate
     469           1 :              : TimeDuration::FromMilliseconds(1000.0 / 60.0);
     470             :   }
     471             : 
     472             : private:
     473             :   // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
     474             :   // explicitly shutdown. We create an inner class that has the VsyncObserver
     475             :   // and is shutdown when the RefreshDriverTimer is deleted. The alternative is
     476             :   // to (a) make all RefreshDriverTimer RefCounted or (b) use different
     477             :   // VsyncObserver types.
     478             :   class RefreshDriverVsyncObserver final : public VsyncObserver
     479             :   {
     480             :   public:
     481           2 :     explicit RefreshDriverVsyncObserver(VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
     482           2 :       : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer)
     483             :       , mRefreshTickLock("RefreshTickLock")
     484             :       , mRecentVsync(TimeStamp::Now())
     485             :       , mLastChildTick(TimeStamp::Now())
     486             :       , mVsyncRate(TimeDuration::Forever())
     487           2 :       , mProcessedVsync(true)
     488             :     {
     489           2 :       MOZ_ASSERT(NS_IsMainThread());
     490           2 :     }
     491             : 
     492             :     class ParentProcessVsyncNotifier final: public Runnable,
     493             :                                             public nsIRunnablePriority
     494             :     {
     495             :     public:
     496          67 :       ParentProcessVsyncNotifier(RefreshDriverVsyncObserver* aObserver,
     497             :                                  TimeStamp aVsyncTimestamp)
     498          67 :         : Runnable("VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::"
     499             :                    "ParentProcessVsyncNotifier")
     500             :         , mObserver(aObserver)
     501          67 :         , mVsyncTimestamp(aVsyncTimestamp)
     502             :       {
     503          67 :       }
     504             : 
     505             :       NS_DECL_ISUPPORTS_INHERITED
     506             : 
     507          67 :       NS_IMETHOD Run() override
     508             :       {
     509          67 :         MOZ_ASSERT(NS_IsMainThread());
     510             :         static bool sCacheInitialized = false;
     511             :         static bool sHighPriorityPrefValue = false;
     512          67 :         if (!sCacheInitialized) {
     513           1 :           sCacheInitialized = true;
     514           1 :           Preferences::AddBoolVarCache(&sHighPriorityPrefValue,
     515             :                                        "vsync.parentProcess.highPriority",
     516           2 :                                        mozilla::BrowserTabsRemoteAutostart());
     517             :         }
     518          67 :         sHighPriorityEnabled = sHighPriorityPrefValue;
     519             : 
     520          67 :         mObserver->TickRefreshDriver(mVsyncTimestamp);
     521          67 :         return NS_OK;
     522             :       }
     523             : 
     524          67 :       NS_IMETHOD GetPriority(uint32_t* aPriority) override
     525             :       {
     526          67 :         *aPriority =
     527          67 :           sHighPriorityEnabled ? nsIRunnablePriority::PRIORITY_HIGH :
     528          67 :                                  nsIRunnablePriority::PRIORITY_NORMAL;
     529          67 :         return NS_OK;
     530             :       }
     531             : 
     532             :     private:
     533         201 :       ~ParentProcessVsyncNotifier() {}
     534             :       RefPtr<RefreshDriverVsyncObserver> mObserver;
     535             :       TimeStamp mVsyncTimestamp;
     536             :       static mozilla::Atomic<bool> sHighPriorityEnabled;
     537             :     };
     538             : 
     539         511 :     bool NotifyVsync(TimeStamp aVsyncTimestamp) override
     540             :     {
     541         511 :       if (!NS_IsMainThread()) {
     542         507 :         MOZ_ASSERT(XRE_IsParentProcess());
     543             :         // Compress vsync notifications such that only 1 may run at a time
     544             :         // This is so that we don't flood the refresh driver with vsync messages
     545             :         // if the main thread is blocked for long periods of time
     546             :         { // scope lock
     547         574 :           MonitorAutoLock lock(mRefreshTickLock);
     548         507 :           mRecentVsync = aVsyncTimestamp;
     549         507 :           if (!mProcessedVsync) {
     550         440 :             return true;
     551             :           }
     552          67 :           mProcessedVsync = false;
     553             :         }
     554             : 
     555             :         nsCOMPtr<nsIRunnable> vsyncEvent =
     556         201 :           new ParentProcessVsyncNotifier(this, aVsyncTimestamp);
     557          67 :         NS_DispatchToMainThread(vsyncEvent);
     558             :       } else {
     559           4 :         mRecentVsync = aVsyncTimestamp;
     560           4 :         if (!mBlockUntil.IsNull() && mBlockUntil > aVsyncTimestamp) {
     561           0 :           if (mProcessedVsync) {
     562             :             // Re-post vsync update as a normal priority runnable. This way
     563             :             // runnables already in normal priority queue get processed.
     564           0 :             mProcessedVsync = false;
     565             :             nsCOMPtr<nsIRunnable> vsyncEvent =
     566           0 :               NewRunnableMethod<>(
     567             :                 "RefreshDriverVsyncObserver::NormalPriorityNotify",
     568           0 :                 this, &RefreshDriverVsyncObserver::NormalPriorityNotify);
     569           0 :             NS_DispatchToMainThread(vsyncEvent);
     570             :           }
     571             : 
     572           0 :           return true;
     573             :         }
     574             : 
     575           4 :         TickRefreshDriver(aVsyncTimestamp);
     576             :       }
     577             : 
     578          71 :       return true;
     579             :     }
     580             : 
     581           0 :     void Shutdown()
     582             :     {
     583           0 :       MOZ_ASSERT(NS_IsMainThread());
     584           0 :       mVsyncRefreshDriverTimer = nullptr;
     585           0 :     }
     586             : 
     587           2 :     void OnTimerStart()
     588             :     {
     589           2 :       if (!XRE_IsParentProcess()) {
     590           2 :         mLastChildTick = TimeStamp::Now();
     591             :       }
     592           2 :     }
     593             : 
     594           0 :     void NormalPriorityNotify()
     595             :     {
     596           0 :       if (mLastProcessedTickInChildProcess.IsNull() ||
     597           0 :           mRecentVsync > mLastProcessedTickInChildProcess) {
     598             :         // mBlockUntil is for high priority vsync notifications only.
     599           0 :         mBlockUntil = TimeStamp();
     600           0 :         TickRefreshDriver(mRecentVsync);
     601             :       }
     602             : 
     603           0 :       mProcessedVsync = true;
     604           0 :     }
     605             : 
     606             :   private:
     607           0 :     ~RefreshDriverVsyncObserver() = default;
     608             : 
     609          71 :     void RecordTelemetryProbes(TimeStamp aVsyncTimestamp)
     610             :     {
     611          71 :       MOZ_ASSERT(NS_IsMainThread());
     612             :     #ifndef ANDROID  /* bug 1142079 */
     613          71 :       if (XRE_IsParentProcess()) {
     614          67 :         TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
     615          67 :         uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
     616             :         Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
     617          67 :                               sample);
     618             :         Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
     619          67 :                               sample);
     620          67 :         RecordJank(sample);
     621           4 :       } else if (mVsyncRate != TimeDuration::Forever()) {
     622           3 :         TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
     623           3 :         if (contentDelay.ToMilliseconds() < 0 ){
     624             :           // Vsyncs are noisy and some can come at a rate quicker than
     625             :           // the reported hardware rate. In those cases, consider that we have 0 delay.
     626           1 :           contentDelay = TimeDuration::FromMilliseconds(0);
     627             :         }
     628           3 :         uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
     629             :         Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
     630           3 :                               sample);
     631             :         Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
     632           3 :                               sample);
     633           3 :         RecordJank(sample);
     634             :       } else {
     635             :         // Request the vsync rate from the parent process. Might be a few vsyncs
     636             :         // until the parent responds.
     637           1 :         if (mVsyncRefreshDriverTimer) {
     638           1 :           mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
     639             :         }
     640             :       }
     641             :     #endif
     642          71 :     }
     643             : 
     644          70 :     void RecordJank(uint32_t aJankMS)
     645             :     {
     646          70 :       uint32_t duration = 1 /* ms */;
     647        1073 :       for (size_t i = 0;
     648         381 :            i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
     649         311 :            ++i, duration *= 2) {
     650         311 :         sJankLevels[i]++;
     651             :       }
     652          70 :     }
     653             : 
     654          71 :     void TickRefreshDriver(TimeStamp aVsyncTimestamp)
     655             :     {
     656          71 :       MOZ_ASSERT(NS_IsMainThread());
     657             : 
     658          71 :       RecordTelemetryProbes(aVsyncTimestamp);
     659          71 :       if (XRE_IsParentProcess()) {
     660         134 :         MonitorAutoLock lock(mRefreshTickLock);
     661          67 :         aVsyncTimestamp = mRecentVsync;
     662          67 :         mProcessedVsync = true;
     663             :       } else {
     664             : 
     665           4 :         mLastChildTick = TimeStamp::Now();
     666           4 :         mLastProcessedTickInChildProcess = aVsyncTimestamp;
     667             :       }
     668          71 :       MOZ_ASSERT(aVsyncTimestamp <= TimeStamp::Now());
     669             : 
     670             :       // We might have a problem that we call ~VsyncRefreshDriverTimer() before
     671             :       // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
     672             :       // before use.
     673          71 :       if (mVsyncRefreshDriverTimer) {
     674          71 :         mVsyncRefreshDriverTimer->RunRefreshDrivers(aVsyncTimestamp);
     675             :       }
     676             : 
     677          71 :       if (!XRE_IsParentProcess()) {
     678           4 :         TimeDuration tickDuration = TimeStamp::Now() - mLastChildTick;
     679           4 :         mBlockUntil = aVsyncTimestamp + tickDuration;
     680             :       }
     681          71 :     }
     682             : 
     683             :     // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
     684             :     // be always available before Shutdown(). We can just use the raw pointer
     685             :     // here.
     686             :     VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
     687             :     Monitor mRefreshTickLock;
     688             :     TimeStamp mRecentVsync;
     689             :     TimeStamp mLastChildTick;
     690             :     TimeStamp mLastProcessedTickInChildProcess;
     691             :     TimeStamp mBlockUntil;
     692             :     TimeDuration mVsyncRate;
     693             :     bool mProcessedVsync;
     694             :   }; // RefreshDriverVsyncObserver
     695             : 
     696           0 :   ~VsyncRefreshDriverTimer() override
     697           0 :   {
     698           0 :     if (XRE_IsParentProcess()) {
     699           0 :       mVsyncDispatcher->SetParentRefreshTimer(nullptr);
     700           0 :       mVsyncDispatcher = nullptr;
     701             :     } else {
     702             :       // Since the PVsyncChild actors live through the life of the process, just
     703             :       // send the unobserveVsync message to disable vsync event. We don't need
     704             :       // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
     705             :       // will be called and clean up this actor.
     706           0 :       Unused << mVsyncChild->SendUnobserve();
     707           0 :       mVsyncChild->SetVsyncObserver(nullptr);
     708           0 :       mVsyncChild = nullptr;
     709             :     }
     710             : 
     711             :     // Detach current vsync timer from this VsyncObserver. The observer will no
     712             :     // longer tick this timer.
     713           0 :     mVsyncObserver->Shutdown();
     714           0 :     mVsyncObserver = nullptr;
     715           0 :   }
     716             : 
     717           6 :   void StartTimer() override
     718             :   {
     719             :     // Protect updates to `sActiveVsyncTimers`.
     720           6 :     MOZ_ASSERT(NS_IsMainThread());
     721             : 
     722           6 :     mLastFireEpoch = JS_Now();
     723           6 :     mLastFireTime = TimeStamp::Now();
     724             : 
     725           6 :     if (XRE_IsParentProcess()) {
     726           4 :       mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
     727             :     } else {
     728           2 :       Unused << mVsyncChild->SendObserve();
     729           2 :       mVsyncObserver->OnTimerStart();
     730             :     }
     731             : 
     732           6 :     ++sActiveVsyncTimers;
     733           6 :   }
     734             : 
     735           6 :   void StopTimer() override
     736             :   {
     737             :     // Protect updates to `sActiveVsyncTimers`.
     738           6 :     MOZ_ASSERT(NS_IsMainThread());
     739             : 
     740           6 :     if (XRE_IsParentProcess()) {
     741           4 :       mVsyncDispatcher->SetParentRefreshTimer(nullptr);
     742             :     } else {
     743           2 :       Unused << mVsyncChild->SendUnobserve();
     744             :     }
     745             : 
     746           6 :     MOZ_ASSERT(sActiveVsyncTimers > 0);
     747           6 :     --sActiveVsyncTimers;
     748           6 :   }
     749             : 
     750          71 :   void ScheduleNextTick(TimeStamp aNowTime) override
     751             :   {
     752             :     // Do nothing since we just wait for the next vsync from
     753             :     // RefreshDriverVsyncObserver.
     754          71 :   }
     755             : 
     756          71 :   void RunRefreshDrivers(TimeStamp aTimeStamp)
     757             :   {
     758          71 :     int64_t jsnow = JS_Now();
     759          71 :     TimeDuration diff = TimeStamp::Now() - aTimeStamp;
     760          71 :     int64_t vsyncJsNow = jsnow - diff.ToMicroseconds();
     761          71 :     Tick(vsyncJsNow, aTimeStamp);
     762          71 :   }
     763             : 
     764             :   RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
     765             :   // Used for parent process.
     766             :   RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
     767             :   // Used for child process.
     768             :   // The mVsyncChild will be always available before VsncChild::ActorDestroy().
     769             :   // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
     770             :   RefPtr<VsyncChild> mVsyncChild;
     771             :   TimeDuration mVsyncRate;
     772             : }; // VsyncRefreshDriverTimer
     773             : 
     774        1340 : NS_IMPL_ISUPPORTS_INHERITED(VsyncRefreshDriverTimer::
     775             :                             RefreshDriverVsyncObserver::
     776             :                             ParentProcessVsyncNotifier,
     777             :                             Runnable, nsIRunnablePriority)
     778             : 
     779             : mozilla::Atomic<bool>
     780             : VsyncRefreshDriverTimer::
     781             : RefreshDriverVsyncObserver::
     782             : ParentProcessVsyncNotifier::sHighPriorityEnabled(false);
     783             : 
     784             : /**
     785             :  * Since the content process takes some time to setup
     786             :  * the vsync IPC connection, this timer is used
     787             :  * during the intial startup process.
     788             :  * During initial startup, the refresh drivers
     789             :  * are ticked off this timer, and are swapped out once content
     790             :  * vsync IPC connection is established.
     791             :  */
     792           0 : class StartupRefreshDriverTimer :
     793             :     public SimpleTimerBasedRefreshDriverTimer
     794             : {
     795             : public:
     796           0 :   explicit StartupRefreshDriverTimer(double aRate)
     797           0 :     : SimpleTimerBasedRefreshDriverTimer(aRate)
     798             :   {
     799           0 :   }
     800             : 
     801             : protected:
     802           0 :   void ScheduleNextTick(TimeStamp aNowTime) override
     803             :   {
     804             :     // Since this is only used for startup, it isn't super critical
     805             :     // that we tick at consistent intervals.
     806           0 :     TimeStamp newTarget = aNowTime + mRateDuration;
     807           0 :     uint32_t delay = static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
     808           0 :     mTimer->InitWithNamedFuncCallback(
     809             :       TimerTick,
     810             :       this,
     811             :       delay,
     812             :       nsITimer::TYPE_ONE_SHOT,
     813           0 :       "StartupRefreshDriverTimer::ScheduleNextTick");
     814           0 :     mTargetTime = newTarget;
     815           0 :   }
     816             : };
     817             : 
     818             : /*
     819             :  * A RefreshDriverTimer for inactive documents.  When a new refresh driver is
     820             :  * added, the rate is reset to the base (normally 1s/1fps).  Every time
     821             :  * it ticks, a single refresh driver is poked.  Once they have all been poked,
     822             :  * the duration between ticks doubles, up to mDisableAfterMilliseconds.  At that point,
     823             :  * the timer is quiet and doesn't tick (until something is added to it again).
     824             :  *
     825             :  * When a timer is removed, there is a possibility of another timer
     826             :  * being skipped for one cycle.  We could avoid this by adjusting
     827             :  * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
     828             :  * add that complexity.  All we want is for inactive drivers to tick
     829             :  * at some point, but we don't care too much about how often.
     830             :  */
     831           0 : class InactiveRefreshDriverTimer final :
     832             :     public SimpleTimerBasedRefreshDriverTimer
     833             : {
     834             : public:
     835             :   explicit InactiveRefreshDriverTimer(double aRate)
     836             :     : SimpleTimerBasedRefreshDriverTimer(aRate),
     837             :       mNextTickDuration(aRate),
     838             :       mDisableAfterMilliseconds(-1.0),
     839             :       mNextDriverIndex(0)
     840             :   {
     841             :   }
     842             : 
     843           0 :   InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
     844           0 :     : SimpleTimerBasedRefreshDriverTimer(aRate),
     845             :       mNextTickDuration(aRate),
     846             :       mDisableAfterMilliseconds(aDisableAfterMilliseconds),
     847           0 :       mNextDriverIndex(0)
     848             :   {
     849           0 :   }
     850             : 
     851           0 :   void AddRefreshDriver(nsRefreshDriver* aDriver) override
     852             :   {
     853           0 :     RefreshDriverTimer::AddRefreshDriver(aDriver);
     854             : 
     855           0 :     LOG("[%p] inactive timer got new refresh driver %p, resetting rate",
     856             :         this, aDriver);
     857             : 
     858             :     // reset the timer, and start with the newly added one next time.
     859           0 :     mNextTickDuration = mRateMilliseconds;
     860             : 
     861             :     // we don't really have to start with the newly added one, but we may as well
     862             :     // not tick the old ones at the fastest rate any more than we need to.
     863           0 :     mNextDriverIndex = GetRefreshDriverCount() - 1;
     864             : 
     865           0 :     StopTimer();
     866           0 :     StartTimer();
     867           0 :   }
     868             : 
     869           0 :   TimeDuration GetTimerRate() override
     870             :   {
     871           0 :     return TimeDuration::FromMilliseconds(mNextTickDuration);
     872             :   }
     873             : 
     874             : protected:
     875           0 :   uint32_t GetRefreshDriverCount()
     876             :   {
     877           0 :     return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
     878             :   }
     879             : 
     880           0 :   void StartTimer() override
     881             :   {
     882           0 :     mLastFireEpoch = JS_Now();
     883           0 :     mLastFireTime = TimeStamp::Now();
     884             : 
     885           0 :     mTargetTime = mLastFireTime + mRateDuration;
     886             : 
     887           0 :     uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
     888           0 :     mTimer->InitWithNamedFuncCallback(TimerTickOne,
     889             :                                       this,
     890             :                                       delay,
     891             :                                       nsITimer::TYPE_ONE_SHOT,
     892           0 :                                       "InactiveRefreshDriverTimer::StartTimer");
     893           0 :   }
     894             : 
     895           0 :   void StopTimer() override
     896             :   {
     897           0 :     mTimer->Cancel();
     898           0 :   }
     899             : 
     900           0 :   void ScheduleNextTick(TimeStamp aNowTime) override
     901             :   {
     902           0 :     if (mDisableAfterMilliseconds > 0.0 &&
     903           0 :         mNextTickDuration > mDisableAfterMilliseconds)
     904             :     {
     905             :       // We hit the time after which we should disable
     906             :       // inactive window refreshes; don't schedule anything
     907             :       // until we get kicked by an AddRefreshDriver call.
     908           0 :       return;
     909             :     }
     910             : 
     911             :     // double the next tick time if we've already gone through all of them once
     912           0 :     if (mNextDriverIndex >= GetRefreshDriverCount()) {
     913           0 :       mNextTickDuration *= 2.0;
     914           0 :       mNextDriverIndex = 0;
     915             :     }
     916             : 
     917             :     // this doesn't need to be precise; do a simple schedule
     918           0 :     uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
     919           0 :     mTimer->InitWithNamedFuncCallback(
     920             :       TimerTickOne,
     921             :       this,
     922             :       delay,
     923             :       nsITimer::TYPE_ONE_SHOT,
     924           0 :       "InactiveRefreshDriverTimer::ScheduleNextTick");
     925             : 
     926           0 :     LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this, mNextTickDuration,
     927             :         mNextDriverIndex, GetRefreshDriverCount());
     928             :   }
     929             : 
     930             :   /* Runs just one driver's tick. */
     931           0 :   void TickOne()
     932             :   {
     933           0 :     int64_t jsnow = JS_Now();
     934           0 :     TimeStamp now = TimeStamp::Now();
     935             : 
     936           0 :     ScheduleNextTick(now);
     937             : 
     938           0 :     mLastFireEpoch = jsnow;
     939           0 :     mLastFireTime = now;
     940           0 :     mLastFireSkipped = false;
     941             : 
     942           0 :     nsTArray<RefPtr<nsRefreshDriver> > drivers(mContentRefreshDrivers);
     943           0 :     drivers.AppendElements(mRootRefreshDrivers);
     944           0 :     size_t index = mNextDriverIndex;
     945             : 
     946           0 :     if (index < drivers.Length() &&
     947           0 :         !drivers[index]->IsTestControllingRefreshesEnabled())
     948             :     {
     949           0 :       TickDriver(drivers[index], jsnow, now);
     950           0 :       mLastFireSkipped = mLastFireSkipped || drivers[index]->SkippedPaints();
     951             :     }
     952             : 
     953           0 :     mNextDriverIndex++;
     954           0 :   }
     955             : 
     956           0 :   static void TimerTickOne(nsITimer* aTimer, void* aClosure)
     957             :   {
     958           0 :     InactiveRefreshDriverTimer *timer = static_cast<InactiveRefreshDriverTimer*>(aClosure);
     959           0 :     timer->TickOne();
     960           0 :   }
     961             : 
     962             :   double mNextTickDuration;
     963             :   double mDisableAfterMilliseconds;
     964             :   uint32_t mNextDriverIndex;
     965             : };
     966             : 
     967             : // The PBackground protocol connection callback. It will be called when
     968             : // PBackground is ready. Then we create the PVsync sub-protocol for our
     969             : // vsync-base RefreshTimer.
     970             : class VsyncChildCreateCallback final : public nsIIPCBackgroundChildCreateCallback
     971             : {
     972             :   NS_DECL_ISUPPORTS
     973             : 
     974             : public:
     975           0 :   VsyncChildCreateCallback()
     976           0 :   {
     977           0 :     MOZ_ASSERT(NS_IsMainThread());
     978           0 :   }
     979             : 
     980           1 :   static void CreateVsyncActor(PBackgroundChild* aPBackgroundChild)
     981             :   {
     982           1 :     MOZ_ASSERT(NS_IsMainThread());
     983           1 :     MOZ_ASSERT(aPBackgroundChild);
     984             : 
     985           1 :     layout::PVsyncChild* actor = aPBackgroundChild->SendPVsyncConstructor();
     986           1 :     layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
     987           1 :     nsRefreshDriver::PVsyncActorCreated(child);
     988           1 :   }
     989             : 
     990             : private:
     991           0 :   virtual ~VsyncChildCreateCallback() = default;
     992             : 
     993           0 :   void ActorCreated(PBackgroundChild* aPBackgroundChild) override
     994             :   {
     995           0 :     MOZ_ASSERT(NS_IsMainThread());
     996           0 :     MOZ_ASSERT(aPBackgroundChild);
     997           0 :     CreateVsyncActor(aPBackgroundChild);
     998           0 :   }
     999             : 
    1000           0 :   void ActorFailed() override
    1001             :   {
    1002           0 :     MOZ_ASSERT(NS_IsMainThread());
    1003           0 :     MOZ_CRASH("Failed To Create VsyncChild Actor");
    1004             :   }
    1005             : }; // VsyncChildCreateCallback
    1006           0 : NS_IMPL_ISUPPORTS(VsyncChildCreateCallback, nsIIPCBackgroundChildCreateCallback)
    1007             : 
    1008             : } // namespace mozilla
    1009             : 
    1010             : static RefreshDriverTimer* sRegularRateTimer;
    1011             : static InactiveRefreshDriverTimer* sThrottledRateTimer;
    1012             : 
    1013             : static void
    1014           1 : CreateContentVsyncRefreshTimer(void*)
    1015             : {
    1016           1 :   MOZ_ASSERT(NS_IsMainThread());
    1017           1 :   MOZ_ASSERT(!XRE_IsParentProcess());
    1018             : 
    1019             :   // Create the PVsync actor child for vsync-base refresh timer.
    1020             :   // PBackgroundChild is created asynchronously. If PBackgroundChild is still
    1021             :   // unavailable, setup VsyncChildCreateCallback callback to handle the async
    1022             :   // connect. We will still use software timer before PVsync ready, and change
    1023             :   // to use hw timer when the connection is done. Please check
    1024             :   // VsyncChildCreateCallback::CreateVsyncActor() and
    1025             :   // nsRefreshDriver::PVsyncActorCreated().
    1026           1 :   PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
    1027           1 :   if (backgroundChild) {
    1028             :     // If we already have PBackgroundChild, create the
    1029             :     // child VsyncRefreshDriverTimer here.
    1030           1 :     VsyncChildCreateCallback::CreateVsyncActor(backgroundChild);
    1031           1 :     return;
    1032             :   }
    1033             :   // Setup VsyncChildCreateCallback callback
    1034           0 :   RefPtr<nsIIPCBackgroundChildCreateCallback> callback = new VsyncChildCreateCallback();
    1035           0 :   if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
    1036           0 :     MOZ_CRASH("PVsync actor create failed!");
    1037             :   }
    1038             : }
    1039             : 
    1040             : static void
    1041           2 : CreateVsyncRefreshTimer()
    1042             : {
    1043           2 :   MOZ_ASSERT(NS_IsMainThread());
    1044             : 
    1045           2 :   PodArrayZero(sJankLevels);
    1046             :   // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
    1047             :   // ready.
    1048           2 :   gfxPrefs::GetSingleton();
    1049             : 
    1050           2 :   if (gfxPlatform::IsInLayoutAsapMode()) {
    1051           0 :     return;
    1052             :   }
    1053             : 
    1054           2 :   if (XRE_IsParentProcess()) {
    1055             :     // Make sure all vsync systems are ready.
    1056           1 :     gfxPlatform::GetPlatform();
    1057             :     // In parent process, we don't need to use ipc. We can create the
    1058             :     // VsyncRefreshDriverTimer directly.
    1059           1 :     sRegularRateTimer = new VsyncRefreshDriverTimer();
    1060           1 :     return;
    1061             :   }
    1062             : 
    1063             :   // If this process is not created by NUWA, just create the vsync timer here.
    1064           1 :   CreateContentVsyncRefreshTimer(nullptr);
    1065             : }
    1066             : 
    1067             : static uint32_t
    1068           4 : GetFirstFrameDelay(imgIRequest* req)
    1069             : {
    1070           8 :   nsCOMPtr<imgIContainer> container;
    1071           4 :   if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
    1072           0 :     return 0;
    1073             :   }
    1074             : 
    1075             :   // If this image isn't animated, there isn't a first frame delay.
    1076           4 :   int32_t delay = container->GetFirstFrameDelay();
    1077           4 :   if (delay < 0)
    1078           0 :     return 0;
    1079             : 
    1080           4 :   return static_cast<uint32_t>(delay);
    1081             : }
    1082             : 
    1083             : /* static */ void
    1084           0 : nsRefreshDriver::Shutdown()
    1085             : {
    1086             :   // clean up our timers
    1087           0 :   delete sRegularRateTimer;
    1088           0 :   delete sThrottledRateTimer;
    1089             : 
    1090           0 :   sRegularRateTimer = nullptr;
    1091           0 :   sThrottledRateTimer = nullptr;
    1092           0 : }
    1093             : 
    1094             : /* static */ int32_t
    1095           1 : nsRefreshDriver::DefaultInterval()
    1096             : {
    1097           1 :   return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
    1098             : }
    1099             : 
    1100             : // Compute the interval to use for the refresh driver timer, in milliseconds.
    1101             : // outIsDefault indicates that rate was not explicitly set by the user
    1102             : // so we might choose other, more appropriate rates (e.g. vsync, etc)
    1103             : // layout.frame_rate=0 indicates "ASAP mode".
    1104             : // In ASAP mode rendering is iterated as fast as possible (typically for stress testing).
    1105             : // A target rate of 10k is used internally instead of special-handling 0.
    1106             : // Backends which block on swap/present/etc should try to not block
    1107             : // when layout.frame_rate=0 - to comply with "ASAP" as much as possible.
    1108             : double
    1109           2 : nsRefreshDriver::GetRegularTimerInterval(bool *outIsDefault) const
    1110             : {
    1111           2 :   int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
    1112           2 :   if (rate < 0) {
    1113           2 :     rate = gfxPlatform::GetDefaultFrameRate();
    1114           2 :     if (outIsDefault) {
    1115           2 :       *outIsDefault = true;
    1116             :     }
    1117             :   } else {
    1118           0 :     if (outIsDefault) {
    1119           0 :       *outIsDefault = false;
    1120             :     }
    1121             :   }
    1122             : 
    1123           2 :   if (rate == 0) {
    1124           0 :     rate = 10000;
    1125             :   }
    1126             : 
    1127           2 :   return 1000.0 / rate;
    1128             : }
    1129             : 
    1130             : /* static */ double
    1131          28 : nsRefreshDriver::GetThrottledTimerInterval()
    1132             : {
    1133          28 :   int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
    1134          28 :   if (rate <= 0) {
    1135          28 :     rate = DEFAULT_THROTTLED_FRAME_RATE;
    1136             :   }
    1137          28 :   return 1000.0 / rate;
    1138             : }
    1139             : 
    1140             : /* static */ mozilla::TimeDuration
    1141          28 : nsRefreshDriver::GetMinRecomputeVisibilityInterval()
    1142             : {
    1143             :   int32_t interval =
    1144          28 :     Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
    1145          28 :   if (interval <= 0) {
    1146          28 :     interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
    1147             :   }
    1148          28 :   return TimeDuration::FromMilliseconds(interval);
    1149             : }
    1150             : 
    1151             : double
    1152           0 : nsRefreshDriver::GetRefreshTimerInterval() const
    1153             : {
    1154           0 :   return mThrottled ? GetThrottledTimerInterval() : GetRegularTimerInterval();
    1155             : }
    1156             : 
    1157             : RefreshDriverTimer*
    1158           8 : nsRefreshDriver::ChooseTimer() const
    1159             : {
    1160           8 :   if (mThrottled) {
    1161           0 :     if (!sThrottledRateTimer)
    1162           0 :       sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
    1163           0 :                                                            DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
    1164           0 :     return sThrottledRateTimer;
    1165             :   }
    1166             : 
    1167           8 :   if (!sRegularRateTimer) {
    1168           2 :     bool isDefault = true;
    1169           2 :     double rate = GetRegularTimerInterval(&isDefault);
    1170             : 
    1171             :     // Try to use vsync-base refresh timer first for sRegularRateTimer.
    1172           2 :     CreateVsyncRefreshTimer();
    1173             : 
    1174           2 :     if (!sRegularRateTimer) {
    1175           0 :       sRegularRateTimer = new StartupRefreshDriverTimer(rate);
    1176             :     }
    1177             :   }
    1178           8 :   return sRegularRateTimer;
    1179             : }
    1180             : 
    1181          28 : nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
    1182             :   : mActiveTimer(nullptr),
    1183             :     mPresContext(aPresContext),
    1184             :     mRootRefresh(nullptr),
    1185             :     mPendingTransaction(0),
    1186             :     mCompletedTransaction(0),
    1187             :     mFreezeCount(0),
    1188             :     mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
    1189          28 :                                      GetThrottledTimerInterval())),
    1190             :     mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
    1191             :     mThrottled(false),
    1192             :     mNeedToRecomputeVisibility(false),
    1193             :     mTestControllingRefreshes(false),
    1194             :     mViewManagerFlushIsPending(false),
    1195             :     mInRefresh(false),
    1196             :     mWaitingForTransaction(false),
    1197             :     mSkippedPaints(false),
    1198             :     mResizeSuppressed(false),
    1199          56 :     mWarningThreshold(REFRESH_WAIT_WARNING)
    1200             : {
    1201          28 :   MOZ_ASSERT(NS_IsMainThread());
    1202          28 :   MOZ_ASSERT(mPresContext,
    1203             :              "Need a pres context to tell us to call Disconnect() later "
    1204             :              "and decrement sRefreshDriverCount.");
    1205          28 :   mMostRecentRefreshEpochTime = JS_Now();
    1206          28 :   mMostRecentRefresh = TimeStamp::Now();
    1207          28 :   mMostRecentTick = mMostRecentRefresh;
    1208          28 :   mNextThrottledFrameRequestTick = mMostRecentTick;
    1209          28 :   mNextRecomputeVisibilityTick = mMostRecentTick;
    1210             : 
    1211          28 :   ++sRefreshDriverCount;
    1212          28 : }
    1213             : 
    1214           0 : nsRefreshDriver::~nsRefreshDriver()
    1215             : {
    1216           0 :   MOZ_ASSERT(NS_IsMainThread());
    1217           0 :   MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
    1218             :              "observers, except pending selection scrolls, "
    1219             :              "should have been unregistered");
    1220           0 :   MOZ_ASSERT(!mActiveTimer, "timer should be gone");
    1221           0 :   MOZ_ASSERT(!mPresContext,
    1222             :              "Should have called Disconnect() and decremented "
    1223             :              "sRefreshDriverCount!");
    1224             : 
    1225           0 :   if (mRootRefresh) {
    1226           0 :     mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
    1227           0 :     mRootRefresh = nullptr;
    1228             :   }
    1229           0 : }
    1230             : 
    1231             : // Method for testing.  See nsIDOMWindowUtils.advanceTimeAndRefresh
    1232             : // for description.
    1233             : void
    1234           0 : nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
    1235             : {
    1236             :   // ensure that we're removed from our driver
    1237           0 :   StopTimer();
    1238             : 
    1239           0 :   if (!mTestControllingRefreshes) {
    1240           0 :     mMostRecentRefreshEpochTime = JS_Now();
    1241           0 :     mMostRecentRefresh = TimeStamp::Now();
    1242             : 
    1243           0 :     mTestControllingRefreshes = true;
    1244           0 :     if (mWaitingForTransaction) {
    1245             :       // Disable any refresh driver throttling when entering test mode
    1246           0 :       mWaitingForTransaction = false;
    1247           0 :       mSkippedPaints = false;
    1248           0 :       mWarningThreshold = REFRESH_WAIT_WARNING;
    1249             :     }
    1250             :   }
    1251             : 
    1252           0 :   mMostRecentRefreshEpochTime += aMilliseconds * 1000;
    1253           0 :   mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
    1254             : 
    1255           0 :   mozilla::dom::AutoNoJSAPI nojsapi;
    1256           0 :   DoTick();
    1257           0 : }
    1258             : 
    1259             : void
    1260           0 : nsRefreshDriver::RestoreNormalRefresh()
    1261             : {
    1262           0 :   mTestControllingRefreshes = false;
    1263           0 :   EnsureTimerStarted(eAllowTimeToGoBackwards);
    1264           0 :   mCompletedTransaction = mPendingTransaction;
    1265           0 : }
    1266             : 
    1267             : TimeStamp
    1268         117 : nsRefreshDriver::MostRecentRefresh() const
    1269             : {
    1270             :   // In case of stylo traversal, we have already activated the refresh driver in
    1271             :   // ServoRestyleManager::ProcessPendingRestyles().
    1272         117 :   if (!ServoStyleSet::IsInServoTraversal()) {
    1273         117 :     const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
    1274             :   }
    1275             : 
    1276         117 :   return mMostRecentRefresh;
    1277             : }
    1278             : 
    1279             : int64_t
    1280           0 : nsRefreshDriver::MostRecentRefreshEpochTime() const
    1281             : {
    1282           0 :   const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
    1283             : 
    1284           0 :   return mMostRecentRefreshEpochTime;
    1285             : }
    1286             : 
    1287             : bool
    1288           2 : nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
    1289             :                                     FlushType aFlushType)
    1290             : {
    1291           2 :   ObserverArray& array = ArrayFor(aFlushType);
    1292           2 :   bool success = array.AppendElement(aObserver) != nullptr;
    1293           2 :   EnsureTimerStarted();
    1294           2 :   return success;
    1295             : }
    1296             : 
    1297             : bool
    1298           2 : nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
    1299             :                                        FlushType aFlushType)
    1300             : {
    1301           2 :   ObserverArray& array = ArrayFor(aFlushType);
    1302           2 :   return array.RemoveElement(aObserver);
    1303             : }
    1304             : 
    1305             : void
    1306           0 : nsRefreshDriver::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
    1307             : {
    1308           0 :   mPostRefreshObservers.AppendElement(aObserver);
    1309           0 : }
    1310             : 
    1311             : void
    1312           0 : nsRefreshDriver::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
    1313             : {
    1314           0 :   mPostRefreshObservers.RemoveElement(aObserver);
    1315           0 : }
    1316             : 
    1317             : bool
    1318           2 : nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
    1319             : {
    1320           2 :   uint32_t delay = GetFirstFrameDelay(aRequest);
    1321           2 :   if (delay == 0) {
    1322           0 :     mRequests.PutEntry(aRequest);
    1323             :   } else {
    1324           4 :     ImageStartData* start = mStartTable.LookupForAdd(delay).OrInsert(
    1325           6 :       [] () { return new ImageStartData(); });
    1326           2 :     start->mEntries.PutEntry(aRequest);
    1327             :   }
    1328             : 
    1329           2 :   EnsureTimerStarted();
    1330             : 
    1331           2 :   return true;
    1332             : }
    1333             : 
    1334             : void
    1335           2 : nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
    1336             : {
    1337             :   // Try to remove from both places, just in case, because we can't tell
    1338             :   // whether RemoveEntry() succeeds.
    1339           2 :   mRequests.RemoveEntry(aRequest);
    1340           2 :   uint32_t delay = GetFirstFrameDelay(aRequest);
    1341           2 :   if (delay != 0) {
    1342           2 :     ImageStartData* start = mStartTable.Get(delay);
    1343           2 :     if (start) {
    1344           2 :       start->mEntries.RemoveEntry(aRequest);
    1345             :     }
    1346             :   }
    1347           2 : }
    1348             : 
    1349             : void
    1350         444 : nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
    1351             : {
    1352             :   // FIXME: Bug 1346065: We should also assert the case where we have
    1353             :   // STYLO_THREADS=1.
    1354         444 :   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
    1355             :              "EnsureTimerStarted should be called only when we are not "
    1356             :              "in servo traversal or on the main-thread");
    1357             : 
    1358         444 :   if (mTestControllingRefreshes)
    1359           0 :     return;
    1360             : 
    1361             :   // will it already fire, and no other changes needed?
    1362         444 :   if (mActiveTimer && !(aFlags & eForceAdjustTimer))
    1363         394 :     return;
    1364             : 
    1365          50 :   if (IsFrozen() || !mPresContext) {
    1366             :     // If we don't want to start it now, or we've been disconnected.
    1367           0 :     StopTimer();
    1368           0 :     return;
    1369             :   }
    1370             : 
    1371          50 :   if (mPresContext->Document()->IsBeingUsedAsImage()) {
    1372             :     // Image documents receive ticks from clients' refresh drivers.
    1373             :     // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
    1374             :     // they receive refresh-driver ticks from their client docs (bug 1107252).
    1375          42 :     nsIURI* uri = mPresContext->Document()->GetDocumentURI();
    1376          42 :     if (!uri || !IsFontTableURI(uri)) {
    1377          42 :       MOZ_ASSERT(!mActiveTimer,
    1378             :                  "image doc refresh driver should never have its own timer");
    1379          42 :       return;
    1380             :     }
    1381             :   }
    1382             : 
    1383             :   // We got here because we're either adjusting the time *or* we're
    1384             :   // starting it for the first time.  Add to the right timer,
    1385             :   // prehaps removing it from a previously-set one.
    1386           8 :   RefreshDriverTimer *newTimer = ChooseTimer();
    1387           8 :   if (newTimer != mActiveTimer) {
    1388           8 :     if (mActiveTimer)
    1389           0 :       mActiveTimer->RemoveRefreshDriver(this);
    1390           8 :     mActiveTimer = newTimer;
    1391           8 :     mActiveTimer->AddRefreshDriver(this);
    1392             :   }
    1393             : 
    1394             :   // When switching from an inactive timer to an active timer, the root
    1395             :   // refresh driver is skipped due to being set to the content refresh
    1396             :   // driver's timestamp. In case of EnsureTimerStarted is called from
    1397             :   // ScheduleViewManagerFlush, we should avoid this behavior to flush
    1398             :   // a paint in the same tick on the root refresh driver.
    1399           8 :   if (aFlags & eNeverAdjustTimer) {
    1400           4 :     return;
    1401             :   }
    1402             : 
    1403             :   // Since the different timers are sampled at different rates, when switching
    1404             :   // timers, the most recent refresh of the new timer may be *before* the
    1405             :   // most recent refresh of the old timer. However, the refresh driver time
    1406             :   // should not go backwards so we clamp the most recent refresh time.
    1407             :   //
    1408             :   // The one exception to this is when we are restoring the refresh driver
    1409             :   // from test control in which case the time is expected to go backwards
    1410             :   // (see bug 1043078).
    1411             :   mMostRecentRefresh =
    1412           4 :     aFlags & eAllowTimeToGoBackwards
    1413           0 :     ? mActiveTimer->MostRecentRefresh()
    1414           4 :     : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
    1415           4 :   mMostRecentRefreshEpochTime =
    1416           4 :     aFlags & eAllowTimeToGoBackwards
    1417          12 :     ? mActiveTimer->MostRecentRefreshEpochTime()
    1418          12 :     : std::max(mActiveTimer->MostRecentRefreshEpochTime(),
    1419          12 :                mMostRecentRefreshEpochTime);
    1420             : }
    1421             : 
    1422             : void
    1423          12 : nsRefreshDriver::StopTimer()
    1424             : {
    1425          12 :   if (!mActiveTimer)
    1426           4 :     return;
    1427             : 
    1428           8 :   mActiveTimer->RemoveRefreshDriver(this);
    1429           8 :   mActiveTimer = nullptr;
    1430             : }
    1431             : 
    1432             : uint32_t
    1433          70 : nsRefreshDriver::ObserverCount() const
    1434             : {
    1435          70 :   uint32_t sum = 0;
    1436         280 :   for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
    1437         210 :     sum += mObservers[i].Length();
    1438             :   }
    1439             : 
    1440             :   // Even while throttled, we need to process layout and style changes.  Style
    1441             :   // changes can trigger transitions which fire events when they complete, and
    1442             :   // layout changes can affect media queries on child documents, triggering
    1443             :   // style changes, etc.
    1444          70 :   sum += mStyleFlushObservers.Length();
    1445          70 :   sum += mLayoutFlushObservers.Length();
    1446          70 :   sum += mPendingEvents.Length();
    1447          70 :   sum += mFrameRequestCallbackDocs.Length();
    1448          70 :   sum += mThrottledFrameRequestCallbackDocs.Length();
    1449          70 :   sum += mViewManagerFlushIsPending;
    1450          70 :   sum += mEarlyRunners.Length();
    1451          70 :   return sum;
    1452             : }
    1453             : 
    1454             : uint32_t
    1455          19 : nsRefreshDriver::ImageRequestCount() const
    1456             : {
    1457          19 :   uint32_t count = 0;
    1458          40 :   for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
    1459          21 :     count += iter.UserData()->mEntries.Count();
    1460             :   }
    1461          19 :   return count + mRequests.Count();
    1462             : }
    1463             : 
    1464             : nsRefreshDriver::ObserverArray&
    1465           4 : nsRefreshDriver::ArrayFor(FlushType aFlushType)
    1466             : {
    1467           4 :   switch (aFlushType) {
    1468             :     case FlushType::Style:
    1469           2 :       return mObservers[0];
    1470             :     case FlushType::Layout:
    1471           0 :       return mObservers[1];
    1472             :     case FlushType::Display:
    1473           2 :       return mObservers[2];
    1474             :     default:
    1475           0 :       MOZ_CRASH("We don't track refresh observers for this flush type");
    1476             :   }
    1477             : }
    1478             : 
    1479             : /*
    1480             :  * nsITimerCallback implementation
    1481             :  */
    1482             : 
    1483             : void
    1484          16 : nsRefreshDriver::DoTick()
    1485             : {
    1486          16 :   NS_PRECONDITION(!IsFrozen(), "Why are we notified while frozen?");
    1487          16 :   NS_PRECONDITION(mPresContext, "Why are we notified after disconnection?");
    1488          16 :   NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
    1489             :                   "Shouldn't have a JSContext on the stack");
    1490             : 
    1491          16 :   if (mTestControllingRefreshes) {
    1492           0 :     Tick(mMostRecentRefreshEpochTime, mMostRecentRefresh);
    1493             :   } else {
    1494          16 :     Tick(JS_Now(), TimeStamp::Now());
    1495             :   }
    1496          16 : }
    1497             : 
    1498           4 : struct DocumentFrameCallbacks {
    1499           4 :   explicit DocumentFrameCallbacks(nsIDocument* aDocument) :
    1500           4 :     mDocument(aDocument)
    1501           4 :   {}
    1502             : 
    1503             :   nsCOMPtr<nsIDocument> mDocument;
    1504             :   nsIDocument::FrameRequestCallbackList mCallbacks;
    1505             : };
    1506             : 
    1507          32 : static nsDocShell* GetDocShell(nsPresContext* aPresContext)
    1508             : {
    1509          32 :   return static_cast<nsDocShell*>(aPresContext->GetDocShell());
    1510             : }
    1511             : 
    1512             : static bool
    1513          24 : HasPendingAnimations(nsIPresShell* aShell)
    1514             : {
    1515          24 :   nsIDocument* doc = aShell->GetDocument();
    1516          24 :   if (!doc) {
    1517           0 :     return false;
    1518             :   }
    1519             : 
    1520          24 :   PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
    1521          24 :   return tracker && tracker->HasPendingAnimations();
    1522             : }
    1523             : 
    1524             : /**
    1525             :  * Return a list of all the child docShells in a given root docShell that are
    1526             :  * visible and are recording markers for the profilingTimeline
    1527             :  */
    1528          32 : static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
    1529             :                                            nsTArray<nsDocShell*>& aShells)
    1530             : {
    1531          32 :   if (!aRootDocShell) {
    1532          32 :     return;
    1533             :   }
    1534             : 
    1535          32 :   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
    1536          32 :   if (!timelines || timelines->IsEmpty()) {
    1537          32 :     return;
    1538             :   }
    1539             : 
    1540           0 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
    1541           0 :   nsresult rv = aRootDocShell->GetDocShellEnumerator(
    1542             :     nsIDocShellTreeItem::typeAll,
    1543             :     nsIDocShell::ENUMERATE_BACKWARDS,
    1544           0 :     getter_AddRefs(enumerator));
    1545             : 
    1546           0 :   if (NS_FAILED(rv)) {
    1547           0 :     return;
    1548             :   }
    1549             : 
    1550           0 :   nsCOMPtr<nsIDocShell> curItem;
    1551           0 :   bool hasMore = false;
    1552           0 :   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
    1553           0 :     nsCOMPtr<nsISupports> curSupports;
    1554           0 :     enumerator->GetNext(getter_AddRefs(curSupports));
    1555           0 :     curItem = do_QueryInterface(curSupports);
    1556             : 
    1557           0 :     if (!curItem || !curItem->GetRecordProfileTimelineMarkers()) {
    1558           0 :       continue;
    1559             :     }
    1560             : 
    1561           0 :     nsDocShell* shell = static_cast<nsDocShell*>(curItem.get());
    1562           0 :     bool isVisible = false;
    1563           0 :     shell->GetVisibility(&isVisible);
    1564           0 :     if (!isVisible) {
    1565           0 :       continue;
    1566             :     }
    1567             : 
    1568           0 :     aShells.AppendElement(shell);
    1569             :   }
    1570             : }
    1571             : 
    1572             : static void
    1573           4 : TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
    1574             :                               nsTArray<DocumentFrameCallbacks>& aTarget)
    1575             : {
    1576           4 :   aTarget.AppendElement(aDocument);
    1577           4 :   aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
    1578           4 : }
    1579             : 
    1580             : void
    1581          42 : nsRefreshDriver::DispatchPendingEvents()
    1582             : {
    1583             :   // Swap out the current pending events
    1584          84 :   nsTArray<PendingEvent> pendingEvents(Move(mPendingEvents));
    1585          42 :   for (PendingEvent& event : pendingEvents) {
    1586             :     bool dummy;
    1587           0 :     event.mTarget->DispatchEvent(event.mEvent, &dummy);
    1588             :   }
    1589          42 : }
    1590             : 
    1591             : static bool
    1592         148 : CollectDocuments(nsIDocument* aDocument, void* aDocArray)
    1593             : {
    1594             :   static_cast<AutoTArray<nsCOMPtr<nsIDocument>, 32>*>(aDocArray)->
    1595         148 :     AppendElement(aDocument);
    1596         148 :   aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray);
    1597         148 :   return true;
    1598             : }
    1599             : 
    1600             : void
    1601          42 : nsRefreshDriver::DispatchAnimationEvents()
    1602             : {
    1603          42 :   if (!mPresContext) {
    1604           0 :     return;
    1605             :   }
    1606             : 
    1607          84 :   AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
    1608          42 :   CollectDocuments(mPresContext->Document(), &documents);
    1609             : 
    1610         116 :   for (uint32_t i = 0; i < documents.Length(); ++i) {
    1611          74 :     nsIDocument* doc = documents[i];
    1612          74 :     nsIPresShell* shell = doc->GetShell();
    1613          74 :     if (!shell) {
    1614          64 :       continue;
    1615             :     }
    1616             : 
    1617          84 :     RefPtr<nsPresContext> context = shell->GetPresContext();
    1618          42 :     if (!context || context->RefreshDriver() != this) {
    1619           0 :       continue;
    1620             :     }
    1621             : 
    1622          42 :     context->TransitionManager()->SortEvents();
    1623          42 :     context->AnimationManager()->SortEvents();
    1624             : 
    1625             :     // Dispatch transition events first since transitions conceptually sit
    1626             :     // below animations in terms of compositing order.
    1627          42 :     context->TransitionManager()->DispatchEvents();
    1628             :     // Check that the presshell has not been destroyed
    1629          42 :     if (context->GetPresShell()) {
    1630          42 :       context->AnimationManager()->DispatchEvents();
    1631             :     }
    1632             :   }
    1633             : }
    1634             : 
    1635             : void
    1636          42 : nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
    1637             : {
    1638             :   // Grab all of our frame request callbacks up front.
    1639             :   nsTArray<DocumentFrameCallbacks>
    1640          42 :     frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
    1641          84 :                           mThrottledFrameRequestCallbackDocs.Length());
    1642             : 
    1643             :   // First, grab throttled frame request callbacks.
    1644             :   {
    1645          84 :     nsTArray<nsIDocument*> docsToRemove;
    1646             : 
    1647             :     // We always tick throttled frame requests if the entire refresh driver is
    1648             :     // throttled, because in that situation throttled frame requests tick at the
    1649             :     // same frequency as non-throttled frame requests.
    1650          42 :     bool tickThrottledFrameRequests = mThrottled;
    1651             : 
    1652          84 :     if (!tickThrottledFrameRequests &&
    1653          42 :         aNowTime >= mNextThrottledFrameRequestTick) {
    1654           7 :       mNextThrottledFrameRequestTick = aNowTime + mThrottledFrameRequestInterval;
    1655           7 :       tickThrottledFrameRequests = true;
    1656             :     }
    1657             : 
    1658          43 :     for (nsIDocument* doc : mThrottledFrameRequestCallbackDocs) {
    1659           1 :       if (tickThrottledFrameRequests) {
    1660             :         // We're ticking throttled documents, so grab this document's requests.
    1661             :         // We don't bother appending to docsToRemove because we're going to
    1662             :         // clear mThrottledFrameRequestCallbackDocs anyway.
    1663           0 :         TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
    1664           1 :       } else if (!doc->ShouldThrottleFrameRequests()) {
    1665             :         // This document is no longer throttled, so grab its requests even
    1666             :         // though we're not ticking throttled frame requests right now. If
    1667             :         // this is the first unthrottled document with frame requests, we'll
    1668             :         // enter high precision mode the next time the callback is scheduled.
    1669           1 :         TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
    1670           1 :         docsToRemove.AppendElement(doc);
    1671             :       }
    1672             :     }
    1673             : 
    1674             :     // Remove all the documents we're ticking from
    1675             :     // mThrottledFrameRequestCallbackDocs so they can be readded as needed.
    1676          42 :     if (tickThrottledFrameRequests) {
    1677           7 :       mThrottledFrameRequestCallbackDocs.Clear();
    1678             :     } else {
    1679             :       // XXX(seth): We're using this approach to avoid concurrent modification
    1680             :       // of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
    1681             :       // zero elements or a very small number, so this should be OK in practice.
    1682          36 :       for (nsIDocument* doc : docsToRemove) {
    1683           1 :         mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
    1684             :       }
    1685             :     }
    1686             :   }
    1687             : 
    1688             :   // Now grab unthrottled frame request callbacks.
    1689          45 :   for (nsIDocument* doc : mFrameRequestCallbackDocs) {
    1690           3 :     TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
    1691             :   }
    1692             : 
    1693             :   // Reset mFrameRequestCallbackDocs so they can be readded as needed.
    1694          42 :   mFrameRequestCallbackDocs.Clear();
    1695             : 
    1696          42 :   if (!frameRequestCallbacks.IsEmpty()) {
    1697           8 :     AutoProfilerTracing tracing("Paint", "Scripts");
    1698           8 :     for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
    1699             :       // XXXbz Bug 863140: GetInnerWindow can return the outer
    1700             :       // window in some cases.
    1701             :       nsPIDOMWindowInner* innerWindow =
    1702           4 :         docCallbacks.mDocument->GetInnerWindow();
    1703           4 :       DOMHighResTimeStamp timeStamp = 0;
    1704           4 :       if (innerWindow && innerWindow->IsInnerWindow()) {
    1705           4 :         mozilla::dom::Performance* perf = innerWindow->GetPerformance();
    1706           4 :         if (perf) {
    1707           4 :           timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
    1708             :         }
    1709             :         // else window is partially torn down already
    1710             :       }
    1711           8 :       for (auto& callback : docCallbacks.mCallbacks) {
    1712           4 :         callback->Call(timeStamp);
    1713             :       }
    1714             :     }
    1715             :   }
    1716          42 : }
    1717             : 
    1718           8 : struct RunnableWithDelay
    1719             : {
    1720             :   nsCOMPtr<nsIRunnable> mRunnable;
    1721             :   uint32_t mDelay;
    1722             : };
    1723             : 
    1724             : static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
    1725             : 
    1726             : void
    1727           3 : nsRefreshDriver::DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
    1728             :                                                uint32_t aDelay)
    1729             : {
    1730           3 :   if (!sPendingIdleRunnables) {
    1731           3 :     sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
    1732             :   }
    1733             : 
    1734           6 :   RunnableWithDelay rwd = {aRunnable, aDelay};
    1735           3 :   sPendingIdleRunnables->AppendElement(rwd);
    1736           3 : }
    1737             : 
    1738             : void
    1739           6 : nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable)
    1740             : {
    1741           6 :   if (!sPendingIdleRunnables) {
    1742           6 :     return;
    1743             :   }
    1744             : 
    1745           0 :   for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
    1746           0 :     if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
    1747           0 :       sPendingIdleRunnables->RemoveElementAt(i);
    1748           0 :       break;
    1749             :     }
    1750             :   }
    1751             : 
    1752           0 :   if (sPendingIdleRunnables->IsEmpty()) {
    1753           0 :     delete sPendingIdleRunnables;
    1754           0 :     sPendingIdleRunnables = nullptr;
    1755             :   }
    1756             : }
    1757             : 
    1758             : void
    1759          89 : nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
    1760             : {
    1761          89 :   NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
    1762             :                   "Shouldn't have a JSContext on the stack");
    1763             : 
    1764          89 :   if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
    1765           0 :     NS_ERROR("Refresh driver should not run during plugin call!");
    1766             :     // Try to survive this by just ignoring the refresh tick.
    1767           0 :     return;
    1768             :   }
    1769             : 
    1770         131 :   AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", GRAPHICS);
    1771             : 
    1772             :   // We're either frozen or we were disconnected (likely in the middle
    1773             :   // of a tick iteration).  Just do nothing here, since our
    1774             :   // prescontext went away.
    1775          89 :   if (IsFrozen() || !mPresContext) {
    1776           0 :     return;
    1777             :   }
    1778             : 
    1779             :   // We can have a race condition where the vsync timestamp
    1780             :   // is before the most recent refresh due to a forced refresh.
    1781             :   // The underlying assumption is that the refresh driver tick can only
    1782             :   // go forward in time, not backwards. To prevent the refresh
    1783             :   // driver from going back in time, just skip this tick and
    1784             :   // wait until the next tick.
    1785          89 :   if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
    1786           0 :     return;
    1787             :   }
    1788             : 
    1789          89 :   TimeStamp previousRefresh = mMostRecentRefresh;
    1790             : 
    1791          89 :   mMostRecentRefresh = aNowTime;
    1792          89 :   mMostRecentRefreshEpochTime = aNowEpoch;
    1793             : 
    1794          89 :   if (IsWaitingForPaint(aNowTime)) {
    1795             :     // We're currently suspended waiting for earlier Tick's to
    1796             :     // be completed (on the Compositor). Mark that we missed the paint
    1797             :     // and keep waiting.
    1798          39 :     return;
    1799             :   }
    1800          50 :   mMostRecentTick = aNowTime;
    1801          50 :   if (mRootRefresh) {
    1802           0 :     mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
    1803           0 :     mRootRefresh = nullptr;
    1804             :   }
    1805          50 :   mSkippedPaints = false;
    1806          50 :   mWarningThreshold = 1;
    1807             : 
    1808          92 :   nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
    1809          50 :   if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
    1810             :     // Things are being destroyed, or we no longer have any observers.
    1811             :     // We don't want to stop the timer when observers are initially
    1812             :     // removed, because sometimes observers can be added and removed
    1813             :     // often depending on what other things are going on and in that
    1814             :     // situation we don't want to thrash our timer.  So instead we
    1815             :     // wait until we get a Notify() call when we have no observers
    1816             :     // before stopping the timer.
    1817           8 :     StopTimer();
    1818           8 :     return;
    1819             :   }
    1820             : 
    1821          42 :   mResizeSuppressed = false;
    1822             : 
    1823          84 :   AutoRestore<bool> restoreInRefresh(mInRefresh);
    1824          42 :   mInRefresh = true;
    1825             : 
    1826          84 :   AutoRestore<TimeStamp> restoreTickStart(mTickStart);
    1827          42 :   mTickStart = TimeStamp::Now();
    1828             : 
    1829          42 :   gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
    1830             : 
    1831             :   // We want to process any pending APZ metrics ahead of their positions
    1832             :   // in the queue. This will prevent us from spending precious time
    1833             :   // painting a stale displayport.
    1834          42 :   if (gfxPrefs::APZPeekMessages()) {
    1835          42 :     nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
    1836             :   }
    1837             : 
    1838          84 :   AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners;
    1839          42 :   earlyRunners.SwapElements(mEarlyRunners);
    1840          44 :   for (uint32_t i = 0; i < earlyRunners.Length(); ++i) {
    1841           2 :     earlyRunners[i]->Run();
    1842             :   }
    1843             : 
    1844             :   /*
    1845             :    * The timer holds a reference to |this| while calling |Notify|.
    1846             :    * However, implementations of |WillRefresh| are permitted to destroy
    1847             :    * the pres context, which will cause our |mPresContext| to become
    1848             :    * null.  If this happens, we must stop notifying observers.
    1849             :    */
    1850         168 :   for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
    1851         252 :     ObserverArray::EndLimitedIterator etor(mObservers[i]);
    1852         152 :     while (etor.HasMore()) {
    1853          26 :       RefPtr<nsARefreshObserver> obs = etor.GetNext();
    1854          13 :       obs->WillRefresh(aNowTime);
    1855             : 
    1856          13 :       if (!mPresContext || !mPresContext->GetPresShell()) {
    1857           0 :         StopTimer();
    1858           0 :         return;
    1859             :       }
    1860             :     }
    1861             : 
    1862         126 :     if (i == 0) {
    1863             :       // This is the FlushType::Style case.
    1864             : 
    1865          42 :       DispatchAnimationEvents();
    1866          42 :       DispatchPendingEvents();
    1867          42 :       RunFrameRequestCallbacks(aNowTime);
    1868             : 
    1869          42 :       if (mPresContext && mPresContext->GetPresShell()) {
    1870          84 :         Maybe<AutoProfilerTracing> tracingStyleFlush;
    1871          84 :         AutoTArray<nsIPresShell*, 16> observers;
    1872          42 :         observers.AppendElements(mStyleFlushObservers);
    1873         118 :         for (uint32_t j = observers.Length();
    1874          59 :              j && mPresContext && mPresContext->GetPresShell(); --j) {
    1875             :           // Make sure to not process observers which might have been removed
    1876             :           // during previous iterations.
    1877          17 :           nsIPresShell* shell = observers[j - 1];
    1878          17 :           if (!mStyleFlushObservers.RemoveElement(shell))
    1879           0 :             continue;
    1880             : 
    1881          17 :           if (!tracingStyleFlush) {
    1882          17 :             tracingStyleFlush.emplace("Paint", "Styles", Move(mStyleCause));
    1883          17 :             mStyleCause = nullptr;
    1884             :           }
    1885             : 
    1886          34 :           nsCOMPtr<nsIPresShell> shellKungFuDeathGrip(shell);
    1887          17 :           shell->mObservingStyleFlushes = false;
    1888          17 :           shell->FlushPendingNotifications(ChangesToFlush(FlushType::Style, false));
    1889             :           // Inform the FontFaceSet that we ticked, so that it can resolve its
    1890             :           // ready promise if it needs to (though it might still be waiting on
    1891             :           // a layout flush).
    1892          17 :           nsPresContext* presContext = shell->GetPresContext();
    1893          17 :           if (presContext) {
    1894          17 :             presContext->NotifyFontFaceSetOnRefresh();
    1895             :           }
    1896          17 :           mNeedToRecomputeVisibility = true;
    1897             :         }
    1898             :       }
    1899          84 :     } else if  (i == 1) {
    1900             :       // This is the FlushType::Layout case.
    1901          84 :       Maybe<AutoProfilerTracing> tracingLayoutFlush;
    1902          84 :       AutoTArray<nsIPresShell*, 16> observers;
    1903          42 :       observers.AppendElements(mLayoutFlushObservers);
    1904         132 :       for (uint32_t j = observers.Length();
    1905          66 :            j && mPresContext && mPresContext->GetPresShell(); --j) {
    1906             :         // Make sure to not process observers which might have been removed
    1907             :         // during previous iterations.
    1908          24 :         nsIPresShell* shell = observers[j - 1];
    1909          24 :         if (!mLayoutFlushObservers.RemoveElement(shell))
    1910           0 :           continue;
    1911             : 
    1912          24 :         if (!tracingLayoutFlush) {
    1913          24 :           tracingLayoutFlush.emplace("Paint", "Reflow", Move(mReflowCause));
    1914          24 :           mReflowCause = nullptr;
    1915             :         }
    1916             : 
    1917          48 :         nsCOMPtr<nsIPresShell> shellKungFuDeathGrip(shell);
    1918          24 :         shell->mObservingLayoutFlushes = false;
    1919          24 :         shell->mSuppressInterruptibleReflows = false;
    1920          24 :         FlushType flushType = HasPendingAnimations(shell)
    1921          24 :                                ? FlushType::Layout
    1922          24 :                                : FlushType::InterruptibleLayout;
    1923          24 :         shell->FlushPendingNotifications(ChangesToFlush(flushType, false));
    1924             :         // Inform the FontFaceSet that we ticked, so that it can resolve its
    1925             :         // ready promise if it needs to.
    1926          24 :         nsPresContext* presContext = shell->GetPresContext();
    1927          24 :         if (presContext) {
    1928          24 :           presContext->NotifyFontFaceSetOnRefresh();
    1929             :         }
    1930          24 :         mNeedToRecomputeVisibility = true;
    1931             :       }
    1932             :     }
    1933             : 
    1934             :     // The pres context may be destroyed during we do the flushing.
    1935         126 :     if (!mPresContext || !mPresContext->GetPresShell()) {
    1936           0 :       StopTimer();
    1937           0 :       return;
    1938             :     }
    1939             :   }
    1940             : 
    1941             :   // Recompute approximate frame visibility if it's necessary and enough time
    1942             :   // has passed since the last time we did it.
    1943         168 :   if (mNeedToRecomputeVisibility && !mThrottled &&
    1944          99 :       aNowTime >= mNextRecomputeVisibilityTick &&
    1945          15 :       !presShell->IsPaintingSuppressed()) {
    1946           7 :     mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
    1947           7 :     mNeedToRecomputeVisibility = false;
    1948             : 
    1949           7 :     presShell->ScheduleApproximateFrameVisibilityUpdateNow();
    1950             :   }
    1951             : 
    1952             : #ifdef MOZ_XUL
    1953             :   // Update any popups that may need to be moved or hidden due to their
    1954             :   // anchor changing.
    1955          42 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    1956          42 :   if (pm) {
    1957          42 :     pm->UpdatePopupPositions(this);
    1958             :   }
    1959             : #endif
    1960             : 
    1961          84 :   AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
    1962          42 :   CollectDocuments(mPresContext->Document(), &documents);
    1963         116 :   for (uint32_t i = 0; i < documents.Length(); ++i) {
    1964          74 :     nsIDocument* doc = documents[i];
    1965          74 :     doc->UpdateIntersectionObservations();
    1966          74 :     doc->ScheduleIntersectionObserverNotification();
    1967             :   }
    1968             : 
    1969             :   /*
    1970             :    * Perform notification to imgIRequests subscribed to listen
    1971             :    * for refresh events.
    1972             :    */
    1973             : 
    1974          71 :   for (auto iter = mStartTable.Iter(); !iter.Done(); iter.Next()) {
    1975          29 :     const uint32_t& delay = iter.Key();
    1976          29 :     ImageStartData* data = iter.UserData();
    1977             : 
    1978          29 :     if (data->mStartTime) {
    1979          27 :       TimeStamp& start = *data->mStartTime;
    1980          27 :       TimeDuration prev = previousRefresh - start;
    1981          27 :       TimeDuration curr = aNowTime - start;
    1982          27 :       uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
    1983             : 
    1984             :       // We want to trigger images' refresh if we've just crossed over a
    1985             :       // multiple of the first image's start time. If so, set the animation
    1986             :       // start time to the nearest multiple of the delay and move all the
    1987             :       // images in this table to the main requests table.
    1988          27 :       if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
    1989             :         mozilla::TimeStamp desired =
    1990           9 :           start + TimeDuration::FromMilliseconds(prevMultiple * delay);
    1991           9 :         BeginRefreshingImages(data->mEntries, desired);
    1992             :       }
    1993             :     } else {
    1994             :       // This is the very first time we've drawn images with this time delay.
    1995             :       // Set the animation start time to "now" and move all the images in this
    1996             :       // table to the main requests table.
    1997           2 :       mozilla::TimeStamp desired = aNowTime;
    1998           2 :       BeginRefreshingImages(data->mEntries, desired);
    1999           2 :       data->mStartTime.emplace(aNowTime);
    2000             :     }
    2001             :   }
    2002             : 
    2003          42 :   if (mRequests.Count()) {
    2004             :     // RequestRefresh may run scripts, so it's not safe to directly call it
    2005             :     // while using a hashtable enumerator to enumerate mRequests in case
    2006             :     // script modifies the hashtable. Instead, we build a (local) array of
    2007             :     // images to refresh, and then we refresh each image in that array.
    2008          30 :     nsCOMArray<imgIContainer> imagesToRefresh(mRequests.Count());
    2009             : 
    2010          30 :     for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
    2011          15 :       nsISupportsHashKey* entry = iter.Get();
    2012          15 :       auto req = static_cast<imgIRequest*>(entry->GetKey());
    2013          15 :       MOZ_ASSERT(req, "Unable to retrieve the image request");
    2014          30 :       nsCOMPtr<imgIContainer> image;
    2015          15 :       if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
    2016          15 :         imagesToRefresh.AppendElement(image.forget());
    2017             :       }
    2018             :     }
    2019             : 
    2020          30 :     for (uint32_t i = 0; i < imagesToRefresh.Length(); i++) {
    2021          15 :       imagesToRefresh[i]->RequestRefresh(aNowTime);
    2022             :     }
    2023             :   }
    2024             : 
    2025          42 :   bool dispatchRunnablesAfterTick = false;
    2026          42 :   if (mViewManagerFlushIsPending) {
    2027          64 :     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
    2028             : 
    2029          64 :     nsTArray<nsDocShell*> profilingDocShells;
    2030          32 :     GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
    2031          32 :     for (nsDocShell* docShell : profilingDocShells) {
    2032             :       // For the sake of the profile timeline's simplicity, this is flagged as
    2033             :       // paint even if it includes creating display lists
    2034           0 :       MOZ_ASSERT(timelines);
    2035           0 :       MOZ_ASSERT(timelines->HasConsumer(docShell));
    2036           0 :       timelines->AddMarkerForDocShell(docShell, "Paint",  MarkerTracingType::START);
    2037             :     }
    2038             : 
    2039             : #ifdef MOZ_DUMP_PAINTING
    2040          32 :     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
    2041           0 :       printf_stderr("Starting ProcessPendingUpdates\n");
    2042             :     }
    2043             : #endif
    2044             : 
    2045          32 :     mViewManagerFlushIsPending = false;
    2046          64 :     RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
    2047             :     {
    2048          64 :       PaintTelemetry::AutoRecordPaint record;
    2049          32 :       vm->ProcessPendingUpdates();
    2050             :     }
    2051             : 
    2052             : #ifdef MOZ_DUMP_PAINTING
    2053          32 :     if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
    2054           0 :       printf_stderr("Ending ProcessPendingUpdates\n");
    2055             :     }
    2056             : #endif
    2057             : 
    2058          32 :     for (nsDocShell* docShell : profilingDocShells) {
    2059           0 :       MOZ_ASSERT(timelines);
    2060           0 :       MOZ_ASSERT(timelines->HasConsumer(docShell));
    2061           0 :       timelines->AddMarkerForDocShell(docShell, "Paint",  MarkerTracingType::END);
    2062             :     }
    2063             : 
    2064          32 :     dispatchRunnablesAfterTick = true;
    2065             :   }
    2066             : 
    2067             : #ifndef ANDROID  /* bug 1142079 */
    2068          42 :   mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::REFRESH_DRIVER_TICK, mTickStart);
    2069             : #endif
    2070             : 
    2071          84 :   nsTObserverArray<nsAPostRefreshObserver*>::ForwardIterator iter(mPostRefreshObservers);
    2072          42 :   while (iter.HasMore()) {
    2073           0 :     nsAPostRefreshObserver* observer = iter.GetNext();
    2074           0 :     observer->DidRefresh();
    2075             :   }
    2076             : 
    2077          42 :   NS_ASSERTION(mInRefresh, "Still in refresh");
    2078             : 
    2079          42 :   if (mPresContext->IsRoot() && XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
    2080           0 :     ScheduleViewManagerFlush();
    2081             :   }
    2082             : 
    2083          42 :   if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
    2084           2 :     AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
    2085           2 :     sPendingIdleRunnables = nullptr;
    2086           4 :     for (uint32_t i = 0; i < runnables->Length(); ++i) {
    2087           4 :       NS_IdleDispatchToCurrentThread((*runnables)[i].mRunnable.forget(),
    2088           4 :                                      (*runnables)[i].mDelay);
    2089             :     }
    2090           2 :     delete runnables;
    2091             :   }
    2092             : }
    2093             : 
    2094             : void
    2095          11 : nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
    2096             :                                        mozilla::TimeStamp aDesired)
    2097             : {
    2098          13 :   for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
    2099           2 :     auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
    2100           2 :     MOZ_ASSERT(req, "Unable to retrieve the image request");
    2101             : 
    2102           2 :     mRequests.PutEntry(req);
    2103             : 
    2104           4 :     nsCOMPtr<imgIContainer> image;
    2105           2 :     if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
    2106           2 :       image->SetAnimationStartTime(aDesired);
    2107             :     }
    2108             :   }
    2109          11 :   aEntries.Clear();
    2110          11 : }
    2111             : 
    2112             : void
    2113           0 : nsRefreshDriver::Freeze()
    2114             : {
    2115           0 :   StopTimer();
    2116           0 :   mFreezeCount++;
    2117           0 : }
    2118             : 
    2119             : void
    2120           0 : nsRefreshDriver::Thaw()
    2121             : {
    2122           0 :   NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
    2123             : 
    2124           0 :   if (mFreezeCount > 0) {
    2125           0 :     mFreezeCount--;
    2126             :   }
    2127             : 
    2128           0 :   if (mFreezeCount == 0) {
    2129           0 :     if (ObserverCount() || ImageRequestCount()) {
    2130             :       // FIXME: This isn't quite right, since our EnsureTimerStarted call
    2131             :       // updates our mMostRecentRefresh, but the DoRefresh call won't run
    2132             :       // and notify our observers until we get back to the event loop.
    2133             :       // Thus MostRecentRefresh() will lie between now and the DoRefresh.
    2134           0 :       RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
    2135           0 :         "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
    2136           0 :       nsPresContext* pc = GetPresContext();
    2137           0 :       if (pc) {
    2138           0 :         pc->Document()->Dispatch("nsRefreshDriver::DoRefresh",
    2139             :                                  TaskCategory::Other,
    2140           0 :                                  event.forget());
    2141           0 :         EnsureTimerStarted();
    2142             :       } else {
    2143           0 :         NS_ERROR("Thawing while document is being destroyed");
    2144             :       }
    2145             :     }
    2146             :   }
    2147           0 : }
    2148             : 
    2149             : void
    2150          21 : nsRefreshDriver::FinishedWaitingForTransaction()
    2151             : {
    2152          21 :   mWaitingForTransaction = false;
    2153          62 :   if (mSkippedPaints &&
    2154          57 :       !IsInRefresh() &&
    2155          27 :       (ObserverCount() || ImageRequestCount())) {
    2156          32 :     AutoProfilerTracing tracing("Paint", "RefreshDriverTick");
    2157          16 :     DoRefresh();
    2158             :   }
    2159          21 :   mSkippedPaints = false;
    2160          21 :   mWarningThreshold = 1;
    2161          21 : }
    2162             : 
    2163             : uint64_t
    2164          29 : nsRefreshDriver::GetTransactionId(bool aThrottle)
    2165             : {
    2166          29 :   ++mPendingTransaction;
    2167             : 
    2168          58 :   if (aThrottle &&
    2169          51 :       mPendingTransaction >= mCompletedTransaction + 2 &&
    2170          43 :       !mWaitingForTransaction &&
    2171          21 :       !mTestControllingRefreshes) {
    2172          21 :     mWaitingForTransaction = true;
    2173          21 :     mSkippedPaints = false;
    2174          21 :     mWarningThreshold = 1;
    2175             :   }
    2176             : 
    2177          29 :   return mPendingTransaction;
    2178             : }
    2179             : 
    2180             : uint64_t
    2181           1 : nsRefreshDriver::LastTransactionId() const
    2182             : {
    2183           1 :   return mPendingTransaction;
    2184             : }
    2185             : 
    2186             : void
    2187           1 : nsRefreshDriver::RevokeTransactionId(uint64_t aTransactionId)
    2188             : {
    2189           1 :   MOZ_ASSERT(aTransactionId == mPendingTransaction);
    2190           2 :   if (mPendingTransaction == mCompletedTransaction + 2 &&
    2191           1 :       mWaitingForTransaction) {
    2192           1 :     MOZ_ASSERT(!mSkippedPaints, "How did we skip a paint when we're in the middle of one?");
    2193           1 :     FinishedWaitingForTransaction();
    2194             :   }
    2195           1 :   mPendingTransaction--;
    2196           1 : }
    2197             : 
    2198             : void
    2199           1 : nsRefreshDriver::ClearPendingTransactions()
    2200             : {
    2201           1 :   mCompletedTransaction = mPendingTransaction;
    2202           1 :   mWaitingForTransaction = false;
    2203           1 : }
    2204             : 
    2205             : void
    2206           1 : nsRefreshDriver::ResetInitialTransactionId(uint64_t aTransactionId)
    2207             : {
    2208           1 :   mCompletedTransaction = mPendingTransaction = aTransactionId;
    2209           1 : }
    2210             : 
    2211             : mozilla::TimeStamp
    2212          55 : nsRefreshDriver::GetTransactionStart()
    2213             : {
    2214          55 :   return mTickStart;
    2215             : }
    2216             : 
    2217             : void
    2218          27 : nsRefreshDriver::NotifyTransactionCompleted(uint64_t aTransactionId)
    2219             : {
    2220          27 :   if (aTransactionId > mCompletedTransaction) {
    2221          48 :     if (mPendingTransaction > mCompletedTransaction + 1 &&
    2222          21 :         mWaitingForTransaction) {
    2223          20 :       mCompletedTransaction = aTransactionId;
    2224          20 :       FinishedWaitingForTransaction();
    2225             :     } else {
    2226           7 :       mCompletedTransaction = aTransactionId;
    2227             :     }
    2228             :   }
    2229          27 : }
    2230             : 
    2231             : void
    2232           0 : nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime)
    2233             : {
    2234           0 :   mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
    2235           0 :   mRootRefresh = nullptr;
    2236           0 :   if (mSkippedPaints) {
    2237           0 :     DoRefresh();
    2238             :   }
    2239           0 : }
    2240             : 
    2241             : bool
    2242          89 : nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime)
    2243             : {
    2244          89 :   if (mTestControllingRefreshes) {
    2245           0 :     return false;
    2246             :   }
    2247             : 
    2248          89 :   if (mWaitingForTransaction) {
    2249          39 :     if (mSkippedPaints && aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
    2250             :       // XXX - Bug 1303369 - too many false positives.
    2251             :       //gfxCriticalNote << "Refresh driver waiting for the compositor for "
    2252             :       //                << (aTime - mMostRecentTick).ToSeconds()
    2253             :       //                << " seconds.";
    2254           0 :       mWarningThreshold *= 2;
    2255             :     }
    2256             : 
    2257          39 :     mSkippedPaints = true;
    2258          39 :     return true;
    2259             :   }
    2260             : 
    2261             :   // Try find the 'root' refresh driver for the current window and check
    2262             :   // if that is waiting for a paint.
    2263          50 :   nsPresContext* pc = GetPresContext();
    2264          50 :   nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
    2265          50 :   if (rootContext) {
    2266          50 :     nsRefreshDriver *rootRefresh = rootContext->RefreshDriver();
    2267          50 :     if (rootRefresh && rootRefresh != this) {
    2268           0 :       if (rootRefresh->IsWaitingForPaint(aTime)) {
    2269           0 :         if (mRootRefresh != rootRefresh) {
    2270           0 :           if (mRootRefresh) {
    2271           0 :             mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
    2272             :           }
    2273           0 :           rootRefresh->AddRefreshObserver(this, FlushType::Style);
    2274           0 :           mRootRefresh = rootRefresh;
    2275             :         }
    2276           0 :         mSkippedPaints = true;
    2277           0 :         return true;
    2278             :       }
    2279             :     }
    2280             :   }
    2281          50 :   return false;
    2282             : }
    2283             : 
    2284             : void
    2285           7 : nsRefreshDriver::SetThrottled(bool aThrottled)
    2286             : {
    2287           7 :   if (aThrottled != mThrottled) {
    2288           0 :     mThrottled = aThrottled;
    2289           0 :     if (mActiveTimer) {
    2290             :       // We want to switch our timer type here, so just stop and
    2291             :       // restart the timer.
    2292           0 :       EnsureTimerStarted(eForceAdjustTimer);
    2293             :     }
    2294             :   }
    2295           7 : }
    2296             : 
    2297             : /*static*/ void
    2298           1 : nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild)
    2299             : {
    2300           1 :   MOZ_ASSERT(NS_IsMainThread());
    2301           1 :   MOZ_ASSERT(!XRE_IsParentProcess());
    2302             :   auto* vsyncRefreshDriverTimer =
    2303           1 :       new VsyncRefreshDriverTimer(aVsyncChild);
    2304             : 
    2305             :   // If we are using software timer, swap current timer to
    2306             :   // VsyncRefreshDriverTimer.
    2307           1 :   if (sRegularRateTimer) {
    2308           0 :     sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
    2309           0 :     delete sRegularRateTimer;
    2310             :   }
    2311           1 :   sRegularRateTimer = vsyncRefreshDriverTimer;
    2312           1 : }
    2313             : 
    2314             : void
    2315          16 : nsRefreshDriver::DoRefresh()
    2316             : {
    2317             :   // Don't do a refresh unless we're in a state where we should be refreshing.
    2318          16 :   if (!IsFrozen() && mPresContext && mActiveTimer) {
    2319          16 :     DoTick();
    2320             :   }
    2321          16 : }
    2322             : 
    2323             : #ifdef DEBUG
    2324             : bool
    2325           0 : nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
    2326             :                                    FlushType aFlushType)
    2327             : {
    2328           0 :   ObserverArray& array = ArrayFor(aFlushType);
    2329           0 :   return array.Contains(aObserver);
    2330             : }
    2331             : #endif
    2332             : 
    2333             : void
    2334         234 : nsRefreshDriver::ScheduleViewManagerFlush()
    2335             : {
    2336         234 :   NS_ASSERTION(mPresContext->IsRoot(),
    2337             :                "Should only schedule view manager flush on root prescontexts");
    2338         234 :   mViewManagerFlushIsPending = true;
    2339         234 :   EnsureTimerStarted(eNeverAdjustTimer);
    2340         234 : }
    2341             : 
    2342             : void
    2343           4 : nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
    2344             : {
    2345           4 :   NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
    2346             :                mFrameRequestCallbackDocs.NoIndex &&
    2347             :                mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
    2348             :                mThrottledFrameRequestCallbackDocs.NoIndex,
    2349             :                "Don't schedule the same document multiple times");
    2350           4 :   if (aDocument->ShouldThrottleFrameRequests()) {
    2351           1 :     mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
    2352             :   } else {
    2353           3 :     mFrameRequestCallbackDocs.AppendElement(aDocument);
    2354             :   }
    2355             : 
    2356             :   // make sure that the timer is running
    2357           4 :   EnsureTimerStarted();
    2358           4 : }
    2359             : 
    2360             : void
    2361           0 : nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
    2362             : {
    2363           0 :   mFrameRequestCallbackDocs.RemoveElement(aDocument);
    2364           0 :   mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
    2365             :   // No need to worry about restarting our timer in slack mode if it's already
    2366             :   // running; that will happen automatically when it fires.
    2367           0 : }
    2368             : 
    2369             : void
    2370           0 : nsRefreshDriver::ScheduleEventDispatch(nsINode* aTarget, nsIDOMEvent* aEvent)
    2371             : {
    2372           0 :   mPendingEvents.AppendElement(PendingEvent{aTarget, aEvent});
    2373             :   // make sure that the timer is running
    2374           0 :   EnsureTimerStarted();
    2375           0 : }
    2376             : 
    2377             : void
    2378           4 : nsRefreshDriver::CancelPendingEvents(nsIDocument* aDocument)
    2379             : {
    2380           4 :   for (auto i : Reversed(IntegerRange(mPendingEvents.Length()))) {
    2381           0 :     if (mPendingEvents[i].mTarget->OwnerDoc() == aDocument) {
    2382           0 :       mPendingEvents.RemoveElementAt(i);
    2383             :     }
    2384             :   }
    2385           4 : }
    2386             : 
    2387             : /* static */ TimeStamp
    2388         165 : nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault)
    2389             : {
    2390         165 :   MOZ_ASSERT(NS_IsMainThread());
    2391         165 :   MOZ_ASSERT(!aDefault.IsNull());
    2392             : 
    2393         165 :   if (!sRegularRateTimer) {
    2394         124 :     return aDefault;
    2395             :   }
    2396             : 
    2397             :   // For computing idleness of refresh drivers we only care about
    2398             :   // sRegularRateTimer, since we consider refresh drivers attached to
    2399             :   // sThrottledRateTimer to be inactive. This implies that tasks
    2400             :   // resulting from a tick on the sRegularRateTimer counts as being
    2401             :   // busy but tasks resulting from a tick on sThrottledRateTimer
    2402             :   // counts as being idle.
    2403          41 :   return sRegularRateTimer->GetIdleDeadlineHint(aDefault);
    2404             : }
    2405             : 
    2406             : void
    2407           4 : nsRefreshDriver::Disconnect()
    2408             : {
    2409           4 :   MOZ_ASSERT(NS_IsMainThread());
    2410             : 
    2411           4 :   StopTimer();
    2412             : 
    2413           4 :   if (mPresContext) {
    2414           4 :     mPresContext = nullptr;
    2415           4 :     if (--sRefreshDriverCount == 0) {
    2416           0 :       Shutdown();
    2417             :     }
    2418             :   }
    2419           4 : }
    2420             : 
    2421             : /* static */ bool
    2422           0 : nsRefreshDriver::IsJankCritical()
    2423             : {
    2424           0 :   MOZ_ASSERT(NS_IsMainThread());
    2425           0 :   return sActiveVsyncTimers > 0;
    2426             : }
    2427             : 
    2428             : /* static */ bool
    2429           0 : nsRefreshDriver::GetJankLevels(Vector<uint64_t>& aJank) {
    2430           0 :   aJank.clear();
    2431           0 :   return aJank.append(sJankLevels, ArrayLength(sJankLevels));
    2432             : }
    2433             : 
    2434             : #undef LOG

Generated by: LCOV version 1.13