tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsRefreshDriver.cpp (107891B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 #include "nsRefreshDriver.h"
     21 
     22 #include "mozilla/DataMutex.h"
     23 #include "mozilla/dom/VideoFrameProvider.h"
     24 #include "nsThreadUtils.h"
     25 
     26 #ifdef XP_WIN
     27 #  include <windows.h>
     28 // mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
     29 // to manually include it
     30 #  include <mmsystem.h>
     31 
     32 #  include "WinUtils.h"
     33 #endif
     34 
     35 #include "GeckoProfiler.h"
     36 #include "VsyncSource.h"
     37 #include "imgIContainer.h"
     38 #include "imgRequest.h"
     39 #include "jsapi.h"
     40 #include "mozilla/AnimationEventDispatcher.h"
     41 #include "mozilla/Assertions.h"
     42 #include "mozilla/AutoRestore.h"
     43 #include "mozilla/BasePrincipal.h"
     44 #include "mozilla/CycleCollectedJSContext.h"
     45 #include "mozilla/DisplayPortUtils.h"
     46 #include "mozilla/Hal.h"
     47 #include "mozilla/InputTaskManager.h"
     48 #include "mozilla/Logging.h"
     49 #include "mozilla/PendingFullscreenEvent.h"
     50 #include "mozilla/Preferences.h"
     51 #include "mozilla/PresShell.h"
     52 #include "mozilla/RestyleManager.h"
     53 #include "mozilla/SMILAnimationController.h"
     54 #include "mozilla/ScopeExit.h"
     55 #include "mozilla/StaticPrefs_apz.h"
     56 #include "mozilla/StaticPrefs_gfx.h"
     57 #include "mozilla/StaticPrefs_idle_period.h"
     58 #include "mozilla/StaticPrefs_layout.h"
     59 #include "mozilla/StaticPrefs_page_load.h"
     60 #include "mozilla/TaskController.h"
     61 #include "mozilla/VsyncDispatcher.h"
     62 #include "mozilla/VsyncTaskManager.h"
     63 #include "mozilla/dom/AnimationTimelinesController.h"
     64 #include "mozilla/dom/BrowserChild.h"
     65 #include "mozilla/dom/CallbackDebuggerNotification.h"
     66 #include "mozilla/dom/ContentChild.h"
     67 #include "mozilla/dom/Document.h"
     68 #include "mozilla/dom/DocumentInlines.h"
     69 #include "mozilla/dom/DocumentTimeline.h"
     70 #include "mozilla/dom/Event.h"
     71 #include "mozilla/dom/HTMLVideoElement.h"
     72 #include "mozilla/dom/LargestContentfulPaint.h"
     73 #include "mozilla/dom/MediaQueryList.h"
     74 #include "mozilla/dom/Performance.h"
     75 #include "mozilla/dom/PerformanceMainThread.h"
     76 #include "mozilla/dom/ScriptSettings.h"
     77 #include "mozilla/dom/Selection.h"
     78 #include "mozilla/dom/VsyncMainChild.h"
     79 #include "mozilla/dom/WindowBinding.h"
     80 #include "mozilla/glean/LayoutMetrics.h"
     81 #include "mozilla/ipc/BackgroundChild.h"
     82 #include "mozilla/ipc/PBackgroundChild.h"
     83 #include "mozilla/layers/WebRenderLayerManager.h"
     84 #include "nsAnimationManager.h"
     85 #include "nsComponentManagerUtils.h"
     86 #include "nsContentUtils.h"
     87 #include "nsDOMNavigationTiming.h"
     88 #include "nsDisplayList.h"
     89 #include "nsDocShell.h"
     90 #include "nsISimpleEnumerator.h"
     91 #include "nsITimer.h"
     92 #include "nsIXULRuntime.h"
     93 #include "nsJSEnvironment.h"
     94 #include "nsLayoutUtils.h"
     95 #include "nsPresContext.h"
     96 #include "nsTextFrame.h"
     97 #include "nsTransitionManager.h"
     98 
     99 #if defined(MOZ_WIDGET_ANDROID)
    100 #  include "VRManagerChild.h"
    101 #endif  // defined(MOZ_WIDGET_ANDROID)
    102 
    103 #include "nsXULPopupManager.h"
    104 
    105 using namespace mozilla;
    106 using namespace mozilla::widget;
    107 using namespace mozilla::ipc;
    108 using namespace mozilla::dom;
    109 using namespace mozilla::layout;
    110 
    111 static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
    112 #define LOG(...) \
    113  MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
    114 
    115 // after 10 minutes, stop firing off inactive timers
    116 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
    117 
    118 // The number of seconds spent skipping frames because we are waiting for the
    119 // compositor before logging.
    120 #if defined(MOZ_ASAN)
    121 #  define REFRESH_WAIT_WARNING 5
    122 #elif defined(DEBUG) && !defined(MOZ_VALGRIND)
    123 #  define REFRESH_WAIT_WARNING 5
    124 #elif defined(DEBUG) && defined(MOZ_VALGRIND)
    125 #  define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
    126 #elif defined(MOZ_VALGRIND)
    127 #  define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
    128 #else
    129 #  define REFRESH_WAIT_WARNING 1
    130 #endif
    131 
    132 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsRefreshDriver::TickReasons);
    133 
    134 namespace {
    135 // The number outstanding nsRefreshDrivers (that have been created but not
    136 // disconnected). When this reaches zero we will call
    137 // nsRefreshDriver::Shutdown.
    138 static uint32_t sRefreshDriverCount = 0;
    139 }  // namespace
    140 
    141 namespace mozilla {
    142 
    143 static TimeStamp sMostRecentHighRateVsync;
    144 
    145 static TimeDuration sMostRecentHighRate;
    146 
    147 /*
    148 * The base class for all global refresh driver timers.  It takes care
    149 * of managing the list of refresh drivers attached to them and
    150 * provides interfaces for querying/setting the rate and actually
    151 * running a timer 'Tick'.  Subclasses must implement StartTimer(),
    152 * StopTimer(), and ScheduleNextTick() -- the first two just
    153 * start/stop whatever timer mechanism is in use, and ScheduleNextTick
    154 * is called at the start of the Tick() implementation to set a time
    155 * for the next tick.
    156 */
    157 class RefreshDriverTimer {
    158 public:
    159  RefreshDriverTimer() = default;
    160 
    161  NS_INLINE_DECL_REFCOUNTING(RefreshDriverTimer)
    162 
    163  virtual void AddRefreshDriver(nsRefreshDriver* aDriver) {
    164    LOG("[%p] AddRefreshDriver %p", this, aDriver);
    165 
    166    bool startTimer =
    167        mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
    168    if (IsRootRefreshDriver(aDriver)) {
    169      NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver),
    170                   "Adding a duplicate root refresh driver!");
    171      mRootRefreshDrivers.AppendElement(aDriver);
    172    } else {
    173      NS_ASSERTION(!mContentRefreshDrivers.Contains(aDriver),
    174                   "Adding a duplicate content refresh driver!");
    175      mContentRefreshDrivers.AppendElement(aDriver);
    176    }
    177 
    178    if (startTimer) {
    179      StartTimer();
    180    }
    181  }
    182 
    183  void RemoveRefreshDriver(nsRefreshDriver* aDriver) {
    184    LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
    185 
    186    if (IsRootRefreshDriver(aDriver)) {
    187      NS_ASSERTION(mRootRefreshDrivers.Contains(aDriver),
    188                   "RemoveRefreshDriver for a refresh driver that's not in the "
    189                   "root refresh list!");
    190      mRootRefreshDrivers.RemoveElement(aDriver);
    191    } else {
    192      nsPresContext* pc = aDriver->GetPresContext();
    193      nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
    194      // During PresContext shutdown, we can't accurately detect
    195      // if a root refresh driver exists or not. Therefore, we have to
    196      // search and find out which list this driver exists in.
    197      if (!rootContext) {
    198        if (mRootRefreshDrivers.Contains(aDriver)) {
    199          mRootRefreshDrivers.RemoveElement(aDriver);
    200        } else {
    201          NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
    202                       "RemoveRefreshDriver without a display root for a "
    203                       "driver that is not in the content refresh list");
    204          mContentRefreshDrivers.RemoveElement(aDriver);
    205        }
    206      } else {
    207        NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
    208                     "RemoveRefreshDriver for a driver that is not in the "
    209                     "content refresh list");
    210        mContentRefreshDrivers.RemoveElement(aDriver);
    211      }
    212    }
    213 
    214    bool stopTimer =
    215        mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
    216    if (stopTimer) {
    217      StopTimer();
    218    }
    219  }
    220 
    221  TimeStamp MostRecentRefresh() const { return mLastFireTime; }
    222  VsyncId MostRecentRefreshVsyncId() const { return mLastFireId; }
    223  virtual bool IsBlocked() { return false; }
    224 
    225  virtual TimeDuration GetTimerRate() = 0;
    226 
    227  TimeStamp GetIdleDeadlineHint(TimeStamp aDefault) {
    228    MOZ_ASSERT(NS_IsMainThread());
    229 
    230    if (!IsTicking() && !gfxPlatform::IsInLayoutAsapMode()) {
    231      return aDefault;
    232    }
    233 
    234    TimeStamp mostRecentRefresh = MostRecentRefresh();
    235    TimeDuration refreshPeriod = GetTimerRate();
    236    TimeStamp idleEnd = mostRecentRefresh + refreshPeriod;
    237    double highRateMultiplier = nsRefreshDriver::HighRateMultiplier();
    238 
    239    // If we haven't painted for some time, then guess that we won't paint
    240    // again for a while, so the refresh driver is not a good way to predict
    241    // idle time.
    242    if (highRateMultiplier == 1.0 &&
    243        (idleEnd +
    244             refreshPeriod *
    245                 StaticPrefs::layout_idle_period_required_quiescent_frames() <
    246         TimeStamp::Now())) {
    247      return aDefault;
    248    }
    249 
    250    // End the predicted idle time a little early, the amount controlled by a
    251    // pref, to prevent overrunning the idle time and delaying a frame.
    252    // But do that only if we aren't in high rate mode.
    253    idleEnd = idleEnd - TimeDuration::FromMilliseconds(
    254                            highRateMultiplier *
    255                            StaticPrefs::layout_idle_period_time_limit());
    256    return idleEnd < aDefault ? idleEnd : aDefault;
    257  }
    258 
    259  Maybe<TimeStamp> GetNextTickHint() {
    260    MOZ_ASSERT(NS_IsMainThread());
    261    TimeStamp nextTick = MostRecentRefresh() + GetTimerRate();
    262    return nextTick < TimeStamp::Now() ? Nothing() : Some(nextTick);
    263  }
    264 
    265  // Returns null if the RefreshDriverTimer is attached to several
    266  // RefreshDrivers. That may happen for example when there are
    267  // several windows open.
    268  nsPresContext* GetPresContextForOnlyRefreshDriver() {
    269    if (mRootRefreshDrivers.Length() == 1 && mContentRefreshDrivers.IsEmpty()) {
    270      return mRootRefreshDrivers[0]->GetPresContext();
    271    }
    272    if (mContentRefreshDrivers.Length() == 1 && mRootRefreshDrivers.IsEmpty()) {
    273      return mContentRefreshDrivers[0]->GetPresContext();
    274    }
    275    return nullptr;
    276  }
    277 
    278  bool IsAnyToplevelContentPageLoading() {
    279    for (nsTArray<RefPtr<nsRefreshDriver>>* drivers :
    280         {&mRootRefreshDrivers, &mContentRefreshDrivers}) {
    281      for (RefPtr<nsRefreshDriver>& driver : *drivers) {
    282        if (nsPresContext* pc = driver->GetPresContext()) {
    283          if (pc->Document()->IsTopLevelContentDocument() &&
    284              pc->Document()->GetReadyStateEnum() <
    285                  Document::READYSTATE_COMPLETE) {
    286            return true;
    287          }
    288        }
    289      }
    290    }
    291 
    292    return false;
    293  }
    294 
    295 protected:
    296  virtual ~RefreshDriverTimer() {
    297    MOZ_ASSERT(
    298        mContentRefreshDrivers.Length() == 0,
    299        "Should have removed all content refresh drivers from here by now!");
    300    MOZ_ASSERT(
    301        mRootRefreshDrivers.Length() == 0,
    302        "Should have removed all root refresh drivers from here by now!");
    303  }
    304 
    305  virtual void StartTimer() = 0;
    306  virtual void StopTimer() = 0;
    307  virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
    308 
    309 public:
    310  virtual bool IsTicking() const = 0;
    311 
    312 protected:
    313  bool IsRootRefreshDriver(nsRefreshDriver* aDriver) {
    314    nsPresContext* pc = aDriver->GetPresContext();
    315    nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
    316    if (!rootContext) {
    317      return false;
    318    }
    319 
    320    return aDriver == rootContext->RefreshDriver();
    321  }
    322 
    323  /*
    324   * Actually runs a tick, poking all the attached RefreshDrivers.
    325   * Grabs the "now" time via TimeStamp::Now().
    326   */
    327  void Tick() {
    328    TimeStamp now = TimeStamp::Now();
    329    Tick(VsyncId(), now);
    330  }
    331 
    332  void TickRefreshDrivers(VsyncId aId, TimeStamp aNow,
    333                          nsTArray<RefPtr<nsRefreshDriver>>& aDrivers) {
    334    if (aDrivers.IsEmpty()) {
    335      return;
    336    }
    337 
    338    for (nsRefreshDriver* driver : aDrivers.Clone()) {
    339      // don't poke this driver if it's in test mode
    340      if (driver->IsTestControllingRefreshesEnabled()) {
    341        continue;
    342      }
    343 
    344      TickDriver(driver, aId, aNow);
    345    }
    346  }
    347 
    348  /*
    349   * Tick the refresh drivers based on the given timestamp.
    350   */
    351  void Tick(VsyncId aId, TimeStamp now) {
    352    ScheduleNextTick(now);
    353 
    354    mLastFireTime = now;
    355    mLastFireId = aId;
    356 
    357    LOG("[%p] ticking drivers...", this);
    358 
    359    TickRefreshDrivers(aId, now, mContentRefreshDrivers);
    360    TickRefreshDrivers(aId, now, mRootRefreshDrivers);
    361 
    362    LOG("[%p] done.", this);
    363  }
    364 
    365  static void TickDriver(nsRefreshDriver* driver, VsyncId aId, TimeStamp now) {
    366    driver->Tick(aId, now);
    367  }
    368 
    369  TimeStamp mLastFireTime;
    370  VsyncId mLastFireId;
    371  TimeStamp mTargetTime;
    372 
    373  nsTArray<RefPtr<nsRefreshDriver>> mContentRefreshDrivers;
    374  nsTArray<RefPtr<nsRefreshDriver>> mRootRefreshDrivers;
    375 
    376  // useful callback for nsITimer-based derived classes, here
    377  // because of c++ protected shenanigans
    378  static void TimerTick(nsITimer* aTimer, void* aClosure) {
    379    RefPtr<RefreshDriverTimer> timer =
    380        static_cast<RefreshDriverTimer*>(aClosure);
    381    timer->Tick();
    382  }
    383 };
    384 
    385 /*
    386 * A RefreshDriverTimer that uses a nsITimer as the underlying timer.  Note that
    387 * this is a ONE_SHOT timer, not a repeating one!  Subclasses are expected to
    388 * implement ScheduleNextTick and intelligently calculate the next time to tick,
    389 * and to reset mTimer.  Using a repeating nsITimer gets us into a lot of pain
    390 * with its attempt at intelligent slack removal and such, so we don't do it.
    391 */
    392 class SimpleTimerBasedRefreshDriverTimer : public RefreshDriverTimer {
    393 public:
    394  /*
    395   * aRate -- the delay, in milliseconds, requested between timer firings
    396   */
    397  explicit SimpleTimerBasedRefreshDriverTimer(double aRate) {
    398    SetRate(aRate);
    399    mTimer = NS_NewTimer();
    400  }
    401 
    402  virtual ~SimpleTimerBasedRefreshDriverTimer() override { StopTimer(); }
    403 
    404  // will take effect at next timer tick
    405  virtual void SetRate(double aNewRate) {
    406    mRateMilliseconds = aNewRate;
    407    mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
    408  }
    409 
    410  double GetRate() const { return mRateMilliseconds; }
    411 
    412  TimeDuration GetTimerRate() override { return mRateDuration; }
    413 
    414 protected:
    415  void StartTimer() override {
    416    // pretend we just fired, and we schedule the next tick normally
    417    mLastFireTime = TimeStamp::Now();
    418    mLastFireId = VsyncId();
    419 
    420    mTargetTime = mLastFireTime + mRateDuration;
    421 
    422    uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
    423    mTimer->InitWithNamedFuncCallback(
    424        TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
    425        "SimpleTimerBasedRefreshDriverTimer::StartTimer"_ns);
    426  }
    427 
    428  void StopTimer() override { mTimer->Cancel(); }
    429 
    430  double mRateMilliseconds;
    431  TimeDuration mRateDuration;
    432  RefPtr<nsITimer> mTimer;
    433 };
    434 
    435 /*
    436 * A refresh driver that listens to vsync events and ticks the refresh driver
    437 * on vsync intervals. We throttle the refresh driver if we get too many
    438 * vsync events and wait to catch up again.
    439 */
    440 class VsyncRefreshDriverTimer : public RefreshDriverTimer {
    441 public:
    442  // This is used in the parent process for all platforms except Linux Wayland.
    443  static RefPtr<VsyncRefreshDriverTimer>
    444  CreateForParentProcessWithGlobalVsync() {
    445    MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    446    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    447    RefPtr<VsyncDispatcher> vsyncDispatcher =
    448        gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher();
    449    RefPtr<VsyncRefreshDriverTimer> timer =
    450        new VsyncRefreshDriverTimer(std::move(vsyncDispatcher), nullptr);
    451    return timer.forget();
    452  }
    453 
    454  // This is used in the parent process for Linux Wayland only, where we have a
    455  // per-widget VsyncSource which is independent from the gfxPlatform's global
    456  // VsyncSource.
    457  static RefPtr<VsyncRefreshDriverTimer>
    458  CreateForParentProcessWithLocalVsyncDispatcher(
    459      RefPtr<VsyncDispatcher>&& aVsyncDispatcher) {
    460    MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    461    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    462    RefPtr<VsyncRefreshDriverTimer> timer =
    463        new VsyncRefreshDriverTimer(std::move(aVsyncDispatcher), nullptr);
    464    return timer.forget();
    465  }
    466 
    467  // This is used in the content process.
    468  static RefPtr<VsyncRefreshDriverTimer> CreateForContentProcess(
    469      RefPtr<VsyncMainChild>&& aVsyncChild) {
    470    MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
    471    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    472    RefPtr<VsyncRefreshDriverTimer> timer =
    473        new VsyncRefreshDriverTimer(nullptr, std::move(aVsyncChild));
    474    return timer.forget();
    475  }
    476 
    477  TimeDuration GetTimerRate() override {
    478    if (mVsyncDispatcher) {
    479      mVsyncRate = mVsyncDispatcher->GetVsyncRate();
    480    } else if (mVsyncChild) {
    481      mVsyncRate = mVsyncChild->GetVsyncRate();
    482    }
    483 
    484    // If hardware queries fail / are unsupported, we have to just guess.
    485    return mVsyncRate != TimeDuration::Forever()
    486               ? mVsyncRate
    487               : TimeDuration::FromMilliseconds(1000.0 / 60.0);
    488  }
    489 
    490  bool IsBlocked() override {
    491    return !mSuspendVsyncPriorityTicksUntil.IsNull() &&
    492           mSuspendVsyncPriorityTicksUntil > TimeStamp::Now() &&
    493           ShouldGiveNonVsyncTasksMoreTime();
    494  }
    495 
    496 private:
    497  // RefreshDriverVsyncObserver redirects vsync notifications to the main thread
    498  // and calls VsyncRefreshDriverTimer::NotifyVsyncOnMainThread on it. It also
    499  // acts as a weak reference to the refresh driver timer, dropping its
    500  // reference when RefreshDriverVsyncObserver::Shutdown is called from the
    501  // timer's destructor.
    502  //
    503  // RefreshDriverVsyncObserver::NotifyVsync is called from different places
    504  // depending on the process type.
    505  //
    506  // Parent process:
    507  // NotifyVsync is called by RefreshDriverVsyncDispatcher, on a background
    508  // thread. RefreshDriverVsyncDispatcher keeps strong references to its
    509  // VsyncObservers, both in its array of observers and while calling
    510  // NotifyVsync. So it might drop its last reference to the observer on a
    511  // background thread. This means that the VsyncRefreshDriverTimer itself can't
    512  // be the observer (because its destructor would potentially be run on a
    513  // background thread), and it's why we use this separate class.
    514  //
    515  // Child process:
    516  // NotifyVsync is called by VsyncMainChild, on the main thread.
    517  // VsyncMainChild keeps raw pointers to its observers.
    518  class RefreshDriverVsyncObserver final : public VsyncObserver {
    519    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(
    520        VsyncRefreshDriverTimer::RefreshDriverVsyncObserver, override)
    521 
    522   public:
    523    explicit RefreshDriverVsyncObserver(
    524        VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
    525        : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer),
    526          mLastPendingVsyncNotification(
    527              "RefreshDriverVsyncObserver::mLastPendingVsyncNotification") {
    528      MOZ_ASSERT(NS_IsMainThread());
    529    }
    530 
    531    void NotifyVsync(const VsyncEvent& aVsync) override {
    532      // Compress vsync notifications such that only 1 may run at a time
    533      // This is so that we don't flood the refresh driver with vsync messages
    534      // if the main thread is blocked for long periods of time
    535      {  // scope lock
    536        auto pendingVsync = mLastPendingVsyncNotification.Lock();
    537        bool hadPendingVsync = pendingVsync->isSome();
    538        *pendingVsync = Some(aVsync);
    539        if (hadPendingVsync) {
    540          return;
    541        }
    542      }
    543 
    544      if (XRE_IsContentProcess()) {
    545        // In the content process, NotifyVsync is called by VsyncMainChild on
    546        // the main thread. No need to use a runnable, just call
    547        // NotifyVsyncTimerOnMainThread() directly.
    548        NotifyVsyncTimerOnMainThread();
    549        return;
    550      }
    551 
    552      // In the parent process, NotifyVsync is called on the vsync thread, which
    553      // on most platforms is different from the main thread, so we need to
    554      // dispatch a runnable for running NotifyVsyncTimerOnMainThread on the
    555      // main thread.
    556      // TODO: On Linux Wayland, the vsync thread is currently the main thread,
    557      // and yet we still dispatch the runnable. Do we need to?
    558      bool useVsyncPriority = mozilla::BrowserTabsRemoteAutostart();
    559      nsCOMPtr<nsIRunnable> vsyncEvent = new PrioritizableRunnable(
    560          NS_NewRunnableFunction(
    561              "RefreshDriverVsyncObserver::NotifyVsyncTimerOnMainThread",
    562              [self = RefPtr{this}]() {
    563                self->NotifyVsyncTimerOnMainThread();
    564              }),
    565          useVsyncPriority ? nsIRunnablePriority::PRIORITY_VSYNC
    566                           : nsIRunnablePriority::PRIORITY_NORMAL);
    567      NS_DispatchToMainThread(vsyncEvent);
    568    }
    569 
    570    void NotifyVsyncTimerOnMainThread() {
    571      MOZ_ASSERT(NS_IsMainThread());
    572 
    573      if (!mVsyncRefreshDriverTimer) {
    574        // Ignore calls after Shutdown.
    575        return;
    576      }
    577 
    578      VsyncEvent vsyncEvent;
    579      {
    580        // Get the last of the queued-up vsync notifications.
    581        auto pendingVsync = mLastPendingVsyncNotification.Lock();
    582        MOZ_RELEASE_ASSERT(
    583            pendingVsync->isSome(),
    584            "We should always have a pending vsync notification here.");
    585        vsyncEvent = pendingVsync->extract();
    586      }
    587 
    588      // Call VsyncRefreshDriverTimer::NotifyVsyncOnMainThread, and keep a
    589      // strong reference to it while calling the method.
    590      RefPtr<VsyncRefreshDriverTimer> timer = mVsyncRefreshDriverTimer;
    591      timer->NotifyVsyncOnMainThread(vsyncEvent);
    592    }
    593 
    594    void Shutdown() {
    595      MOZ_ASSERT(NS_IsMainThread());
    596      mVsyncRefreshDriverTimer = nullptr;
    597    }
    598 
    599   private:
    600    ~RefreshDriverVsyncObserver() = default;
    601 
    602    // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
    603    // be always available before Shutdown(). We can just use the raw pointer
    604    // here.
    605    // Only accessed on the main thread.
    606    VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
    607 
    608    // Non-empty between a call to NotifyVsync and a call to
    609    // NotifyVsyncOnMainThread. When multiple vsync notifications have been
    610    // received between those two calls, this contains the last of the pending
    611    // notifications. This is used both in the parent process and in the child
    612    // process, but it only does something useful in the parent process. In the
    613    // child process, both calls happen on the main thread right after one
    614    // another, so there's only one notification to keep track of; vsync
    615    // notification coalescing for child processes happens at the IPC level
    616    // instead.
    617    DataMutex<Maybe<VsyncEvent>> mLastPendingVsyncNotification;
    618 
    619  };  // RefreshDriverVsyncObserver
    620 
    621  VsyncRefreshDriverTimer(RefPtr<VsyncDispatcher>&& aVsyncDispatcher,
    622                          RefPtr<VsyncMainChild>&& aVsyncChild)
    623      : mVsyncDispatcher(aVsyncDispatcher),
    624        mVsyncChild(aVsyncChild),
    625        mVsyncRate(TimeDuration::Forever()),
    626        mRecentVsync(TimeStamp::Now()),
    627        mLastTickStart(TimeStamp::Now()),
    628        mLastIdleTaskCount(0),
    629        mLastRunOutOfMTTasksCount(0),
    630        mProcessedVsync(true),
    631        mHasPendingLowPrioTask(false) {
    632    mVsyncObserver = new RefreshDriverVsyncObserver(this);
    633  }
    634 
    635  ~VsyncRefreshDriverTimer() override {
    636    if (mVsyncDispatcher) {
    637      mVsyncDispatcher->RemoveVsyncObserver(mVsyncObserver);
    638      mVsyncDispatcher = nullptr;
    639    } else if (mVsyncChild) {
    640      mVsyncChild->RemoveChildRefreshTimer(mVsyncObserver);
    641      mVsyncChild = nullptr;
    642    }
    643 
    644    // Detach current vsync timer from this VsyncObserver. The observer will no
    645    // longer tick this timer.
    646    mVsyncObserver->Shutdown();
    647    mVsyncObserver = nullptr;
    648  }
    649 
    650  bool ShouldGiveNonVsyncTasksMoreTime(bool aCheckOnlyNewPendingTasks = false) {
    651    TaskController* taskController = TaskController::Get();
    652    IdleTaskManager* idleTaskManager = taskController->GetIdleTaskManager();
    653    VsyncTaskManager* vsyncTaskManager = VsyncTaskManager::Get();
    654 
    655    // Note, pendingTaskCount includes also all the pending idle and vsync
    656    // tasks.
    657    uint64_t pendingTaskCount =
    658        taskController->PendingMainthreadTaskCountIncludingSuspended();
    659    uint64_t pendingIdleTaskCount = idleTaskManager->PendingTaskCount();
    660    uint64_t pendingVsyncTaskCount = vsyncTaskManager->PendingTaskCount();
    661    if (!(pendingTaskCount > (pendingIdleTaskCount + pendingVsyncTaskCount))) {
    662      return false;
    663    }
    664    if (aCheckOnlyNewPendingTasks) {
    665      return true;
    666    }
    667 
    668    uint64_t idleTaskCount = idleTaskManager->ProcessedTaskCount();
    669 
    670    // If we haven't processed new idle tasks and we have pending
    671    // non-idle tasks, give those non-idle tasks more time,
    672    // but only if the main thread wasn't totally empty at some point.
    673    // In the parent process RunOutOfMTTasksCount() is less meaningful
    674    // because some of the tasks run through AppShell.
    675    return mLastIdleTaskCount == idleTaskCount &&
    676           (taskController->RunOutOfMTTasksCount() ==
    677                mLastRunOutOfMTTasksCount ||
    678            XRE_IsParentProcess());
    679  }
    680 
    681  void NotifyVsyncOnMainThread(const VsyncEvent& aVsyncEvent) {
    682    MOZ_ASSERT(NS_IsMainThread());
    683 
    684    mRecentVsync = aVsyncEvent.mTime;
    685    mRecentVsyncId = aVsyncEvent.mId;
    686    if (!mSuspendVsyncPriorityTicksUntil.IsNull() &&
    687        mSuspendVsyncPriorityTicksUntil > TimeStamp::Now()) {
    688      if (ShouldGiveNonVsyncTasksMoreTime()) {
    689        if (!IsAnyToplevelContentPageLoading()) {
    690          // If pages aren't loading and there aren't other tasks to run,
    691          // trigger the pending vsync notification.
    692          mPendingVsync = mRecentVsync;
    693          mPendingVsyncId = mRecentVsyncId;
    694          if (!mHasPendingLowPrioTask) {
    695            mHasPendingLowPrioTask = true;
    696            NS_DispatchToMainThreadQueue(
    697                NS_NewRunnableFunction(
    698                    "NotifyVsyncOnMainThread[low priority]",
    699                    [self = RefPtr{this}]() {
    700                      self->mHasPendingLowPrioTask = false;
    701                      if (self->mRecentVsync == self->mPendingVsync &&
    702                          self->mRecentVsyncId == self->mPendingVsyncId &&
    703                          !self->ShouldGiveNonVsyncTasksMoreTime()) {
    704                        self->mSuspendVsyncPriorityTicksUntil = TimeStamp();
    705                        self->NotifyVsyncOnMainThread({self->mPendingVsyncId,
    706                                                       self->mPendingVsync,
    707                                                       /* unused */
    708                                                       TimeStamp()});
    709                      }
    710                    }),
    711                EventQueuePriority::Low);
    712          }
    713        }
    714        return;
    715      }
    716 
    717      // Clear the value since we aren't blocking anymore because there aren't
    718      // any non-idle tasks to process.
    719      mSuspendVsyncPriorityTicksUntil = TimeStamp();
    720    }
    721 
    722    if (StaticPrefs::layout_lower_priority_refresh_driver_during_load() &&
    723        ShouldGiveNonVsyncTasksMoreTime()) {
    724      nsPresContext* pctx = GetPresContextForOnlyRefreshDriver();
    725      if (pctx && pctx->HadFirstContentfulPaint() && pctx->Document() &&
    726          pctx->Document()->GetReadyStateEnum() <
    727              Document::READYSTATE_COMPLETE) {
    728        nsPIDOMWindowInner* win = pctx->Document()->GetInnerWindow();
    729        uint32_t frameRateMultiplier = pctx->GetNextFrameRateMultiplier();
    730        if (!frameRateMultiplier) {
    731          pctx->DidUseFrameRateMultiplier();
    732        }
    733        if (win && frameRateMultiplier) {
    734          dom::Performance* perf = win->GetPerformance();
    735          // Limit slower refresh rate to 5 seconds between the
    736          // first contentful paint and page load.
    737          if (perf &&
    738              perf->Now() < StaticPrefs::page_load_deprioritization_period()) {
    739            if (mProcessedVsync) {
    740              mProcessedVsync = false;
    741              TimeDuration rate = GetTimerRate();
    742              uint32_t slowRate = static_cast<uint32_t>(rate.ToMilliseconds() *
    743                                                        frameRateMultiplier);
    744              pctx->DidUseFrameRateMultiplier();
    745              nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<>(
    746                  "VsyncRefreshDriverTimer::IdlePriorityNotify", this,
    747                  &VsyncRefreshDriverTimer::IdlePriorityNotify);
    748              NS_DispatchToCurrentThreadQueue(vsyncEvent.forget(), slowRate,
    749                                              EventQueuePriority::Idle);
    750            }
    751            return;
    752          }
    753        }
    754      }
    755    }
    756 
    757    TickRefreshDriver(aVsyncEvent.mId, aVsyncEvent.mTime);
    758  }
    759 
    760  void RecordTelemetryProbes(TimeStamp aVsyncTimestamp) {
    761    MOZ_ASSERT(NS_IsMainThread());
    762 #ifndef ANDROID /* bug 1142079 */
    763    if (XRE_IsParentProcess()) {
    764      TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
    765      glean::layout::refresh_driver_chrome_frame_delay.AccumulateRawDuration(
    766          vsyncLatency);
    767    } else if (mVsyncRate != TimeDuration::Forever()) {
    768      TimeDuration contentDelay =
    769          (TimeStamp::Now() - mLastTickStart) - mVsyncRate;
    770      if (contentDelay.ToMilliseconds() < 0) {
    771        // Vsyncs are noisy and some can come at a rate quicker than
    772        // the reported hardware rate. In those cases, consider that we have 0
    773        // delay.
    774        contentDelay = TimeDuration::FromMilliseconds(0);
    775      }
    776      glean::layout::refresh_driver_content_frame_delay.AccumulateRawDuration(
    777          contentDelay);
    778    } else {
    779      // Request the vsync rate which VsyncChild stored the last time it got a
    780      // vsync notification.
    781      mVsyncRate = mVsyncChild->GetVsyncRate();
    782    }
    783 #endif
    784  }
    785 
    786  void OnTimerStart() {
    787    mLastTickStart = TimeStamp::Now();
    788    mLastTickEnd = TimeStamp();
    789    mLastIdleTaskCount = 0;
    790  }
    791 
    792  void IdlePriorityNotify() {
    793    if (mLastProcessedTick.IsNull() || mRecentVsync > mLastProcessedTick) {
    794      // mSuspendVsyncPriorityTicksUntil is for high priority vsync
    795      // notifications only.
    796      mSuspendVsyncPriorityTicksUntil = TimeStamp();
    797      TickRefreshDriver(mRecentVsyncId, mRecentVsync);
    798    }
    799 
    800    mProcessedVsync = true;
    801  }
    802 
    803  hal::PerformanceHintSession* GetPerformanceHintSession() {
    804    // The ContentChild creates/destroys the PerformanceHintSession in response
    805    // to the process' priority being foregrounded/backgrounded. We can only use
    806    // this session when using a single vsync source for the process, otherwise
    807    // these threads may be performing work for multiple
    808    // VsyncRefreshDriverTimers and we will misreport the work duration.
    809    const ContentChild* contentChild = ContentChild::GetSingleton();
    810    if (contentChild && mVsyncChild) {
    811      return contentChild->PerformanceHintSession();
    812    }
    813 
    814    return nullptr;
    815  }
    816 
    817  void TickRefreshDriver(VsyncId aId, TimeStamp aVsyncTimestamp) {
    818    MOZ_ASSERT(NS_IsMainThread());
    819 
    820    RecordTelemetryProbes(aVsyncTimestamp);
    821 
    822    TimeStamp tickStart = TimeStamp::Now();
    823 
    824    const TimeDuration previousRate = mVsyncRate;
    825    const TimeDuration rate = GetTimerRate();
    826 
    827    if (rate != previousRate) {
    828      if (auto* const performanceHintSession = GetPerformanceHintSession()) {
    829        performanceHintSession->UpdateTargetWorkDuration(
    830            ContentChild::GetPerformanceHintTarget(rate));
    831      }
    832    }
    833 
    834    if (TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval()) >
    835        rate) {
    836      sMostRecentHighRateVsync = tickStart;
    837      sMostRecentHighRate = rate;
    838    }
    839 
    840 #ifdef DEBUG
    841    // On 32-bit Windows we sometimes get times where TimeStamp::Now() is not
    842    // monotonic because the underlying system apis produce non-monontonic
    843    // results; see bug 1306896.
    844    // On Wayland, vsync timestamp might not precisely match system time; see
    845    // bug 1958043.
    846 #  if defined(_WIN32) || defined(MOZ_WAYLAND)
    847    (void)NS_WARN_IF(aVsyncTimestamp > tickStart);
    848 #  else
    849    MOZ_ASSERT(aVsyncTimestamp <= tickStart);
    850 #  endif
    851 #endif
    852 
    853    bool shouldGiveNonVSyncTasksMoreTime = ShouldGiveNonVsyncTasksMoreTime();
    854 
    855    // Set these variables before calling RunRefreshDrivers so that they are
    856    // visible to any nested ticks.
    857    mLastTickStart = tickStart;
    858    mLastProcessedTick = aVsyncTimestamp;
    859 
    860    RunRefreshDrivers(aId, aVsyncTimestamp);
    861 
    862    TimeStamp tickEnd = TimeStamp::Now();
    863 
    864    if (auto* const performanceHintSession = GetPerformanceHintSession()) {
    865      performanceHintSession->ReportActualWorkDuration(tickEnd - tickStart);
    866    }
    867 
    868    // Re-read mLastTickStart in case there was a nested tick inside this
    869    // tick.
    870    TimeStamp mostRecentTickStart = mLastTickStart;
    871 
    872    // Let also non-RefreshDriver code to run at least for awhile if we have
    873    // a mVsyncRefreshDriverTimer.
    874    // Always give a tiny bit, 5% of the vsync interval, time outside the
    875    // tick
    876    // In case there are both normal tasks and RefreshDrivers are doing
    877    // work, mSuspendVsyncPriorityTicksUntil will be set to a timestamp in the
    878    // future where the period between the previous tick start
    879    // (mostRecentTickStart) and the next tick needs to be at least the amount
    880    // of work normal tasks and RefreshDrivers did together (minus short grace
    881    // period).
    882    TimeDuration gracePeriod = rate / int64_t(20);
    883 
    884    if (shouldGiveNonVSyncTasksMoreTime && !mLastTickEnd.IsNull() &&
    885        XRE_IsContentProcess() &&
    886        // For RefreshDriver scheduling during page load there is currently
    887        // idle priority based setup.
    888        // XXX Consider to remove the page load specific code paths.
    889        !IsAnyToplevelContentPageLoading()) {
    890      // In case normal tasks are doing lots of work, we still want to paint
    891      // every now and then, so only at maximum 4 * rate of work is counted
    892      // here.
    893      // If we're giving extra time for tasks outside a tick, try to
    894      // ensure the next vsync after that period is handled, so subtract
    895      // a grace period.
    896      TimeDuration timeForOutsideTick = std::clamp(
    897          tickStart - mLastTickEnd - gracePeriod, gracePeriod, rate * 4);
    898      mSuspendVsyncPriorityTicksUntil = tickEnd + timeForOutsideTick;
    899    } else if (ShouldGiveNonVsyncTasksMoreTime(true)) {
    900      // We've got some new tasks, give them some extra time.
    901      // This handles also the case when mLastTickEnd.IsNull() above and we
    902      // should give some more time for non-vsync tasks.
    903      mSuspendVsyncPriorityTicksUntil = tickEnd + gracePeriod;
    904    } else {
    905      mSuspendVsyncPriorityTicksUntil = mostRecentTickStart + gracePeriod;
    906    }
    907 
    908    mLastIdleTaskCount =
    909        TaskController::Get()->GetIdleTaskManager()->ProcessedTaskCount();
    910    mLastRunOutOfMTTasksCount = TaskController::Get()->RunOutOfMTTasksCount();
    911    mLastTickEnd = tickEnd;
    912  }
    913 
    914  void StartTimer() override {
    915    MOZ_ASSERT(NS_IsMainThread());
    916 
    917    mLastFireTime = TimeStamp::Now();
    918    mLastFireId = VsyncId();
    919 
    920    if (mVsyncDispatcher) {
    921      mVsyncDispatcher->AddVsyncObserver(mVsyncObserver);
    922    } else if (mVsyncChild) {
    923      mVsyncChild->AddChildRefreshTimer(mVsyncObserver);
    924      OnTimerStart();
    925    }
    926    mIsTicking = true;
    927  }
    928 
    929  void StopTimer() override {
    930    MOZ_ASSERT(NS_IsMainThread());
    931 
    932    if (mVsyncDispatcher) {
    933      mVsyncDispatcher->RemoveVsyncObserver(mVsyncObserver);
    934    } else if (mVsyncChild) {
    935      mVsyncChild->RemoveChildRefreshTimer(mVsyncObserver);
    936    }
    937    mIsTicking = false;
    938  }
    939 
    940 public:
    941  bool IsTicking() const override { return mIsTicking; }
    942 
    943 protected:
    944  void ScheduleNextTick(TimeStamp aNowTime) override {
    945    // Do nothing since we just wait for the next vsync from
    946    // RefreshDriverVsyncObserver.
    947  }
    948 
    949  void RunRefreshDrivers(VsyncId aId, TimeStamp aTimeStamp) {
    950    Tick(aId, aTimeStamp);
    951    for (auto& driver : mContentRefreshDrivers) {
    952      driver->FinishedVsyncTick();
    953    }
    954    for (auto& driver : mRootRefreshDrivers) {
    955      driver->FinishedVsyncTick();
    956    }
    957  }
    958 
    959  // Always non-null. Has a weak pointer to us and notifies us of vsync.
    960  RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
    961 
    962  // Used in the parent process. We register mVsyncObserver with it for the
    963  // duration during which we want to receive vsync notifications. We also
    964  // use it to query the current vsync rate.
    965  RefPtr<VsyncDispatcher> mVsyncDispatcher;
    966  // Used it the content process. We register mVsyncObserver with it for the
    967  // duration during which we want to receive vsync notifications. The
    968  // mVsyncChild will be always available before VsyncChild::ActorDestroy().
    969  // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
    970  RefPtr<VsyncMainChild> mVsyncChild;
    971 
    972  TimeDuration mVsyncRate;
    973  bool mIsTicking = false;
    974 
    975  TimeStamp mRecentVsync;
    976  VsyncId mRecentVsyncId;
    977  // The local start time when RefreshDrivers' Tick was called last time.
    978  TimeStamp mLastTickStart;
    979  // The local end time of the last RefreshDrivers' tick.
    980  TimeStamp mLastTickEnd;
    981  // The number of idle tasks the main thread has processed. It is updated
    982  // right after RefreshDrivers' tick.
    983  uint64_t mLastIdleTaskCount;
    984  // If there were no idle tasks, we need to check if the main event queue
    985  // was totally empty at times.
    986  uint64_t mLastRunOutOfMTTasksCount;
    987  // Note, mLastProcessedTick stores the vsync timestamp, which may be coming
    988  // from a different process.
    989  TimeStamp mLastProcessedTick;
    990  // mSuspendVsyncPriorityTicksUntil is used to block too high refresh rate in
    991  // case the main thread has also other non-idle tasks to process.
    992  // The timestamp is effectively mLastTickEnd + some duration.
    993  TimeStamp mSuspendVsyncPriorityTicksUntil;
    994  bool mProcessedVsync;
    995 
    996  TimeStamp mPendingVsync;
    997  VsyncId mPendingVsyncId;
    998  bool mHasPendingLowPrioTask;
    999 };  // VsyncRefreshDriverTimer
   1000 
   1001 /**
   1002 * Since the content process takes some time to setup
   1003 * the vsync IPC connection, this timer is used
   1004 * during the intial startup process.
   1005 * During initial startup, the refresh drivers
   1006 * are ticked off this timer, and are swapped out once content
   1007 * vsync IPC connection is established.
   1008 */
   1009 class StartupRefreshDriverTimer : public SimpleTimerBasedRefreshDriverTimer {
   1010 public:
   1011  explicit StartupRefreshDriverTimer(double aRate)
   1012      : SimpleTimerBasedRefreshDriverTimer(aRate) {}
   1013 
   1014 protected:
   1015  void ScheduleNextTick(TimeStamp aNowTime) override {
   1016    // Since this is only used for startup, it isn't super critical
   1017    // that we tick at consistent intervals.
   1018    TimeStamp newTarget = aNowTime + mRateDuration;
   1019    uint32_t delay =
   1020        static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
   1021    mTimer->InitWithNamedFuncCallback(
   1022        TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
   1023        "StartupRefreshDriverTimer::ScheduleNextTick"_ns);
   1024    mTargetTime = newTarget;
   1025  }
   1026 
   1027 public:
   1028  bool IsTicking() const override { return true; }
   1029 };
   1030 
   1031 /*
   1032 * A RefreshDriverTimer for inactive documents.  When a new refresh driver is
   1033 * added, the rate is reset to the base (normally 1s/1fps).  Every time
   1034 * it ticks, a single refresh driver is poked.  Once they have all been poked,
   1035 * the duration between ticks doubles, up to mDisableAfterMilliseconds.  At that
   1036 * point, the timer is quiet and doesn't tick (until something is added to it
   1037 * again).
   1038 *
   1039 * When a timer is removed, there is a possibility of another timer
   1040 * being skipped for one cycle.  We could avoid this by adjusting
   1041 * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
   1042 * add that complexity.  All we want is for inactive drivers to tick
   1043 * at some point, but we don't care too much about how often.
   1044 */
   1045 class InactiveRefreshDriverTimer final
   1046    : public SimpleTimerBasedRefreshDriverTimer {
   1047 public:
   1048  explicit InactiveRefreshDriverTimer(double aRate)
   1049      : SimpleTimerBasedRefreshDriverTimer(aRate),
   1050        mNextTickDuration(aRate),
   1051        mDisableAfterMilliseconds(-1.0),
   1052        mNextDriverIndex(0) {}
   1053 
   1054  InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
   1055      : SimpleTimerBasedRefreshDriverTimer(aRate),
   1056        mNextTickDuration(aRate),
   1057        mDisableAfterMilliseconds(aDisableAfterMilliseconds),
   1058        mNextDriverIndex(0) {}
   1059 
   1060  void AddRefreshDriver(nsRefreshDriver* aDriver) override {
   1061    RefreshDriverTimer::AddRefreshDriver(aDriver);
   1062 
   1063    LOG("[%p] inactive timer got new refresh driver %p, resetting rate", this,
   1064        aDriver);
   1065 
   1066    // reset the timer, and start with the newly added one next time.
   1067    mNextTickDuration = mRateMilliseconds;
   1068 
   1069    // we don't really have to start with the newly added one, but we may as
   1070    // well not tick the old ones at the fastest rate any more than we need to.
   1071    mNextDriverIndex = GetRefreshDriverCount() - 1;
   1072 
   1073    StopTimer();
   1074    StartTimer();
   1075  }
   1076 
   1077  TimeDuration GetTimerRate() override {
   1078    return TimeDuration::FromMilliseconds(mNextTickDuration);
   1079  }
   1080 
   1081 protected:
   1082  uint32_t GetRefreshDriverCount() {
   1083    return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
   1084  }
   1085 
   1086  void StartTimer() override {
   1087    mLastFireTime = TimeStamp::Now();
   1088    mLastFireId = VsyncId();
   1089 
   1090    mTargetTime = mLastFireTime + mRateDuration;
   1091 
   1092    uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
   1093    mTimer->InitWithNamedFuncCallback(
   1094        TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT,
   1095        "InactiveRefreshDriverTimer::StartTimer"_ns);
   1096    mIsTicking = true;
   1097  }
   1098 
   1099  void StopTimer() override {
   1100    mTimer->Cancel();
   1101    mIsTicking = false;
   1102  }
   1103 
   1104  void ScheduleNextTick(TimeStamp aNowTime) override {
   1105    if (mDisableAfterMilliseconds > 0.0 &&
   1106        mNextTickDuration > mDisableAfterMilliseconds) {
   1107      // We hit the time after which we should disable
   1108      // inactive window refreshes; don't schedule anything
   1109      // until we get kicked by an AddRefreshDriver call.
   1110      return;
   1111    }
   1112 
   1113    // double the next tick time if we've already gone through all of them once
   1114    if (mNextDriverIndex >= GetRefreshDriverCount()) {
   1115      mNextTickDuration *= 2.0;
   1116      mNextDriverIndex = 0;
   1117    }
   1118 
   1119    // this doesn't need to be precise; do a simple schedule
   1120    uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
   1121    mTimer->InitWithNamedFuncCallback(
   1122        TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT,
   1123        "InactiveRefreshDriverTimer::ScheduleNextTick"_ns);
   1124 
   1125    LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this,
   1126        mNextTickDuration, mNextDriverIndex, GetRefreshDriverCount());
   1127  }
   1128 
   1129 public:
   1130  bool IsTicking() const override { return mIsTicking; }
   1131 
   1132 protected:
   1133  /* Runs just one driver's tick. */
   1134  void TickOne() {
   1135    TimeStamp now = TimeStamp::Now();
   1136 
   1137    ScheduleNextTick(now);
   1138 
   1139    mLastFireTime = now;
   1140    mLastFireId = VsyncId();
   1141 
   1142    nsTArray<RefPtr<nsRefreshDriver>> drivers(mContentRefreshDrivers.Clone());
   1143    drivers.AppendElements(mRootRefreshDrivers);
   1144    size_t index = mNextDriverIndex;
   1145 
   1146    if (index < drivers.Length() &&
   1147        !drivers[index]->IsTestControllingRefreshesEnabled()) {
   1148      TickDriver(drivers[index], VsyncId(), now);
   1149    }
   1150 
   1151    mNextDriverIndex++;
   1152  }
   1153 
   1154  static void TimerTickOne(nsITimer* aTimer, void* aClosure) {
   1155    RefPtr<InactiveRefreshDriverTimer> timer =
   1156        static_cast<InactiveRefreshDriverTimer*>(aClosure);
   1157    timer->TickOne();
   1158  }
   1159 
   1160  double mNextTickDuration;
   1161  double mDisableAfterMilliseconds;
   1162  uint32_t mNextDriverIndex;
   1163  bool mIsTicking = false;
   1164 };
   1165 
   1166 }  // namespace mozilla
   1167 
   1168 static StaticRefPtr<RefreshDriverTimer> sRegularRateTimer;
   1169 static StaticAutoPtr<nsTArray<RefreshDriverTimer*>> sRegularRateTimerList;
   1170 static StaticRefPtr<InactiveRefreshDriverTimer> sThrottledRateTimer;
   1171 
   1172 static bool IsPresentingInVR() {
   1173 #ifdef MOZ_WIDGET_ANDROID
   1174  return gfx::VRManagerChild::IsPresenting();
   1175 #else
   1176  return false;
   1177 #endif
   1178 }
   1179 
   1180 void nsRefreshDriver::CreateVsyncRefreshTimer() {
   1181  MOZ_ASSERT(NS_IsMainThread());
   1182 
   1183  if (gfxPlatform::IsInLayoutAsapMode()) {
   1184    return;
   1185  }
   1186 
   1187  if (!mOwnTimer) {
   1188    // If available, we fetch the widget-specific vsync source.
   1189    nsPresContext* pc = GetPresContext();
   1190    nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
   1191    if (widget) {
   1192      if (RefPtr<VsyncDispatcher> vsyncDispatcher =
   1193              widget->GetVsyncDispatcher()) {
   1194        mOwnTimer = VsyncRefreshDriverTimer::
   1195            CreateForParentProcessWithLocalVsyncDispatcher(
   1196                std::move(vsyncDispatcher));
   1197        sRegularRateTimerList->AppendElement(mOwnTimer.get());
   1198        return;
   1199      }
   1200      if (BrowserChild* browserChild = widget->GetOwningBrowserChild()) {
   1201        if (RefPtr<VsyncMainChild> vsyncChildViaPBrowser =
   1202                browserChild->GetVsyncChild()) {
   1203          mOwnTimer = VsyncRefreshDriverTimer::CreateForContentProcess(
   1204              std::move(vsyncChildViaPBrowser));
   1205          sRegularRateTimerList->AppendElement(mOwnTimer.get());
   1206          return;
   1207        }
   1208      }
   1209    }
   1210  }
   1211  if (!sRegularRateTimer) {
   1212    if (XRE_IsParentProcess()) {
   1213      // Make sure all vsync systems are ready.
   1214      gfxPlatform::GetPlatform();
   1215      // In parent process, we can create the VsyncRefreshDriverTimer directly.
   1216      sRegularRateTimer =
   1217          VsyncRefreshDriverTimer::CreateForParentProcessWithGlobalVsync();
   1218    } else {
   1219      PBackgroundChild* actorChild =
   1220          BackgroundChild::GetOrCreateForCurrentThread();
   1221      if (NS_WARN_IF(!actorChild)) {
   1222        return;
   1223      }
   1224 
   1225      auto vsyncChildViaPBackground = MakeRefPtr<dom::VsyncMainChild>();
   1226      dom::PVsyncChild* actor =
   1227          actorChild->SendPVsyncConstructor(vsyncChildViaPBackground);
   1228      if (NS_WARN_IF(!actor)) {
   1229        return;
   1230      }
   1231 
   1232      RefPtr<RefreshDriverTimer> vsyncRefreshDriverTimer =
   1233          VsyncRefreshDriverTimer::CreateForContentProcess(
   1234              std::move(vsyncChildViaPBackground));
   1235 
   1236      sRegularRateTimer = std::move(vsyncRefreshDriverTimer);
   1237    }
   1238  }
   1239 }
   1240 
   1241 static uint32_t GetFirstFrameDelay(imgIRequest* req) {
   1242  nsCOMPtr<imgIContainer> container;
   1243  if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
   1244    return 0;
   1245  }
   1246 
   1247  // If this image isn't animated, there isn't a first frame delay.
   1248  int32_t delay = container->GetFirstFrameDelay();
   1249  if (delay < 0) {
   1250    return 0;
   1251  }
   1252 
   1253  return static_cast<uint32_t>(delay);
   1254 }
   1255 
   1256 static constexpr nsLiteralCString sRenderingPhaseNames[] = {
   1257    "Flush autofocus candidates"_ns,                 // FlushAutoFocusCandidates
   1258    "Resize steps"_ns,                               // ResizeSteps
   1259    "Scroll steps"_ns,                               // ScrollSteps
   1260    "Evaluate media queries and report changes"_ns,  // EvaluateMediaQueriesAndReportChanges
   1261    "Update animations and send events"_ns,    // UpdateAnimationsAndSendEvents
   1262    "Fullscreen steps"_ns,                     // FullscreenSteps
   1263    "Animation and video frame callbacks"_ns,  // AnimationFrameCallbacks
   1264    "Layout, content-visibility and resize observers"_ns,  // Layout
   1265    "View transition operations"_ns,        // ViewTransitionOperations
   1266    "Update intersection observations"_ns,  // UpdateIntersectionObservations
   1267    "Paint"_ns,                             // Paint
   1268 };
   1269 
   1270 static_assert(std::size(sRenderingPhaseNames) == size_t(RenderingPhase::Count),
   1271              "Unexpected rendering phase?");
   1272 
   1273 template <typename Callback>
   1274 void nsRefreshDriver::RunRenderingPhaseLegacy(RenderingPhase aPhase,
   1275                                              Callback&& aCallback) {
   1276  if (!mRenderingPhasesNeeded.contains(aPhase)) {
   1277    return;
   1278  }
   1279  mRenderingPhasesNeeded -= aPhase;
   1280 
   1281  AUTO_PROFILER_LABEL_DYNAMIC_CSTR_RELEVANT_FOR_JS(
   1282      "Update the rendering", LAYOUT,
   1283      sRenderingPhaseNames[size_t(aPhase)].get());
   1284  aCallback();
   1285 }
   1286 
   1287 template <typename Callback>
   1288 void nsRefreshDriver::RunRenderingPhase(RenderingPhase aPhase,
   1289                                        Callback&& aCallback,
   1290                                        DocFilter aExtraFilter) {
   1291  RunRenderingPhaseLegacy(aPhase, [&] {
   1292    if (MOZ_UNLIKELY(!mPresContext)) {
   1293      return;
   1294    }
   1295    // https://html.spec.whatwg.org/#update-the-rendering step 3
   1296    //
   1297    //     Remove from docs any Document object doc for which any of the
   1298    //     following are true.
   1299    //
   1300    // TODO(emilio): Per spec we should collect all these upfront, once.
   1301    AutoTArray<RefPtr<Document>, 32> documents;
   1302    auto ShouldCollect = [aExtraFilter](const Document* aDocument) {
   1303      return !aDocument->IsRenderingSuppressed() &&
   1304             (!aExtraFilter || aExtraFilter(*aDocument));
   1305    };
   1306    if (ShouldCollect(mPresContext->Document())) {
   1307      documents.AppendElement(mPresContext->Document());
   1308    }
   1309    mPresContext->Document()->CollectDescendantDocuments(
   1310        documents, Document::IncludeSubResources::Yes, ShouldCollect);
   1311    for (auto& doc : documents) {
   1312      aCallback(*doc);
   1313    }
   1314  });
   1315 }
   1316 
   1317 /* static */
   1318 void nsRefreshDriver::Shutdown() {
   1319  MOZ_ASSERT(NS_IsMainThread());
   1320  // clean up our timers
   1321  sRegularRateTimer = nullptr;
   1322  sRegularRateTimerList = nullptr;
   1323  sThrottledRateTimer = nullptr;
   1324 }
   1325 
   1326 /* static */
   1327 int32_t nsRefreshDriver::DefaultInterval() {
   1328  return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
   1329 }
   1330 
   1331 /* static */
   1332 double nsRefreshDriver::HighRateMultiplier() {
   1333  // We're in high rate mode if we've gotten a fast rate during the last
   1334  // DefaultInterval().
   1335  bool inHighRateMode =
   1336      !gfxPlatform::IsInLayoutAsapMode() &&
   1337      StaticPrefs::layout_expose_high_rate_mode_from_refreshdriver() &&
   1338      !sMostRecentHighRateVsync.IsNull() &&
   1339      (sMostRecentHighRateVsync +
   1340       TimeDuration::FromMilliseconds(DefaultInterval())) > TimeStamp::Now();
   1341  if (!inHighRateMode) {
   1342    // Clear the timestamp so that the next call is faster.
   1343    sMostRecentHighRateVsync = TimeStamp();
   1344    sMostRecentHighRate = TimeDuration();
   1345    return 1.0;
   1346  }
   1347 
   1348  return sMostRecentHighRate.ToMilliseconds() / DefaultInterval();
   1349 }
   1350 
   1351 // Compute the interval to use for the refresh driver timer, in milliseconds.
   1352 // outIsDefault indicates that rate was not explicitly set by the user
   1353 // so we might choose other, more appropriate rates (e.g. vsync, etc)
   1354 // layout.frame_rate=0 indicates "ASAP mode".
   1355 // In ASAP mode rendering is iterated as fast as possible (typically for stress
   1356 // testing). A target rate of 10k is used internally instead of special-handling
   1357 // 0. Backends which block on swap/present/etc should try to not block when
   1358 // layout.frame_rate=0 - to comply with "ASAP" as much as possible.
   1359 double nsRefreshDriver::GetRegularTimerInterval() const {
   1360  int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
   1361  if (rate < 0) {
   1362    rate = gfxPlatform::GetDefaultFrameRate();
   1363  } else if (rate == 0) {
   1364    rate = 10000;
   1365  }
   1366 
   1367  return 1000.0 / rate;
   1368 }
   1369 
   1370 /* static */
   1371 double nsRefreshDriver::GetThrottledTimerInterval() {
   1372  uint32_t rate = StaticPrefs::layout_throttled_frame_rate();
   1373  return 1000.0 / rate;
   1374 }
   1375 
   1376 /* static */
   1377 TimeDuration nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
   1378  return TimeDuration::FromMilliseconds(
   1379      StaticPrefs::layout_visibility_min_recompute_interval_ms());
   1380 }
   1381 
   1382 RefreshDriverTimer* nsRefreshDriver::ChooseTimer() {
   1383  if (mThrottled) {
   1384    if (!sThrottledRateTimer) {
   1385      sThrottledRateTimer = new InactiveRefreshDriverTimer(
   1386          GetThrottledTimerInterval(),
   1387          DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
   1388    }
   1389    return sThrottledRateTimer;
   1390  }
   1391 
   1392  if (!mOwnTimer) {
   1393    CreateVsyncRefreshTimer();
   1394  }
   1395 
   1396  if (mOwnTimer) {
   1397    return mOwnTimer.get();
   1398  }
   1399 
   1400  if (!sRegularRateTimer) {
   1401    double rate = GetRegularTimerInterval();
   1402    sRegularRateTimer = new StartupRefreshDriverTimer(rate);
   1403  }
   1404 
   1405  return sRegularRateTimer;
   1406 }
   1407 
   1408 static nsDocShell* GetDocShell(nsPresContext* aPresContext) {
   1409  if (!aPresContext) {
   1410    return nullptr;
   1411  }
   1412  return static_cast<nsDocShell*>(aPresContext->GetDocShell());
   1413 }
   1414 
   1415 nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
   1416    : mActiveTimer(nullptr),
   1417      mOwnTimer(nullptr),
   1418      mPresContext(aPresContext),
   1419      mRootRefresh(nullptr),
   1420      mNextTransactionId{0},
   1421      mFreezeCount(0),
   1422      mThrottledFrameRequestInterval(
   1423          TimeDuration::FromMilliseconds(GetThrottledTimerInterval())),
   1424      mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
   1425      mThrottled(false),
   1426      mNeedToRecomputeVisibility(false),
   1427      mTestControllingRefreshes(false),
   1428      mInRefresh(false),
   1429      mWaitingForTransaction(false),
   1430      mSkippedPaints(false),
   1431      mResizeSuppressed(false),
   1432      mInNormalTick(false),
   1433      mAttemptedExtraTickSinceLastVsync(false),
   1434      mHasExceededAfterLoadTickPeriod(false),
   1435      mHasImageAnimations(false),
   1436      mHasStartedTimerAtLeastOnce(false) {
   1437  MOZ_ASSERT(NS_IsMainThread());
   1438  MOZ_ASSERT(mPresContext,
   1439             "Need a pres context to tell us to call Disconnect() later "
   1440             "and decrement sRefreshDriverCount.");
   1441  mMostRecentRefresh = TimeStamp::Now();
   1442  mNextThrottledFrameRequestTick = mMostRecentRefresh;
   1443  mNextRecomputeVisibilityTick = mMostRecentRefresh;
   1444 
   1445  if (!sRegularRateTimerList) {
   1446    sRegularRateTimerList = new nsTArray<RefreshDriverTimer*>();
   1447  }
   1448  ++sRefreshDriverCount;
   1449 }
   1450 
   1451 nsRefreshDriver::~nsRefreshDriver() {
   1452  MOZ_ASSERT(NS_IsMainThread());
   1453  MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
   1454             "observers, except pending selection scrolls, "
   1455             "should have been unregistered");
   1456  MOZ_ASSERT(!mActiveTimer, "timer should be gone");
   1457  MOZ_ASSERT(!mPresContext,
   1458             "Should have called Disconnect() and decremented "
   1459             "sRefreshDriverCount!");
   1460 
   1461  if (mRootRefresh) {
   1462    mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
   1463    mRootRefresh = nullptr;
   1464  }
   1465  if (mOwnTimer && sRegularRateTimerList) {
   1466    sRegularRateTimerList->RemoveElement(mOwnTimer.get());
   1467  }
   1468 }
   1469 
   1470 // Method for testing.  See nsIDOMWindowUtils.advanceTimeAndRefresh
   1471 // for description.
   1472 void nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds) {
   1473  // ensure that we're removed from our driver
   1474  StopTimer();
   1475 
   1476  if (!mTestControllingRefreshes) {
   1477    mMostRecentRefresh = TimeStamp::Now();
   1478 
   1479    mTestControllingRefreshes = true;
   1480    if (mWaitingForTransaction) {
   1481      // Disable any refresh driver throttling when entering test mode
   1482      mWaitingForTransaction = false;
   1483      mSkippedPaints = false;
   1484    }
   1485  }
   1486 
   1487  mMostRecentRefresh += TimeDuration::FromMilliseconds((double)aMilliseconds);
   1488 
   1489  mozilla::dom::AutoNoJSAPI nojsapi;
   1490  DoTick();
   1491 }
   1492 
   1493 void nsRefreshDriver::RestoreNormalRefresh() {
   1494  mTestControllingRefreshes = false;
   1495  EnsureTimerStarted(eAllowTimeToGoBackwards);
   1496  mPendingTransactions.Clear();
   1497 }
   1498 
   1499 void nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
   1500                                         FlushType aFlushType,
   1501                                         const char* aObserverDescription) {
   1502  ObserverArray& array = ArrayFor(aFlushType);
   1503  MOZ_ASSERT(!array.Contains(aObserver),
   1504             "We don't want to redundantly register the same observer");
   1505  array.AppendElement(
   1506      ObserverData{aObserver, aObserverDescription, TimeStamp::Now(),
   1507                   MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)),
   1508                   profiler_capture_backtrace(), aFlushType});
   1509 #ifdef DEBUG
   1510  MOZ_ASSERT(aObserver->mRegistrationCount >= 0,
   1511             "Registration count shouldn't be able to go negative");
   1512  aObserver->mRegistrationCount++;
   1513 #endif
   1514  EnsureTimerStarted();
   1515 }
   1516 
   1517 bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
   1518                                            FlushType aFlushType) {
   1519  ObserverArray& array = ArrayFor(aFlushType);
   1520  auto index = array.IndexOf(aObserver);
   1521  if (index == ObserverArray::array_type::NoIndex) {
   1522    return false;
   1523  }
   1524 
   1525  if (profiler_thread_is_being_profiled_for_markers()) {
   1526    auto& data = array.ElementAt(index);
   1527    nsPrintfCString str("%s [%s]", data.mDescription,
   1528                        kFlushTypeNames[aFlushType]);
   1529    PROFILER_MARKER_TEXT(
   1530        "RefreshObserver", GRAPHICS,
   1531        MarkerOptions(MarkerStack::TakeBacktrace(std::move(data.mCause)),
   1532                      MarkerTiming::IntervalUntilNowFrom(data.mRegisterTime),
   1533                      std::move(data.mInnerWindowId)),
   1534        str);
   1535  }
   1536 
   1537  array.RemoveElementAt(index);
   1538 #ifdef DEBUG
   1539  aObserver->mRegistrationCount--;
   1540  MOZ_ASSERT(aObserver->mRegistrationCount >= 0,
   1541             "Registration count shouldn't be able to go negative");
   1542 #endif
   1543  return true;
   1544 }
   1545 
   1546 void nsRefreshDriver::AddPostRefreshObserver(
   1547    nsAPostRefreshObserver* aObserver) {
   1548  MOZ_ASSERT(!mPostRefreshObservers.Contains(aObserver));
   1549  mPostRefreshObservers.AppendElement(aObserver);
   1550 }
   1551 
   1552 void nsRefreshDriver::RemovePostRefreshObserver(
   1553    nsAPostRefreshObserver* aObserver) {
   1554  bool removed = mPostRefreshObservers.RemoveElement(aObserver);
   1555  MOZ_DIAGNOSTIC_ASSERT(removed);
   1556  (void)removed;
   1557 }
   1558 
   1559 void nsRefreshDriver::StartTimerForAnimatedImagesIfNeeded() {
   1560  if (mHasImageAnimations) {
   1561    return;
   1562  }
   1563  mHasImageAnimations = ComputeHasImageAnimations();
   1564  if (!mHasImageAnimations || mThrottled) {
   1565    return;
   1566  }
   1567  EnsureTimerStarted();
   1568 }
   1569 
   1570 void nsRefreshDriver::StopTimerForAnimatedImagesIfNeeded() {
   1571  if (!mHasImageAnimations) {
   1572    return;
   1573  }
   1574  mHasImageAnimations = ComputeHasImageAnimations();
   1575 }
   1576 
   1577 void nsRefreshDriver::AddImageRequest(imgIRequest* aRequest) {
   1578  uint32_t delay = GetFirstFrameDelay(aRequest);
   1579  if (delay == 0) {
   1580    mRequests.Insert(aRequest);
   1581  } else {
   1582    auto* const start = mStartTable.GetOrInsertNew(delay);
   1583    start->mEntries.Insert(aRequest);
   1584  }
   1585 
   1586  StartTimerForAnimatedImagesIfNeeded();
   1587  if (profiler_thread_is_being_profiled_for_markers()) {
   1588    nsCOMPtr<nsIURI> uri = aRequest->GetURI();
   1589 
   1590    PROFILER_MARKER_TEXT("Image Animation", GRAPHICS,
   1591                         MarkerOptions(MarkerTiming::IntervalStart(),
   1592                                       MarkerInnerWindowIdFromDocShell(
   1593                                           GetDocShell(mPresContext))),
   1594                         nsContentUtils::TruncatedURLForDisplay(uri));
   1595  }
   1596 }
   1597 
   1598 void nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest) {
   1599  // Try to remove from both places, just in case.
   1600  bool removed = mRequests.EnsureRemoved(aRequest);
   1601  uint32_t delay = GetFirstFrameDelay(aRequest);
   1602  if (delay != 0) {
   1603    ImageStartData* start = mStartTable.Get(delay);
   1604    if (start) {
   1605      removed |= start->mEntries.EnsureRemoved(aRequest);
   1606    }
   1607  }
   1608 
   1609  if (!removed) {
   1610    return;
   1611  }
   1612 
   1613  StopTimerForAnimatedImagesIfNeeded();
   1614  if (profiler_thread_is_being_profiled_for_markers()) {
   1615    nsCOMPtr<nsIURI> uri = aRequest->GetURI();
   1616    PROFILER_MARKER_TEXT("Image Animation", GRAPHICS,
   1617                         MarkerOptions(MarkerTiming::IntervalEnd(),
   1618                                       MarkerInnerWindowIdFromDocShell(
   1619                                           GetDocShell(mPresContext))),
   1620                         nsContentUtils::TruncatedURLForDisplay(uri));
   1621  }
   1622 }
   1623 
   1624 void nsRefreshDriver::RegisterCompositionPayload(
   1625    const mozilla::layers::CompositionPayload& aPayload) {
   1626  mCompositionPayloads.AppendElement(aPayload);
   1627 }
   1628 
   1629 void nsRefreshDriver::AddForceNotifyContentfulPaintPresContext(
   1630    nsPresContext* aPresContext) {
   1631  mForceNotifyContentfulPaintPresContexts.AppendElement(aPresContext);
   1632 }
   1633 
   1634 void nsRefreshDriver::FlushForceNotifyContentfulPaintPresContext() {
   1635  while (!mForceNotifyContentfulPaintPresContexts.IsEmpty()) {
   1636    WeakPtr<nsPresContext> presContext =
   1637        mForceNotifyContentfulPaintPresContexts.PopLastElement();
   1638    if (presContext) {
   1639      presContext->NotifyContentfulPaint();
   1640    }
   1641  }
   1642 }
   1643 
   1644 bool nsRefreshDriver::CanDoCatchUpTick() {
   1645  if (mTestControllingRefreshes || !mActiveTimer) {
   1646    return false;
   1647  }
   1648 
   1649  // If we've already ticked for the current timer refresh (or more recently
   1650  // than that), then we don't need to do any catching up.
   1651  if (mMostRecentRefresh >= mActiveTimer->MostRecentRefresh()) {
   1652    return false;
   1653  }
   1654 
   1655  if (mActiveTimer->IsBlocked()) {
   1656    return false;
   1657  }
   1658 
   1659  if (mTickVsyncTime.IsNull()) {
   1660    // Don't try to run a catch-up tick before there has been at least one
   1661    // normal tick. The catch-up tick could negatively affect page load
   1662    // performance.
   1663    return false;
   1664  }
   1665 
   1666  if (mPresContext && mPresContext->Document()->GetReadyStateEnum() <
   1667                          Document::READYSTATE_COMPLETE) {
   1668    // Don't try to run a catch-up tick before the page has finished loading.
   1669    // The catch-up tick could negatively affect page load performance.
   1670    return false;
   1671  }
   1672 
   1673  return true;
   1674 }
   1675 
   1676 bool nsRefreshDriver::CanDoExtraTick() {
   1677  // Only allow one extra tick per normal vsync tick.
   1678  if (mAttemptedExtraTickSinceLastVsync) {
   1679    return false;
   1680  }
   1681 
   1682  // If we don't have a timer, or we didn't tick on the timer's
   1683  // refresh then we can't do an 'extra' tick (but we may still
   1684  // do a catch up tick).
   1685  if (!mActiveTimer ||
   1686      mActiveTimer->MostRecentRefresh() != mMostRecentRefresh) {
   1687    return false;
   1688  }
   1689 
   1690  // Grab the current timestamp before checking the tick hint to be sure
   1691  // sure that it's equal or smaller than the value used within checking
   1692  // the tick hint.
   1693  TimeStamp now = TimeStamp::Now();
   1694  Maybe<TimeStamp> nextTick = mActiveTimer->GetNextTickHint();
   1695  int32_t minimumRequiredTime = StaticPrefs::layout_extra_tick_minimum_ms();
   1696  // If there's less than 4 milliseconds until the next tick, it's probably
   1697  // not worth trying to catch up.
   1698  if (minimumRequiredTime < 0 || !nextTick ||
   1699      (*nextTick - now) < TimeDuration::FromMilliseconds(minimumRequiredTime)) {
   1700    return false;
   1701  }
   1702 
   1703  return true;
   1704 }
   1705 
   1706 void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
   1707  // FIXME: Bug 1346065: We should also assert the case where we have no
   1708  // stylo-threads.
   1709  MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
   1710             "EnsureTimerStarted should be called only when we are not "
   1711             "in servo traversal or on the main-thread");
   1712 
   1713  if (mTestControllingRefreshes) {
   1714    return;
   1715  }
   1716 
   1717  if (!mRefreshTimerStartedCause) {
   1718    mRefreshTimerStartedCause = profiler_capture_backtrace();
   1719  }
   1720 
   1721  // will it already fire, and no other changes needed?
   1722  if (mActiveTimer && !(aFlags & eForceAdjustTimer)) {
   1723    // If we're being called from within a user input handler, and we think
   1724    // there's time to rush an extra tick immediately, then schedule a runnable
   1725    // to run the extra tick.
   1726    if (mUserInputProcessingCount && CanDoExtraTick()) {
   1727      RefPtr<nsRefreshDriver> self = this;
   1728      NS_DispatchToCurrentThreadQueue(
   1729          NS_NewRunnableFunction(
   1730              "RefreshDriver::EnsureTimerStarted::extra",
   1731              [self]() -> void {
   1732                // Re-check if we can still do an extra tick, in case anything
   1733                // changed while the runnable was pending.
   1734                if (self->CanDoExtraTick()) {
   1735                  PROFILER_MARKER_UNTYPED("ExtraRefreshDriverTick", GRAPHICS);
   1736                  LOG("[%p] Doing extra tick for user input", self.get());
   1737                  self->mAttemptedExtraTickSinceLastVsync = true;
   1738                  self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
   1739                             self->mActiveTimer->MostRecentRefresh(),
   1740                             IsExtraTick::Yes);
   1741                }
   1742              }),
   1743          EventQueuePriority::Vsync);
   1744    }
   1745    return;
   1746  }
   1747 
   1748  if (IsFrozen() || !mPresContext) {
   1749    // If we don't want to start it now, or we've been disconnected.
   1750    StopTimer();
   1751    return;
   1752  }
   1753 
   1754  if (mPresContext->Document()->IsBeingUsedAsImage()) {
   1755    // Image documents receive ticks from clients' refresh drivers.
   1756    // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
   1757    // they receive refresh-driver ticks from their client docs (bug 1107252).
   1758    if (!mPresContext->Document()->IsSVGGlyphsDocument()) {
   1759      MOZ_ASSERT(!mActiveTimer,
   1760                 "image doc refresh driver should never have its own timer");
   1761      return;
   1762    }
   1763  }
   1764 
   1765  // We got here because we're either adjusting the time *or* we're
   1766  // starting it for the first time.  Add to the right timer,
   1767  // prehaps removing it from a previously-set one.
   1768  RefreshDriverTimer* newTimer = ChooseTimer();
   1769  if (newTimer != mActiveTimer) {
   1770    if (mActiveTimer) {
   1771      mActiveTimer->RemoveRefreshDriver(this);
   1772    }
   1773    mActiveTimer = newTimer;
   1774    mActiveTimer->AddRefreshDriver(this);
   1775 
   1776    if (!mHasStartedTimerAtLeastOnce) {
   1777      mHasStartedTimerAtLeastOnce = true;
   1778      if (profiler_thread_is_being_profiled_for_markers()) {
   1779        nsCString text = "initial timer start "_ns;
   1780        if (mPresContext->Document()->GetDocumentURI()) {
   1781          text.Append(nsContentUtils::TruncatedURLForDisplay(
   1782              mPresContext->Document()->GetDocumentURI()));
   1783        }
   1784 
   1785        PROFILER_MARKER_TEXT("nsRefreshDriver", LAYOUT,
   1786                             MarkerOptions(MarkerInnerWindowIdFromDocShell(
   1787                                 GetDocShell(mPresContext))),
   1788                             text);
   1789      }
   1790    }
   1791 
   1792    // If the timer has ticked since we last ticked, consider doing a 'catch-up'
   1793    // tick immediately.
   1794    if (CanDoCatchUpTick()) {
   1795      RefPtr<nsRefreshDriver> self = this;
   1796      NS_DispatchToCurrentThreadQueue(
   1797          NS_NewRunnableFunction(
   1798              "RefreshDriver::EnsureTimerStarted::catch-up",
   1799              [self]() -> void {
   1800                // Re-check if we can still do a catch-up, in case anything
   1801                // changed while the runnable was pending.
   1802                if (self->CanDoCatchUpTick()) {
   1803                  LOG("[%p] Doing catch up tick", self.get());
   1804                  self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
   1805                             self->mActiveTimer->MostRecentRefresh());
   1806                }
   1807              }),
   1808          EventQueuePriority::Vsync);
   1809    }
   1810  }
   1811 
   1812  // Since the different timers are sampled at different rates, when switching
   1813  // timers, the most recent refresh of the new timer may be *before* the
   1814  // most recent refresh of the old timer.
   1815  // If we are restoring the refresh driver from test control, the time is
   1816  // expected to go backwards (see bug 1043078), otherwise we just keep the most
   1817  // recent tick of this driver (which may be older than the most recent tick of
   1818  // the timer).
   1819  if (!(aFlags & eAllowTimeToGoBackwards)) {
   1820    return;
   1821  }
   1822 
   1823  if (mMostRecentRefresh != mActiveTimer->MostRecentRefresh()) {
   1824    mMostRecentRefresh = mActiveTimer->MostRecentRefresh();
   1825  }
   1826 }
   1827 
   1828 void nsRefreshDriver::StopTimer() {
   1829  if (!mActiveTimer) {
   1830    return;
   1831  }
   1832 
   1833  mActiveTimer->RemoveRefreshDriver(this);
   1834  mActiveTimer = nullptr;
   1835  mRefreshTimerStartedCause = nullptr;
   1836 }
   1837 
   1838 uint32_t nsRefreshDriver::ObserverCount() const {
   1839  uint32_t sum = 0;
   1840  for (const ObserverArray& array : mObservers) {
   1841    sum += array.Length();
   1842  }
   1843  sum += mEarlyRunners.Length();
   1844  return sum;
   1845 }
   1846 
   1847 bool nsRefreshDriver::HasObservers() const {
   1848  for (const ObserverArray& array : mObservers) {
   1849    if (!array.IsEmpty()) {
   1850      return true;
   1851    }
   1852  }
   1853 
   1854  return !mEarlyRunners.IsEmpty();
   1855 }
   1856 
   1857 void nsRefreshDriver::AppendObserverDescriptionsToString(
   1858    nsACString& aStr) const {
   1859  for (const ObserverArray& array : mObservers) {
   1860    for (const auto& observer : array.EndLimitedRange()) {
   1861      aStr.AppendPrintf("%s [%s], ", observer.mDescription,
   1862                        kFlushTypeNames[observer.mFlushType]);
   1863    }
   1864  }
   1865  if (!mEarlyRunners.IsEmpty()) {
   1866    aStr.AppendPrintf("%zux Early runner, ", mEarlyRunners.Length());
   1867  }
   1868  // Remove last ", "
   1869  aStr.Truncate(aStr.Length() - 2);
   1870 }
   1871 
   1872 bool nsRefreshDriver::ComputeHasImageAnimations() const {
   1873  for (const auto& data : mStartTable.Values()) {
   1874    if (!data->mEntries.IsEmpty()) {
   1875      return true;
   1876    }
   1877  }
   1878 
   1879  for (const auto& entry : mRequests) {
   1880    if (entry->GetHasAnimationConsumers()) {
   1881      return true;
   1882    }
   1883  }
   1884 
   1885  return false;
   1886 }
   1887 
   1888 auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons {
   1889  TickReasons reasons = TickReasons::None;
   1890  if (HasObservers()) {
   1891    reasons |= TickReasons::HasObservers;
   1892  }
   1893  if (mHasImageAnimations && !mThrottled) {
   1894    reasons |= TickReasons::HasImageAnimations;
   1895  }
   1896  if (!mRenderingPhasesNeeded.isEmpty()) {
   1897    reasons |= TickReasons::HasPendingRenderingSteps;
   1898  }
   1899  if (mPresContext && mPresContext->IsRoot() &&
   1900      mPresContext->NeedsMoreTicksForUserInput()) {
   1901    reasons |= TickReasons::RootNeedsMoreTicksForUserInput;
   1902  }
   1903  return reasons;
   1904 }
   1905 
   1906 void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons,
   1907                                                nsACString& aStr) const {
   1908  if (aReasons == TickReasons::None) {
   1909    aStr.AppendLiteral(" <none>");
   1910    return;
   1911  }
   1912 
   1913  if (aReasons & TickReasons::HasObservers) {
   1914    aStr.AppendLiteral(" HasObservers (");
   1915    AppendObserverDescriptionsToString(aStr);
   1916    aStr.AppendLiteral(")");
   1917  }
   1918  if (aReasons & TickReasons::HasImageAnimations) {
   1919    aStr.AppendLiteral(" HasImageAnimations");
   1920  }
   1921  if (aReasons & TickReasons::HasPendingRenderingSteps) {
   1922    aStr.AppendLiteral(" HasPendingRenderingSteps(");
   1923    bool first = true;
   1924    for (auto phase : mRenderingPhasesNeeded) {
   1925      if (!first) {
   1926        aStr.AppendLiteral(", ");
   1927      }
   1928      first = false;
   1929      aStr.Append(sRenderingPhaseNames[size_t(phase)]);
   1930    }
   1931    aStr.AppendLiteral(")");
   1932  }
   1933  if (aReasons & TickReasons::RootNeedsMoreTicksForUserInput) {
   1934    aStr.AppendLiteral(" RootNeedsMoreTicksForUserInput");
   1935  }
   1936 }
   1937 
   1938 bool nsRefreshDriver::
   1939    ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint() {
   1940  // On top level content pages keep the timer running initially so that we
   1941  // paint the page soon enough.
   1942  if (mThrottled || mTestControllingRefreshes || !XRE_IsContentProcess() ||
   1943      !mPresContext->Document()->IsTopLevelContentDocument() ||
   1944      mPresContext->Document()->IsInitialDocument() ||
   1945      gfxPlatform::IsInLayoutAsapMode() ||
   1946      mPresContext->HadFirstContentfulPaint() ||
   1947      mPresContext->Document()->GetReadyStateEnum() ==
   1948          Document::READYSTATE_COMPLETE) {
   1949    return false;
   1950  }
   1951  if (mBeforeFirstContentfulPaintTimerRunningLimit.IsNull()) {
   1952    // Don't let the timer to run forever, so limit to 4s for now.
   1953    mBeforeFirstContentfulPaintTimerRunningLimit =
   1954        TimeStamp::Now() + TimeDuration::FromSeconds(4.0f);
   1955  }
   1956 
   1957  return TimeStamp::Now() <= mBeforeFirstContentfulPaintTimerRunningLimit;
   1958 }
   1959 
   1960 bool nsRefreshDriver::ShouldKeepTimerRunningAfterPageLoad() {
   1961  if (mHasExceededAfterLoadTickPeriod ||
   1962      !StaticPrefs::layout_keep_ticking_after_load_ms() || mThrottled ||
   1963      mTestControllingRefreshes || !XRE_IsContentProcess() ||
   1964      !mPresContext->Document()->IsTopLevelContentDocument() ||
   1965      TaskController::Get()->PendingMainthreadTaskCountIncludingSuspended() ==
   1966          0 ||
   1967      gfxPlatform::IsInLayoutAsapMode()) {
   1968    // Make the next check faster.
   1969    mHasExceededAfterLoadTickPeriod = true;
   1970    return false;
   1971  }
   1972 
   1973  nsPIDOMWindowInner* innerWindow = mPresContext->Document()->GetInnerWindow();
   1974  if (!innerWindow) {
   1975    return false;
   1976  }
   1977  auto* perf =
   1978      static_cast<PerformanceMainThread*>(innerWindow->GetPerformance());
   1979  if (!perf) {
   1980    return false;
   1981  }
   1982  nsDOMNavigationTiming* timing = perf->GetDOMTiming();
   1983  if (!timing) {
   1984    return false;
   1985  }
   1986  TimeStamp loadend = timing->LoadEventEnd();
   1987  if (!loadend) {
   1988    return false;
   1989  }
   1990  // Keep ticking after the page load for some time.
   1991  const bool retval =
   1992      (loadend + TimeDuration::FromMilliseconds(
   1993                     StaticPrefs::layout_keep_ticking_after_load_ms())) >
   1994      TimeStamp::Now();
   1995  if (!retval) {
   1996    mHasExceededAfterLoadTickPeriod = true;
   1997  }
   1998  return retval;
   1999 }
   2000 
   2001 nsRefreshDriver::ObserverArray& nsRefreshDriver::ArrayFor(
   2002    FlushType aFlushType) {
   2003  switch (aFlushType) {
   2004    case FlushType::Event:
   2005      return mObservers[0];
   2006    case FlushType::Style:
   2007      return mObservers[1];
   2008    case FlushType::Display:
   2009      return mObservers[2];
   2010    default:
   2011      MOZ_CRASH("We don't track refresh observers for this flush type");
   2012  }
   2013 }
   2014 
   2015 /*
   2016 * nsITimerCallback implementation
   2017 */
   2018 
   2019 void nsRefreshDriver::DoTick() {
   2020  MOZ_ASSERT(!IsFrozen(), "Why are we notified while frozen?");
   2021  MOZ_ASSERT(mPresContext, "Why are we notified after disconnection?");
   2022  MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
   2023             "Shouldn't have a JSContext on the stack");
   2024 
   2025  if (mTestControllingRefreshes) {
   2026    Tick(VsyncId(), mMostRecentRefresh);
   2027  } else {
   2028    Tick(VsyncId(), TimeStamp::Now());
   2029  }
   2030 }
   2031 
   2032 void nsRefreshDriver::MaybeIncreaseMeasuredTicksSinceLoading() {
   2033  if (mPresContext && mPresContext->IsRoot()) {
   2034    mPresContext->MaybeIncreaseMeasuredTicksSinceLoading();
   2035  }
   2036 }
   2037 
   2038 void nsRefreshDriver::UpdateRemoteFrameEffects() {
   2039  mPresContext->Document()->UpdateRemoteFrameEffects();
   2040 }
   2041 
   2042 static void UpdateAndReduceAnimations(Document& aDocument) {
   2043  aDocument.TimelinesController().WillRefresh();
   2044 
   2045  if (nsPresContext* pc = aDocument.GetPresContext()) {
   2046    if (pc->EffectCompositor()->NeedsReducing()) {
   2047      pc->EffectCompositor()->ReduceAnimations();
   2048    }
   2049  }
   2050 }
   2051 
   2052 void nsRefreshDriver::RunVideoFrameCallbacks(
   2053    const nsTArray<RefPtr<Document>>& aDocs, TimeStamp aNowTime) {
   2054  // For each fully active Document in docs, for each associated video element
   2055  // for that Document, run the video frame request callbacks passing now as the
   2056  // timestamp.
   2057  Maybe<TimeStamp> nextTickHint;
   2058  for (Document* doc : aDocs) {
   2059    nsTArray<RefPtr<HTMLVideoElement>> videoElms;
   2060    doc->TakeVideoFrameRequestCallbacks(videoElms);
   2061    if (videoElms.IsEmpty()) {
   2062      continue;
   2063    }
   2064 
   2065    DOMHighResTimeStamp timeStamp = 0;
   2066    DOMHighResTimeStamp nextTickTimeStamp = 0;
   2067    if (auto* innerWindow = doc->GetInnerWindow()) {
   2068      if (Performance* perf = innerWindow->GetPerformance()) {
   2069        if (!nextTickHint) {
   2070          nextTickHint = GetNextTickHint();
   2071        }
   2072        timeStamp = perf->TimeStampToDOMHighResForRendering(aNowTime);
   2073        nextTickTimeStamp =
   2074            nextTickHint
   2075                ? perf->TimeStampToDOMHighResForRendering(*nextTickHint)
   2076                : timeStamp;
   2077      }
   2078      // else window is partially torn down already
   2079    }
   2080 
   2081    AUTO_PROFILER_MARKER_INNERWINDOWID("requestVideoFrame callbacks", GRAPHICS,
   2082                                       doc->InnerWindowID());
   2083    for (const auto& videoElm : videoElms) {
   2084      VideoFrameCallbackMetadata metadata;
   2085 
   2086      // Presentation time is our best estimate of when the video frame was
   2087      // submitted for compositing. Given that we decode frames in advance,
   2088      // this can be most closely estimated as the vsync time (aNowTime), as
   2089      // that is when the compositor samples the ImageHost to get the next
   2090      // frame to present.
   2091      metadata.mPresentationTime = timeStamp;
   2092 
   2093      // Expected display time is our best estimate of when the video frame we
   2094      // are submitting for compositing this cycle is shown to the user's eye.
   2095      // This will generally be when the next vsync triggers, assuming we do
   2096      // not fall behind on compositing.
   2097      metadata.mExpectedDisplayTime = nextTickTimeStamp;
   2098 
   2099      // WillFireVideoFrameCallbacks is responsible for populating the rest
   2100      // of the metadata fields. If it is not ready, or there has been no
   2101      // change, it will not populate metadata and will return false.
   2102      if (!videoElm->WillFireVideoFrameCallbacks(aNowTime, nextTickHint,
   2103                                                 metadata)) {
   2104        continue;
   2105      }
   2106 
   2107      VideoFrameRequestManager::FiringCallbacks callbacks(
   2108          videoElm->FrameRequestManager());
   2109 
   2110      for (auto& callback : callbacks.mList) {
   2111        if (callback.mCancelled) {
   2112          continue;
   2113        }
   2114 
   2115        // MOZ_KnownLive is OK, because the stack FiringCallbacks keeps callback
   2116        // alive and the mCallback strong reference can't be mutated by the
   2117        // call.
   2118        LogVideoFrameRequestCallback::Run run(callback.mCallback);
   2119        MOZ_KnownLive(callback.mCallback)->Call(timeStamp, metadata);
   2120      }
   2121    }
   2122  }
   2123 }
   2124 
   2125 void nsRefreshDriver::RunFrameRequestCallbacks(
   2126    const nsTArray<RefPtr<Document>>& aDocs, TimeStamp aNowTime) {
   2127  for (Document* doc : aDocs) {
   2128    FrameRequestManager::FiringCallbacks callbacks(doc->FrameRequestManager());
   2129    if (callbacks.mList.IsEmpty()) {
   2130      continue;
   2131    }
   2132 
   2133    DOMHighResTimeStamp timeStamp = 0;
   2134    RefPtr innerWindow = nsGlobalWindowInner::Cast(doc->GetInnerWindow());
   2135    if (innerWindow) {
   2136      if (Performance* perf = innerWindow->GetPerformance()) {
   2137        timeStamp = perf->TimeStampToDOMHighResForRendering(aNowTime);
   2138      }
   2139      // else window is partially torn down already
   2140    }
   2141 
   2142    AUTO_PROFILER_MARKER_INNERWINDOWID("requestAnimationFrame callbacks",
   2143                                       GRAPHICS, doc->InnerWindowID());
   2144    for (auto& callback : callbacks.mList) {
   2145      if (callback.mCancelled) {
   2146        continue;
   2147      }
   2148 
   2149      CallbackDebuggerNotificationGuard guard(
   2150          innerWindow, DebuggerNotificationType::RequestAnimationFrameCallback);
   2151 
   2152      // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
   2153      // keeps callback alive and the mCallback strong reference can't be
   2154      // mutated by the call.
   2155      LogFrameRequestCallback::Run run(callback.mCallback);
   2156      MOZ_KnownLive(callback.mCallback)->Call(timeStamp);
   2157    }
   2158  }
   2159 }
   2160 
   2161 void nsRefreshDriver::RunVideoAndFrameRequestCallbacks(TimeStamp aNowTime) {
   2162  const bool tickThrottledFrameRequests = [&] {
   2163    if (mThrottled) {
   2164      // We always tick throttled frame requests if the entire refresh driver is
   2165      // throttled, because in that situation throttled frame requests tick at
   2166      // the same frequency as non-throttled frame requests.
   2167      return true;
   2168    }
   2169    if (aNowTime >= mNextThrottledFrameRequestTick) {
   2170      mNextThrottledFrameRequestTick =
   2171          aNowTime + mThrottledFrameRequestInterval;
   2172      return true;
   2173    }
   2174    return false;
   2175  }();
   2176 
   2177  if (NS_WARN_IF(!mPresContext)) {
   2178    return;
   2179  }
   2180  bool skippedAnyThrottledDoc = false;
   2181  // Grab all of our documents that can fire frame request callbacks up front.
   2182  AutoTArray<RefPtr<Document>, 8> docs;
   2183  auto ShouldCollect = [&](const Document* aDoc) {
   2184    if (aDoc->IsRenderingSuppressed()) {
   2185      return false;
   2186    }
   2187    if (!aDoc->HasFrameRequestCallbacks()) {
   2188      // TODO(emilio): Consider removing this check to deal with callbacks
   2189      // posted from other documents more per spec... If we do that we also need
   2190      // to tweak the throttling code to not set mRenderingPhasesNeeded below.
   2191      // Check what other engines do too.
   2192      return false;
   2193    }
   2194    if (!tickThrottledFrameRequests && aDoc->ShouldThrottleFrameRequests()) {
   2195      // Skip throttled docs if it's not time to un-throttle them yet.
   2196      skippedAnyThrottledDoc = true;
   2197      return false;
   2198    }
   2199    return true;
   2200  };
   2201  if (ShouldCollect(mPresContext->Document())) {
   2202    docs.AppendElement(mPresContext->Document());
   2203  }
   2204  mPresContext->Document()->CollectDescendantDocuments(
   2205      docs, Document::IncludeSubResources::Yes, ShouldCollect);
   2206  if (skippedAnyThrottledDoc) {
   2207    // FIXME(emilio): It's a bit subtle to just set this here, but matches
   2208    // pre-existing behavior for throttled docs. It seems at least we should
   2209    // EnsureTimerStarted too? But that kinda defeats the throttling, a little
   2210    // bit? For now, preserve behavior.
   2211    mRenderingPhasesNeeded += RenderingPhase::AnimationFrameCallbacks;
   2212  }
   2213 
   2214  if (docs.IsEmpty()) {
   2215    return;
   2216  }
   2217 
   2218  RunVideoFrameCallbacks(docs, aNowTime);
   2219  RunFrameRequestCallbacks(docs, aNowTime);
   2220 }
   2221 
   2222 static StaticAutoPtr<AutoTArray<RefPtr<Task>, 8>> sPendingIdleTasks;
   2223 
   2224 void nsRefreshDriver::DispatchIdleTaskAfterTickUnlessExists(Task* aTask) {
   2225  if (!sPendingIdleTasks) {
   2226    sPendingIdleTasks = new AutoTArray<RefPtr<Task>, 8>();
   2227  } else {
   2228    if (sPendingIdleTasks->Contains(aTask)) {
   2229      return;
   2230    }
   2231  }
   2232 
   2233  sPendingIdleTasks->AppendElement(aTask);
   2234 }
   2235 
   2236 void nsRefreshDriver::CancelIdleTask(Task* aTask) {
   2237  if (!sPendingIdleTasks) {
   2238    return;
   2239  }
   2240 
   2241  sPendingIdleTasks->RemoveElement(aTask);
   2242 
   2243  if (sPendingIdleTasks->IsEmpty()) {
   2244    sPendingIdleTasks = nullptr;
   2245  }
   2246 }
   2247 
   2248 bool nsRefreshDriver::TickObserverArray(uint32_t aIdx, TimeStamp aNowTime) {
   2249  MOZ_ASSERT(aIdx < std::size(mObservers));
   2250  for (RefPtr<nsARefreshObserver> obs : mObservers[aIdx].EndLimitedRange()) {
   2251    obs->WillRefresh(aNowTime);
   2252 
   2253    if (!mPresContext || !mPresContext->GetPresShell()) {
   2254      return false;
   2255    }
   2256  }
   2257  return true;
   2258 }
   2259 
   2260 void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
   2261                           IsExtraTick aIsExtraTick /* = No */) {
   2262  MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
   2263             "Shouldn't have a JSContext on the stack");
   2264 
   2265  // We're either frozen or we were disconnected (likely in the middle
   2266  // of a tick iteration).  Just do nothing here, since our
   2267  // prescontext went away.
   2268  if (IsFrozen() || !mPresContext) {
   2269    return;
   2270  }
   2271 
   2272  // We can have a race condition where the vsync timestamp
   2273  // is before the most recent refresh due to a forced refresh.
   2274  // The underlying assumption is that the refresh driver tick can only
   2275  // go forward in time, not backwards. To prevent the refresh
   2276  // driver from going back in time, just skip this tick and
   2277  // wait until the next tick.
   2278  // If this is an 'extra' tick, then we expect it to be using the same
   2279  // vsync id and timestamp as the original tick, so also allow those.
   2280  if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes &&
   2281      aIsExtraTick == IsExtraTick::No) {
   2282    return;
   2283  }
   2284  auto cleanupInExtraTick = MakeScopeExit([&] { mInNormalTick = false; });
   2285  mInNormalTick = aIsExtraTick != IsExtraTick::Yes;
   2286 
   2287  if (!IsPresentingInVR() && IsWaitingForPaint(aNowTime)) {
   2288    // In immersive VR mode, we do not get notifications when frames are
   2289    // presented, so we do not wait for the compositor in that mode.
   2290 
   2291    // We're currently suspended waiting for earlier Tick's to
   2292    // be completed (on the Compositor). Mark that we missed the paint
   2293    // and keep waiting.
   2294    PROFILER_MARKER_UNTYPED(
   2295        "RefreshDriverTick waiting for paint", GRAPHICS,
   2296        MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)));
   2297    return;
   2298  }
   2299 
   2300  const TimeStamp previousRefresh = mMostRecentRefresh;
   2301  mMostRecentRefresh = aNowTime;
   2302 
   2303  if (mRootRefresh) {
   2304    mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
   2305    mRootRefresh = nullptr;
   2306  }
   2307  mSkippedPaints = false;
   2308 
   2309  RefPtr<PresShell> presShell = mPresContext->GetPresShell();
   2310  if (!presShell) {
   2311    StopTimer();
   2312    return;
   2313  }
   2314 
   2315  TickReasons tickReasons = GetReasonsToTick();
   2316  if (tickReasons == TickReasons::None) {
   2317    // We no longer have any observers.
   2318    // Discard composition payloads because there is no paint.
   2319    mCompositionPayloads.Clear();
   2320 
   2321    // We don't want to stop the timer when observers are initially
   2322    // removed, because sometimes observers can be added and removed
   2323    // often depending on what other things are going on and in that
   2324    // situation we don't want to thrash our timer.  So instead we
   2325    // wait until we get a Notify() call when we have no observers
   2326    // before stopping the timer.
   2327    // On top level content pages keep the timer running initially so that we
   2328    // paint the page soon enough.
   2329    if (ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint()) {
   2330      PROFILER_MARKER(
   2331          "RefreshDriverTick waiting for first contentful paint", GRAPHICS,
   2332          MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)), Tracing,
   2333          "Paint");
   2334    } else if (ShouldKeepTimerRunningAfterPageLoad()) {
   2335      PROFILER_MARKER(
   2336          "RefreshDriverTick after page load", GRAPHICS,
   2337          MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)), Tracing,
   2338          "Paint");
   2339    } else {
   2340      StopTimer();
   2341    }
   2342    return;
   2343  }
   2344 
   2345  AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("RefreshDriver tick", LAYOUT);
   2346 
   2347  nsAutoCString profilerStr;
   2348  if (profiler_thread_is_being_profiled_for_markers()) {
   2349    profilerStr.AppendLiteral("Tick reasons:");
   2350    AppendTickReasonsToString(tickReasons, profilerStr);
   2351  }
   2352  AUTO_PROFILER_MARKER_TEXT(
   2353      "RefreshDriverTick", GRAPHICS,
   2354      MarkerOptions(
   2355          MarkerStack::TakeBacktrace(std::move(mRefreshTimerStartedCause)),
   2356          MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext))),
   2357      profilerStr);
   2358 
   2359  mResizeSuppressed = false;
   2360 
   2361  bool oldInRefresh = mInRefresh;
   2362  auto restoreInRefresh = MakeScopeExit([&] { mInRefresh = oldInRefresh; });
   2363  mInRefresh = true;
   2364 
   2365  AutoRestore<TimeStamp> restoreTickStart(mTickStart);
   2366  mTickStart = TimeStamp::Now();
   2367  mTickVsyncId = aId;
   2368  mTickVsyncTime = aNowTime;
   2369 
   2370  gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
   2371 
   2372  FlushForceNotifyContentfulPaintPresContext();
   2373 
   2374  AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners = std::move(mEarlyRunners);
   2375  for (auto& runner : earlyRunners) {
   2376    runner->Run();
   2377    // Early runners might destroy this pres context.
   2378    if (!mPresContext || !mPresContext->GetPresShell()) {
   2379      return StopTimer();
   2380    }
   2381  }
   2382 
   2383  // Dispatch coalesced input events.
   2384  if (!TickObserverArray(0, aNowTime)) {
   2385    return StopTimer();
   2386  }
   2387 
   2388  // Notify style flush observers.
   2389  if (!TickObserverArray(1, aNowTime)) {
   2390    return StopTimer();
   2391  }
   2392 
   2393  // Check if running the microtask checkpoint above caused the pres context to
   2394  // be destroyed.
   2395  if (!mPresContext || !mPresContext->GetPresShell()) {
   2396    return StopTimer();
   2397  }
   2398 
   2399  // Step 7. For each doc of docs, flush autofocus candidates for doc if its
   2400  // node navigable is a top-level traversable.
   2401  // NOTE(emilio): Docs with autofocus candidates must be the top-level.
   2402  RunRenderingPhase(
   2403      RenderingPhase::FlushAutoFocusCandidates,
   2404      [](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2405        MOZ_KnownLive(aDoc).FlushAutoFocusCandidates();
   2406      },
   2407      [](const Document& aDoc) { return aDoc.HasAutoFocusCandidates(); });
   2408 
   2409  // Step 8. For each doc of docs, run the resize steps for doc.
   2410  RunRenderingPhase(RenderingPhase::ResizeSteps,
   2411                    [](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2412                      if (RefPtr<PresShell> ps = aDoc.GetPresShell()) {
   2413                        ps->RunResizeSteps();
   2414                      }
   2415                    });
   2416 
   2417  // Step 9. For each doc of docs, run the scroll steps for doc.
   2418  RunRenderingPhase(RenderingPhase::ScrollSteps,
   2419                    [](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2420                      if (RefPtr<PresShell> ps = aDoc.GetPresShell()) {
   2421                        ps->RunScrollSteps();
   2422                      }
   2423                    });
   2424 
   2425  // Step 10. For each doc of docs, evaluate media queries and report changes
   2426  // for doc.
   2427  // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
   2428  RunRenderingPhase(
   2429      RenderingPhase::EvaluateMediaQueriesAndReportChanges,
   2430      [&](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2431        MOZ_KnownLive(aDoc).EvaluateMediaQueriesAndReportChanges();
   2432      });
   2433 
   2434  // Step 11. For each doc of docs, update animations and send events for doc.
   2435  RunRenderingPhase(RenderingPhase::UpdateAnimationsAndSendEvents,
   2436                    [&](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2437                      if (aDoc.HasAnimationController()) {
   2438                        RefPtr controller = aDoc.GetAnimationController();
   2439                        controller->WillRefresh(aNowTime);
   2440                      }
   2441 
   2442                      {
   2443                        // Animation updates may queue Promise resolution
   2444                        // microtasks. We shouldn't run these, however, until we
   2445                        // have fully updated the animation state. As per the
   2446                        // "update animations and send events" procedure[1], we
   2447                        // should remove replaced animations and then run these
   2448                        // microtasks before dispatching the corresponding
   2449                        // animation events.
   2450                        //
   2451                        // [1]:
   2452                        // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
   2453                        nsAutoMicroTask mt;
   2454                        UpdateAndReduceAnimations(aDoc);
   2455                      }
   2456                      if (RefPtr pc = aDoc.GetPresContext()) {
   2457                        RefPtr dispatcher = pc->AnimationEventDispatcher();
   2458                        dispatcher->DispatchEvents();
   2459                      }
   2460                    });
   2461 
   2462  // Step 12. For each doc of docs, run the fullscreen steps for doc.
   2463  RunRenderingPhase(RenderingPhase::FullscreenSteps,
   2464                    [&](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2465                      MOZ_KnownLive(aDoc).RunFullscreenSteps();
   2466                    });
   2467 
   2468  // TODO: Step 13. For each doc of docs, if the user agent detects that the
   2469  // backing storage associated with a CanvasRenderingContext2D or an
   2470  // OffscreenCanvasRenderingContext2D, context, has been lost, then it must run
   2471  // the context lost steps for each such context.
   2472 
   2473  // Step 13.5. (https://wicg.github.io/video-rvfc/#video-rvfc-procedures):
   2474  //
   2475  //   For each fully active Document in docs, for each associated video element
   2476  //   for that Document, run the video frame request callbacks passing now as
   2477  //   the timestamp.
   2478  //
   2479  // Step 14. For each doc of docs, run the animation frame callbacks for doc,
   2480  // passing in the relative high resolution time given frameTimestamp and doc's
   2481  // relevant global object as the timestamp.
   2482  RunRenderingPhaseLegacy(RenderingPhase::AnimationFrameCallbacks,
   2483                          [&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2484                            RunVideoAndFrameRequestCallbacks(aNowTime);
   2485                          });
   2486 
   2487  MaybeIncreaseMeasuredTicksSinceLoading();
   2488 
   2489  if (!mPresContext || !mPresContext->GetPresShell()) {
   2490    return StopTimer();
   2491  }
   2492 
   2493  if (mRenderingPhasesNeeded.contains(RenderingPhase::Layout)) {
   2494    mNeedToRecomputeVisibility = true;
   2495    // Layout changes can cause intersection observers to need updates.
   2496    mRenderingPhasesNeeded += RenderingPhase::UpdateIntersectionObservations;
   2497  }
   2498 
   2499  // Steps 16 and 17.
   2500  RunRenderingPhase(
   2501      RenderingPhase::Layout,
   2502      [](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2503        MOZ_KnownLive(aDoc)
   2504            .DetermineProximityToViewportAndNotifyResizeObservers();
   2505      });
   2506  if (MOZ_UNLIKELY(!mPresContext || !mPresContext->GetPresShell())) {
   2507    return StopTimer();
   2508  }
   2509 
   2510  // Update any popups that may need to be moved or hidden due to their
   2511  // anchor changing.
   2512  if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
   2513    pm->UpdatePopupPositions(this);
   2514  }
   2515 
   2516  // Recompute approximate frame visibility if it's necessary and enough time
   2517  // has passed since the last time we did it.
   2518  if (mNeedToRecomputeVisibility && !mThrottled &&
   2519      aNowTime >= mNextRecomputeVisibilityTick &&
   2520      !presShell->IsPaintingSuppressed()) {
   2521    mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
   2522    mNeedToRecomputeVisibility = false;
   2523    presShell->ScheduleApproximateFrameVisibilityUpdateNow();
   2524  }
   2525 
   2526  // Step 18: For each doc of docs, perform pending transition operations for
   2527  // doc.
   2528  RunRenderingPhase(
   2529      RenderingPhase::ViewTransitionOperations,
   2530      [](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
   2531        MOZ_KnownLive(aDoc).PerformPendingViewTransitionOperations();
   2532      });
   2533 
   2534  // Step 19. For each doc of docs, run the update intersection observations
   2535  // steps for doc.
   2536  RunRenderingPhase(
   2537      RenderingPhase::UpdateIntersectionObservations,
   2538      [&](Document& aDoc) { aDoc.UpdateIntersections(aNowTime); });
   2539 
   2540  // Notify display flush observers (like a11y).
   2541  if (!TickObserverArray(2, aNowTime)) {
   2542    return StopTimer();
   2543  }
   2544 
   2545  UpdateAnimatedImages(previousRefresh, aNowTime);
   2546 
   2547  bool painted = false;
   2548  RunRenderingPhaseLegacy(
   2549      RenderingPhase::Paint,
   2550      [&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { painted = PaintIfNeeded(); });
   2551 
   2552  if (!painted) {
   2553    // No paint happened, discard composition payloads.
   2554    mCompositionPayloads.Clear();
   2555    mPaintCause = nullptr;
   2556  }
   2557 
   2558  if (MOZ_UNLIKELY(!mPresContext || !mPresContext->GetPresShell())) {
   2559    return StopTimer();
   2560  }
   2561 
   2562  // This needs to happen after DL building since we rely on the raster scales
   2563  // being stored in nsSubDocumentFrame.
   2564  UpdateRemoteFrameEffects();
   2565 
   2566 #ifndef ANDROID /* bug 1142079 */
   2567  mozilla::glean::layout::refresh_driver_tick.AccumulateRawDuration(
   2568      TimeStamp::Now() - mTickStart);
   2569 #endif
   2570 
   2571  for (nsAPostRefreshObserver* observer :
   2572       mPostRefreshObservers.ForwardRange()) {
   2573    observer->DidRefresh();
   2574  }
   2575 
   2576  NS_ASSERTION(mInRefresh, "Still in refresh");
   2577 
   2578  if (mPresContext->IsRoot() && XRE_IsContentProcess() &&
   2579      StaticPrefs::gfx_content_always_paint()) {
   2580    SchedulePaint();
   2581  }
   2582 
   2583  if (painted && sPendingIdleTasks) {
   2584    UniquePtr<AutoTArray<RefPtr<Task>, 8>> tasks(sPendingIdleTasks.forget());
   2585    for (RefPtr<Task>& taskWithDelay : *tasks) {
   2586      TaskController::Get()->AddTask(taskWithDelay.forget());
   2587    }
   2588  }
   2589 }
   2590 
   2591 bool nsRefreshDriver::PaintIfNeeded() {
   2592  if (mThrottled) {
   2593    return false;
   2594  }
   2595  if (IsPresentingInVR()) {
   2596    // Skip the paint in immersive VR mode because whatever we paint here will
   2597    // not end up on the screen. The screen is displaying WebGL content from a
   2598    // single canvas in that mode.
   2599    return false;
   2600  }
   2601  if (mPresContext->Document()->IsRenderingSuppressed()) {
   2602    // If the top level document is suppressed, skip painting altogether.
   2603    // TODO(emilio): Deal with this properly for subdocuments.
   2604    return false;
   2605  }
   2606  nsCString transactionId;
   2607  if (profiler_thread_is_being_profiled_for_markers()) {
   2608    transactionId.AppendLiteral("Transaction ID: ");
   2609    transactionId.AppendInt((uint64_t)mNextTransactionId);
   2610  }
   2611  AUTO_PROFILER_MARKER_TEXT(
   2612      "ViewManagerFlush", GRAPHICS,
   2613      MarkerOptions(MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)),
   2614                    MarkerStack::TakeBacktrace(std::move(mPaintCause))),
   2615      transactionId);
   2616 
   2617  // Forward our composition payloads to the layer manager.
   2618  if (!mCompositionPayloads.IsEmpty()) {
   2619    nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
   2620    WindowRenderer* renderer = widget ? widget->GetWindowRenderer() : nullptr;
   2621    if (renderer && renderer->AsWebRender()) {
   2622      renderer->AsWebRender()->RegisterPayloads(
   2623          std::move(mCompositionPayloads));
   2624    }
   2625    mCompositionPayloads.Clear();
   2626  }
   2627  RefPtr<PresShell> ps = mPresContext->PresShell();
   2628  {
   2629    PaintTelemetry::AutoRecordPaint record;
   2630    ps->SyncWindowPropertiesIfNeeded();
   2631    // Paint our popups.
   2632    if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
   2633      pm->PaintPopups(this);
   2634    }
   2635    ps->PaintSynchronously();
   2636  }
   2637  return true;
   2638 }
   2639 
   2640 void nsRefreshDriver::UpdateAnimatedImages(TimeStamp aPreviousRefresh,
   2641                                           TimeStamp aNowTime) {
   2642  if (!mHasImageAnimations || mThrottled) {
   2643    // Don't do this when throttled, as the compositor might be paused and we
   2644    // don't want to queue a lot of paints, see bug 1828587.
   2645    return;
   2646  }
   2647  // Perform notification to imgIRequests subscribed to listen for refresh
   2648  // events.
   2649  for (const auto& entry : mStartTable) {
   2650    const uint32_t& delay = entry.GetKey();
   2651    ImageStartData* data = entry.GetWeak();
   2652 
   2653    if (data->mEntries.IsEmpty()) {
   2654      continue;
   2655    }
   2656 
   2657    if (data->mStartTime) {
   2658      TimeStamp& start = *data->mStartTime;
   2659 
   2660      if (aPreviousRefresh >= start && aNowTime >= start) {
   2661        TimeDuration prev = aPreviousRefresh - start;
   2662        TimeDuration curr = aNowTime - start;
   2663        uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
   2664 
   2665        // We want to trigger images' refresh if we've just crossed over a
   2666        // multiple of the first image's start time. If so, set the animation
   2667        // start time to the nearest multiple of the delay and move all the
   2668        // images in this table to the main requests table.
   2669        if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
   2670          mozilla::TimeStamp desired =
   2671              start + TimeDuration::FromMilliseconds(prevMultiple * delay);
   2672          BeginRefreshingImages(data->mEntries, desired);
   2673        }
   2674      } else {
   2675        // Sometimes the start time can be in the future if we spin a nested
   2676        // event loop and re-entrantly tick. In that case, setting the
   2677        // animation start time to the start time seems like the least bad
   2678        // thing we can do.
   2679        mozilla::TimeStamp desired = start;
   2680        BeginRefreshingImages(data->mEntries, desired);
   2681      }
   2682    } else {
   2683      // This is the very first time we've drawn images with this time delay.
   2684      // Set the animation start time to "now" and move all the images in this
   2685      // table to the main requests table.
   2686      mozilla::TimeStamp desired = aNowTime;
   2687      BeginRefreshingImages(data->mEntries, desired);
   2688      data->mStartTime.emplace(aNowTime);
   2689    }
   2690  }
   2691 
   2692  if (!mRequests.IsEmpty()) {
   2693    // RequestRefresh may run scripts, so it's not safe to directly call it
   2694    // while using a hashtable enumerator to enumerate mRequests in case
   2695    // script modifies the hashtable. Instead, we build a (local) array of
   2696    // images to refresh, and then we refresh each image in that array.
   2697    nsTArray<nsCOMPtr<imgIContainer>> imagesToRefresh(mRequests.Count());
   2698 
   2699    for (const auto& req : mRequests) {
   2700      nsCOMPtr<imgIContainer> image;
   2701      if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
   2702        imagesToRefresh.AppendElement(image.forget());
   2703      }
   2704    }
   2705 
   2706    for (const auto& image : imagesToRefresh) {
   2707      image->RequestRefresh(aNowTime);
   2708    }
   2709  }
   2710 }
   2711 
   2712 void nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
   2713                                            mozilla::TimeStamp aDesired) {
   2714  for (const auto& req : aEntries) {
   2715    mRequests.Insert(req);
   2716 
   2717    nsCOMPtr<imgIContainer> image;
   2718    if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
   2719      image->SetAnimationStartTime(aDesired);
   2720    }
   2721  }
   2722  aEntries.Clear();
   2723 }
   2724 
   2725 void nsRefreshDriver::Freeze() {
   2726  StopTimer();
   2727  mFreezeCount++;
   2728 }
   2729 
   2730 void nsRefreshDriver::Thaw() {
   2731  NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
   2732 
   2733  if (mFreezeCount > 0) {
   2734    mFreezeCount--;
   2735  }
   2736 
   2737  if (mFreezeCount == 0 && HasReasonsToTick()) {
   2738    // FIXME: This isn't quite right, since our EnsureTimerStarted call
   2739    // updates our mMostRecentRefresh, but the DoRefresh call won't run
   2740    // and notify our observers until we get back to the event loop.
   2741    // Thus MostRecentRefresh() will lie between now and the DoRefresh.
   2742    RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
   2743        "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
   2744    if (nsPresContext* pc = GetPresContext()) {
   2745      pc->Document()->Dispatch(event.forget());
   2746      EnsureTimerStarted();
   2747    } else {
   2748      NS_ERROR("Thawing while document is being destroyed");
   2749    }
   2750  }
   2751 }
   2752 
   2753 void nsRefreshDriver::FinishedWaitingForTransaction() {
   2754  if (mSkippedPaints && !IsInRefresh() && HasReasonsToTick() &&
   2755      CanDoCatchUpTick()) {
   2756    NS_DispatchToCurrentThreadQueue(
   2757        NS_NewRunnableFunction(
   2758            "nsRefreshDriver::FinishedWaitingForTransaction",
   2759            [self = RefPtr{this}]() {
   2760              if (self->CanDoCatchUpTick()) {
   2761                self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
   2762                           self->mActiveTimer->MostRecentRefresh());
   2763              }
   2764            }),
   2765        EventQueuePriority::Vsync);
   2766  }
   2767  mWaitingForTransaction = false;
   2768  mSkippedPaints = false;
   2769 }
   2770 
   2771 mozilla::layers::TransactionId nsRefreshDriver::GetTransactionId(
   2772    bool aThrottle) {
   2773  mNextTransactionId = mNextTransactionId.Next();
   2774  LOG("[%p] Allocating transaction id %" PRIu64, this, mNextTransactionId.mId);
   2775 
   2776  // If this a paint from within a normal tick, and the caller hasn't explicitly
   2777  // asked for it to skip being throttled, then record this transaction as
   2778  // pending and maybe disable painting until some transactions are processed.
   2779  if (aThrottle && mInNormalTick) {
   2780    mPendingTransactions.AppendElement(mNextTransactionId);
   2781    if (TooManyPendingTransactions() && !mWaitingForTransaction &&
   2782        !mTestControllingRefreshes) {
   2783      LOG("[%p] Hit max pending transaction limit, entering wait mode", this);
   2784      mWaitingForTransaction = true;
   2785      mSkippedPaints = false;
   2786    }
   2787  }
   2788 
   2789  return mNextTransactionId;
   2790 }
   2791 
   2792 mozilla::layers::TransactionId nsRefreshDriver::LastTransactionId() const {
   2793  return mNextTransactionId;
   2794 }
   2795 
   2796 void nsRefreshDriver::RevokeTransactionId(
   2797    mozilla::layers::TransactionId aTransactionId) {
   2798  MOZ_ASSERT(aTransactionId == mNextTransactionId);
   2799  LOG("[%p] Revoking transaction id %" PRIu64, this, aTransactionId.mId);
   2800  if (AtPendingTransactionLimit() &&
   2801      mPendingTransactions.Contains(aTransactionId) && mWaitingForTransaction) {
   2802    LOG("[%p] No longer over pending transaction limit, leaving wait state",
   2803        this);
   2804    MOZ_ASSERT(!mSkippedPaints,
   2805               "How did we skip a paint when we're in the middle of one?");
   2806    FinishedWaitingForTransaction();
   2807  }
   2808 
   2809  // Notify the pres context so that it can deliver MozAfterPaint for this
   2810  // id if any caller was expecting it.
   2811  nsPresContext* pc = GetPresContext();
   2812  if (pc) {
   2813    pc->NotifyRevokingDidPaint(aTransactionId);
   2814  }
   2815  // Remove aTransactionId from the set of outstanding transactions since we're
   2816  // no longer waiting on it to be completed, but don't revert
   2817  // mNextTransactionId since we can't use the id again.
   2818  mPendingTransactions.RemoveElement(aTransactionId);
   2819 }
   2820 
   2821 void nsRefreshDriver::ClearPendingTransactions() {
   2822  LOG("[%p] ClearPendingTransactions", this);
   2823  mPendingTransactions.Clear();
   2824  mWaitingForTransaction = false;
   2825 }
   2826 
   2827 void nsRefreshDriver::ResetInitialTransactionId(
   2828    mozilla::layers::TransactionId aTransactionId) {
   2829  mNextTransactionId = aTransactionId;
   2830 }
   2831 
   2832 mozilla::TimeStamp nsRefreshDriver::GetTransactionStart() { return mTickStart; }
   2833 
   2834 VsyncId nsRefreshDriver::GetVsyncId() { return mTickVsyncId; }
   2835 
   2836 mozilla::TimeStamp nsRefreshDriver::GetVsyncStart() { return mTickVsyncTime; }
   2837 
   2838 void nsRefreshDriver::NotifyTransactionCompleted(
   2839    mozilla::layers::TransactionId aTransactionId) {
   2840  LOG("[%p] Completed transaction id %" PRIu64, this, aTransactionId.mId);
   2841  mPendingTransactions.RemoveElement(aTransactionId);
   2842  if (mWaitingForTransaction && !TooManyPendingTransactions()) {
   2843    LOG("[%p] No longer over pending transaction limit, leaving wait state",
   2844        this);
   2845    FinishedWaitingForTransaction();
   2846  }
   2847 }
   2848 
   2849 void nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime) {
   2850  mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
   2851  mRootRefresh = nullptr;
   2852  if (mSkippedPaints) {
   2853    DoRefresh();
   2854  }
   2855 }
   2856 
   2857 bool nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime) {
   2858  if (mTestControllingRefreshes) {
   2859    return false;
   2860  }
   2861 
   2862  if (mWaitingForTransaction) {
   2863    LOG("[%p] Over max pending transaction limit when trying to paint, "
   2864        "skipping",
   2865        this);
   2866    mSkippedPaints = true;
   2867    return true;
   2868  }
   2869 
   2870  // Try find the 'root' refresh driver for the current window and check
   2871  // if that is waiting for a paint.
   2872  nsPresContext* pc = GetPresContext();
   2873  nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
   2874  if (rootContext) {
   2875    nsRefreshDriver* rootRefresh = rootContext->RefreshDriver();
   2876    if (rootRefresh && rootRefresh != this) {
   2877      if (rootRefresh->IsWaitingForPaint(aTime)) {
   2878        if (mRootRefresh != rootRefresh) {
   2879          if (mRootRefresh) {
   2880            mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
   2881          }
   2882          rootRefresh->AddRefreshObserver(this, FlushType::Style,
   2883                                          "Waiting for paint");
   2884          mRootRefresh = rootRefresh;
   2885        }
   2886        mSkippedPaints = true;
   2887        return true;
   2888      }
   2889    }
   2890  }
   2891  return false;
   2892 }
   2893 
   2894 void nsRefreshDriver::SetActivity(bool aIsActive) {
   2895  const bool shouldThrottle = !aIsActive;
   2896  if (mThrottled == shouldThrottle) {
   2897    return;
   2898  }
   2899  mThrottled = shouldThrottle;
   2900  if (mActiveTimer || GetReasonsToTick() != TickReasons::None) {
   2901    // We want to switch our timer type here, so just stop and restart the
   2902    // timer.
   2903    EnsureTimerStarted(eForceAdjustTimer);
   2904  }
   2905 }
   2906 
   2907 nsPresContext* nsRefreshDriver::GetPresContext() const { return mPresContext; }
   2908 
   2909 void nsRefreshDriver::DoRefresh() {
   2910  // Don't do a refresh unless we're in a state where we should be refreshing.
   2911  if (!IsFrozen() && mPresContext && mActiveTimer) {
   2912    DoTick();
   2913  }
   2914 }
   2915 
   2916 #ifdef DEBUG
   2917 bool nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
   2918                                        FlushType aFlushType) {
   2919  ObserverArray& array = ArrayFor(aFlushType);
   2920  return array.Contains(aObserver);
   2921 }
   2922 #endif
   2923 
   2924 void nsRefreshDriver::SchedulePaint() {
   2925  NS_ASSERTION(mPresContext && mPresContext->IsRoot(),
   2926               "Should only schedule view manager flush on root prescontexts");
   2927  if (!mPaintCause) {
   2928    mPaintCause = profiler_capture_backtrace();
   2929  }
   2930  ScheduleRenderingPhase(RenderingPhase::Paint);
   2931  EnsureTimerStarted();
   2932 }
   2933 
   2934 /* static */
   2935 TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
   2936                                               IdleCheck aCheckType) {
   2937  MOZ_ASSERT(NS_IsMainThread());
   2938  MOZ_ASSERT(!aDefault.IsNull());
   2939 
   2940  // For computing idleness of refresh drivers we only care about
   2941  // sRegularRateTimerList, since we consider refresh drivers attached to
   2942  // sThrottledRateTimer to be inactive. This implies that tasks
   2943  // resulting from a tick on the sRegularRateTimer counts as being
   2944  // busy but tasks resulting from a tick on sThrottledRateTimer
   2945  // counts as being idle.
   2946  if (sRegularRateTimer) {
   2947    TimeStamp retVal = sRegularRateTimer->GetIdleDeadlineHint(aDefault);
   2948    if (retVal != aDefault) {
   2949      return retVal;
   2950    }
   2951  }
   2952 
   2953  TimeStamp hint = TimeStamp();
   2954  if (sRegularRateTimerList) {
   2955    for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
   2956      TimeStamp newHint = timer->GetIdleDeadlineHint(aDefault);
   2957      if (newHint < aDefault && (hint.IsNull() || newHint < hint)) {
   2958        hint = newHint;
   2959      }
   2960    }
   2961  }
   2962 
   2963  if (!hint.IsNull()) {
   2964    return hint;
   2965  }
   2966 
   2967  if (aCheckType == IdleCheck::AllVsyncListeners && XRE_IsParentProcess()) {
   2968    Maybe<TimeDuration> maybeRate =
   2969        mozilla::gfx::VsyncSource::GetFastestVsyncRate();
   2970    if (maybeRate.isSome()) {
   2971      TimeDuration minIdlePeriod =
   2972          TimeDuration::FromMilliseconds(StaticPrefs::idle_period_min());
   2973      TimeDuration layoutIdleLimit = TimeDuration::FromMilliseconds(
   2974          StaticPrefs::layout_idle_period_time_limit());
   2975      TimeDuration rate = *maybeRate - layoutIdleLimit;
   2976 
   2977      // If the rate is very short, don't let it affect idle processing in the
   2978      // parent process too much.
   2979      rate = std::max(rate, minIdlePeriod + minIdlePeriod);
   2980 
   2981      TimeStamp newHint = TimeStamp::Now() + rate;
   2982      if (newHint < aDefault) {
   2983        return newHint;
   2984      }
   2985    }
   2986  }
   2987 
   2988  return aDefault;
   2989 }
   2990 
   2991 /* static */
   2992 Maybe<TimeStamp> nsRefreshDriver::GetNextTickHint() {
   2993  MOZ_ASSERT(NS_IsMainThread());
   2994  Maybe<TimeStamp> hint;
   2995  auto UpdateHint = [&hint](const Maybe<TimeStamp>& aNewHint) {
   2996    if (!aNewHint) {
   2997      return;
   2998    }
   2999    if (!hint || *aNewHint < *hint) {
   3000      hint = aNewHint;
   3001    }
   3002  };
   3003  if (sRegularRateTimer) {
   3004    UpdateHint(sRegularRateTimer->GetNextTickHint());
   3005  }
   3006  if (sRegularRateTimerList) {
   3007    for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
   3008      UpdateHint(timer->GetNextTickHint());
   3009    }
   3010  }
   3011  return hint;
   3012 }
   3013 
   3014 /* static */
   3015 bool nsRefreshDriver::IsRegularRateTimerTicking() {
   3016  MOZ_ASSERT(NS_IsMainThread());
   3017 
   3018  if (sRegularRateTimer) {
   3019    if (sRegularRateTimer->IsTicking()) {
   3020      return true;
   3021    }
   3022  }
   3023 
   3024  if (sRegularRateTimerList) {
   3025    for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
   3026      if (timer->IsTicking()) {
   3027        return true;
   3028      }
   3029    }
   3030  }
   3031 
   3032  return false;
   3033 }
   3034 
   3035 void nsRefreshDriver::Disconnect() {
   3036  MOZ_ASSERT(NS_IsMainThread());
   3037 
   3038  StopTimer();
   3039 
   3040  mEarlyRunners.Clear();
   3041 
   3042  if (mPresContext) {
   3043    mPresContext = nullptr;
   3044    if (--sRefreshDriverCount == 0) {
   3045      Shutdown();
   3046    }
   3047  }
   3048 }
   3049 
   3050 #undef LOG