Bug 1646700 [wpt PR 24235] - Update picture-in-picture idlharness test, a=testonly
[gecko.git] / layout / base / nsRefreshDriver.cpp
blob172473618943aabf8462aaf88834ec13b470ef15
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/. */
7 /*
8 * Code to notify things that animate before a refresh, at an appropriate
9 * refresh rate. (Perhaps temporary, until replaced by compositor.)
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.
20 #include "nsRefreshDriver.h"
22 #ifdef XP_WIN
23 # include <windows.h>
24 // mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
25 // to manually include it
26 # include <mmsystem.h>
27 # include "WinUtils.h"
28 #endif
30 #include "mozilla/AnimationEventDispatcher.h"
31 #include "mozilla/ArrayUtils.h"
32 #include "mozilla/Assertions.h"
33 #include "mozilla/AutoRestore.h"
34 #include "mozilla/BasePrincipal.h"
35 #include "mozilla/DebugOnly.h"
36 #include "mozilla/IntegerRange.h"
37 #include "mozilla/PresShell.h"
38 #include "mozilla/dom/FontTableURIProtocolHandler.h"
39 #include "nsITimer.h"
40 #include "nsLayoutUtils.h"
41 #include "nsPresContext.h"
42 #include "nsComponentManagerUtils.h"
43 #include "mozilla/Logging.h"
44 #include "mozilla/dom/Document.h"
45 #include "mozilla/dom/DocumentInlines.h"
46 #include "nsIXULRuntime.h"
47 #include "jsapi.h"
48 #include "nsContentUtils.h"
49 #include "mozilla/PendingAnimationTracker.h"
50 #include "mozilla/PendingFullscreenEvent.h"
51 #include "mozilla/Preferences.h"
52 #include "mozilla/StaticPrefs_apz.h"
53 #include "mozilla/StaticPrefs_gfx.h"
54 #include "mozilla/StaticPrefs_layout.h"
55 #include "mozilla/StaticPrefs_page_load.h"
56 #include "nsViewManager.h"
57 #include "GeckoProfiler.h"
58 #include "nsNPAPIPluginInstance.h"
59 #include "mozilla/dom/CallbackDebuggerNotification.h"
60 #include "mozilla/dom/Event.h"
61 #include "mozilla/dom/Performance.h"
62 #include "mozilla/dom/Selection.h"
63 #include "mozilla/dom/WindowBinding.h"
64 #include "mozilla/RestyleManager.h"
65 #include "Layers.h"
66 #include "imgIContainer.h"
67 #include "mozilla/dom/ScriptSettings.h"
68 #include "nsDocShell.h"
69 #include "nsISimpleEnumerator.h"
70 #include "nsJSEnvironment.h"
71 #include "mozilla/Telemetry.h"
73 #include "BackgroundChild.h"
74 #include "mozilla/ipc/PBackgroundChild.h"
75 #include "mozilla/layout/VsyncChild.h"
76 #include "VsyncSource.h"
77 #include "mozilla/VsyncDispatcher.h"
78 #include "nsThreadUtils.h"
79 #include "mozilla/Unused.h"
80 #include "mozilla/TimelineConsumers.h"
81 #include "nsAnimationManager.h"
82 #include "nsDisplayList.h"
83 #include "nsTransitionManager.h"
85 #if defined(MOZ_WIDGET_ANDROID)
86 # include "VRManagerChild.h"
87 #endif // defined(MOZ_WIDGET_ANDROID)
89 #ifdef MOZ_XUL
90 # include "nsXULPopupManager.h"
91 #endif
93 #include <numeric>
95 using namespace mozilla;
96 using namespace mozilla::widget;
97 using namespace mozilla::ipc;
98 using namespace mozilla::dom;
99 using namespace mozilla::layout;
101 static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
102 #define LOG(...) \
103 MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
105 #define DEFAULT_THROTTLED_FRAME_RATE 1
106 #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
107 #define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
108 // after 10 minutes, stop firing off inactive timers
109 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
111 // The number of seconds spent skipping frames because we are waiting for the
112 // compositor before logging.
113 #if defined(MOZ_ASAN)
114 # define REFRESH_WAIT_WARNING 5
115 #elif defined(DEBUG) && !defined(MOZ_VALGRIND)
116 # define REFRESH_WAIT_WARNING 5
117 #elif defined(DEBUG) && defined(MOZ_VALGRIND)
118 # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
119 #elif defined(MOZ_VALGRIND)
120 # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
121 #else
122 # define REFRESH_WAIT_WARNING 1
123 #endif
125 namespace {
126 // `true` if we are currently in jank-critical mode.
128 // In jank-critical mode, any iteration of the event loop that takes
129 // more than 16ms to compute will cause an ongoing animation to miss
130 // frames.
132 // For simplicity, the current implementation assumes that we are in
133 // jank-critical mode if and only if at least one vsync driver has
134 // at least one observer.
135 static uint64_t sActiveVsyncTimers = 0;
137 // The latest value of process-wide jank levels.
139 // For each i, sJankLevels[i] counts the number of times delivery of
140 // vsync to the main thread has been delayed by at least 2^i ms. Use
141 // GetJankLevels to grab a copy of this array.
142 uint64_t sJankLevels[12];
144 // The number outstanding nsRefreshDrivers (that have been created but not
145 // disconnected). When this reaches zero we will call
146 // nsRefreshDriver::Shutdown.
147 static uint32_t sRefreshDriverCount = 0;
149 // RAII-helper for recording elapsed duration for refresh tick phases.
150 class AutoRecordPhase {
151 public:
152 explicit AutoRecordPhase(double* aResultMs)
153 : mTotalMs(aResultMs), mStartTime(TimeStamp::Now()) {
154 MOZ_ASSERT(mTotalMs);
156 ~AutoRecordPhase() {
157 *mTotalMs = (TimeStamp::Now() - mStartTime).ToMilliseconds();
160 private:
161 double* mTotalMs;
162 mozilla::TimeStamp mStartTime;
165 } // namespace
167 namespace mozilla {
170 * The base class for all global refresh driver timers. It takes care
171 * of managing the list of refresh drivers attached to them and
172 * provides interfaces for querying/setting the rate and actually
173 * running a timer 'Tick'. Subclasses must implement StartTimer(),
174 * StopTimer(), and ScheduleNextTick() -- the first two just
175 * start/stop whatever timer mechanism is in use, and ScheduleNextTick
176 * is called at the start of the Tick() implementation to set a time
177 * for the next tick.
179 class RefreshDriverTimer {
180 public:
181 RefreshDriverTimer() = default;
183 NS_INLINE_DECL_REFCOUNTING(RefreshDriverTimer)
185 virtual void AddRefreshDriver(nsRefreshDriver* aDriver) {
186 LOG("[%p] AddRefreshDriver %p", this, aDriver);
188 bool startTimer =
189 mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
190 if (IsRootRefreshDriver(aDriver)) {
191 NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver),
192 "Adding a duplicate root refresh driver!");
193 mRootRefreshDrivers.AppendElement(aDriver);
194 } else {
195 NS_ASSERTION(!mContentRefreshDrivers.Contains(aDriver),
196 "Adding a duplicate content refresh driver!");
197 mContentRefreshDrivers.AppendElement(aDriver);
200 if (startTimer) {
201 StartTimer();
205 void RemoveRefreshDriver(nsRefreshDriver* aDriver) {
206 LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
208 if (IsRootRefreshDriver(aDriver)) {
209 NS_ASSERTION(mRootRefreshDrivers.Contains(aDriver),
210 "RemoveRefreshDriver for a refresh driver that's not in the "
211 "root refresh list!");
212 mRootRefreshDrivers.RemoveElement(aDriver);
213 } else {
214 nsPresContext* pc = aDriver->GetPresContext();
215 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
216 // During PresContext shutdown, we can't accurately detect
217 // if a root refresh driver exists or not. Therefore, we have to
218 // search and find out which list this driver exists in.
219 if (!rootContext) {
220 if (mRootRefreshDrivers.Contains(aDriver)) {
221 mRootRefreshDrivers.RemoveElement(aDriver);
222 } else {
223 NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
224 "RemoveRefreshDriver without a display root for a "
225 "driver that is not in the content refresh list");
226 mContentRefreshDrivers.RemoveElement(aDriver);
228 } else {
229 NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
230 "RemoveRefreshDriver for a driver that is not in the "
231 "content refresh list");
232 mContentRefreshDrivers.RemoveElement(aDriver);
236 bool stopTimer =
237 mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
238 if (stopTimer) {
239 StopTimer();
243 TimeStamp MostRecentRefresh() const { return mLastFireTime; }
245 void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer) {
246 MOZ_ASSERT(NS_IsMainThread());
248 for (nsRefreshDriver* driver : mContentRefreshDrivers) {
249 aNewTimer->AddRefreshDriver(driver);
250 driver->mActiveTimer = aNewTimer;
252 mContentRefreshDrivers.Clear();
254 for (nsRefreshDriver* driver : mRootRefreshDrivers) {
255 aNewTimer->AddRefreshDriver(driver);
256 driver->mActiveTimer = aNewTimer;
258 mRootRefreshDrivers.Clear();
260 aNewTimer->mLastFireTime = mLastFireTime;
262 StopTimer();
265 virtual TimeDuration GetTimerRate() = 0;
267 TimeStamp GetIdleDeadlineHint(TimeStamp aDefault) {
268 MOZ_ASSERT(NS_IsMainThread());
270 TimeStamp mostRecentRefresh = MostRecentRefresh();
271 TimeDuration refreshRate = GetTimerRate();
272 TimeStamp idleEnd = mostRecentRefresh + refreshRate;
274 if (idleEnd +
275 refreshRate *
276 StaticPrefs::layout_idle_period_required_quiescent_frames() <
277 TimeStamp::Now()) {
278 return aDefault;
281 idleEnd = idleEnd - TimeDuration::FromMilliseconds(
282 StaticPrefs::layout_idle_period_time_limit());
283 return idleEnd < aDefault ? idleEnd : aDefault;
286 Maybe<TimeStamp> GetNextTickHint() {
287 MOZ_ASSERT(NS_IsMainThread());
288 TimeStamp nextTick = MostRecentRefresh() + GetTimerRate();
289 return nextTick < TimeStamp::Now() ? Nothing() : Some(nextTick);
292 // Returns null if the RefreshDriverTimer is attached to several
293 // RefreshDrivers. That may happen for example when there are
294 // several windows open.
295 nsPresContext* GetPresContextForOnlyRefreshDriver() {
296 if (mRootRefreshDrivers.Length() == 1 && mContentRefreshDrivers.IsEmpty()) {
297 return mRootRefreshDrivers[0]->GetPresContext();
299 if (mContentRefreshDrivers.Length() == 1 && mRootRefreshDrivers.IsEmpty()) {
300 return mContentRefreshDrivers[0]->GetPresContext();
302 return nullptr;
305 protected:
306 virtual ~RefreshDriverTimer() {
307 MOZ_ASSERT(
308 mContentRefreshDrivers.Length() == 0,
309 "Should have removed all content refresh drivers from here by now!");
310 MOZ_ASSERT(
311 mRootRefreshDrivers.Length() == 0,
312 "Should have removed all root refresh drivers from here by now!");
315 virtual void StartTimer() = 0;
316 virtual void StopTimer() = 0;
317 virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
319 bool IsRootRefreshDriver(nsRefreshDriver* aDriver) {
320 nsPresContext* pc = aDriver->GetPresContext();
321 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
322 if (!rootContext) {
323 return false;
326 return aDriver == rootContext->RefreshDriver();
330 * Actually runs a tick, poking all the attached RefreshDrivers.
331 * Grabs the "now" time via TimeStamp::Now().
333 void Tick() {
334 TimeStamp now = TimeStamp::Now();
335 Tick(VsyncId(), now);
338 void TickRefreshDrivers(VsyncId aId, TimeStamp aNow,
339 nsTArray<RefPtr<nsRefreshDriver>>& aDrivers) {
340 if (aDrivers.IsEmpty()) {
341 return;
344 for (nsRefreshDriver* driver : aDrivers.Clone()) {
345 // don't poke this driver if it's in test mode
346 if (driver->IsTestControllingRefreshesEnabled()) {
347 continue;
350 TickDriver(driver, aId, aNow);
355 * Tick the refresh drivers based on the given timestamp.
357 void Tick(VsyncId aId, TimeStamp now) {
358 ScheduleNextTick(now);
360 mLastFireTime = now;
362 LOG("[%p] ticking drivers...", this);
363 // RD is short for RefreshDriver
364 AUTO_PROFILER_TRACING_MARKER("Paint", "RefreshDriverTick", GRAPHICS);
366 TickRefreshDrivers(aId, now, mContentRefreshDrivers);
367 TickRefreshDrivers(aId, now, mRootRefreshDrivers);
369 LOG("[%p] done.", this);
372 static void TickDriver(nsRefreshDriver* driver, VsyncId aId, TimeStamp now) {
373 driver->Tick(aId, now);
376 TimeStamp mLastFireTime;
377 TimeStamp mTargetTime;
379 nsTArray<RefPtr<nsRefreshDriver>> mContentRefreshDrivers;
380 nsTArray<RefPtr<nsRefreshDriver>> mRootRefreshDrivers;
382 // useful callback for nsITimer-based derived classes, here
383 // because of c++ protected shenanigans
384 static void TimerTick(nsITimer* aTimer, void* aClosure) {
385 RefPtr<RefreshDriverTimer> timer =
386 static_cast<RefreshDriverTimer*>(aClosure);
387 timer->Tick();
392 * A RefreshDriverTimer that uses a nsITimer as the underlying timer. Note that
393 * this is a ONE_SHOT timer, not a repeating one! Subclasses are expected to
394 * implement ScheduleNextTick and intelligently calculate the next time to tick,
395 * and to reset mTimer. Using a repeating nsITimer gets us into a lot of pain
396 * with its attempt at intelligent slack removal and such, so we don't do it.
398 class SimpleTimerBasedRefreshDriverTimer : public RefreshDriverTimer {
399 public:
401 * aRate -- the delay, in milliseconds, requested between timer firings
403 explicit SimpleTimerBasedRefreshDriverTimer(double aRate) {
404 SetRate(aRate);
405 mTimer = NS_NewTimer();
408 virtual ~SimpleTimerBasedRefreshDriverTimer() override { StopTimer(); }
410 // will take effect at next timer tick
411 virtual void SetRate(double aNewRate) {
412 mRateMilliseconds = aNewRate;
413 mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
416 double GetRate() const { return mRateMilliseconds; }
418 TimeDuration GetTimerRate() override { return mRateDuration; }
420 protected:
421 void StartTimer() override {
422 // pretend we just fired, and we schedule the next tick normally
423 mLastFireTime = TimeStamp::Now();
425 mTargetTime = mLastFireTime + mRateDuration;
427 uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
428 mTimer->InitWithNamedFuncCallback(
429 TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
430 "SimpleTimerBasedRefreshDriverTimer::StartTimer");
433 void StopTimer() override { mTimer->Cancel(); }
435 double mRateMilliseconds;
436 TimeDuration mRateDuration;
437 RefPtr<nsITimer> mTimer;
441 * A refresh driver that listens to vsync events and ticks the refresh driver
442 * on vsync intervals. We throttle the refresh driver if we get too many
443 * vsync events and wait to catch up again.
445 class VsyncRefreshDriverTimer : public RefreshDriverTimer {
446 public:
447 VsyncRefreshDriverTimer() : mVsyncChild(nullptr) {
448 MOZ_ASSERT(XRE_IsParentProcess());
449 MOZ_ASSERT(NS_IsMainThread());
450 mVsyncObserver = new RefreshDriverVsyncObserver(this);
451 RefPtr<mozilla::gfx::VsyncSource> vsyncSource =
452 gfxPlatform::GetPlatform()->GetHardwareVsync();
453 MOZ_ALWAYS_TRUE(mVsyncDispatcher =
454 vsyncSource->GetRefreshTimerVsyncDispatcher());
455 mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver);
456 mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
459 explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
460 : mVsyncChild(aVsyncChild) {
461 MOZ_ASSERT(!XRE_IsParentProcess());
462 MOZ_ASSERT(NS_IsMainThread());
463 MOZ_ASSERT(mVsyncChild);
464 mVsyncObserver = new RefreshDriverVsyncObserver(this);
465 mVsyncChild->SetVsyncObserver(mVsyncObserver);
466 mVsyncRate = mVsyncChild->GetVsyncRate();
469 // Constructor for when we have a local vsync source. As it is local, we do
470 // not have to worry about it being re-inited by gfxPlatform on frame rate
471 // change on the global source.
472 explicit VsyncRefreshDriverTimer(const RefPtr<gfx::VsyncSource>& aVsyncSource)
473 : mVsyncChild(nullptr) {
474 MOZ_ASSERT(XRE_IsParentProcess());
475 MOZ_ASSERT(NS_IsMainThread());
476 mVsyncSource = aVsyncSource;
477 mVsyncObserver = new RefreshDriverVsyncObserver(this);
478 MOZ_ALWAYS_TRUE(mVsyncDispatcher =
479 aVsyncSource->GetRefreshTimerVsyncDispatcher());
480 mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver);
481 mVsyncRate = aVsyncSource->GetGlobalDisplay().GetVsyncRate();
484 TimeDuration GetTimerRate() override {
485 if (mVsyncRate != TimeDuration::Forever()) {
486 return mVsyncRate;
489 if (mVsyncChild) {
490 // VsyncChild::VsyncRate() is a simple getter for the cached
491 // hardware vsync rate. We depend on that
492 // VsyncChild::GetVsyncRate() being called in the constructor
493 // will result in a response with the actual vsync rate sooner
494 // or later. Until that happens VsyncChild::VsyncRate() returns
495 // TimeDuration::Forever() and we have to guess below.
496 mVsyncRate = mVsyncChild->VsyncRate();
499 // If hardware queries fail / are unsupported, we have to just guess.
500 return mVsyncRate != TimeDuration::Forever()
501 ? mVsyncRate
502 : TimeDuration::FromMilliseconds(1000.0 / 60.0);
505 private:
506 // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
507 // explicitly shutdown. We create an inner class that has the VsyncObserver
508 // and is shutdown when the RefreshDriverTimer is deleted.
509 class RefreshDriverVsyncObserver final : public VsyncObserver {
510 public:
511 explicit RefreshDriverVsyncObserver(
512 VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
513 : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer),
514 mParentProcessRefreshTickLock("RefreshTickLock"),
515 mPendingParentProcessVsync(false),
516 mRecentVsync(TimeStamp::Now()),
517 mLastTick(TimeStamp::Now()),
518 mVsyncRate(TimeDuration::Forever()),
519 mProcessedVsync(true) {
520 MOZ_ASSERT(NS_IsMainThread());
523 class ParentProcessVsyncNotifier final : public Runnable,
524 public nsIRunnablePriority {
525 public:
526 explicit ParentProcessVsyncNotifier(RefreshDriverVsyncObserver* aObserver)
527 : Runnable(
528 "VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::"
529 "ParentProcessVsyncNotifier"),
530 mObserver(aObserver) {}
532 NS_DECL_ISUPPORTS_INHERITED
534 NS_IMETHOD Run() override {
535 MOZ_ASSERT(NS_IsMainThread());
536 sHighPriorityEnabled = mozilla::BrowserTabsRemoteAutostart();
538 mObserver->NotifyParentProcessVsync();
539 return NS_OK;
542 NS_IMETHOD GetPriority(uint32_t* aPriority) override {
543 *aPriority = sHighPriorityEnabled
544 ? nsIRunnablePriority::PRIORITY_HIGH
545 : nsIRunnablePriority::PRIORITY_NORMAL;
546 return NS_OK;
549 private:
550 ~ParentProcessVsyncNotifier() = default;
551 RefPtr<RefreshDriverVsyncObserver> mObserver;
552 static mozilla::Atomic<bool> sHighPriorityEnabled;
555 bool NotifyVsync(const VsyncEvent& aVsync) override {
556 // Compress vsync notifications such that only 1 may run at a time
557 // This is so that we don't flood the refresh driver with vsync messages
558 // if the main thread is blocked for long periods of time
559 { // scope lock
560 MonitorAutoLock lock(mParentProcessRefreshTickLock);
561 mRecentParentProcessVsync = aVsync;
562 if (mPendingParentProcessVsync) {
563 return true;
565 mPendingParentProcessVsync = true;
567 nsCOMPtr<nsIRunnable> vsyncEvent = new ParentProcessVsyncNotifier(this);
568 NS_DispatchToMainThread(vsyncEvent);
569 return true;
572 void NotifyParentProcessVsync() {
573 // IMPORTANT: All paths through this method MUST hold a strong ref on
574 // |this| for the duration of the TickRefreshDriver callback.
575 MOZ_ASSERT(NS_IsMainThread());
577 VsyncEvent aVsync;
579 MonitorAutoLock lock(mParentProcessRefreshTickLock);
580 aVsync = mRecentParentProcessVsync;
581 mPendingParentProcessVsync = false;
584 mRecentVsync = aVsync.mTime;
585 mRecentVsyncId = aVsync.mId;
586 if (!mBlockUntil.IsNull() && mBlockUntil > aVsync.mTime) {
587 if (mProcessedVsync) {
588 // Re-post vsync update as a normal priority runnable. This way
589 // runnables already in normal priority queue get processed.
590 mProcessedVsync = false;
591 nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<>(
592 "RefreshDriverVsyncObserver::NormalPriorityNotify", this,
593 &RefreshDriverVsyncObserver::NormalPriorityNotify);
594 NS_DispatchToMainThread(vsyncEvent);
597 return;
600 if (StaticPrefs::layout_lower_priority_refresh_driver_during_load() &&
601 mVsyncRefreshDriverTimer) {
602 nsPresContext* pctx =
603 mVsyncRefreshDriverTimer->GetPresContextForOnlyRefreshDriver();
604 if (pctx && pctx->HadContentfulPaint() && pctx->Document() &&
605 pctx->Document()->GetReadyStateEnum() <
606 Document::READYSTATE_COMPLETE) {
607 nsPIDOMWindowInner* win = pctx->Document()->GetInnerWindow();
608 uint32_t frameRateMultiplier = pctx->GetNextFrameRateMultiplier();
609 if (!frameRateMultiplier) {
610 pctx->DidUseFrameRateMultiplier();
612 if (win && frameRateMultiplier) {
613 dom::Performance* perf = win->GetPerformance();
614 // Limit slower refresh rate to 5 seconds between the
615 // first contentful paint and page load.
616 if (perf && perf->Now() <
617 StaticPrefs::page_load_deprioritization_period()) {
618 if (mProcessedVsync) {
619 mProcessedVsync = false;
620 // Handle this case similarly to the code above, but just
621 // use idle queue.
622 TimeDuration rate = mVsyncRefreshDriverTimer->GetTimerRate();
623 uint32_t slowRate = static_cast<uint32_t>(
624 rate.ToMilliseconds() * frameRateMultiplier);
625 pctx->DidUseFrameRateMultiplier();
626 nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<>(
627 "RefreshDriverVsyncObserver::NormalPriorityNotify[IDLE]",
628 this, &RefreshDriverVsyncObserver::NormalPriorityNotify);
629 NS_DispatchToCurrentThreadQueue(vsyncEvent.forget(), slowRate,
630 EventQueuePriority::Idle);
632 return;
638 RefPtr<RefreshDriverVsyncObserver> kungFuDeathGrip(this);
639 TickRefreshDriver(aVsync.mId, aVsync.mTime);
642 void Shutdown() {
643 MOZ_ASSERT(NS_IsMainThread());
644 mVsyncRefreshDriverTimer = nullptr;
647 void OnTimerStart() { mLastTick = TimeStamp::Now(); }
649 void NormalPriorityNotify() {
650 if (mLastProcessedTick.IsNull() || mRecentVsync > mLastProcessedTick) {
651 // mBlockUntil is for high priority vsync notifications only.
652 mBlockUntil = TimeStamp();
653 TickRefreshDriver(mRecentVsyncId, mRecentVsync);
656 mProcessedVsync = true;
659 private:
660 ~RefreshDriverVsyncObserver() = default;
662 void RecordTelemetryProbes(TimeStamp aVsyncTimestamp) {
663 MOZ_ASSERT(NS_IsMainThread());
664 #ifndef ANDROID /* bug 1142079 */
665 if (XRE_IsParentProcess()) {
666 TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
667 uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
668 Telemetry::Accumulate(
669 Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS, sample);
670 Telemetry::Accumulate(
671 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, sample);
672 RecordJank(sample);
673 } else if (mVsyncRate != TimeDuration::Forever()) {
674 TimeDuration contentDelay = (TimeStamp::Now() - mLastTick) - mVsyncRate;
675 if (contentDelay.ToMilliseconds() < 0) {
676 // Vsyncs are noisy and some can come at a rate quicker than
677 // the reported hardware rate. In those cases, consider that we have 0
678 // delay.
679 contentDelay = TimeDuration::FromMilliseconds(0);
681 uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
682 Telemetry::Accumulate(
683 Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS, sample);
684 Telemetry::Accumulate(
685 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, sample);
686 RecordJank(sample);
687 } else {
688 // Request the vsync rate from the parent process. Might be a few vsyncs
689 // until the parent responds.
690 if (mVsyncRefreshDriverTimer) {
691 mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
694 #endif
697 void RecordJank(uint32_t aJankMS) {
698 uint32_t duration = 1 /* ms */;
699 for (size_t i = 0;
700 i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
701 ++i, duration *= 2) {
702 sJankLevels[i]++;
706 void TickRefreshDriver(VsyncId aId, TimeStamp aVsyncTimestamp) {
707 MOZ_ASSERT(NS_IsMainThread());
709 RecordTelemetryProbes(aVsyncTimestamp);
710 mLastTick = TimeStamp::Now();
711 mLastProcessedTick = aVsyncTimestamp;
713 // On 32-bit Windows we sometimes get times where TimeStamp::Now() is not
714 // monotonic because the underlying system apis produce non-monontonic
715 // results. (bug 1306896)
716 #if !defined(_WIN32)
717 // Do not compare timestamps unless they are both canonical or fuzzy
718 DebugOnly<TimeStamp> rightnow = TimeStamp::Now();
719 MOZ_ASSERT_IF(
720 (*&rightnow).UsedCanonicalNow() == aVsyncTimestamp.UsedCanonicalNow(),
721 aVsyncTimestamp <= *&rightnow);
722 #endif
724 // Let also non-RefreshDriver code to run at least for awhile if we have
725 // a mVsyncRefreshDriverTimer. Note, if nothing else is running,
726 // RefreshDriver will still run as fast as possible, some ticks will
727 // just be triggered from a normal priority runnable.
728 TimeDuration timeForOutsideTick = TimeDuration::FromMilliseconds(0.0f);
730 // We might have a problem that we call ~VsyncRefreshDriverTimer() before
731 // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
732 // before use.
733 if (mVsyncRefreshDriverTimer) {
734 timeForOutsideTick = TimeDuration::FromMilliseconds(
735 mVsyncRefreshDriverTimer->GetTimerRate().ToMilliseconds() / 100.0f);
736 RefPtr<VsyncRefreshDriverTimer> timer = mVsyncRefreshDriverTimer;
737 timer->RunRefreshDrivers(aId, aVsyncTimestamp);
738 // Note: mVsyncRefreshDriverTimer might be null now.
741 TimeDuration tickDuration = TimeStamp::Now() - mLastTick;
742 mBlockUntil = aVsyncTimestamp + tickDuration + timeForOutsideTick;
745 // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
746 // be always available before Shutdown(). We can just use the raw pointer
747 // here.
748 VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
750 Monitor mParentProcessRefreshTickLock;
751 VsyncEvent mRecentParentProcessVsync;
752 bool mPendingParentProcessVsync;
754 TimeStamp mRecentVsync;
755 VsyncId mRecentVsyncId;
756 TimeStamp mLastTick;
757 TimeStamp mLastProcessedTick;
758 TimeStamp mBlockUntil;
759 TimeDuration mVsyncRate;
760 bool mProcessedVsync;
761 }; // RefreshDriverVsyncObserver
763 ~VsyncRefreshDriverTimer() override {
764 if (XRE_IsParentProcess()) {
765 mVsyncDispatcher->RemoveChildRefreshTimer(mVsyncObserver);
766 mVsyncDispatcher = nullptr;
767 } else {
768 // Since the PVsyncChild actors live through the life of the process, just
769 // send the unobserveVsync message to disable vsync event. We don't need
770 // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
771 // will be called and clean up this actor.
772 Unused << mVsyncChild->SendUnobserve();
773 mVsyncChild->SetVsyncObserver(nullptr);
774 mVsyncChild = nullptr;
777 // Detach current vsync timer from this VsyncObserver. The observer will no
778 // longer tick this timer.
779 mVsyncObserver->Shutdown();
780 mVsyncObserver = nullptr;
783 void StartTimer() override {
784 // Protect updates to `sActiveVsyncTimers`.
785 MOZ_ASSERT(NS_IsMainThread());
787 mLastFireTime = TimeStamp::Now();
789 if (XRE_IsParentProcess()) {
790 mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver);
791 } else {
792 Unused << mVsyncChild->SendObserve();
793 mVsyncObserver->OnTimerStart();
796 ++sActiveVsyncTimers;
799 void StopTimer() override {
800 // Protect updates to `sActiveVsyncTimers`.
801 MOZ_ASSERT(NS_IsMainThread());
803 if (XRE_IsParentProcess()) {
804 mVsyncDispatcher->RemoveChildRefreshTimer(mVsyncObserver);
805 } else {
806 Unused << mVsyncChild->SendUnobserve();
809 MOZ_ASSERT(sActiveVsyncTimers > 0);
810 --sActiveVsyncTimers;
813 void ScheduleNextTick(TimeStamp aNowTime) override {
814 // Do nothing since we just wait for the next vsync from
815 // RefreshDriverVsyncObserver.
818 void RunRefreshDrivers(VsyncId aId, TimeStamp aTimeStamp) {
819 Tick(aId, aTimeStamp);
822 // When using local vsync source, we keep a strong ref to it here to ensure
823 // that the weak ref in the vsync dispatcher does not end up dangling.
824 // As this is a local vsync source, it is not affected by gfxPlatform vsync
825 // source reinit.
826 RefPtr<gfx::VsyncSource> mVsyncSource;
827 RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
828 // Used for parent process.
829 RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
830 // Used for child process.
831 // The mVsyncChild will be always available before VsncChild::ActorDestroy().
832 // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
833 RefPtr<VsyncChild> mVsyncChild;
834 TimeDuration mVsyncRate;
835 }; // VsyncRefreshDriverTimer
837 NS_IMPL_ISUPPORTS_INHERITED(
838 VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::
839 ParentProcessVsyncNotifier,
840 Runnable, nsIRunnablePriority)
842 mozilla::Atomic<bool> VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::
843 ParentProcessVsyncNotifier::sHighPriorityEnabled(false);
846 * Since the content process takes some time to setup
847 * the vsync IPC connection, this timer is used
848 * during the intial startup process.
849 * During initial startup, the refresh drivers
850 * are ticked off this timer, and are swapped out once content
851 * vsync IPC connection is established.
853 class StartupRefreshDriverTimer : public SimpleTimerBasedRefreshDriverTimer {
854 public:
855 explicit StartupRefreshDriverTimer(double aRate)
856 : SimpleTimerBasedRefreshDriverTimer(aRate) {}
858 protected:
859 void ScheduleNextTick(TimeStamp aNowTime) override {
860 // Since this is only used for startup, it isn't super critical
861 // that we tick at consistent intervals.
862 TimeStamp newTarget = aNowTime + mRateDuration;
863 uint32_t delay =
864 static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
865 mTimer->InitWithNamedFuncCallback(
866 TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
867 "StartupRefreshDriverTimer::ScheduleNextTick");
868 mTargetTime = newTarget;
873 * A RefreshDriverTimer for inactive documents. When a new refresh driver is
874 * added, the rate is reset to the base (normally 1s/1fps). Every time
875 * it ticks, a single refresh driver is poked. Once they have all been poked,
876 * the duration between ticks doubles, up to mDisableAfterMilliseconds. At that
877 * point, the timer is quiet and doesn't tick (until something is added to it
878 * again).
880 * When a timer is removed, there is a possibility of another timer
881 * being skipped for one cycle. We could avoid this by adjusting
882 * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
883 * add that complexity. All we want is for inactive drivers to tick
884 * at some point, but we don't care too much about how often.
886 class InactiveRefreshDriverTimer final
887 : public SimpleTimerBasedRefreshDriverTimer {
888 public:
889 explicit InactiveRefreshDriverTimer(double aRate)
890 : SimpleTimerBasedRefreshDriverTimer(aRate),
891 mNextTickDuration(aRate),
892 mDisableAfterMilliseconds(-1.0),
893 mNextDriverIndex(0) {}
895 InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
896 : SimpleTimerBasedRefreshDriverTimer(aRate),
897 mNextTickDuration(aRate),
898 mDisableAfterMilliseconds(aDisableAfterMilliseconds),
899 mNextDriverIndex(0) {}
901 void AddRefreshDriver(nsRefreshDriver* aDriver) override {
902 RefreshDriverTimer::AddRefreshDriver(aDriver);
904 LOG("[%p] inactive timer got new refresh driver %p, resetting rate", this,
905 aDriver);
907 // reset the timer, and start with the newly added one next time.
908 mNextTickDuration = mRateMilliseconds;
910 // we don't really have to start with the newly added one, but we may as
911 // well not tick the old ones at the fastest rate any more than we need to.
912 mNextDriverIndex = GetRefreshDriverCount() - 1;
914 StopTimer();
915 StartTimer();
918 TimeDuration GetTimerRate() override {
919 return TimeDuration::FromMilliseconds(mNextTickDuration);
922 protected:
923 uint32_t GetRefreshDriverCount() {
924 return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
927 void StartTimer() override {
928 mLastFireTime = TimeStamp::Now();
930 mTargetTime = mLastFireTime + mRateDuration;
932 uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
933 mTimer->InitWithNamedFuncCallback(TimerTickOne, this, delay,
934 nsITimer::TYPE_ONE_SHOT,
935 "InactiveRefreshDriverTimer::StartTimer");
938 void StopTimer() override { mTimer->Cancel(); }
940 void ScheduleNextTick(TimeStamp aNowTime) override {
941 if (mDisableAfterMilliseconds > 0.0 &&
942 mNextTickDuration > mDisableAfterMilliseconds) {
943 // We hit the time after which we should disable
944 // inactive window refreshes; don't schedule anything
945 // until we get kicked by an AddRefreshDriver call.
946 return;
949 // double the next tick time if we've already gone through all of them once
950 if (mNextDriverIndex >= GetRefreshDriverCount()) {
951 mNextTickDuration *= 2.0;
952 mNextDriverIndex = 0;
955 // this doesn't need to be precise; do a simple schedule
956 uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
957 mTimer->InitWithNamedFuncCallback(
958 TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT,
959 "InactiveRefreshDriverTimer::ScheduleNextTick");
961 LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this,
962 mNextTickDuration, mNextDriverIndex, GetRefreshDriverCount());
965 /* Runs just one driver's tick. */
966 void TickOne() {
967 TimeStamp now = TimeStamp::Now();
969 ScheduleNextTick(now);
971 mLastFireTime = now;
973 nsTArray<RefPtr<nsRefreshDriver>> drivers(mContentRefreshDrivers.Clone());
974 drivers.AppendElements(mRootRefreshDrivers);
975 size_t index = mNextDriverIndex;
977 if (index < drivers.Length() &&
978 !drivers[index]->IsTestControllingRefreshesEnabled()) {
979 TickDriver(drivers[index], VsyncId(), now);
982 mNextDriverIndex++;
985 static void TimerTickOne(nsITimer* aTimer, void* aClosure) {
986 RefPtr<InactiveRefreshDriverTimer> timer =
987 static_cast<InactiveRefreshDriverTimer*>(aClosure);
988 timer->TickOne();
991 double mNextTickDuration;
992 double mDisableAfterMilliseconds;
993 uint32_t mNextDriverIndex;
996 } // namespace mozilla
998 static StaticRefPtr<RefreshDriverTimer> sRegularRateTimer;
999 static StaticRefPtr<InactiveRefreshDriverTimer> sThrottledRateTimer;
1001 static void CreateContentVsyncRefreshTimer(void*) {
1002 MOZ_ASSERT(NS_IsMainThread());
1003 MOZ_ASSERT(!XRE_IsParentProcess());
1005 // Create the PVsync actor child for vsync-base refresh timer.
1006 // PBackgroundChild is created synchronously. We will still use software
1007 // timer before PVsync ready, and change to use hw timer when the connection
1008 // is done. Please check nsRefreshDriver::PVsyncActorCreated().
1010 PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
1011 if (NS_WARN_IF(!actorChild)) {
1012 return;
1015 layout::PVsyncChild* actor = actorChild->SendPVsyncConstructor();
1016 if (NS_WARN_IF(!actor)) {
1017 return;
1020 layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
1021 nsRefreshDriver::PVsyncActorCreated(child);
1024 void nsRefreshDriver::CreateVsyncRefreshTimer() {
1025 MOZ_ASSERT(NS_IsMainThread());
1027 PodArrayZero(sJankLevels);
1029 if (gfxPlatform::IsInLayoutAsapMode()) {
1030 return;
1033 // If available, we fetch the widget-specific vsync source.
1035 // NOTE(heycam): If we are initializing an nsRefreshDriver under
1036 // nsPresContext::Init, then this GetRootWidget() call will fail, as the
1037 // pres context does not yet have a pres shell. For now, null check the
1038 // pres shell to avoid a console warning.
1039 nsPresContext* pc = GetPresContext();
1040 nsIWidget* widget = pc->GetPresShell() ? pc->GetRootWidget() : nullptr;
1041 if (widget) {
1042 RefPtr<gfx::VsyncSource> localVsyncSource = widget->GetVsyncSource();
1043 if (localVsyncSource) {
1044 mOwnTimer = new VsyncRefreshDriverTimer(localVsyncSource);
1045 return;
1049 if (XRE_IsParentProcess()) {
1050 // Make sure all vsync systems are ready.
1051 gfxPlatform::GetPlatform();
1052 // In parent process, we don't need to use ipc. We can create the
1053 // VsyncRefreshDriverTimer directly.
1054 sRegularRateTimer = new VsyncRefreshDriverTimer();
1055 return;
1058 // If this process is not created by NUWA, just create the vsync timer here.
1059 CreateContentVsyncRefreshTimer(nullptr);
1062 static uint32_t GetFirstFrameDelay(imgIRequest* req) {
1063 nsCOMPtr<imgIContainer> container;
1064 if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
1065 return 0;
1068 // If this image isn't animated, there isn't a first frame delay.
1069 int32_t delay = container->GetFirstFrameDelay();
1070 if (delay < 0) return 0;
1072 return static_cast<uint32_t>(delay);
1075 /* static */
1076 void nsRefreshDriver::Shutdown() {
1077 // clean up our timers
1078 sRegularRateTimer = nullptr;
1079 sThrottledRateTimer = nullptr;
1082 /* static */
1083 int32_t nsRefreshDriver::DefaultInterval() {
1084 return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
1087 // Compute the interval to use for the refresh driver timer, in milliseconds.
1088 // outIsDefault indicates that rate was not explicitly set by the user
1089 // so we might choose other, more appropriate rates (e.g. vsync, etc)
1090 // layout.frame_rate=0 indicates "ASAP mode".
1091 // In ASAP mode rendering is iterated as fast as possible (typically for stress
1092 // testing). A target rate of 10k is used internally instead of special-handling
1093 // 0. Backends which block on swap/present/etc should try to not block when
1094 // layout.frame_rate=0 - to comply with "ASAP" as much as possible.
1095 double nsRefreshDriver::GetRegularTimerInterval() const {
1096 int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
1097 if (rate < 0) {
1098 rate = gfxPlatform::GetDefaultFrameRate();
1099 } else if (rate == 0) {
1100 rate = 10000;
1103 return 1000.0 / rate;
1106 /* static */
1107 double nsRefreshDriver::GetThrottledTimerInterval() {
1108 int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
1109 if (rate <= 0) {
1110 rate = DEFAULT_THROTTLED_FRAME_RATE;
1112 return 1000.0 / rate;
1115 /* static */ mozilla::TimeDuration
1116 nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
1117 int32_t interval =
1118 Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
1119 if (interval <= 0) {
1120 interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
1122 return TimeDuration::FromMilliseconds(interval);
1125 RefreshDriverTimer* nsRefreshDriver::ChooseTimer() {
1126 if (mThrottled) {
1127 if (!sThrottledRateTimer)
1128 sThrottledRateTimer = new InactiveRefreshDriverTimer(
1129 GetThrottledTimerInterval(),
1130 DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
1131 return sThrottledRateTimer;
1134 if (!sRegularRateTimer && !mOwnTimer) {
1135 double rate = GetRegularTimerInterval();
1137 // Try to use vsync-base refresh timer first for sRegularRateTimer.
1138 CreateVsyncRefreshTimer();
1140 if (mOwnTimer) {
1141 return mOwnTimer.get();
1144 if (!sRegularRateTimer) {
1145 sRegularRateTimer = new StartupRefreshDriverTimer(rate);
1149 if (mOwnTimer) {
1150 return mOwnTimer.get();
1153 return sRegularRateTimer;
1156 nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
1157 : mActiveTimer(nullptr),
1158 mOwnTimer(nullptr),
1159 mPresContext(aPresContext),
1160 mRootRefresh(nullptr),
1161 mNextTransactionId{0},
1162 mOutstandingTransactionId{0},
1163 mCompletedTransaction{0},
1164 mFreezeCount(0),
1165 mThrottledFrameRequestInterval(
1166 TimeDuration::FromMilliseconds(GetThrottledTimerInterval())),
1167 mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
1168 mThrottled(false),
1169 mNeedToRecomputeVisibility(false),
1170 mTestControllingRefreshes(false),
1171 mViewManagerFlushIsPending(false),
1172 mHasScheduleFlush(false),
1173 mInRefresh(false),
1174 mWaitingForTransaction(false),
1175 mSkippedPaints(false),
1176 mResizeSuppressed(false),
1177 mNotifyDOMContentFlushed(false),
1178 mNeedToUpdateIntersectionObservations(false),
1179 mWarningThreshold(REFRESH_WAIT_WARNING) {
1180 MOZ_ASSERT(NS_IsMainThread());
1181 MOZ_ASSERT(mPresContext,
1182 "Need a pres context to tell us to call Disconnect() later "
1183 "and decrement sRefreshDriverCount.");
1184 mMostRecentRefresh = TimeStamp::Now();
1185 mNextThrottledFrameRequestTick = mMostRecentRefresh;
1186 mNextRecomputeVisibilityTick = mMostRecentRefresh;
1188 ++sRefreshDriverCount;
1191 nsRefreshDriver::~nsRefreshDriver() {
1192 MOZ_ASSERT(NS_IsMainThread());
1193 MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
1194 "observers, except pending selection scrolls, "
1195 "should have been unregistered");
1196 MOZ_ASSERT(!mActiveTimer, "timer should be gone");
1197 MOZ_ASSERT(!mPresContext,
1198 "Should have called Disconnect() and decremented "
1199 "sRefreshDriverCount!");
1201 if (mRootRefresh) {
1202 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1203 mRootRefresh = nullptr;
1207 // Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
1208 // for description.
1209 void nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds) {
1210 // ensure that we're removed from our driver
1211 StopTimer();
1213 if (!mTestControllingRefreshes) {
1214 mMostRecentRefresh = TimeStamp::Now();
1216 mTestControllingRefreshes = true;
1217 if (mWaitingForTransaction) {
1218 // Disable any refresh driver throttling when entering test mode
1219 mWaitingForTransaction = false;
1220 mSkippedPaints = false;
1221 mWarningThreshold = REFRESH_WAIT_WARNING;
1225 mMostRecentRefresh += TimeDuration::FromMilliseconds((double)aMilliseconds);
1227 mozilla::dom::AutoNoJSAPI nojsapi;
1228 DoTick();
1231 void nsRefreshDriver::RestoreNormalRefresh() {
1232 mTestControllingRefreshes = false;
1233 EnsureTimerStarted(eAllowTimeToGoBackwards);
1234 mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId;
1237 TimeStamp nsRefreshDriver::MostRecentRefresh() const {
1238 // In case of stylo traversal, we have already activated the refresh driver in
1239 // RestyleManager::ProcessPendingRestyles().
1240 if (!ServoStyleSet::IsInServoTraversal()) {
1241 const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
1244 return mMostRecentRefresh;
1247 void nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
1248 FlushType aFlushType) {
1249 ObserverArray& array = ArrayFor(aFlushType);
1250 array.AppendElement(aObserver);
1251 EnsureTimerStarted();
1254 bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
1255 FlushType aFlushType) {
1256 ObserverArray& array = ArrayFor(aFlushType);
1257 return array.RemoveElement(aObserver);
1260 void nsRefreshDriver::AddTimerAdjustmentObserver(
1261 nsATimerAdjustmentObserver* aObserver) {
1262 MOZ_ASSERT(!mTimerAdjustmentObservers.Contains(aObserver));
1263 mTimerAdjustmentObservers.AppendElement(aObserver);
1266 void nsRefreshDriver::RemoveTimerAdjustmentObserver(
1267 nsATimerAdjustmentObserver* aObserver) {
1268 MOZ_ASSERT(mTimerAdjustmentObservers.Contains(aObserver));
1269 mTimerAdjustmentObservers.RemoveElement(aObserver);
1272 void nsRefreshDriver::PostVisualViewportResizeEvent(
1273 VVPResizeEvent* aResizeEvent) {
1274 mVisualViewportResizeEvents.AppendElement(aResizeEvent);
1275 EnsureTimerStarted();
1278 void nsRefreshDriver::DispatchVisualViewportResizeEvents() {
1279 // We're taking a hint from scroll events and only dispatch the current set
1280 // of queued resize events. If additional events are posted in response to
1281 // the current events being dispatched, we'll dispatch them on the next tick.
1282 VisualViewportResizeEventArray events;
1283 events.SwapElements(mVisualViewportResizeEvents);
1284 for (auto& event : events) {
1285 event->Run();
1289 void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent,
1290 bool aDelayed) {
1291 if (aDelayed) {
1292 mDelayedScrollEvents.AppendElement(aScrollEvent);
1293 } else {
1294 mScrollEvents.AppendElement(aScrollEvent);
1295 EnsureTimerStarted();
1299 void nsRefreshDriver::DispatchScrollEvents() {
1300 // Scroll events are one-shot, so after running them we can drop them.
1301 // However, dispatching a scroll event can potentially cause more scroll
1302 // events to be posted, so we move the initial set into a temporary array
1303 // first. (Newly posted scroll events will be dispatched on the next tick.)
1304 ScrollEventArray events;
1305 events.SwapElements(mScrollEvents);
1306 for (auto& event : events) {
1307 event->Run();
1311 void nsRefreshDriver::PostVisualViewportScrollEvent(
1312 VVPScrollEvent* aScrollEvent) {
1313 mVisualViewportScrollEvents.AppendElement(aScrollEvent);
1314 EnsureTimerStarted();
1317 void nsRefreshDriver::DispatchVisualViewportScrollEvents() {
1318 // Scroll events are one-shot, so after running them we can drop them.
1319 // However, dispatching a scroll event can potentially cause more scroll
1320 // events to be posted, so we move the initial set into a temporary array
1321 // first. (Newly posted scroll events will be dispatched on the next tick.)
1322 VisualViewportScrollEventArray events;
1323 events.SwapElements(mVisualViewportScrollEvents);
1324 for (auto& event : events) {
1325 event->Run();
1329 void nsRefreshDriver::AddPostRefreshObserver(
1330 nsAPostRefreshObserver* aObserver) {
1331 mPostRefreshObservers.AppendElement(aObserver);
1334 void nsRefreshDriver::RemovePostRefreshObserver(
1335 nsAPostRefreshObserver* aObserver) {
1336 mPostRefreshObservers.RemoveElement(aObserver);
1339 bool nsRefreshDriver::AddImageRequest(imgIRequest* aRequest) {
1340 uint32_t delay = GetFirstFrameDelay(aRequest);
1341 if (delay == 0) {
1342 mRequests.PutEntry(aRequest);
1343 } else {
1344 const auto& start = mStartTable.LookupForAdd(delay).OrInsert(
1345 []() { return new ImageStartData(); });
1346 start->mEntries.PutEntry(aRequest);
1349 EnsureTimerStarted();
1351 return true;
1354 void nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest) {
1355 // Try to remove from both places, just in case, because we can't tell
1356 // whether RemoveEntry() succeeds.
1357 mRequests.RemoveEntry(aRequest);
1358 uint32_t delay = GetFirstFrameDelay(aRequest);
1359 if (delay != 0) {
1360 ImageStartData* start = mStartTable.Get(delay);
1361 if (start) {
1362 start->mEntries.RemoveEntry(aRequest);
1367 void nsRefreshDriver::NotifyDOMContentLoaded() {
1368 // If the refresh driver is going to tick, we mark the timestamp after
1369 // everything is flushed in the next tick. If it isn't, mark ourselves as
1370 // flushed now.
1371 if (!HasObservers()) {
1372 GetPresContext()->NotifyDOMContentFlushed();
1373 } else {
1374 mNotifyDOMContentFlushed = true;
1378 void nsRefreshDriver::RunDelayedEventsSoon() {
1379 // Place entries for delayed events into their corresponding normal list,
1380 // and schedule a refresh. When these delayed events run, if their document
1381 // still has events suppressed then they will be readded to the delayed
1382 // events list.
1384 mScrollEvents.AppendElements(mDelayedScrollEvents);
1385 mDelayedScrollEvents.Clear();
1387 mResizeEventFlushObservers.AppendElements(mDelayedResizeEventFlushObservers);
1388 mDelayedResizeEventFlushObservers.Clear();
1390 EnsureTimerStarted();
1393 void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
1394 // FIXME: Bug 1346065: We should also assert the case where we have
1395 // STYLO_THREADS=1.
1396 MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
1397 "EnsureTimerStarted should be called only when we are not "
1398 "in servo traversal or on the main-thread");
1400 if (mTestControllingRefreshes) return;
1402 // will it already fire, and no other changes needed?
1403 if (mActiveTimer && !(aFlags & eForceAdjustTimer)) return;
1405 if (IsFrozen() || !mPresContext) {
1406 // If we don't want to start it now, or we've been disconnected.
1407 StopTimer();
1408 return;
1411 if (mPresContext->Document()->IsBeingUsedAsImage()) {
1412 // Image documents receive ticks from clients' refresh drivers.
1413 // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
1414 // they receive refresh-driver ticks from their client docs (bug 1107252).
1415 nsIURI* uri = mPresContext->Document()->GetDocumentURI();
1416 if (!uri || !mozilla::dom::IsFontTableURI(uri)) {
1417 MOZ_ASSERT(!mActiveTimer,
1418 "image doc refresh driver should never have its own timer");
1419 return;
1423 // We got here because we're either adjusting the time *or* we're
1424 // starting it for the first time. Add to the right timer,
1425 // prehaps removing it from a previously-set one.
1426 RefreshDriverTimer* newTimer = ChooseTimer();
1427 if (newTimer != mActiveTimer) {
1428 if (mActiveTimer) mActiveTimer->RemoveRefreshDriver(this);
1429 mActiveTimer = newTimer;
1430 mActiveTimer->AddRefreshDriver(this);
1433 // When switching from an inactive timer to an active timer, the root
1434 // refresh driver is skipped due to being set to the content refresh
1435 // driver's timestamp. In case of EnsureTimerStarted is called from
1436 // ScheduleViewManagerFlush, we should avoid this behavior to flush
1437 // a paint in the same tick on the root refresh driver.
1438 if (aFlags & eNeverAdjustTimer) {
1439 return;
1442 // Since the different timers are sampled at different rates, when switching
1443 // timers, the most recent refresh of the new timer may be *before* the
1444 // most recent refresh of the old timer. However, the refresh driver time
1445 // should not go backwards so we clamp the most recent refresh time.
1447 // The one exception to this is when we are restoring the refresh driver
1448 // from test control in which case the time is expected to go backwards
1449 // (see bug 1043078).
1450 TimeStamp newMostRecentRefresh =
1451 aFlags & eAllowTimeToGoBackwards
1452 ? mActiveTimer->MostRecentRefresh()
1453 : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
1455 if (mMostRecentRefresh != newMostRecentRefresh) {
1456 mMostRecentRefresh = newMostRecentRefresh;
1458 for (nsATimerAdjustmentObserver* obs :
1459 mTimerAdjustmentObservers.EndLimitedRange()) {
1460 obs->NotifyTimerAdjusted(mMostRecentRefresh);
1465 void nsRefreshDriver::StopTimer() {
1466 if (!mActiveTimer) return;
1468 mActiveTimer->RemoveRefreshDriver(this);
1469 mActiveTimer = nullptr;
1472 uint32_t nsRefreshDriver::ObserverCount() const {
1473 uint32_t sum = 0;
1474 for (const ObserverArray& array : mObservers) {
1475 sum += array.Length();
1478 // Even while throttled, we need to process layout and style changes. Style
1479 // changes can trigger transitions which fire events when they complete, and
1480 // layout changes can affect media queries on child documents, triggering
1481 // style changes, etc.
1482 sum += mAnimationEventFlushObservers.Length();
1483 sum += mResizeEventFlushObservers.Length();
1484 sum += mStyleFlushObservers.Length();
1485 sum += mLayoutFlushObservers.Length();
1486 sum += mPendingFullscreenEvents.Length();
1487 sum += mFrameRequestCallbackDocs.Length();
1488 sum += mThrottledFrameRequestCallbackDocs.Length();
1489 sum += mViewManagerFlushIsPending;
1490 sum += mEarlyRunners.Length();
1491 sum += mTimerAdjustmentObservers.Length();
1492 return sum;
1495 bool nsRefreshDriver::HasObservers() const {
1496 for (const ObserverArray& array : mObservers) {
1497 if (!array.IsEmpty()) {
1498 return true;
1502 // We should NOT count mTimerAdjustmentObservers here since this method is
1503 // used to determine whether or not to stop the timer or re-start it and timer
1504 // adjustment observers should not influence timer starting or stopping.
1505 return mViewManagerFlushIsPending || !mStyleFlushObservers.IsEmpty() ||
1506 !mLayoutFlushObservers.IsEmpty() ||
1507 !mAnimationEventFlushObservers.IsEmpty() ||
1508 !mResizeEventFlushObservers.IsEmpty() ||
1509 !mPendingFullscreenEvents.IsEmpty() ||
1510 !mFrameRequestCallbackDocs.IsEmpty() ||
1511 !mThrottledFrameRequestCallbackDocs.IsEmpty() ||
1512 !mEarlyRunners.IsEmpty();
1515 bool nsRefreshDriver::HasImageRequests() const {
1516 for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
1517 if (!iter.UserData()->mEntries.IsEmpty()) {
1518 return true;
1522 return !mRequests.IsEmpty();
1525 nsRefreshDriver::ObserverArray& nsRefreshDriver::ArrayFor(
1526 FlushType aFlushType) {
1527 switch (aFlushType) {
1528 case FlushType::Event:
1529 return mObservers[0];
1530 case FlushType::Style:
1531 case FlushType::Frames:
1532 return mObservers[1];
1533 case FlushType::Layout:
1534 return mObservers[2];
1535 case FlushType::Display:
1536 return mObservers[3];
1537 default:
1538 MOZ_CRASH("We don't track refresh observers for this flush type");
1543 * nsITimerCallback implementation
1546 void nsRefreshDriver::DoTick() {
1547 MOZ_ASSERT(!IsFrozen(), "Why are we notified while frozen?");
1548 MOZ_ASSERT(mPresContext, "Why are we notified after disconnection?");
1549 MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1550 "Shouldn't have a JSContext on the stack");
1552 if (mTestControllingRefreshes) {
1553 Tick(VsyncId(), mMostRecentRefresh);
1554 } else {
1555 Tick(VsyncId(), TimeStamp::Now());
1559 struct DocumentFrameCallbacks {
1560 explicit DocumentFrameCallbacks(Document* aDocument) : mDocument(aDocument) {}
1562 RefPtr<Document> mDocument;
1563 nsTArray<Document::FrameRequest> mCallbacks;
1566 static nsDocShell* GetDocShell(nsPresContext* aPresContext) {
1567 return static_cast<nsDocShell*>(aPresContext->GetDocShell());
1570 static bool HasPendingAnimations(PresShell* aPresShell) {
1571 Document* doc = aPresShell->GetDocument();
1572 if (!doc) {
1573 return false;
1576 PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
1577 return tracker && tracker->HasPendingAnimations();
1581 * Return a list of all the child docShells in a given root docShell that are
1582 * visible and are recording markers for the profilingTimeline
1584 static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
1585 nsTArray<nsDocShell*>& aShells) {
1586 if (!aRootDocShell) {
1587 return;
1590 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1591 if (!timelines || timelines->IsEmpty()) {
1592 return;
1595 RefPtr<BrowsingContext> bc = aRootDocShell->GetBrowsingContext();
1596 if (!bc) {
1597 return;
1600 bc->PostOrderWalk([&](BrowsingContext* aContext) {
1601 nsDocShell* shell = nsDocShell::Cast(aContext->GetDocShell());
1602 if (!shell || !shell->GetRecordProfileTimelineMarkers()) {
1603 // This process isn't painting OOP iframes so we ignore
1604 // docshells that are OOP.
1605 return;
1608 bool isVisible = false;
1609 shell->GetVisibility(&isVisible);
1610 if (!isVisible) {
1611 return;
1614 aShells.AppendElement(shell);
1618 static void TakeFrameRequestCallbacksFrom(
1619 Document* aDocument, nsTArray<DocumentFrameCallbacks>& aTarget) {
1620 aTarget.AppendElement(aDocument);
1621 aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
1624 // https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps
1625 void nsRefreshDriver::RunFullscreenSteps() {
1626 // Swap out the current pending events
1627 nsTArray<UniquePtr<PendingFullscreenEvent>> pendings(
1628 std::move(mPendingFullscreenEvents));
1629 for (UniquePtr<PendingFullscreenEvent>& event : pendings) {
1630 event->Dispatch();
1634 void nsRefreshDriver::UpdateIntersectionObservations() {
1635 AutoTArray<RefPtr<Document>, 32> documents;
1637 if (mPresContext->Document()->HasIntersectionObservers()) {
1638 documents.AppendElement(mPresContext->Document());
1641 mPresContext->Document()->CollectDescendantDocuments(
1642 documents, [](const Document* document) -> bool {
1643 return document->HasIntersectionObservers();
1646 for (uint32_t i = 0; i < documents.Length(); ++i) {
1647 Document* doc = documents[i];
1648 doc->UpdateIntersectionObservations();
1649 doc->ScheduleIntersectionObserverNotification();
1652 mNeedToUpdateIntersectionObservations = false;
1655 void nsRefreshDriver::DispatchAnimationEvents() {
1656 if (!mPresContext) {
1657 return;
1660 // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
1661 // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
1662 // during processing the previous dispatcher.
1663 AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
1664 dispatchers.AppendElements(mAnimationEventFlushObservers);
1665 mAnimationEventFlushObservers.Clear();
1667 for (auto& dispatcher : dispatchers) {
1668 dispatcher->DispatchEvents();
1672 void nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime) {
1673 // Grab all of our frame request callbacks up front.
1674 nsTArray<DocumentFrameCallbacks> frameRequestCallbacks(
1675 mFrameRequestCallbackDocs.Length() +
1676 mThrottledFrameRequestCallbackDocs.Length());
1678 // First, grab throttled frame request callbacks.
1680 nsTArray<Document*> docsToRemove;
1682 // We always tick throttled frame requests if the entire refresh driver is
1683 // throttled, because in that situation throttled frame requests tick at the
1684 // same frequency as non-throttled frame requests.
1685 bool tickThrottledFrameRequests = mThrottled;
1687 if (!tickThrottledFrameRequests &&
1688 aNowTime >= mNextThrottledFrameRequestTick) {
1689 mNextThrottledFrameRequestTick =
1690 aNowTime + mThrottledFrameRequestInterval;
1691 tickThrottledFrameRequests = true;
1694 for (Document* doc : mThrottledFrameRequestCallbackDocs) {
1695 if (tickThrottledFrameRequests) {
1696 // We're ticking throttled documents, so grab this document's requests.
1697 // We don't bother appending to docsToRemove because we're going to
1698 // clear mThrottledFrameRequestCallbackDocs anyway.
1699 TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1700 } else if (!doc->ShouldThrottleFrameRequests()) {
1701 // This document is no longer throttled, so grab its requests even
1702 // though we're not ticking throttled frame requests right now. If
1703 // this is the first unthrottled document with frame requests, we'll
1704 // enter high precision mode the next time the callback is scheduled.
1705 TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1706 docsToRemove.AppendElement(doc);
1710 // Remove all the documents we're ticking from
1711 // mThrottledFrameRequestCallbackDocs so they can be readded as needed.
1712 if (tickThrottledFrameRequests) {
1713 mThrottledFrameRequestCallbackDocs.Clear();
1714 } else {
1715 // XXX(seth): We're using this approach to avoid concurrent modification
1716 // of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
1717 // zero elements or a very small number, so this should be OK in practice.
1718 for (Document* doc : docsToRemove) {
1719 mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
1724 // Now grab unthrottled frame request callbacks.
1725 for (Document* doc : mFrameRequestCallbackDocs) {
1726 TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1729 // Reset mFrameRequestCallbackDocs so they can be readded as needed.
1730 mFrameRequestCallbackDocs.Clear();
1732 if (!frameRequestCallbacks.IsEmpty()) {
1733 AUTO_PROFILER_TRACING_MARKER_DOCSHELL("Paint",
1734 "requestAnimationFrame callbacks",
1735 GRAPHICS, GetDocShell(mPresContext));
1736 for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
1737 // XXXbz Bug 863140: GetInnerWindow can return the outer
1738 // window in some cases.
1739 nsPIDOMWindowInner* innerWindow =
1740 docCallbacks.mDocument->GetInnerWindow();
1741 DOMHighResTimeStamp timeStamp = 0;
1742 if (innerWindow) {
1743 mozilla::dom::Performance* perf = innerWindow->GetPerformance();
1744 if (perf) {
1745 timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
1746 // 0 is an inappropriate mixin for this this area; however CSS
1747 // Animations needs to have it's Time Reduction Logic refactored, so
1748 // it's currently only clamping for RFP mode. RFP mode gives a much
1749 // lower time precision, so we accept the security leak here for now
1750 if (!perf->IsSystemPrincipal()) {
1751 timeStamp =
1752 nsRFPService::ReduceTimePrecisionAsMSecsRFPOnly(timeStamp, 0);
1755 // else window is partially torn down already
1757 for (auto& callback : docCallbacks.mCallbacks) {
1758 if (docCallbacks.mDocument->IsCanceledFrameRequestCallback(
1759 callback.mHandle)) {
1760 continue;
1763 nsCOMPtr<nsIGlobalObject> global(innerWindow ? innerWindow->AsGlobal()
1764 : nullptr);
1765 CallbackDebuggerNotificationGuard guard(
1766 global, DebuggerNotificationType::RequestAnimationFrameCallback);
1768 // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
1769 // keeps callback alive and the mCallback strong reference can't be
1770 // mutated by the call.
1771 MOZ_KnownLive(callback.mCallback)->Call(timeStamp);
1777 struct RunnableWithDelay {
1778 nsCOMPtr<nsIRunnable> mRunnable;
1779 uint32_t mDelay;
1782 static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
1784 void nsRefreshDriver::DispatchIdleRunnableAfterTickUnlessExists(
1785 nsIRunnable* aRunnable, uint32_t aDelay) {
1786 if (!sPendingIdleRunnables) {
1787 sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
1788 } else {
1789 for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
1790 if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
1791 return;
1796 RunnableWithDelay rwd = {aRunnable, aDelay};
1797 sPendingIdleRunnables->AppendElement(rwd);
1800 void nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable) {
1801 if (!sPendingIdleRunnables) {
1802 return;
1805 for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
1806 if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
1807 sPendingIdleRunnables->RemoveElementAt(i);
1808 break;
1812 if (sPendingIdleRunnables->IsEmpty()) {
1813 delete sPendingIdleRunnables;
1814 sPendingIdleRunnables = nullptr;
1818 static CallState ReduceAnimations(Document& aDocument) {
1819 if (nsPresContext* pc = aDocument.GetPresContext()) {
1820 if (pc->EffectCompositor()->NeedsReducing()) {
1821 pc->EffectCompositor()->ReduceAnimations();
1824 aDocument.EnumerateSubDocuments(ReduceAnimations);
1825 return CallState::Continue;
1828 void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
1829 MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1830 "Shouldn't have a JSContext on the stack");
1832 if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
1833 NS_ERROR("Refresh driver should not run during plugin call!");
1834 // Try to survive this by just ignoring the refresh tick.
1835 return;
1838 AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", LAYOUT);
1840 // We're either frozen or we were disconnected (likely in the middle
1841 // of a tick iteration). Just do nothing here, since our
1842 // prescontext went away.
1843 if (IsFrozen() || !mPresContext) {
1844 return;
1847 // We can have a race condition where the vsync timestamp
1848 // is before the most recent refresh due to a forced refresh.
1849 // The underlying assumption is that the refresh driver tick can only
1850 // go forward in time, not backwards. To prevent the refresh
1851 // driver from going back in time, just skip this tick and
1852 // wait until the next tick.
1853 if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
1854 return;
1857 bool isPresentingInVR = false;
1858 #if defined(MOZ_WIDGET_ANDROID)
1859 isPresentingInVR = gfx::VRManagerChild::IsPresenting();
1860 #endif // defined(MOZ_WIDGET_ANDROID)
1862 if (!isPresentingInVR && IsWaitingForPaint(aNowTime)) {
1863 // In immersive VR mode, we do not get notifications when frames are
1864 // presented, so we do not wait for the compositor in that mode.
1866 // We're currently suspended waiting for earlier Tick's to
1867 // be completed (on the Compositor). Mark that we missed the paint
1868 // and keep waiting.
1869 PROFILER_ADD_MARKER("nsRefreshDriver::Tick waiting for paint", LAYOUT);
1870 return;
1873 TimeStamp previousRefresh = mMostRecentRefresh;
1874 mMostRecentRefresh = aNowTime;
1876 if (mRootRefresh) {
1877 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1878 mRootRefresh = nullptr;
1880 mSkippedPaints = false;
1881 mWarningThreshold = 1;
1883 RefPtr<PresShell> presShell = mPresContext->GetPresShell();
1884 if (!presShell ||
1885 (!HasObservers() && !HasImageRequests() &&
1886 !mNeedToUpdateIntersectionObservations &&
1887 mVisualViewportResizeEvents.IsEmpty() && mScrollEvents.IsEmpty() &&
1888 mVisualViewportScrollEvents.IsEmpty())) {
1889 // Things are being destroyed, or we no longer have any observers.
1890 // We don't want to stop the timer when observers are initially
1891 // removed, because sometimes observers can be added and removed
1892 // often depending on what other things are going on and in that
1893 // situation we don't want to thrash our timer. So instead we
1894 // wait until we get a Notify() call when we have no observers
1895 // before stopping the timer.
1896 // On top level content pages keep the timer running initially so that we
1897 // paint the page soon enough.
1898 if (presShell && !mThrottled && !mTestControllingRefreshes &&
1899 XRE_IsContentProcess() &&
1900 mPresContext->Document()->IsTopLevelContentDocument() &&
1901 !gfxPlatform::IsInLayoutAsapMode() &&
1902 !mPresContext->HadContentfulPaint() &&
1903 mPresContext->Document()->GetReadyStateEnum() <
1904 Document::READYSTATE_COMPLETE) {
1905 if (mInitialTimerRunningLimit.IsNull()) {
1906 mInitialTimerRunningLimit =
1907 TimeStamp::Now() + TimeDuration::FromSeconds(4.0f);
1908 // Don't let the timer to run forever, so limit to 4s for now.
1909 } else if (mInitialTimerRunningLimit < TimeStamp::Now()) {
1910 StopTimer();
1912 } else {
1913 StopTimer();
1915 return;
1918 mResizeSuppressed = false;
1920 bool oldInRefresh = mInRefresh;
1921 auto restoreInRefresh = MakeScopeExit([&] { mInRefresh = oldInRefresh; });
1922 mInRefresh = true;
1924 AutoRestore<TimeStamp> restoreTickStart(mTickStart);
1925 mTickStart = TimeStamp::Now();
1926 mTickVsyncId = aId;
1927 mTickVsyncTime = aNowTime;
1929 gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
1931 // We want to process any pending APZ metrics ahead of their positions
1932 // in the queue. This will prevent us from spending precious time
1933 // painting a stale displayport.
1934 if (StaticPrefs::apz_peek_messages_enabled()) {
1935 nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
1938 AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners;
1939 earlyRunners.SwapElements(mEarlyRunners);
1940 for (auto& runner : earlyRunners) {
1941 runner->Run();
1944 // Resize events should be fired before layout flushes or
1945 // calling animation frame callbacks.
1946 AutoTArray<RefPtr<PresShell>, 16> observers;
1947 observers.AppendElements(mResizeEventFlushObservers);
1948 for (RefPtr<PresShell>& presShell : Reversed(observers)) {
1949 if (!mPresContext || !mPresContext->GetPresShell()) {
1950 StopTimer();
1951 return;
1953 // Make sure to not process observers which might have been removed
1954 // during previous iterations.
1955 if (!mResizeEventFlushObservers.RemoveElement(presShell)) {
1956 continue;
1958 // MOZ_KnownLive because 'observers' is guaranteed to
1959 // keep it alive.
1961 // Fixing https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 on its own
1962 // won't help here, because 'observers' is non-const and we have the
1963 // Reversed() going on too...
1964 MOZ_KnownLive(presShell)->FireResizeEvent();
1966 DispatchVisualViewportResizeEvents();
1968 double phaseMetrics[MOZ_ARRAY_LENGTH(mObservers)] = {
1969 0.0,
1973 * The timer holds a reference to |this| while calling |Notify|.
1974 * However, implementations of |WillRefresh| are permitted to destroy
1975 * the pres context, which will cause our |mPresContext| to become
1976 * null. If this happens, we must stop notifying observers.
1978 for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
1979 AutoRecordPhase phaseRecord(&phaseMetrics[i]);
1981 for (RefPtr<nsARefreshObserver> obs : mObservers[i].EndLimitedRange()) {
1982 obs->WillRefresh(aNowTime);
1984 if (!mPresContext || !mPresContext->GetPresShell()) {
1985 StopTimer();
1986 return;
1990 // Any animation timelines updated above may cause animations to queue
1991 // Promise resolution microtasks. We shouldn't run these, however, until we
1992 // have fully updated the animation state.
1994 // As per the "update animations and send events" procedure[1], we should
1995 // remove replaced animations and then run these microtasks before
1996 // dispatching the corresponding animation events.
1998 // [1]
1999 // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
2000 if (i == 1) {
2001 nsAutoMicroTask mt;
2002 ReduceAnimations(*mPresContext->Document());
2005 // Check if running the microtask checkpoint caused the pres context to
2006 // be destroyed.
2007 if (i == 1 && (!mPresContext || !mPresContext->GetPresShell())) {
2008 StopTimer();
2009 return;
2012 if (i == 1) {
2013 // This is the FlushType::Style case.
2015 DispatchScrollEvents();
2016 DispatchVisualViewportScrollEvents();
2017 DispatchAnimationEvents();
2018 RunFullscreenSteps();
2019 RunFrameRequestCallbacks(aNowTime);
2021 if (mPresContext && mPresContext->GetPresShell()) {
2022 AutoTArray<PresShell*, 16> observers;
2023 observers.AppendElements(mStyleFlushObservers);
2024 for (uint32_t j = observers.Length();
2025 j && mPresContext && mPresContext->GetPresShell(); --j) {
2026 // Make sure to not process observers which might have been removed
2027 // during previous iterations.
2028 PresShell* rawPresShell = observers[j - 1];
2029 if (!mStyleFlushObservers.RemoveElement(rawPresShell)) {
2030 continue;
2032 RefPtr<PresShell> presShell = rawPresShell;
2033 presShell->mObservingStyleFlushes = false;
2034 presShell->FlushPendingNotifications(
2035 ChangesToFlush(FlushType::Style, false));
2036 // Inform the FontFaceSet that we ticked, so that it can resolve its
2037 // ready promise if it needs to (though it might still be waiting on
2038 // a layout flush).
2039 presShell->NotifyFontFaceSetOnRefresh();
2040 mNeedToRecomputeVisibility = true;
2042 // Record the telemetry for events that occurred between ticks.
2043 presShell->PingPerTickTelemetry(FlushType::Style);
2046 } else if (i == 2) {
2047 // This is the FlushType::Layout case.
2048 AutoTArray<PresShell*, 16> observers;
2049 observers.AppendElements(mLayoutFlushObservers);
2050 for (uint32_t j = observers.Length();
2051 j && mPresContext && mPresContext->GetPresShell(); --j) {
2052 // Make sure to not process observers which might have been removed
2053 // during previous iterations.
2054 PresShell* rawPresShell = observers[j - 1];
2055 if (!mLayoutFlushObservers.RemoveElement(rawPresShell)) {
2056 continue;
2058 RefPtr<PresShell> presShell = rawPresShell;
2059 presShell->mObservingLayoutFlushes = false;
2060 presShell->mWasLastReflowInterrupted = false;
2061 FlushType flushType = HasPendingAnimations(presShell)
2062 ? FlushType::Layout
2063 : FlushType::InterruptibleLayout;
2064 presShell->FlushPendingNotifications(ChangesToFlush(flushType, false));
2065 // Inform the FontFaceSet that we ticked, so that it can resolve its
2066 // ready promise if it needs to.
2067 presShell->NotifyFontFaceSetOnRefresh();
2068 mNeedToRecomputeVisibility = true;
2070 // Record the telemetry for events that occurred between ticks.
2071 presShell->PingPerTickTelemetry(FlushType::Layout);
2075 // The pres context may be destroyed during we do the flushing.
2076 if (!mPresContext || !mPresContext->GetPresShell()) {
2077 StopTimer();
2078 return;
2082 // Recompute approximate frame visibility if it's necessary and enough time
2083 // has passed since the last time we did it.
2084 if (mNeedToRecomputeVisibility && !mThrottled &&
2085 aNowTime >= mNextRecomputeVisibilityTick &&
2086 !presShell->IsPaintingSuppressed()) {
2087 mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
2088 mNeedToRecomputeVisibility = false;
2090 presShell->ScheduleApproximateFrameVisibilityUpdateNow();
2093 #ifdef MOZ_XUL
2094 // Update any popups that may need to be moved or hidden due to their
2095 // anchor changing.
2096 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
2097 pm->UpdatePopupPositions(this);
2099 #endif
2101 UpdateIntersectionObservations();
2104 * Perform notification to imgIRequests subscribed to listen
2105 * for refresh events.
2108 for (auto iter = mStartTable.Iter(); !iter.Done(); iter.Next()) {
2109 const uint32_t& delay = iter.Key();
2110 ImageStartData* data = iter.UserData();
2112 if (data->mStartTime) {
2113 TimeStamp& start = *data->mStartTime;
2114 TimeDuration prev = previousRefresh - start;
2115 TimeDuration curr = aNowTime - start;
2116 uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
2118 // We want to trigger images' refresh if we've just crossed over a
2119 // multiple of the first image's start time. If so, set the animation
2120 // start time to the nearest multiple of the delay and move all the
2121 // images in this table to the main requests table.
2122 if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
2123 mozilla::TimeStamp desired =
2124 start + TimeDuration::FromMilliseconds(prevMultiple * delay);
2125 BeginRefreshingImages(data->mEntries, desired);
2127 } else {
2128 // This is the very first time we've drawn images with this time delay.
2129 // Set the animation start time to "now" and move all the images in this
2130 // table to the main requests table.
2131 mozilla::TimeStamp desired = aNowTime;
2132 BeginRefreshingImages(data->mEntries, desired);
2133 data->mStartTime.emplace(aNowTime);
2137 if (mRequests.Count()) {
2138 // RequestRefresh may run scripts, so it's not safe to directly call it
2139 // while using a hashtable enumerator to enumerate mRequests in case
2140 // script modifies the hashtable. Instead, we build a (local) array of
2141 // images to refresh, and then we refresh each image in that array.
2142 nsCOMArray<imgIContainer> imagesToRefresh(mRequests.Count());
2144 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
2145 nsISupportsHashKey* entry = iter.Get();
2146 auto req = static_cast<imgIRequest*>(entry->GetKey());
2147 MOZ_ASSERT(req, "Unable to retrieve the image request");
2148 nsCOMPtr<imgIContainer> image;
2149 if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2150 imagesToRefresh.AppendElement(image.forget());
2154 for (uint32_t i = 0; i < imagesToRefresh.Length(); i++) {
2155 imagesToRefresh[i]->RequestRefresh(aNowTime);
2159 double phasePaint = 0.0;
2160 bool dispatchRunnablesAfterTick = false;
2161 if (mViewManagerFlushIsPending) {
2162 AutoRecordPhase paintRecord(&phasePaint);
2163 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2165 nsTArray<nsDocShell*> profilingDocShells;
2166 GetProfileTimelineSubDocShells(GetDocShell(mPresContext),
2167 profilingDocShells);
2168 for (nsDocShell* docShell : profilingDocShells) {
2169 // For the sake of the profile timeline's simplicity, this is flagged as
2170 // paint even if it includes creating display lists
2171 MOZ_ASSERT(timelines);
2172 MOZ_ASSERT(timelines->HasConsumer(docShell));
2173 timelines->AddMarkerForDocShell(docShell, "Paint",
2174 MarkerTracingType::START);
2177 #ifdef MOZ_DUMP_PAINTING
2178 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2179 printf_stderr("Starting ProcessPendingUpdates\n");
2181 #endif
2183 mViewManagerFlushIsPending = false;
2184 RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
2185 const bool skipPaint = isPresentingInVR;
2186 // Skip the paint in immersive VR mode because whatever we paint here will
2187 // not end up on the screen. The screen is displaying WebGL content from a
2188 // single canvas in that mode.
2189 if (!skipPaint) {
2190 PaintTelemetry::AutoRecordPaint record;
2191 vm->ProcessPendingUpdates();
2194 #ifdef MOZ_DUMP_PAINTING
2195 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2196 printf_stderr("Ending ProcessPendingUpdates\n");
2198 #endif
2200 for (nsDocShell* docShell : profilingDocShells) {
2201 MOZ_ASSERT(timelines);
2202 MOZ_ASSERT(timelines->HasConsumer(docShell));
2203 timelines->AddMarkerForDocShell(docShell, "Paint",
2204 MarkerTracingType::END);
2207 dispatchRunnablesAfterTick = true;
2208 mHasScheduleFlush = false;
2211 double totalMs = (TimeStamp::Now() - mTickStart).ToMilliseconds();
2213 #ifndef ANDROID /* bug 1142079 */
2214 mozilla::Telemetry::Accumulate(mozilla::Telemetry::REFRESH_DRIVER_TICK,
2215 static_cast<uint32_t>(totalMs));
2216 #endif
2218 // Bug 1568107: If the totalMs is greater than 1/60th second (ie. 1000/60 ms)
2219 // then record, via telemetry, the percentage of time spent in each
2220 // sub-system.
2221 if (totalMs > 1000.0 / 60.0) {
2222 auto record = [=](const nsCString& aKey, double aDurationMs) -> void {
2223 MOZ_ASSERT(aDurationMs <= totalMs);
2224 auto phasePercent = static_cast<uint32_t>(aDurationMs * 100.0 / totalMs);
2225 Telemetry::Accumulate(Telemetry::REFRESH_DRIVER_TICK_PHASE_WEIGHT, aKey,
2226 phasePercent);
2229 record("Event"_ns, phaseMetrics[0]);
2230 record("Style"_ns, phaseMetrics[1]);
2231 record("Reflow"_ns, phaseMetrics[2]);
2232 record("Display"_ns, phaseMetrics[3]);
2233 record("Paint"_ns, phasePaint);
2235 // Explicitly record the time unaccounted for.
2236 double other = totalMs -
2237 std::accumulate(phaseMetrics, ArrayEnd(phaseMetrics), 0.0) -
2238 phasePaint;
2239 record("Other"_ns, other);
2242 if (mNotifyDOMContentFlushed) {
2243 mNotifyDOMContentFlushed = false;
2244 mPresContext->NotifyDOMContentFlushed();
2247 for (nsAPostRefreshObserver* observer :
2248 mPostRefreshObservers.ForwardRange()) {
2249 observer->DidRefresh();
2252 NS_ASSERTION(mInRefresh, "Still in refresh");
2254 if (mPresContext->IsRoot() && XRE_IsContentProcess() &&
2255 StaticPrefs::gfx_content_always_paint()) {
2256 ScheduleViewManagerFlush();
2259 if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
2260 AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
2261 sPendingIdleRunnables = nullptr;
2262 for (RunnableWithDelay& runnableWithDelay : *runnables) {
2263 NS_DispatchToCurrentThreadQueue(runnableWithDelay.mRunnable.forget(),
2264 runnableWithDelay.mDelay,
2265 EventQueuePriority::Idle);
2267 delete runnables;
2271 void nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
2272 mozilla::TimeStamp aDesired) {
2273 for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
2274 auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
2275 MOZ_ASSERT(req, "Unable to retrieve the image request");
2277 mRequests.PutEntry(req);
2279 nsCOMPtr<imgIContainer> image;
2280 if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2281 image->SetAnimationStartTime(aDesired);
2284 aEntries.Clear();
2287 void nsRefreshDriver::Freeze() {
2288 StopTimer();
2289 mFreezeCount++;
2292 void nsRefreshDriver::Thaw() {
2293 NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
2295 if (mFreezeCount > 0) {
2296 mFreezeCount--;
2299 if (mFreezeCount == 0) {
2300 if (HasObservers() || HasImageRequests()) {
2301 // FIXME: This isn't quite right, since our EnsureTimerStarted call
2302 // updates our mMostRecentRefresh, but the DoRefresh call won't run
2303 // and notify our observers until we get back to the event loop.
2304 // Thus MostRecentRefresh() will lie between now and the DoRefresh.
2305 RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
2306 "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
2307 nsPresContext* pc = GetPresContext();
2308 if (pc) {
2309 pc->Document()->Dispatch(TaskCategory::Other, event.forget());
2310 EnsureTimerStarted();
2311 } else {
2312 NS_ERROR("Thawing while document is being destroyed");
2318 void nsRefreshDriver::FinishedWaitingForTransaction() {
2319 mWaitingForTransaction = false;
2320 mSkippedPaints = false;
2321 mWarningThreshold = 1;
2324 mozilla::layers::TransactionId nsRefreshDriver::GetTransactionId(
2325 bool aThrottle) {
2326 mOutstandingTransactionId = mOutstandingTransactionId.Next();
2327 mNextTransactionId = mNextTransactionId.Next();
2329 if (aThrottle && mOutstandingTransactionId - mCompletedTransaction >= 2 &&
2330 !mWaitingForTransaction && !mTestControllingRefreshes) {
2331 mWaitingForTransaction = true;
2332 mSkippedPaints = false;
2333 mWarningThreshold = 1;
2336 return mNextTransactionId;
2339 mozilla::layers::TransactionId nsRefreshDriver::LastTransactionId() const {
2340 return mNextTransactionId;
2343 void nsRefreshDriver::RevokeTransactionId(
2344 mozilla::layers::TransactionId aTransactionId) {
2345 MOZ_ASSERT(aTransactionId == mNextTransactionId);
2346 if (mOutstandingTransactionId - mCompletedTransaction == 2 &&
2347 mWaitingForTransaction) {
2348 MOZ_ASSERT(!mSkippedPaints,
2349 "How did we skip a paint when we're in the middle of one?");
2350 FinishedWaitingForTransaction();
2353 // Notify the pres context so that it can deliver MozAfterPaint for this
2354 // id if any caller was expecting it.
2355 nsPresContext* pc = GetPresContext();
2356 if (pc) {
2357 pc->NotifyRevokingDidPaint(aTransactionId);
2359 // Revert the outstanding transaction since we're no longer waiting on it to
2360 // be completed, but don't revert mNextTransactionId since we can't use the id
2361 // again.
2362 mOutstandingTransactionId = mOutstandingTransactionId.Prev();
2365 void nsRefreshDriver::ClearPendingTransactions() {
2366 mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId;
2367 mWaitingForTransaction = false;
2370 void nsRefreshDriver::ResetInitialTransactionId(
2371 mozilla::layers::TransactionId aTransactionId) {
2372 mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId =
2373 aTransactionId;
2376 mozilla::TimeStamp nsRefreshDriver::GetTransactionStart() { return mTickStart; }
2378 VsyncId nsRefreshDriver::GetVsyncId() { return mTickVsyncId; }
2380 mozilla::TimeStamp nsRefreshDriver::GetVsyncStart() { return mTickVsyncTime; }
2382 void nsRefreshDriver::NotifyTransactionCompleted(
2383 mozilla::layers::TransactionId aTransactionId) {
2384 if (aTransactionId > mCompletedTransaction) {
2385 if (mOutstandingTransactionId - mCompletedTransaction > 1 &&
2386 mWaitingForTransaction) {
2387 mCompletedTransaction = aTransactionId;
2388 FinishedWaitingForTransaction();
2389 } else {
2390 mCompletedTransaction = aTransactionId;
2394 // If completed transaction id get ahead of outstanding id, reset to distance
2395 // id.
2396 if (mCompletedTransaction > mOutstandingTransactionId) {
2397 mOutstandingTransactionId = mCompletedTransaction;
2401 void nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime) {
2402 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2403 mRootRefresh = nullptr;
2404 if (mSkippedPaints) {
2405 DoRefresh();
2409 bool nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime) {
2410 if (mTestControllingRefreshes) {
2411 return false;
2414 if (mWaitingForTransaction) {
2415 if (mSkippedPaints &&
2416 aTime > (mMostRecentRefresh +
2417 TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
2418 // XXX - Bug 1303369 - too many false positives.
2419 // gfxCriticalNote << "Refresh driver waiting for the compositor for "
2420 // << (aTime - mMostRecentRefresh).ToSeconds()
2421 // << " seconds.";
2422 mWarningThreshold *= 2;
2425 mSkippedPaints = true;
2426 return true;
2429 // Try find the 'root' refresh driver for the current window and check
2430 // if that is waiting for a paint.
2431 nsPresContext* pc = GetPresContext();
2432 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
2433 if (rootContext) {
2434 nsRefreshDriver* rootRefresh = rootContext->RefreshDriver();
2435 if (rootRefresh && rootRefresh != this) {
2436 if (rootRefresh->IsWaitingForPaint(aTime)) {
2437 if (mRootRefresh != rootRefresh) {
2438 if (mRootRefresh) {
2439 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2441 rootRefresh->AddRefreshObserver(this, FlushType::Style);
2442 mRootRefresh = rootRefresh;
2444 mSkippedPaints = true;
2445 return true;
2449 return false;
2452 void nsRefreshDriver::SetThrottled(bool aThrottled) {
2453 if (aThrottled != mThrottled) {
2454 mThrottled = aThrottled;
2455 if (mActiveTimer) {
2456 // We want to switch our timer type here, so just stop and
2457 // restart the timer.
2458 EnsureTimerStarted(eForceAdjustTimer);
2463 /*static*/
2464 void nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild) {
2465 MOZ_ASSERT(NS_IsMainThread());
2466 MOZ_ASSERT(!XRE_IsParentProcess());
2467 RefPtr<RefreshDriverTimer> vsyncRefreshDriverTimer =
2468 new VsyncRefreshDriverTimer(aVsyncChild);
2470 // If we are using software timer, swap current timer to
2471 // VsyncRefreshDriverTimer.
2472 if (sRegularRateTimer) {
2473 sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
2475 sRegularRateTimer = std::move(vsyncRefreshDriverTimer);
2478 void nsRefreshDriver::DoRefresh() {
2479 // Don't do a refresh unless we're in a state where we should be refreshing.
2480 if (!IsFrozen() && mPresContext && mActiveTimer) {
2481 DoTick();
2485 #ifdef DEBUG
2486 bool nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
2487 FlushType aFlushType) {
2488 ObserverArray& array = ArrayFor(aFlushType);
2489 return array.Contains(aObserver);
2491 #endif
2493 void nsRefreshDriver::ScheduleViewManagerFlush() {
2494 NS_ASSERTION(mPresContext->IsRoot(),
2495 "Should only schedule view manager flush on root prescontexts");
2496 mViewManagerFlushIsPending = true;
2497 mHasScheduleFlush = true;
2498 EnsureTimerStarted(eNeverAdjustTimer);
2501 void nsRefreshDriver::ScheduleFrameRequestCallbacks(Document* aDocument) {
2502 NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
2503 mFrameRequestCallbackDocs.NoIndex &&
2504 mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
2505 mThrottledFrameRequestCallbackDocs.NoIndex,
2506 "Don't schedule the same document multiple times");
2507 if (aDocument->ShouldThrottleFrameRequests()) {
2508 mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
2509 } else {
2510 mFrameRequestCallbackDocs.AppendElement(aDocument);
2513 // make sure that the timer is running
2514 EnsureTimerStarted();
2517 void nsRefreshDriver::RevokeFrameRequestCallbacks(Document* aDocument) {
2518 mFrameRequestCallbackDocs.RemoveElement(aDocument);
2519 mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
2520 // No need to worry about restarting our timer in slack mode if it's already
2521 // running; that will happen automatically when it fires.
2524 void nsRefreshDriver::ScheduleFullscreenEvent(
2525 UniquePtr<PendingFullscreenEvent> aEvent) {
2526 mPendingFullscreenEvents.AppendElement(std::move(aEvent));
2527 // make sure that the timer is running
2528 EnsureTimerStarted();
2531 void nsRefreshDriver::CancelPendingFullscreenEvents(Document* aDocument) {
2532 for (auto i : Reversed(IntegerRange(mPendingFullscreenEvents.Length()))) {
2533 if (mPendingFullscreenEvents[i]->Document() == aDocument) {
2534 mPendingFullscreenEvents.RemoveElementAt(i);
2539 void nsRefreshDriver::CancelPendingAnimationEvents(
2540 AnimationEventDispatcher* aDispatcher) {
2541 MOZ_ASSERT(aDispatcher);
2542 aDispatcher->ClearEventQueue();
2543 mAnimationEventFlushObservers.RemoveElement(aDispatcher);
2546 /* static */
2547 TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
2548 MOZ_ASSERT(NS_IsMainThread());
2549 MOZ_ASSERT(!aDefault.IsNull());
2551 if (!sRegularRateTimer) {
2552 return aDefault;
2555 // For computing idleness of refresh drivers we only care about
2556 // sRegularRateTimer, since we consider refresh drivers attached to
2557 // sThrottledRateTimer to be inactive. This implies that tasks
2558 // resulting from a tick on the sRegularRateTimer counts as being
2559 // busy but tasks resulting from a tick on sThrottledRateTimer
2560 // counts as being idle.
2561 return sRegularRateTimer->GetIdleDeadlineHint(aDefault);
2564 /* static */
2565 Maybe<TimeStamp> nsRefreshDriver::GetNextTickHint() {
2566 MOZ_ASSERT(NS_IsMainThread());
2568 if (!sRegularRateTimer) {
2569 return Nothing();
2571 return sRegularRateTimer->GetNextTickHint();
2574 void nsRefreshDriver::Disconnect() {
2575 MOZ_ASSERT(NS_IsMainThread());
2577 StopTimer();
2579 if (mPresContext) {
2580 mPresContext = nullptr;
2581 if (--sRefreshDriverCount == 0) {
2582 Shutdown();
2587 /* static */
2588 bool nsRefreshDriver::IsJankCritical() {
2589 MOZ_ASSERT(NS_IsMainThread());
2590 return sActiveVsyncTimers > 0;
2593 /* static */
2594 bool nsRefreshDriver::GetJankLevels(Vector<uint64_t>& aJank) {
2595 aJank.clear();
2596 return aJank.append(sJankLevels, ArrayLength(sJankLevels));
2599 #undef LOG