Bug 1468402 - Part 3: Add test for subgrids in the grid list. r=pbro
[gecko.git] / layout / base / nsRefreshDriver.cpp
blobbf35c03c786c6f462759b6f07b10b0afe4bef9a7
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/DebugOnly.h"
35 #include "mozilla/IntegerRange.h"
36 #include "mozilla/PresShell.h"
37 #include "mozilla/dom/FontTableURIProtocolHandler.h"
38 #include "nsITimer.h"
39 #include "nsLayoutUtils.h"
40 #include "nsPresContext.h"
41 #include "nsComponentManagerUtils.h"
42 #include "mozilla/Logging.h"
43 #include "mozilla/dom/Document.h"
44 #include "nsIXULRuntime.h"
45 #include "jsapi.h"
46 #include "nsContentUtils.h"
47 #include "mozilla/PendingAnimationTracker.h"
48 #include "mozilla/PendingFullscreenEvent.h"
49 #include "mozilla/Preferences.h"
50 #include "mozilla/StaticPrefs.h"
51 #include "nsViewManager.h"
52 #include "GeckoProfiler.h"
53 #include "nsNPAPIPluginInstance.h"
54 #include "mozilla/dom/Event.h"
55 #include "mozilla/dom/Performance.h"
56 #include "mozilla/dom/Selection.h"
57 #include "mozilla/dom/WindowBinding.h"
58 #include "mozilla/RestyleManager.h"
59 #include "Layers.h"
60 #include "imgIContainer.h"
61 #include "mozilla/dom/ScriptSettings.h"
62 #include "nsDocShell.h"
63 #include "nsISimpleEnumerator.h"
64 #include "nsJSEnvironment.h"
65 #include "mozilla/Telemetry.h"
66 #include "gfxPrefs.h"
67 #include "BackgroundChild.h"
68 #include "mozilla/ipc/PBackgroundChild.h"
69 #include "mozilla/layout/VsyncChild.h"
70 #include "VsyncSource.h"
71 #include "mozilla/VsyncDispatcher.h"
72 #include "nsThreadUtils.h"
73 #include "mozilla/Unused.h"
74 #include "mozilla/TimelineConsumers.h"
75 #include "nsAnimationManager.h"
76 #include "nsDisplayList.h"
77 #include "nsTransitionManager.h"
79 #if defined(MOZ_WIDGET_ANDROID)
80 # include "VRManager.h"
81 #endif // defined(MOZ_WIDGET_ANDROID)
83 #ifdef MOZ_XUL
84 # include "nsXULPopupManager.h"
85 #endif
87 using namespace mozilla;
88 using namespace mozilla::widget;
89 using namespace mozilla::ipc;
90 using namespace mozilla::dom;
91 using namespace mozilla::layout;
93 static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
94 #define LOG(...) \
95 MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
97 #define DEFAULT_THROTTLED_FRAME_RATE 1
98 #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
99 #define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
100 // after 10 minutes, stop firing off inactive timers
101 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
103 // The number of seconds spent skipping frames because we are waiting for the
104 // compositor before logging.
105 #if defined(MOZ_ASAN)
106 # define REFRESH_WAIT_WARNING 5
107 #elif defined(DEBUG) && !defined(MOZ_VALGRIND)
108 # define REFRESH_WAIT_WARNING 5
109 #elif defined(DEBUG) && defined(MOZ_VALGRIND)
110 # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
111 #elif defined(MOZ_VALGRIND)
112 # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
113 #else
114 # define REFRESH_WAIT_WARNING 1
115 #endif
117 namespace {
118 // `true` if we are currently in jank-critical mode.
120 // In jank-critical mode, any iteration of the event loop that takes
121 // more than 16ms to compute will cause an ongoing animation to miss
122 // frames.
124 // For simplicity, the current implementation assumes that we are in
125 // jank-critical mode if and only if at least one vsync driver has
126 // at least one observer.
127 static uint64_t sActiveVsyncTimers = 0;
129 // The latest value of process-wide jank levels.
131 // For each i, sJankLevels[i] counts the number of times delivery of
132 // vsync to the main thread has been delayed by at least 2^i ms. Use
133 // GetJankLevels to grab a copy of this array.
134 uint64_t sJankLevels[12];
136 // The number outstanding nsRefreshDrivers (that have been created but not
137 // disconnected). When this reaches zero we will call
138 // nsRefreshDriver::Shutdown.
139 static uint32_t sRefreshDriverCount = 0;
140 } // namespace
142 namespace mozilla {
145 * The base class for all global refresh driver timers. It takes care
146 * of managing the list of refresh drivers attached to them and
147 * provides interfaces for querying/setting the rate and actually
148 * running a timer 'Tick'. Subclasses must implement StartTimer(),
149 * StopTimer(), and ScheduleNextTick() -- the first two just
150 * start/stop whatever timer mechanism is in use, and ScheduleNextTick
151 * is called at the start of the Tick() implementation to set a time
152 * for the next tick.
154 class RefreshDriverTimer {
155 public:
156 RefreshDriverTimer() {}
158 NS_INLINE_DECL_REFCOUNTING(RefreshDriverTimer)
160 virtual void AddRefreshDriver(nsRefreshDriver* aDriver) {
161 LOG("[%p] AddRefreshDriver %p", this, aDriver);
163 bool startTimer =
164 mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
165 if (IsRootRefreshDriver(aDriver)) {
166 NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver),
167 "Adding a duplicate root refresh driver!");
168 mRootRefreshDrivers.AppendElement(aDriver);
169 } else {
170 NS_ASSERTION(!mContentRefreshDrivers.Contains(aDriver),
171 "Adding a duplicate content refresh driver!");
172 mContentRefreshDrivers.AppendElement(aDriver);
175 if (startTimer) {
176 StartTimer();
180 void RemoveRefreshDriver(nsRefreshDriver* aDriver) {
181 LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
183 if (IsRootRefreshDriver(aDriver)) {
184 NS_ASSERTION(mRootRefreshDrivers.Contains(aDriver),
185 "RemoveRefreshDriver for a refresh driver that's not in the "
186 "root refresh list!");
187 mRootRefreshDrivers.RemoveElement(aDriver);
188 } else {
189 nsPresContext* pc = aDriver->GetPresContext();
190 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
191 // During PresContext shutdown, we can't accurately detect
192 // if a root refresh driver exists or not. Therefore, we have to
193 // search and find out which list this driver exists in.
194 if (!rootContext) {
195 if (mRootRefreshDrivers.Contains(aDriver)) {
196 mRootRefreshDrivers.RemoveElement(aDriver);
197 } else {
198 NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
199 "RemoveRefreshDriver without a display root for a "
200 "driver that is not in the content refresh list");
201 mContentRefreshDrivers.RemoveElement(aDriver);
203 } else {
204 NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
205 "RemoveRefreshDriver for a driver that is not in the "
206 "content refresh list");
207 mContentRefreshDrivers.RemoveElement(aDriver);
211 bool stopTimer =
212 mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
213 if (stopTimer) {
214 StopTimer();
218 TimeStamp MostRecentRefresh() const { return mLastFireTime; }
220 void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer) {
221 MOZ_ASSERT(NS_IsMainThread());
223 for (nsRefreshDriver* driver : mContentRefreshDrivers) {
224 aNewTimer->AddRefreshDriver(driver);
225 driver->mActiveTimer = aNewTimer;
227 mContentRefreshDrivers.Clear();
229 for (nsRefreshDriver* driver : mRootRefreshDrivers) {
230 aNewTimer->AddRefreshDriver(driver);
231 driver->mActiveTimer = aNewTimer;
233 mRootRefreshDrivers.Clear();
235 aNewTimer->mLastFireTime = mLastFireTime;
237 StopTimer();
240 virtual TimeDuration GetTimerRate() = 0;
242 TimeStamp GetIdleDeadlineHint(TimeStamp aDefault) {
243 MOZ_ASSERT(NS_IsMainThread());
245 TimeStamp mostRecentRefresh = MostRecentRefresh();
246 TimeDuration refreshRate = GetTimerRate();
247 TimeStamp idleEnd = mostRecentRefresh + refreshRate;
249 if (idleEnd +
250 refreshRate * nsLayoutUtils::QuiescentFramesBeforeIdlePeriod() <
251 TimeStamp::Now()) {
252 return aDefault;
255 idleEnd = idleEnd - TimeDuration::FromMilliseconds(
256 nsLayoutUtils::IdlePeriodDeadlineLimit());
257 return idleEnd < aDefault ? idleEnd : aDefault;
260 Maybe<TimeStamp> GetNextTickHint() {
261 MOZ_ASSERT(NS_IsMainThread());
262 TimeStamp nextTick = MostRecentRefresh() + GetTimerRate();
263 return nextTick < TimeStamp::Now() ? Nothing() : Some(nextTick);
266 // Returns null if the RefreshDriverTimer is attached to several
267 // RefreshDrivers. That may happen for example when there are
268 // several windows open.
269 nsPresContext* GetPresContextForOnlyRefreshDriver() {
270 if (mRootRefreshDrivers.Length() == 1 && mContentRefreshDrivers.IsEmpty()) {
271 return mRootRefreshDrivers[0]->GetPresContext();
273 if (mContentRefreshDrivers.Length() == 1 && mRootRefreshDrivers.IsEmpty()) {
274 return mContentRefreshDrivers[0]->GetPresContext();
276 return nullptr;
279 protected:
280 virtual ~RefreshDriverTimer() {
281 MOZ_ASSERT(
282 mContentRefreshDrivers.Length() == 0,
283 "Should have removed all content refresh drivers from here by now!");
284 MOZ_ASSERT(
285 mRootRefreshDrivers.Length() == 0,
286 "Should have removed all root refresh drivers from here by now!");
289 virtual void StartTimer() = 0;
290 virtual void StopTimer() = 0;
291 virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
293 bool IsRootRefreshDriver(nsRefreshDriver* aDriver) {
294 nsPresContext* pc = aDriver->GetPresContext();
295 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
296 if (!rootContext) {
297 return false;
300 return aDriver == rootContext->RefreshDriver();
304 * Actually runs a tick, poking all the attached RefreshDrivers.
305 * Grabs the "now" time via TimeStamp::Now().
307 void Tick() {
308 TimeStamp now = TimeStamp::Now();
309 Tick(VsyncId(), now);
312 void TickRefreshDrivers(VsyncId aId, TimeStamp aNow,
313 nsTArray<RefPtr<nsRefreshDriver>>& aDrivers) {
314 if (aDrivers.IsEmpty()) {
315 return;
318 nsTArray<RefPtr<nsRefreshDriver>> drivers(aDrivers);
319 for (nsRefreshDriver* driver : drivers) {
320 // don't poke this driver if it's in test mode
321 if (driver->IsTestControllingRefreshesEnabled()) {
322 continue;
325 TickDriver(driver, aId, aNow);
330 * Tick the refresh drivers based on the given timestamp.
332 void Tick(VsyncId aId, TimeStamp now) {
333 ScheduleNextTick(now);
335 mLastFireTime = now;
337 LOG("[%p] ticking drivers...", this);
338 // RD is short for RefreshDriver
339 AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick", GRAPHICS);
341 TickRefreshDrivers(aId, now, mContentRefreshDrivers);
342 TickRefreshDrivers(aId, now, mRootRefreshDrivers);
344 LOG("[%p] done.", this);
347 static void TickDriver(nsRefreshDriver* driver, VsyncId aId, TimeStamp now) {
348 driver->Tick(aId, now);
351 TimeStamp mLastFireTime;
352 TimeStamp mTargetTime;
354 nsTArray<RefPtr<nsRefreshDriver>> mContentRefreshDrivers;
355 nsTArray<RefPtr<nsRefreshDriver>> mRootRefreshDrivers;
357 // useful callback for nsITimer-based derived classes, here
358 // because of c++ protected shenanigans
359 static void TimerTick(nsITimer* aTimer, void* aClosure) {
360 RefPtr<RefreshDriverTimer> timer =
361 static_cast<RefreshDriverTimer*>(aClosure);
362 timer->Tick();
367 * A RefreshDriverTimer that uses a nsITimer as the underlying timer. Note that
368 * this is a ONE_SHOT timer, not a repeating one! Subclasses are expected to
369 * implement ScheduleNextTick and intelligently calculate the next time to tick,
370 * and to reset mTimer. Using a repeating nsITimer gets us into a lot of pain
371 * with its attempt at intelligent slack removal and such, so we don't do it.
373 class SimpleTimerBasedRefreshDriverTimer : public RefreshDriverTimer {
374 public:
376 * aRate -- the delay, in milliseconds, requested between timer firings
378 explicit SimpleTimerBasedRefreshDriverTimer(double aRate) {
379 SetRate(aRate);
380 mTimer = NS_NewTimer();
383 virtual ~SimpleTimerBasedRefreshDriverTimer() override { StopTimer(); }
385 // will take effect at next timer tick
386 virtual void SetRate(double aNewRate) {
387 mRateMilliseconds = aNewRate;
388 mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
391 double GetRate() const { return mRateMilliseconds; }
393 TimeDuration GetTimerRate() override { return mRateDuration; }
395 protected:
396 void StartTimer() override {
397 // pretend we just fired, and we schedule the next tick normally
398 mLastFireTime = TimeStamp::Now();
400 mTargetTime = mLastFireTime + mRateDuration;
402 uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
403 mTimer->InitWithNamedFuncCallback(
404 TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
405 "SimpleTimerBasedRefreshDriverTimer::StartTimer");
408 void StopTimer() override { mTimer->Cancel(); }
410 double mRateMilliseconds;
411 TimeDuration mRateDuration;
412 RefPtr<nsITimer> mTimer;
416 * A refresh driver that listens to vsync events and ticks the refresh driver
417 * on vsync intervals. We throttle the refresh driver if we get too many
418 * vsync events and wait to catch up again.
420 class VsyncRefreshDriverTimer : public RefreshDriverTimer {
421 public:
422 VsyncRefreshDriverTimer() : mVsyncChild(nullptr) {
423 MOZ_ASSERT(XRE_IsParentProcess());
424 MOZ_ASSERT(NS_IsMainThread());
425 mVsyncObserver = new RefreshDriverVsyncObserver(this);
426 RefPtr<mozilla::gfx::VsyncSource> vsyncSource =
427 gfxPlatform::GetPlatform()->GetHardwareVsync();
428 MOZ_ALWAYS_TRUE(mVsyncDispatcher =
429 vsyncSource->GetRefreshTimerVsyncDispatcher());
430 mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
431 mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
434 explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
435 : mVsyncChild(aVsyncChild) {
436 MOZ_ASSERT(!XRE_IsParentProcess());
437 MOZ_ASSERT(NS_IsMainThread());
438 MOZ_ASSERT(mVsyncChild);
439 mVsyncObserver = new RefreshDriverVsyncObserver(this);
440 mVsyncChild->SetVsyncObserver(mVsyncObserver);
441 mVsyncRate = mVsyncChild->GetVsyncRate();
444 TimeDuration GetTimerRate() override {
445 if (mVsyncRate != TimeDuration::Forever()) {
446 return mVsyncRate;
449 if (mVsyncChild) {
450 // VsyncChild::VsyncRate() is a simple getter for the cached
451 // hardware vsync rate. We depend on that
452 // VsyncChild::GetVsyncRate() being called in the constructor
453 // will result in a response with the actual vsync rate sooner
454 // or later. Until that happens VsyncChild::VsyncRate() returns
455 // TimeDuration::Forever() and we have to guess below.
456 mVsyncRate = mVsyncChild->VsyncRate();
459 // If hardware queries fail / are unsupported, we have to just guess.
460 return mVsyncRate != TimeDuration::Forever()
461 ? mVsyncRate
462 : TimeDuration::FromMilliseconds(1000.0 / 60.0);
465 private:
466 // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
467 // explicitly shutdown. We create an inner class that has the VsyncObserver
468 // and is shutdown when the RefreshDriverTimer is deleted.
469 class RefreshDriverVsyncObserver final : public VsyncObserver {
470 public:
471 explicit RefreshDriverVsyncObserver(
472 VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
473 : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer),
474 mRefreshTickLock("RefreshTickLock"),
475 mRecentVsync(TimeStamp::Now()),
476 mLastChildTick(TimeStamp::Now()),
477 mVsyncRate(TimeDuration::Forever()),
478 mProcessedVsync(true) {
479 MOZ_ASSERT(NS_IsMainThread());
482 class ParentProcessVsyncNotifier final : public Runnable,
483 public nsIRunnablePriority {
484 public:
485 ParentProcessVsyncNotifier(RefreshDriverVsyncObserver* aObserver,
486 VsyncId aId, TimeStamp aVsyncTimestamp)
487 : Runnable(
488 "VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::"
489 "ParentProcessVsyncNotifier"),
490 mObserver(aObserver),
491 mId(aId),
492 mVsyncTimestamp(aVsyncTimestamp) {}
494 NS_DECL_ISUPPORTS_INHERITED
496 NS_IMETHOD Run() override {
497 MOZ_ASSERT(NS_IsMainThread());
498 static bool sCacheInitialized = false;
499 static bool sHighPriorityPrefValue = false;
500 if (!sCacheInitialized) {
501 sCacheInitialized = true;
502 Preferences::AddBoolVarCache(&sHighPriorityPrefValue,
503 "vsync.parentProcess.highPriority",
504 mozilla::BrowserTabsRemoteAutostart());
506 sHighPriorityEnabled = sHighPriorityPrefValue;
508 mObserver->TickRefreshDriver(mId, mVsyncTimestamp);
509 return NS_OK;
512 NS_IMETHOD GetPriority(uint32_t* aPriority) override {
513 *aPriority = sHighPriorityEnabled
514 ? nsIRunnablePriority::PRIORITY_HIGH
515 : nsIRunnablePriority::PRIORITY_NORMAL;
516 return NS_OK;
519 private:
520 ~ParentProcessVsyncNotifier() {}
521 RefPtr<RefreshDriverVsyncObserver> mObserver;
522 VsyncId mId;
523 TimeStamp mVsyncTimestamp;
524 static mozilla::Atomic<bool> sHighPriorityEnabled;
527 bool NotifyVsync(const VsyncEvent& aVsync) override {
528 // IMPORTANT: All paths through this method MUST hold a strong ref on
529 // |this| for the duration of the TickRefreshDriver callback.
531 if (!NS_IsMainThread()) {
532 MOZ_ASSERT(XRE_IsParentProcess());
533 // Compress vsync notifications such that only 1 may run at a time
534 // This is so that we don't flood the refresh driver with vsync messages
535 // if the main thread is blocked for long periods of time
536 { // scope lock
537 MonitorAutoLock lock(mRefreshTickLock);
538 mRecentVsync = aVsync.mTime;
539 mRecentVsyncId = aVsync.mId;
540 if (!mProcessedVsync) {
541 return true;
543 mProcessedVsync = false;
546 nsCOMPtr<nsIRunnable> vsyncEvent =
547 new ParentProcessVsyncNotifier(this, aVsync.mId, aVsync.mTime);
548 NS_DispatchToMainThread(vsyncEvent);
549 } else {
550 mRecentVsync = aVsync.mTime;
551 mRecentVsyncId = aVsync.mId;
552 if (!mBlockUntil.IsNull() && mBlockUntil > aVsync.mTime) {
553 if (mProcessedVsync) {
554 // Re-post vsync update as a normal priority runnable. This way
555 // runnables already in normal priority queue get processed.
556 mProcessedVsync = false;
557 nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<>(
558 "RefreshDriverVsyncObserver::NormalPriorityNotify", this,
559 &RefreshDriverVsyncObserver::NormalPriorityNotify);
560 NS_DispatchToMainThread(vsyncEvent);
563 return true;
566 if (StaticPrefs::layout_lower_priority_refresh_driver_during_load()) {
567 nsPresContext* pctx =
568 mVsyncRefreshDriverTimer->GetPresContextForOnlyRefreshDriver();
569 if (pctx && pctx->HadContentfulPaint() && pctx->Document() &&
570 pctx->Document()->GetReadyStateEnum() <
571 Document::READYSTATE_COMPLETE) {
572 nsPIDOMWindowInner* win = pctx->Document()->GetInnerWindow();
573 uint32_t frameRateMultiplier = pctx->GetNextFrameRateMultiplier();
574 if (!frameRateMultiplier) {
575 pctx->DidUseFrameRateMultiplier();
577 if (win && frameRateMultiplier) {
578 dom::Performance* perf = win->GetPerformance();
579 // Limit slower refresh rate to 5 seconds between the
580 // first contentful paint and page load.
581 if (perf && perf->Now() < 5000) {
582 if (mProcessedVsync) {
583 mProcessedVsync = false;
584 // Handle this case similarly to the code above, but just
585 // use idle queue.
586 TimeDuration rate = mVsyncRefreshDriverTimer->GetTimerRate();
587 uint32_t slowRate = static_cast<uint32_t>(
588 rate.ToMilliseconds() * frameRateMultiplier);
589 pctx->DidUseFrameRateMultiplier();
590 nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<>(
591 "RefreshDriverVsyncObserver::NormalPriorityNotify[IDLE]",
592 this, &RefreshDriverVsyncObserver::NormalPriorityNotify);
593 NS_DispatchToCurrentThreadQueue(vsyncEvent.forget(), slowRate,
594 EventQueuePriority::Idle);
596 return true;
602 RefPtr<RefreshDriverVsyncObserver> kungFuDeathGrip(this);
603 TickRefreshDriver(aVsync.mId, aVsync.mTime);
606 return true;
609 void Shutdown() {
610 MOZ_ASSERT(NS_IsMainThread());
611 mVsyncRefreshDriverTimer = nullptr;
614 void OnTimerStart() {
615 if (!XRE_IsParentProcess()) {
616 mLastChildTick = TimeStamp::Now();
620 void NormalPriorityNotify() {
621 if (mLastProcessedTickInChildProcess.IsNull() ||
622 mRecentVsync > mLastProcessedTickInChildProcess) {
623 // mBlockUntil is for high priority vsync notifications only.
624 mBlockUntil = TimeStamp();
625 TickRefreshDriver(mRecentVsyncId, mRecentVsync);
628 mProcessedVsync = true;
631 private:
632 ~RefreshDriverVsyncObserver() = default;
634 void RecordTelemetryProbes(TimeStamp aVsyncTimestamp) {
635 MOZ_ASSERT(NS_IsMainThread());
636 #ifndef ANDROID /* bug 1142079 */
637 if (XRE_IsParentProcess()) {
638 TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
639 uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
640 Telemetry::Accumulate(
641 Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS, sample);
642 Telemetry::Accumulate(
643 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, sample);
644 RecordJank(sample);
645 } else if (mVsyncRate != TimeDuration::Forever()) {
646 TimeDuration contentDelay =
647 (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
648 if (contentDelay.ToMilliseconds() < 0) {
649 // Vsyncs are noisy and some can come at a rate quicker than
650 // the reported hardware rate. In those cases, consider that we have 0
651 // delay.
652 contentDelay = TimeDuration::FromMilliseconds(0);
654 uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
655 Telemetry::Accumulate(
656 Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS, sample);
657 Telemetry::Accumulate(
658 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, sample);
659 RecordJank(sample);
660 } else {
661 // Request the vsync rate from the parent process. Might be a few vsyncs
662 // until the parent responds.
663 if (mVsyncRefreshDriverTimer) {
664 mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
667 #endif
670 void RecordJank(uint32_t aJankMS) {
671 uint32_t duration = 1 /* ms */;
672 for (size_t i = 0;
673 i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
674 ++i, duration *= 2) {
675 sJankLevels[i]++;
679 void TickRefreshDriver(VsyncId aId, TimeStamp aVsyncTimestamp) {
680 MOZ_ASSERT(NS_IsMainThread());
682 RecordTelemetryProbes(aVsyncTimestamp);
683 if (XRE_IsParentProcess()) {
684 MonitorAutoLock lock(mRefreshTickLock);
685 aVsyncTimestamp = mRecentVsync;
686 mProcessedVsync = true;
687 } else {
688 mLastChildTick = TimeStamp::Now();
689 mLastProcessedTickInChildProcess = aVsyncTimestamp;
692 // On 32-bit Windows we sometimes get times where TimeStamp::Now() is not
693 // monotonic because the underlying system apis produce non-monontonic
694 // results. (bug 1306896)
695 #if !defined(_WIN32)
696 // Do not compare timestamps unless they are both canonical or fuzzy
697 DebugOnly<TimeStamp> rightnow = TimeStamp::Now();
698 MOZ_ASSERT_IF(
699 (*&rightnow).UsedCanonicalNow() == aVsyncTimestamp.UsedCanonicalNow(),
700 aVsyncTimestamp <= *&rightnow);
701 #endif
703 // We might have a problem that we call ~VsyncRefreshDriverTimer() before
704 // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
705 // before use.
706 if (mVsyncRefreshDriverTimer) {
707 RefPtr<VsyncRefreshDriverTimer> timer = mVsyncRefreshDriverTimer;
708 timer->RunRefreshDrivers(aId, aVsyncTimestamp);
709 // Note: mVsyncRefreshDriverTimer might be null now.
712 if (!XRE_IsParentProcess()) {
713 TimeDuration tickDuration = TimeStamp::Now() - mLastChildTick;
714 mBlockUntil = aVsyncTimestamp + tickDuration;
718 // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
719 // be always available before Shutdown(). We can just use the raw pointer
720 // here.
721 VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
722 Monitor mRefreshTickLock;
723 TimeStamp mRecentVsync;
724 VsyncId mRecentVsyncId;
725 TimeStamp mLastChildTick;
726 TimeStamp mLastProcessedTickInChildProcess;
727 TimeStamp mBlockUntil;
728 TimeDuration mVsyncRate;
729 bool mProcessedVsync;
730 }; // RefreshDriverVsyncObserver
732 ~VsyncRefreshDriverTimer() override {
733 if (XRE_IsParentProcess()) {
734 mVsyncDispatcher->SetParentRefreshTimer(nullptr);
735 mVsyncDispatcher = nullptr;
736 } else {
737 // Since the PVsyncChild actors live through the life of the process, just
738 // send the unobserveVsync message to disable vsync event. We don't need
739 // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
740 // will be called and clean up this actor.
741 Unused << mVsyncChild->SendUnobserve();
742 mVsyncChild->SetVsyncObserver(nullptr);
743 mVsyncChild = nullptr;
746 // Detach current vsync timer from this VsyncObserver. The observer will no
747 // longer tick this timer.
748 mVsyncObserver->Shutdown();
749 mVsyncObserver = nullptr;
752 void StartTimer() override {
753 // Protect updates to `sActiveVsyncTimers`.
754 MOZ_ASSERT(NS_IsMainThread());
756 mLastFireTime = TimeStamp::Now();
758 if (XRE_IsParentProcess()) {
759 mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
760 } else {
761 Unused << mVsyncChild->SendObserve();
762 mVsyncObserver->OnTimerStart();
765 ++sActiveVsyncTimers;
768 void StopTimer() override {
769 // Protect updates to `sActiveVsyncTimers`.
770 MOZ_ASSERT(NS_IsMainThread());
772 if (XRE_IsParentProcess()) {
773 mVsyncDispatcher->SetParentRefreshTimer(nullptr);
774 } else {
775 Unused << mVsyncChild->SendUnobserve();
778 MOZ_ASSERT(sActiveVsyncTimers > 0);
779 --sActiveVsyncTimers;
782 void ScheduleNextTick(TimeStamp aNowTime) override {
783 // Do nothing since we just wait for the next vsync from
784 // RefreshDriverVsyncObserver.
787 void RunRefreshDrivers(VsyncId aId, TimeStamp aTimeStamp) {
788 Tick(aId, aTimeStamp);
791 RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
792 // Used for parent process.
793 RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
794 // Used for child process.
795 // The mVsyncChild will be always available before VsncChild::ActorDestroy().
796 // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
797 RefPtr<VsyncChild> mVsyncChild;
798 TimeDuration mVsyncRate;
799 }; // VsyncRefreshDriverTimer
801 NS_IMPL_ISUPPORTS_INHERITED(
802 VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::
803 ParentProcessVsyncNotifier,
804 Runnable, nsIRunnablePriority)
806 mozilla::Atomic<bool> VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::
807 ParentProcessVsyncNotifier::sHighPriorityEnabled(false);
810 * Since the content process takes some time to setup
811 * the vsync IPC connection, this timer is used
812 * during the intial startup process.
813 * During initial startup, the refresh drivers
814 * are ticked off this timer, and are swapped out once content
815 * vsync IPC connection is established.
817 class StartupRefreshDriverTimer : public SimpleTimerBasedRefreshDriverTimer {
818 public:
819 explicit StartupRefreshDriverTimer(double aRate)
820 : SimpleTimerBasedRefreshDriverTimer(aRate) {}
822 protected:
823 void ScheduleNextTick(TimeStamp aNowTime) override {
824 // Since this is only used for startup, it isn't super critical
825 // that we tick at consistent intervals.
826 TimeStamp newTarget = aNowTime + mRateDuration;
827 uint32_t delay =
828 static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
829 mTimer->InitWithNamedFuncCallback(
830 TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
831 "StartupRefreshDriverTimer::ScheduleNextTick");
832 mTargetTime = newTarget;
837 * A RefreshDriverTimer for inactive documents. When a new refresh driver is
838 * added, the rate is reset to the base (normally 1s/1fps). Every time
839 * it ticks, a single refresh driver is poked. Once they have all been poked,
840 * the duration between ticks doubles, up to mDisableAfterMilliseconds. At that
841 * point, the timer is quiet and doesn't tick (until something is added to it
842 * again).
844 * When a timer is removed, there is a possibility of another timer
845 * being skipped for one cycle. We could avoid this by adjusting
846 * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
847 * add that complexity. All we want is for inactive drivers to tick
848 * at some point, but we don't care too much about how often.
850 class InactiveRefreshDriverTimer final
851 : public SimpleTimerBasedRefreshDriverTimer {
852 public:
853 explicit InactiveRefreshDriverTimer(double aRate)
854 : SimpleTimerBasedRefreshDriverTimer(aRate),
855 mNextTickDuration(aRate),
856 mDisableAfterMilliseconds(-1.0),
857 mNextDriverIndex(0) {}
859 InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
860 : SimpleTimerBasedRefreshDriverTimer(aRate),
861 mNextTickDuration(aRate),
862 mDisableAfterMilliseconds(aDisableAfterMilliseconds),
863 mNextDriverIndex(0) {}
865 void AddRefreshDriver(nsRefreshDriver* aDriver) override {
866 RefreshDriverTimer::AddRefreshDriver(aDriver);
868 LOG("[%p] inactive timer got new refresh driver %p, resetting rate", this,
869 aDriver);
871 // reset the timer, and start with the newly added one next time.
872 mNextTickDuration = mRateMilliseconds;
874 // we don't really have to start with the newly added one, but we may as
875 // well not tick the old ones at the fastest rate any more than we need to.
876 mNextDriverIndex = GetRefreshDriverCount() - 1;
878 StopTimer();
879 StartTimer();
882 TimeDuration GetTimerRate() override {
883 return TimeDuration::FromMilliseconds(mNextTickDuration);
886 protected:
887 uint32_t GetRefreshDriverCount() {
888 return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
891 void StartTimer() override {
892 mLastFireTime = TimeStamp::Now();
894 mTargetTime = mLastFireTime + mRateDuration;
896 uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
897 mTimer->InitWithNamedFuncCallback(TimerTickOne, this, delay,
898 nsITimer::TYPE_ONE_SHOT,
899 "InactiveRefreshDriverTimer::StartTimer");
902 void StopTimer() override { mTimer->Cancel(); }
904 void ScheduleNextTick(TimeStamp aNowTime) override {
905 if (mDisableAfterMilliseconds > 0.0 &&
906 mNextTickDuration > mDisableAfterMilliseconds) {
907 // We hit the time after which we should disable
908 // inactive window refreshes; don't schedule anything
909 // until we get kicked by an AddRefreshDriver call.
910 return;
913 // double the next tick time if we've already gone through all of them once
914 if (mNextDriverIndex >= GetRefreshDriverCount()) {
915 mNextTickDuration *= 2.0;
916 mNextDriverIndex = 0;
919 // this doesn't need to be precise; do a simple schedule
920 uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
921 mTimer->InitWithNamedFuncCallback(
922 TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT,
923 "InactiveRefreshDriverTimer::ScheduleNextTick");
925 LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this,
926 mNextTickDuration, mNextDriverIndex, GetRefreshDriverCount());
929 /* Runs just one driver's tick. */
930 void TickOne() {
931 TimeStamp now = TimeStamp::Now();
933 ScheduleNextTick(now);
935 mLastFireTime = now;
937 nsTArray<RefPtr<nsRefreshDriver>> drivers(mContentRefreshDrivers);
938 drivers.AppendElements(mRootRefreshDrivers);
939 size_t index = mNextDriverIndex;
941 if (index < drivers.Length() &&
942 !drivers[index]->IsTestControllingRefreshesEnabled()) {
943 TickDriver(drivers[index], VsyncId(), now);
946 mNextDriverIndex++;
949 static void TimerTickOne(nsITimer* aTimer, void* aClosure) {
950 RefPtr<InactiveRefreshDriverTimer> timer =
951 static_cast<InactiveRefreshDriverTimer*>(aClosure);
952 timer->TickOne();
955 double mNextTickDuration;
956 double mDisableAfterMilliseconds;
957 uint32_t mNextDriverIndex;
960 } // namespace mozilla
962 static StaticRefPtr<RefreshDriverTimer> sRegularRateTimer;
963 static StaticRefPtr<InactiveRefreshDriverTimer> sThrottledRateTimer;
965 static void CreateContentVsyncRefreshTimer(void*) {
966 MOZ_ASSERT(NS_IsMainThread());
967 MOZ_ASSERT(!XRE_IsParentProcess());
969 // Create the PVsync actor child for vsync-base refresh timer.
970 // PBackgroundChild is created synchronously. We will still use software
971 // timer before PVsync ready, and change to use hw timer when the connection
972 // is done. Please check nsRefreshDriver::PVsyncActorCreated().
974 PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
975 if (NS_WARN_IF(!actorChild)) {
976 return;
979 layout::PVsyncChild* actor = actorChild->SendPVsyncConstructor();
980 if (NS_WARN_IF(!actor)) {
981 return;
984 layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
985 nsRefreshDriver::PVsyncActorCreated(child);
988 static void CreateVsyncRefreshTimer() {
989 MOZ_ASSERT(NS_IsMainThread());
991 PodArrayZero(sJankLevels);
992 // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
993 // ready.
994 gfxPrefs::GetSingleton();
996 if (gfxPlatform::IsInLayoutAsapMode()) {
997 return;
1000 if (XRE_IsParentProcess()) {
1001 // Make sure all vsync systems are ready.
1002 gfxPlatform::GetPlatform();
1003 // In parent process, we don't need to use ipc. We can create the
1004 // VsyncRefreshDriverTimer directly.
1005 sRegularRateTimer = new VsyncRefreshDriverTimer();
1006 return;
1009 // If this process is not created by NUWA, just create the vsync timer here.
1010 CreateContentVsyncRefreshTimer(nullptr);
1013 static uint32_t GetFirstFrameDelay(imgIRequest* req) {
1014 nsCOMPtr<imgIContainer> container;
1015 if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
1016 return 0;
1019 // If this image isn't animated, there isn't a first frame delay.
1020 int32_t delay = container->GetFirstFrameDelay();
1021 if (delay < 0) return 0;
1023 return static_cast<uint32_t>(delay);
1026 /* static */
1027 void nsRefreshDriver::Shutdown() {
1028 // clean up our timers
1029 sRegularRateTimer = nullptr;
1030 sThrottledRateTimer = nullptr;
1033 /* static */
1034 int32_t nsRefreshDriver::DefaultInterval() {
1035 return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
1038 // Compute the interval to use for the refresh driver timer, in milliseconds.
1039 // outIsDefault indicates that rate was not explicitly set by the user
1040 // so we might choose other, more appropriate rates (e.g. vsync, etc)
1041 // layout.frame_rate=0 indicates "ASAP mode".
1042 // In ASAP mode rendering is iterated as fast as possible (typically for stress
1043 // testing). A target rate of 10k is used internally instead of special-handling
1044 // 0. Backends which block on swap/present/etc should try to not block when
1045 // layout.frame_rate=0 - to comply with "ASAP" as much as possible.
1046 double nsRefreshDriver::GetRegularTimerInterval() const {
1047 int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
1048 if (rate < 0) {
1049 rate = gfxPlatform::GetDefaultFrameRate();
1050 } else if (rate == 0) {
1051 rate = 10000;
1054 return 1000.0 / rate;
1057 /* static */
1058 double nsRefreshDriver::GetThrottledTimerInterval() {
1059 int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
1060 if (rate <= 0) {
1061 rate = DEFAULT_THROTTLED_FRAME_RATE;
1063 return 1000.0 / rate;
1066 /* static */ mozilla::TimeDuration
1067 nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
1068 int32_t interval =
1069 Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
1070 if (interval <= 0) {
1071 interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
1073 return TimeDuration::FromMilliseconds(interval);
1076 RefreshDriverTimer* nsRefreshDriver::ChooseTimer() const {
1077 if (mThrottled) {
1078 if (!sThrottledRateTimer)
1079 sThrottledRateTimer = new InactiveRefreshDriverTimer(
1080 GetThrottledTimerInterval(),
1081 DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
1082 return sThrottledRateTimer;
1085 if (!sRegularRateTimer) {
1086 double rate = GetRegularTimerInterval();
1088 // Try to use vsync-base refresh timer first for sRegularRateTimer.
1089 CreateVsyncRefreshTimer();
1091 if (!sRegularRateTimer) {
1092 sRegularRateTimer = new StartupRefreshDriverTimer(rate);
1095 return sRegularRateTimer;
1098 nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
1099 : mActiveTimer(nullptr),
1100 mPresContext(aPresContext),
1101 mRootRefresh(nullptr),
1102 mNextTransactionId{0},
1103 mOutstandingTransactionId{0},
1104 mCompletedTransaction{0},
1105 mFreezeCount(0),
1106 mThrottledFrameRequestInterval(
1107 TimeDuration::FromMilliseconds(GetThrottledTimerInterval())),
1108 mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
1109 mThrottled(false),
1110 mNeedToRecomputeVisibility(false),
1111 mTestControllingRefreshes(false),
1112 mViewManagerFlushIsPending(false),
1113 mHasScheduleFlush(false),
1114 mInRefresh(false),
1115 mWaitingForTransaction(false),
1116 mSkippedPaints(false),
1117 mResizeSuppressed(false),
1118 mNotifyDOMContentFlushed(false),
1119 mWarningThreshold(REFRESH_WAIT_WARNING) {
1120 MOZ_ASSERT(NS_IsMainThread());
1121 MOZ_ASSERT(mPresContext,
1122 "Need a pres context to tell us to call Disconnect() later "
1123 "and decrement sRefreshDriverCount.");
1124 mMostRecentRefresh = TimeStamp::Now();
1125 mNextThrottledFrameRequestTick = mMostRecentRefresh;
1126 mNextRecomputeVisibilityTick = mMostRecentRefresh;
1128 ++sRefreshDriverCount;
1131 nsRefreshDriver::~nsRefreshDriver() {
1132 MOZ_ASSERT(NS_IsMainThread());
1133 MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
1134 "observers, except pending selection scrolls, "
1135 "should have been unregistered");
1136 MOZ_ASSERT(!mActiveTimer, "timer should be gone");
1137 MOZ_ASSERT(!mPresContext,
1138 "Should have called Disconnect() and decremented "
1139 "sRefreshDriverCount!");
1141 if (mRootRefresh) {
1142 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1143 mRootRefresh = nullptr;
1147 // Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
1148 // for description.
1149 void nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds) {
1150 // ensure that we're removed from our driver
1151 StopTimer();
1153 if (!mTestControllingRefreshes) {
1154 mMostRecentRefresh = TimeStamp::Now();
1156 mTestControllingRefreshes = true;
1157 if (mWaitingForTransaction) {
1158 // Disable any refresh driver throttling when entering test mode
1159 mWaitingForTransaction = false;
1160 mSkippedPaints = false;
1161 mWarningThreshold = REFRESH_WAIT_WARNING;
1165 mMostRecentRefresh += TimeDuration::FromMilliseconds((double)aMilliseconds);
1167 mozilla::dom::AutoNoJSAPI nojsapi;
1168 DoTick();
1171 void nsRefreshDriver::RestoreNormalRefresh() {
1172 mTestControllingRefreshes = false;
1173 EnsureTimerStarted(eAllowTimeToGoBackwards);
1174 mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId;
1177 TimeStamp nsRefreshDriver::MostRecentRefresh() const {
1178 // In case of stylo traversal, we have already activated the refresh driver in
1179 // RestyleManager::ProcessPendingRestyles().
1180 if (!ServoStyleSet::IsInServoTraversal()) {
1181 const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
1184 return mMostRecentRefresh;
1187 void nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
1188 FlushType aFlushType) {
1189 ObserverArray& array = ArrayFor(aFlushType);
1190 array.AppendElement(aObserver);
1191 EnsureTimerStarted();
1194 bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
1195 FlushType aFlushType) {
1196 ObserverArray& array = ArrayFor(aFlushType);
1197 return array.RemoveElement(aObserver);
1200 void nsRefreshDriver::AddTimerAdjustmentObserver(
1201 nsATimerAdjustmentObserver* aObserver) {
1202 MOZ_ASSERT(!mTimerAdjustmentObservers.Contains(aObserver));
1203 mTimerAdjustmentObservers.AppendElement(aObserver);
1206 void nsRefreshDriver::RemoveTimerAdjustmentObserver(
1207 nsATimerAdjustmentObserver* aObserver) {
1208 MOZ_ASSERT(mTimerAdjustmentObservers.Contains(aObserver));
1209 mTimerAdjustmentObservers.RemoveElement(aObserver);
1212 void nsRefreshDriver::PostVisualViewportResizeEvent(
1213 VVPResizeEvent* aResizeEvent) {
1214 mVisualViewportResizeEvents.AppendElement(aResizeEvent);
1215 EnsureTimerStarted();
1218 void nsRefreshDriver::DispatchVisualViewportResizeEvents() {
1219 // We're taking a hint from scroll events and only dispatch the current set
1220 // of queued resize events. If additional events are posted in response to
1221 // the current events being dispatched, we'll dispatch them on the next tick.
1222 VisualViewportResizeEventArray events;
1223 events.SwapElements(mVisualViewportResizeEvents);
1224 for (auto& event : events) {
1225 event->Run();
1229 void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent,
1230 bool aDelayed) {
1231 if (aDelayed) {
1232 mDelayedScrollEvents.AppendElement(aScrollEvent);
1233 } else {
1234 mScrollEvents.AppendElement(aScrollEvent);
1235 EnsureTimerStarted();
1239 void nsRefreshDriver::DispatchScrollEvents() {
1240 // Scroll events are one-shot, so after running them we can drop them.
1241 // However, dispatching a scroll event can potentially cause more scroll
1242 // events to be posted, so we move the initial set into a temporary array
1243 // first. (Newly posted scroll events will be dispatched on the next tick.)
1244 ScrollEventArray events;
1245 events.SwapElements(mScrollEvents);
1246 for (auto& event : events) {
1247 event->Run();
1251 void nsRefreshDriver::PostVisualViewportScrollEvent(
1252 VVPScrollEvent* aScrollEvent) {
1253 mVisualViewportScrollEvents.AppendElement(aScrollEvent);
1254 EnsureTimerStarted();
1257 void nsRefreshDriver::DispatchVisualViewportScrollEvents() {
1258 // Scroll events are one-shot, so after running them we can drop them.
1259 // However, dispatching a scroll event can potentially cause more scroll
1260 // events to be posted, so we move the initial set into a temporary array
1261 // first. (Newly posted scroll events will be dispatched on the next tick.)
1262 VisualViewportScrollEventArray events;
1263 events.SwapElements(mVisualViewportScrollEvents);
1264 for (auto& event : events) {
1265 event->Run();
1269 void nsRefreshDriver::AddPostRefreshObserver(
1270 nsAPostRefreshObserver* aObserver) {
1271 mPostRefreshObservers.AppendElement(aObserver);
1274 void nsRefreshDriver::RemovePostRefreshObserver(
1275 nsAPostRefreshObserver* aObserver) {
1276 mPostRefreshObservers.RemoveElement(aObserver);
1279 bool nsRefreshDriver::AddImageRequest(imgIRequest* aRequest) {
1280 uint32_t delay = GetFirstFrameDelay(aRequest);
1281 if (delay == 0) {
1282 mRequests.PutEntry(aRequest);
1283 } else {
1284 ImageStartData* start = mStartTable.LookupForAdd(delay).OrInsert(
1285 []() { return new ImageStartData(); });
1286 start->mEntries.PutEntry(aRequest);
1289 EnsureTimerStarted();
1291 return true;
1294 void nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest) {
1295 // Try to remove from both places, just in case, because we can't tell
1296 // whether RemoveEntry() succeeds.
1297 mRequests.RemoveEntry(aRequest);
1298 uint32_t delay = GetFirstFrameDelay(aRequest);
1299 if (delay != 0) {
1300 ImageStartData* start = mStartTable.Get(delay);
1301 if (start) {
1302 start->mEntries.RemoveEntry(aRequest);
1307 void nsRefreshDriver::NotifyDOMContentLoaded() {
1308 // If the refresh driver is going to tick, we mark the timestamp after
1309 // everything is flushed in the next tick. If it isn't, mark ourselves as
1310 // flushed now.
1311 if (!HasObservers()) {
1312 GetPresContext()->NotifyDOMContentFlushed();
1313 } else {
1314 mNotifyDOMContentFlushed = true;
1318 void nsRefreshDriver::RunDelayedEventsSoon() {
1319 // Place entries for delayed events into their corresponding normal list,
1320 // and schedule a refresh. When these delayed events run, if their document
1321 // still has events suppressed then they will be readded to the delayed
1322 // events list.
1324 mScrollEvents.AppendElements(mDelayedScrollEvents);
1325 mDelayedScrollEvents.Clear();
1327 mResizeEventFlushObservers.AppendElements(mDelayedResizeEventFlushObservers);
1328 mDelayedResizeEventFlushObservers.Clear();
1330 EnsureTimerStarted();
1333 void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
1334 // FIXME: Bug 1346065: We should also assert the case where we have
1335 // STYLO_THREADS=1.
1336 MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
1337 "EnsureTimerStarted should be called only when we are not "
1338 "in servo traversal or on the main-thread");
1340 if (mTestControllingRefreshes) return;
1342 // will it already fire, and no other changes needed?
1343 if (mActiveTimer && !(aFlags & eForceAdjustTimer)) return;
1345 if (IsFrozen() || !mPresContext) {
1346 // If we don't want to start it now, or we've been disconnected.
1347 StopTimer();
1348 return;
1351 if (mPresContext->Document()->IsBeingUsedAsImage()) {
1352 // Image documents receive ticks from clients' refresh drivers.
1353 // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
1354 // they receive refresh-driver ticks from their client docs (bug 1107252).
1355 nsIURI* uri = mPresContext->Document()->GetDocumentURI();
1356 if (!uri || !mozilla::dom::IsFontTableURI(uri)) {
1357 MOZ_ASSERT(!mActiveTimer,
1358 "image doc refresh driver should never have its own timer");
1359 return;
1363 // We got here because we're either adjusting the time *or* we're
1364 // starting it for the first time. Add to the right timer,
1365 // prehaps removing it from a previously-set one.
1366 RefreshDriverTimer* newTimer = ChooseTimer();
1367 if (newTimer != mActiveTimer) {
1368 if (mActiveTimer) mActiveTimer->RemoveRefreshDriver(this);
1369 mActiveTimer = newTimer;
1370 mActiveTimer->AddRefreshDriver(this);
1373 // When switching from an inactive timer to an active timer, the root
1374 // refresh driver is skipped due to being set to the content refresh
1375 // driver's timestamp. In case of EnsureTimerStarted is called from
1376 // ScheduleViewManagerFlush, we should avoid this behavior to flush
1377 // a paint in the same tick on the root refresh driver.
1378 if (aFlags & eNeverAdjustTimer) {
1379 return;
1382 // Since the different timers are sampled at different rates, when switching
1383 // timers, the most recent refresh of the new timer may be *before* the
1384 // most recent refresh of the old timer. However, the refresh driver time
1385 // should not go backwards so we clamp the most recent refresh time.
1387 // The one exception to this is when we are restoring the refresh driver
1388 // from test control in which case the time is expected to go backwards
1389 // (see bug 1043078).
1390 TimeStamp newMostRecentRefresh =
1391 aFlags & eAllowTimeToGoBackwards
1392 ? mActiveTimer->MostRecentRefresh()
1393 : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
1395 if (mMostRecentRefresh != newMostRecentRefresh) {
1396 mMostRecentRefresh = newMostRecentRefresh;
1398 nsTObserverArray<nsATimerAdjustmentObserver*>::EndLimitedIterator iter(
1399 mTimerAdjustmentObservers);
1400 while (iter.HasMore()) {
1401 nsATimerAdjustmentObserver* obs = iter.GetNext();
1402 obs->NotifyTimerAdjusted(mMostRecentRefresh);
1407 void nsRefreshDriver::StopTimer() {
1408 if (!mActiveTimer) return;
1410 mActiveTimer->RemoveRefreshDriver(this);
1411 mActiveTimer = nullptr;
1414 uint32_t nsRefreshDriver::ObserverCount() const {
1415 uint32_t sum = 0;
1416 for (const ObserverArray& array : mObservers) {
1417 sum += array.Length();
1420 // Even while throttled, we need to process layout and style changes. Style
1421 // changes can trigger transitions which fire events when they complete, and
1422 // layout changes can affect media queries on child documents, triggering
1423 // style changes, etc.
1424 sum += mAnimationEventFlushObservers.Length();
1425 sum += mResizeEventFlushObservers.Length();
1426 sum += mStyleFlushObservers.Length();
1427 sum += mLayoutFlushObservers.Length();
1428 sum += mPendingFullscreenEvents.Length();
1429 sum += mFrameRequestCallbackDocs.Length();
1430 sum += mThrottledFrameRequestCallbackDocs.Length();
1431 sum += mViewManagerFlushIsPending;
1432 sum += mEarlyRunners.Length();
1433 sum += mTimerAdjustmentObservers.Length();
1434 return sum;
1437 bool nsRefreshDriver::HasObservers() const {
1438 for (const ObserverArray& array : mObservers) {
1439 if (!array.IsEmpty()) {
1440 return true;
1444 // We should NOT count mTimerAdjustmentObservers here since this method is
1445 // used to determine whether or not to stop the timer or re-start it and timer
1446 // adjustment observers should not influence timer starting or stopping.
1447 return mViewManagerFlushIsPending || !mStyleFlushObservers.IsEmpty() ||
1448 !mLayoutFlushObservers.IsEmpty() ||
1449 !mAnimationEventFlushObservers.IsEmpty() ||
1450 !mResizeEventFlushObservers.IsEmpty() ||
1451 !mPendingFullscreenEvents.IsEmpty() ||
1452 !mFrameRequestCallbackDocs.IsEmpty() ||
1453 !mThrottledFrameRequestCallbackDocs.IsEmpty() ||
1454 !mEarlyRunners.IsEmpty();
1457 bool nsRefreshDriver::HasImageRequests() const {
1458 for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
1459 if (!iter.UserData()->mEntries.IsEmpty()) {
1460 return true;
1464 return !mRequests.IsEmpty();
1467 nsRefreshDriver::ObserverArray& nsRefreshDriver::ArrayFor(
1468 FlushType aFlushType) {
1469 switch (aFlushType) {
1470 case FlushType::Event:
1471 return mObservers[0];
1472 case FlushType::Style:
1473 return mObservers[1];
1474 case FlushType::Layout:
1475 return mObservers[2];
1476 case FlushType::Display:
1477 return mObservers[3];
1478 default:
1479 MOZ_CRASH("We don't track refresh observers for this flush type");
1484 * nsITimerCallback implementation
1487 void nsRefreshDriver::DoTick() {
1488 MOZ_ASSERT(!IsFrozen(), "Why are we notified while frozen?");
1489 MOZ_ASSERT(mPresContext, "Why are we notified after disconnection?");
1490 MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1491 "Shouldn't have a JSContext on the stack");
1493 if (mTestControllingRefreshes) {
1494 Tick(VsyncId(), mMostRecentRefresh);
1495 } else {
1496 Tick(VsyncId(), TimeStamp::Now());
1500 struct DocumentFrameCallbacks {
1501 explicit DocumentFrameCallbacks(Document* aDocument) : mDocument(aDocument) {}
1503 RefPtr<Document> mDocument;
1504 nsTArray<Document::FrameRequest> mCallbacks;
1507 static nsDocShell* GetDocShell(nsPresContext* aPresContext) {
1508 return static_cast<nsDocShell*>(aPresContext->GetDocShell());
1511 static bool HasPendingAnimations(PresShell* aPresShell) {
1512 Document* doc = aPresShell->GetDocument();
1513 if (!doc) {
1514 return false;
1517 PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
1518 return tracker && tracker->HasPendingAnimations();
1522 * Return a list of all the child docShells in a given root docShell that are
1523 * visible and are recording markers for the profilingTimeline
1525 static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
1526 nsTArray<nsDocShell*>& aShells) {
1527 if (!aRootDocShell) {
1528 return;
1531 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1532 if (!timelines || timelines->IsEmpty()) {
1533 return;
1536 nsCOMPtr<nsISimpleEnumerator> enumerator;
1537 nsresult rv = aRootDocShell->GetDocShellEnumerator(
1538 nsIDocShellTreeItem::typeAll, nsIDocShell::ENUMERATE_BACKWARDS,
1539 getter_AddRefs(enumerator));
1541 if (NS_FAILED(rv)) {
1542 return;
1545 nsCOMPtr<nsIDocShell> curItem;
1546 bool hasMore = false;
1547 while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
1548 nsCOMPtr<nsISupports> curSupports;
1549 enumerator->GetNext(getter_AddRefs(curSupports));
1550 curItem = do_QueryInterface(curSupports);
1552 if (!curItem || !curItem->GetRecordProfileTimelineMarkers()) {
1553 continue;
1556 nsDocShell* shell = static_cast<nsDocShell*>(curItem.get());
1557 bool isVisible = false;
1558 shell->GetVisibility(&isVisible);
1559 if (!isVisible) {
1560 continue;
1563 aShells.AppendElement(shell);
1567 static void TakeFrameRequestCallbacksFrom(
1568 Document* aDocument, nsTArray<DocumentFrameCallbacks>& aTarget) {
1569 aTarget.AppendElement(aDocument);
1570 aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
1573 // https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps
1574 void nsRefreshDriver::RunFullscreenSteps() {
1575 // Swap out the current pending events
1576 nsTArray<UniquePtr<PendingFullscreenEvent>> pendings(
1577 std::move(mPendingFullscreenEvents));
1578 for (UniquePtr<PendingFullscreenEvent>& event : pendings) {
1579 event->Dispatch();
1583 void nsRefreshDriver::UpdateIntersectionObservations() {
1584 AutoTArray<RefPtr<Document>, 32> documents;
1586 if (mPresContext->Document()->HasIntersectionObservers()) {
1587 documents.AppendElement(mPresContext->Document());
1590 mPresContext->Document()->CollectDescendantDocuments(
1591 documents, [](const Document* document) -> bool {
1592 return document->HasIntersectionObservers();
1595 for (uint32_t i = 0; i < documents.Length(); ++i) {
1596 Document* doc = documents[i];
1597 doc->UpdateIntersectionObservations();
1598 doc->ScheduleIntersectionObserverNotification();
1602 void nsRefreshDriver::DispatchAnimationEvents() {
1603 if (!mPresContext) {
1604 return;
1607 // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
1608 // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
1609 // during processing the previous dispatcher.
1610 AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
1611 dispatchers.AppendElements(mAnimationEventFlushObservers);
1612 mAnimationEventFlushObservers.Clear();
1614 for (auto& dispatcher : dispatchers) {
1615 dispatcher->DispatchEvents();
1619 void nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime) {
1620 // Grab all of our frame request callbacks up front.
1621 nsTArray<DocumentFrameCallbacks> frameRequestCallbacks(
1622 mFrameRequestCallbackDocs.Length() +
1623 mThrottledFrameRequestCallbackDocs.Length());
1625 // First, grab throttled frame request callbacks.
1627 nsTArray<Document*> docsToRemove;
1629 // We always tick throttled frame requests if the entire refresh driver is
1630 // throttled, because in that situation throttled frame requests tick at the
1631 // same frequency as non-throttled frame requests.
1632 bool tickThrottledFrameRequests = mThrottled;
1634 if (!tickThrottledFrameRequests &&
1635 aNowTime >= mNextThrottledFrameRequestTick) {
1636 mNextThrottledFrameRequestTick =
1637 aNowTime + mThrottledFrameRequestInterval;
1638 tickThrottledFrameRequests = true;
1641 for (Document* doc : mThrottledFrameRequestCallbackDocs) {
1642 if (tickThrottledFrameRequests) {
1643 // We're ticking throttled documents, so grab this document's requests.
1644 // We don't bother appending to docsToRemove because we're going to
1645 // clear mThrottledFrameRequestCallbackDocs anyway.
1646 TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1647 } else if (!doc->ShouldThrottleFrameRequests()) {
1648 // This document is no longer throttled, so grab its requests even
1649 // though we're not ticking throttled frame requests right now. If
1650 // this is the first unthrottled document with frame requests, we'll
1651 // enter high precision mode the next time the callback is scheduled.
1652 TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1653 docsToRemove.AppendElement(doc);
1657 // Remove all the documents we're ticking from
1658 // mThrottledFrameRequestCallbackDocs so they can be readded as needed.
1659 if (tickThrottledFrameRequests) {
1660 mThrottledFrameRequestCallbackDocs.Clear();
1661 } else {
1662 // XXX(seth): We're using this approach to avoid concurrent modification
1663 // of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
1664 // zero elements or a very small number, so this should be OK in practice.
1665 for (Document* doc : docsToRemove) {
1666 mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
1671 // Now grab unthrottled frame request callbacks.
1672 for (Document* doc : mFrameRequestCallbackDocs) {
1673 TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1676 // Reset mFrameRequestCallbackDocs so they can be readded as needed.
1677 mFrameRequestCallbackDocs.Clear();
1679 if (!frameRequestCallbacks.IsEmpty()) {
1680 AUTO_PROFILER_TRACING_DOCSHELL("Paint", "Scripts", GRAPHICS,
1681 GetDocShell(mPresContext));
1682 for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
1683 // XXXbz Bug 863140: GetInnerWindow can return the outer
1684 // window in some cases.
1685 nsPIDOMWindowInner* innerWindow =
1686 docCallbacks.mDocument->GetInnerWindow();
1687 DOMHighResTimeStamp timeStamp = 0;
1688 if (innerWindow) {
1689 mozilla::dom::Performance* perf = innerWindow->GetPerformance();
1690 if (perf) {
1691 timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
1693 // else window is partially torn down already
1695 for (auto& callback : docCallbacks.mCallbacks) {
1696 if (docCallbacks.mDocument->IsCanceledFrameRequestCallback(
1697 callback.mHandle)) {
1698 continue;
1700 // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
1701 // keeps callback alive and the mCallback strong reference can't be
1702 // mutated by the call.
1703 MOZ_KnownLive(callback.mCallback)->Call(timeStamp);
1709 struct RunnableWithDelay {
1710 nsCOMPtr<nsIRunnable> mRunnable;
1711 uint32_t mDelay;
1714 static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
1716 void nsRefreshDriver::DispatchIdleRunnableAfterTickUnlessExists(
1717 nsIRunnable* aRunnable, uint32_t aDelay) {
1718 if (!sPendingIdleRunnables) {
1719 sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
1720 } else {
1721 for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
1722 if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
1723 return;
1728 RunnableWithDelay rwd = {aRunnable, aDelay};
1729 sPendingIdleRunnables->AppendElement(rwd);
1732 void nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable) {
1733 if (!sPendingIdleRunnables) {
1734 return;
1737 for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
1738 if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
1739 sPendingIdleRunnables->RemoveElementAt(i);
1740 break;
1744 if (sPendingIdleRunnables->IsEmpty()) {
1745 delete sPendingIdleRunnables;
1746 sPendingIdleRunnables = nullptr;
1750 void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
1751 MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1752 "Shouldn't have a JSContext on the stack");
1754 if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
1755 NS_ERROR("Refresh driver should not run during plugin call!");
1756 // Try to survive this by just ignoring the refresh tick.
1757 return;
1760 #if defined(MOZ_WIDGET_ANDROID)
1761 gfx::VRManager* vm = gfx::VRManager::Get();
1762 if (vm->IsPresenting()) {
1763 RunFrameRequestCallbacks(aNowTime);
1764 return;
1766 #endif // defined(MOZ_WIDGET_ANDROID)
1768 AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", LAYOUT);
1770 // We're either frozen or we were disconnected (likely in the middle
1771 // of a tick iteration). Just do nothing here, since our
1772 // prescontext went away.
1773 if (IsFrozen() || !mPresContext) {
1774 return;
1777 // We can have a race condition where the vsync timestamp
1778 // is before the most recent refresh due to a forced refresh.
1779 // The underlying assumption is that the refresh driver tick can only
1780 // go forward in time, not backwards. To prevent the refresh
1781 // driver from going back in time, just skip this tick and
1782 // wait until the next tick.
1783 if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
1784 return;
1787 if (IsWaitingForPaint(aNowTime)) {
1788 // We're currently suspended waiting for earlier Tick's to
1789 // be completed (on the Compositor). Mark that we missed the paint
1790 // and keep waiting.
1791 PROFILER_ADD_MARKER("nsRefreshDriver::Tick waiting for paint", LAYOUT);
1792 return;
1795 TimeStamp previousRefresh = mMostRecentRefresh;
1796 mMostRecentRefresh = aNowTime;
1798 if (mRootRefresh) {
1799 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1800 mRootRefresh = nullptr;
1802 mSkippedPaints = false;
1803 mWarningThreshold = 1;
1805 RefPtr<PresShell> presShell = mPresContext->GetPresShell();
1806 if (!presShell ||
1807 (!HasObservers() && !HasImageRequests() &&
1808 mVisualViewportResizeEvents.IsEmpty() && mScrollEvents.IsEmpty() &&
1809 mVisualViewportScrollEvents.IsEmpty())) {
1810 // Things are being destroyed, or we no longer have any observers.
1811 // We don't want to stop the timer when observers are initially
1812 // removed, because sometimes observers can be added and removed
1813 // often depending on what other things are going on and in that
1814 // situation we don't want to thrash our timer. So instead we
1815 // wait until we get a Notify() call when we have no observers
1816 // before stopping the timer.
1817 // On top level content pages keep the timer running initially so that we
1818 // paint the page soon enough.
1819 if (presShell && !mThrottled && !mTestControllingRefreshes &&
1820 XRE_IsContentProcess() &&
1821 mPresContext->Document()->IsTopLevelContentDocument() &&
1822 !gfxPlatform::IsInLayoutAsapMode() &&
1823 !mPresContext->HadContentfulPaint() &&
1824 mPresContext->Document()->GetReadyStateEnum() <
1825 Document::READYSTATE_COMPLETE) {
1826 if (mInitialTimerRunningLimit.IsNull()) {
1827 mInitialTimerRunningLimit =
1828 TimeStamp::Now() + TimeDuration::FromSeconds(4.0f);
1829 // Don't let the timer to run forever, so limit to 4s for now.
1830 } else if (mInitialTimerRunningLimit < TimeStamp::Now()) {
1831 StopTimer();
1833 } else {
1834 StopTimer();
1836 return;
1839 mResizeSuppressed = false;
1841 AutoRestore<bool> restoreInRefresh(mInRefresh);
1842 mInRefresh = true;
1844 AutoRestore<TimeStamp> restoreTickStart(mTickStart);
1845 mTickStart = TimeStamp::Now();
1846 mTickVsyncId = aId;
1847 mTickVsyncTime = aNowTime;
1849 gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
1851 // We want to process any pending APZ metrics ahead of their positions
1852 // in the queue. This will prevent us from spending precious time
1853 // painting a stale displayport.
1854 if (gfxPrefs::APZPeekMessages()) {
1855 nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
1858 AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners;
1859 earlyRunners.SwapElements(mEarlyRunners);
1860 for (auto& runner : earlyRunners) {
1861 runner->Run();
1864 // Resize events should be fired before layout flushes or
1865 // calling animation frame callbacks.
1866 AutoTArray<PresShell*, 16> observers;
1867 observers.AppendElements(mResizeEventFlushObservers);
1868 for (PresShell* shell : Reversed(observers)) {
1869 if (!mPresContext || !mPresContext->GetPresShell()) {
1870 StopTimer();
1871 return;
1873 // Make sure to not process observers which might have been removed
1874 // during previous iterations.
1875 if (!mResizeEventFlushObservers.RemoveElement(shell)) {
1876 continue;
1878 shell->FireResizeEvent();
1880 DispatchVisualViewportResizeEvents();
1883 * The timer holds a reference to |this| while calling |Notify|.
1884 * However, implementations of |WillRefresh| are permitted to destroy
1885 * the pres context, which will cause our |mPresContext| to become
1886 * null. If this happens, we must stop notifying observers.
1888 for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
1889 ObserverArray::EndLimitedIterator etor(mObservers[i]);
1890 while (etor.HasMore()) {
1891 RefPtr<nsARefreshObserver> obs = etor.GetNext();
1892 obs->WillRefresh(aNowTime);
1894 if (!mPresContext || !mPresContext->GetPresShell()) {
1895 StopTimer();
1896 return;
1900 if (i == 1) {
1901 // This is the FlushType::Style case.
1903 DispatchScrollEvents();
1904 DispatchVisualViewportScrollEvents();
1905 DispatchAnimationEvents();
1906 RunFullscreenSteps();
1907 RunFrameRequestCallbacks(aNowTime);
1909 if (mPresContext && mPresContext->GetPresShell()) {
1910 AutoTArray<PresShell*, 16> observers;
1911 observers.AppendElements(mStyleFlushObservers);
1912 for (uint32_t j = observers.Length();
1913 j && mPresContext && mPresContext->GetPresShell(); --j) {
1914 // Make sure to not process observers which might have been removed
1915 // during previous iterations.
1916 PresShell* rawPresShell = observers[j - 1];
1917 if (!mStyleFlushObservers.RemoveElement(rawPresShell)) {
1918 continue;
1920 RefPtr<PresShell> presShell = rawPresShell;
1921 presShell->mObservingStyleFlushes = false;
1922 presShell->FlushPendingNotifications(
1923 ChangesToFlush(FlushType::Style, false));
1924 // Inform the FontFaceSet that we ticked, so that it can resolve its
1925 // ready promise if it needs to (though it might still be waiting on
1926 // a layout flush).
1927 presShell->NotifyFontFaceSetOnRefresh();
1928 mNeedToRecomputeVisibility = true;
1931 } else if (i == 2) {
1932 // This is the FlushType::Layout case.
1933 AutoTArray<PresShell*, 16> observers;
1934 observers.AppendElements(mLayoutFlushObservers);
1935 for (uint32_t j = observers.Length();
1936 j && mPresContext && mPresContext->GetPresShell(); --j) {
1937 // Make sure to not process observers which might have been removed
1938 // during previous iterations.
1939 PresShell* rawPresShell = observers[j - 1];
1940 if (!mLayoutFlushObservers.RemoveElement(rawPresShell)) {
1941 continue;
1943 RefPtr<PresShell> presShell = rawPresShell;
1944 presShell->mObservingLayoutFlushes = false;
1945 presShell->mWasLastReflowInterrupted = false;
1946 FlushType flushType = HasPendingAnimations(presShell)
1947 ? FlushType::Layout
1948 : FlushType::InterruptibleLayout;
1949 presShell->FlushPendingNotifications(ChangesToFlush(flushType, false));
1950 // Inform the FontFaceSet that we ticked, so that it can resolve its
1951 // ready promise if it needs to.
1952 presShell->NotifyFontFaceSetOnRefresh();
1953 mNeedToRecomputeVisibility = true;
1957 // The pres context may be destroyed during we do the flushing.
1958 if (!mPresContext || !mPresContext->GetPresShell()) {
1959 StopTimer();
1960 return;
1964 // Recompute approximate frame visibility if it's necessary and enough time
1965 // has passed since the last time we did it.
1966 if (mNeedToRecomputeVisibility && !mThrottled &&
1967 aNowTime >= mNextRecomputeVisibilityTick &&
1968 !presShell->IsPaintingSuppressed()) {
1969 mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
1970 mNeedToRecomputeVisibility = false;
1972 presShell->ScheduleApproximateFrameVisibilityUpdateNow();
1975 #ifdef MOZ_XUL
1976 // Update any popups that may need to be moved or hidden due to their
1977 // anchor changing.
1978 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
1979 pm->UpdatePopupPositions(this);
1981 #endif
1983 UpdateIntersectionObservations();
1986 * Perform notification to imgIRequests subscribed to listen
1987 * for refresh events.
1990 for (auto iter = mStartTable.Iter(); !iter.Done(); iter.Next()) {
1991 const uint32_t& delay = iter.Key();
1992 ImageStartData* data = iter.UserData();
1994 if (data->mStartTime) {
1995 TimeStamp& start = *data->mStartTime;
1996 TimeDuration prev = previousRefresh - start;
1997 TimeDuration curr = aNowTime - start;
1998 uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
2000 // We want to trigger images' refresh if we've just crossed over a
2001 // multiple of the first image's start time. If so, set the animation
2002 // start time to the nearest multiple of the delay and move all the
2003 // images in this table to the main requests table.
2004 if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
2005 mozilla::TimeStamp desired =
2006 start + TimeDuration::FromMilliseconds(prevMultiple * delay);
2007 BeginRefreshingImages(data->mEntries, desired);
2009 } else {
2010 // This is the very first time we've drawn images with this time delay.
2011 // Set the animation start time to "now" and move all the images in this
2012 // table to the main requests table.
2013 mozilla::TimeStamp desired = aNowTime;
2014 BeginRefreshingImages(data->mEntries, desired);
2015 data->mStartTime.emplace(aNowTime);
2019 if (mRequests.Count()) {
2020 // RequestRefresh may run scripts, so it's not safe to directly call it
2021 // while using a hashtable enumerator to enumerate mRequests in case
2022 // script modifies the hashtable. Instead, we build a (local) array of
2023 // images to refresh, and then we refresh each image in that array.
2024 nsCOMArray<imgIContainer> imagesToRefresh(mRequests.Count());
2026 for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
2027 nsISupportsHashKey* entry = iter.Get();
2028 auto req = static_cast<imgIRequest*>(entry->GetKey());
2029 MOZ_ASSERT(req, "Unable to retrieve the image request");
2030 nsCOMPtr<imgIContainer> image;
2031 if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2032 imagesToRefresh.AppendElement(image.forget());
2036 for (uint32_t i = 0; i < imagesToRefresh.Length(); i++) {
2037 imagesToRefresh[i]->RequestRefresh(aNowTime);
2041 bool dispatchRunnablesAfterTick = false;
2042 if (mViewManagerFlushIsPending) {
2043 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2045 nsTArray<nsDocShell*> profilingDocShells;
2046 GetProfileTimelineSubDocShells(GetDocShell(mPresContext),
2047 profilingDocShells);
2048 for (nsDocShell* docShell : profilingDocShells) {
2049 // For the sake of the profile timeline's simplicity, this is flagged as
2050 // paint even if it includes creating display lists
2051 MOZ_ASSERT(timelines);
2052 MOZ_ASSERT(timelines->HasConsumer(docShell));
2053 timelines->AddMarkerForDocShell(docShell, "Paint",
2054 MarkerTracingType::START);
2057 #ifdef MOZ_DUMP_PAINTING
2058 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2059 printf_stderr("Starting ProcessPendingUpdates\n");
2061 #endif
2063 mViewManagerFlushIsPending = false;
2064 RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
2066 PaintTelemetry::AutoRecordPaint record;
2067 vm->ProcessPendingUpdates();
2070 #ifdef MOZ_DUMP_PAINTING
2071 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2072 printf_stderr("Ending ProcessPendingUpdates\n");
2074 #endif
2076 for (nsDocShell* docShell : profilingDocShells) {
2077 MOZ_ASSERT(timelines);
2078 MOZ_ASSERT(timelines->HasConsumer(docShell));
2079 timelines->AddMarkerForDocShell(docShell, "Paint",
2080 MarkerTracingType::END);
2083 dispatchRunnablesAfterTick = true;
2084 mHasScheduleFlush = false;
2087 #ifndef ANDROID /* bug 1142079 */
2088 mozilla::Telemetry::AccumulateTimeDelta(
2089 mozilla::Telemetry::REFRESH_DRIVER_TICK, mTickStart);
2090 #endif
2092 if (mNotifyDOMContentFlushed) {
2093 mNotifyDOMContentFlushed = false;
2094 mPresContext->NotifyDOMContentFlushed();
2097 nsTObserverArray<nsAPostRefreshObserver*>::ForwardIterator iter(
2098 mPostRefreshObservers);
2099 while (iter.HasMore()) {
2100 nsAPostRefreshObserver* observer = iter.GetNext();
2101 observer->DidRefresh();
2104 NS_ASSERTION(mInRefresh, "Still in refresh");
2106 if (mPresContext->IsRoot() && XRE_IsContentProcess() &&
2107 gfxPrefs::AlwaysPaint()) {
2108 ScheduleViewManagerFlush();
2111 if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
2112 AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
2113 sPendingIdleRunnables = nullptr;
2114 for (RunnableWithDelay& runnableWithDelay : *runnables) {
2115 NS_DispatchToCurrentThreadQueue(runnableWithDelay.mRunnable.forget(),
2116 runnableWithDelay.mDelay,
2117 EventQueuePriority::Idle);
2119 delete runnables;
2123 void nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
2124 mozilla::TimeStamp aDesired) {
2125 for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
2126 auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
2127 MOZ_ASSERT(req, "Unable to retrieve the image request");
2129 mRequests.PutEntry(req);
2131 nsCOMPtr<imgIContainer> image;
2132 if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2133 image->SetAnimationStartTime(aDesired);
2136 aEntries.Clear();
2139 void nsRefreshDriver::Freeze() {
2140 StopTimer();
2141 mFreezeCount++;
2144 void nsRefreshDriver::Thaw() {
2145 NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
2147 if (mFreezeCount > 0) {
2148 mFreezeCount--;
2151 if (mFreezeCount == 0) {
2152 if (HasObservers() || HasImageRequests()) {
2153 // FIXME: This isn't quite right, since our EnsureTimerStarted call
2154 // updates our mMostRecentRefresh, but the DoRefresh call won't run
2155 // and notify our observers until we get back to the event loop.
2156 // Thus MostRecentRefresh() will lie between now and the DoRefresh.
2157 RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
2158 "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
2159 nsPresContext* pc = GetPresContext();
2160 if (pc) {
2161 pc->Document()->Dispatch(TaskCategory::Other, event.forget());
2162 EnsureTimerStarted();
2163 } else {
2164 NS_ERROR("Thawing while document is being destroyed");
2170 void nsRefreshDriver::FinishedWaitingForTransaction() {
2171 mWaitingForTransaction = false;
2172 mSkippedPaints = false;
2173 mWarningThreshold = 1;
2176 mozilla::layers::TransactionId nsRefreshDriver::GetTransactionId(
2177 bool aThrottle) {
2178 mOutstandingTransactionId = mOutstandingTransactionId.Next();
2179 mNextTransactionId = mNextTransactionId.Next();
2181 if (aThrottle && mOutstandingTransactionId - mCompletedTransaction >= 2 &&
2182 !mWaitingForTransaction && !mTestControllingRefreshes) {
2183 mWaitingForTransaction = true;
2184 mSkippedPaints = false;
2185 mWarningThreshold = 1;
2188 return mNextTransactionId;
2191 mozilla::layers::TransactionId nsRefreshDriver::LastTransactionId() const {
2192 return mNextTransactionId;
2195 void nsRefreshDriver::RevokeTransactionId(
2196 mozilla::layers::TransactionId aTransactionId) {
2197 MOZ_ASSERT(aTransactionId == mNextTransactionId);
2198 if (mOutstandingTransactionId - mCompletedTransaction == 2 &&
2199 mWaitingForTransaction) {
2200 MOZ_ASSERT(!mSkippedPaints,
2201 "How did we skip a paint when we're in the middle of one?");
2202 FinishedWaitingForTransaction();
2205 // Notify the pres context so that it can deliver MozAfterPaint for this
2206 // id if any caller was expecting it.
2207 nsPresContext* pc = GetPresContext();
2208 if (pc) {
2209 pc->NotifyRevokingDidPaint(aTransactionId);
2211 // Revert the outstanding transaction since we're no longer waiting on it to
2212 // be completed, but don't revert mNextTransactionId since we can't use the id
2213 // again.
2214 mOutstandingTransactionId = mOutstandingTransactionId.Prev();
2217 void nsRefreshDriver::ClearPendingTransactions() {
2218 mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId;
2219 mWaitingForTransaction = false;
2222 void nsRefreshDriver::ResetInitialTransactionId(
2223 mozilla::layers::TransactionId aTransactionId) {
2224 mCompletedTransaction = mOutstandingTransactionId = mNextTransactionId =
2225 aTransactionId;
2228 mozilla::TimeStamp nsRefreshDriver::GetTransactionStart() { return mTickStart; }
2230 VsyncId nsRefreshDriver::GetVsyncId() { return mTickVsyncId; }
2232 mozilla::TimeStamp nsRefreshDriver::GetVsyncStart() { return mTickVsyncTime; }
2234 void nsRefreshDriver::NotifyTransactionCompleted(
2235 mozilla::layers::TransactionId aTransactionId) {
2236 if (aTransactionId > mCompletedTransaction) {
2237 if (mOutstandingTransactionId - mCompletedTransaction > 1 &&
2238 mWaitingForTransaction) {
2239 mCompletedTransaction = aTransactionId;
2240 FinishedWaitingForTransaction();
2241 } else {
2242 mCompletedTransaction = aTransactionId;
2246 // If completed transaction id get ahead of outstanding id, reset to distance
2247 // id.
2248 if (mCompletedTransaction > mOutstandingTransactionId) {
2249 mOutstandingTransactionId = mCompletedTransaction;
2253 void nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime) {
2254 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2255 mRootRefresh = nullptr;
2256 if (mSkippedPaints) {
2257 DoRefresh();
2261 bool nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime) {
2262 if (mTestControllingRefreshes) {
2263 return false;
2266 if (mWaitingForTransaction) {
2267 if (mSkippedPaints &&
2268 aTime > (mMostRecentRefresh +
2269 TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
2270 // XXX - Bug 1303369 - too many false positives.
2271 // gfxCriticalNote << "Refresh driver waiting for the compositor for "
2272 // << (aTime - mMostRecentRefresh).ToSeconds()
2273 // << " seconds.";
2274 mWarningThreshold *= 2;
2277 mSkippedPaints = true;
2278 return true;
2281 // Try find the 'root' refresh driver for the current window and check
2282 // if that is waiting for a paint.
2283 nsPresContext* pc = GetPresContext();
2284 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
2285 if (rootContext) {
2286 nsRefreshDriver* rootRefresh = rootContext->RefreshDriver();
2287 if (rootRefresh && rootRefresh != this) {
2288 if (rootRefresh->IsWaitingForPaint(aTime)) {
2289 if (mRootRefresh != rootRefresh) {
2290 if (mRootRefresh) {
2291 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2293 rootRefresh->AddRefreshObserver(this, FlushType::Style);
2294 mRootRefresh = rootRefresh;
2296 mSkippedPaints = true;
2297 return true;
2301 return false;
2304 void nsRefreshDriver::SetThrottled(bool aThrottled) {
2305 if (aThrottled != mThrottled) {
2306 mThrottled = aThrottled;
2307 if (mActiveTimer) {
2308 // We want to switch our timer type here, so just stop and
2309 // restart the timer.
2310 EnsureTimerStarted(eForceAdjustTimer);
2315 /*static*/
2316 void nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild) {
2317 MOZ_ASSERT(NS_IsMainThread());
2318 MOZ_ASSERT(!XRE_IsParentProcess());
2319 RefPtr<RefreshDriverTimer> vsyncRefreshDriverTimer =
2320 new VsyncRefreshDriverTimer(aVsyncChild);
2322 // If we are using software timer, swap current timer to
2323 // VsyncRefreshDriverTimer.
2324 if (sRegularRateTimer) {
2325 sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
2327 sRegularRateTimer = vsyncRefreshDriverTimer.forget();
2330 void nsRefreshDriver::DoRefresh() {
2331 // Don't do a refresh unless we're in a state where we should be refreshing.
2332 if (!IsFrozen() && mPresContext && mActiveTimer) {
2333 DoTick();
2337 #ifdef DEBUG
2338 bool nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
2339 FlushType aFlushType) {
2340 ObserverArray& array = ArrayFor(aFlushType);
2341 return array.Contains(aObserver);
2343 #endif
2345 void nsRefreshDriver::ScheduleViewManagerFlush() {
2346 NS_ASSERTION(mPresContext->IsRoot(),
2347 "Should only schedule view manager flush on root prescontexts");
2348 mViewManagerFlushIsPending = true;
2349 mHasScheduleFlush = true;
2350 EnsureTimerStarted(eNeverAdjustTimer);
2353 void nsRefreshDriver::ScheduleFrameRequestCallbacks(Document* aDocument) {
2354 NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
2355 mFrameRequestCallbackDocs.NoIndex &&
2356 mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
2357 mThrottledFrameRequestCallbackDocs.NoIndex,
2358 "Don't schedule the same document multiple times");
2359 if (aDocument->ShouldThrottleFrameRequests()) {
2360 mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
2361 } else {
2362 mFrameRequestCallbackDocs.AppendElement(aDocument);
2365 // make sure that the timer is running
2366 EnsureTimerStarted();
2369 void nsRefreshDriver::RevokeFrameRequestCallbacks(Document* aDocument) {
2370 mFrameRequestCallbackDocs.RemoveElement(aDocument);
2371 mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
2372 // No need to worry about restarting our timer in slack mode if it's already
2373 // running; that will happen automatically when it fires.
2376 void nsRefreshDriver::ScheduleFullscreenEvent(
2377 UniquePtr<PendingFullscreenEvent> aEvent) {
2378 mPendingFullscreenEvents.AppendElement(std::move(aEvent));
2379 // make sure that the timer is running
2380 EnsureTimerStarted();
2383 void nsRefreshDriver::CancelPendingFullscreenEvents(Document* aDocument) {
2384 for (auto i : Reversed(IntegerRange(mPendingFullscreenEvents.Length()))) {
2385 if (mPendingFullscreenEvents[i]->Document() == aDocument) {
2386 mPendingFullscreenEvents.RemoveElementAt(i);
2391 void nsRefreshDriver::CancelPendingAnimationEvents(
2392 AnimationEventDispatcher* aDispatcher) {
2393 MOZ_ASSERT(aDispatcher);
2394 aDispatcher->ClearEventQueue();
2395 mAnimationEventFlushObservers.RemoveElement(aDispatcher);
2398 /* static */
2399 TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
2400 MOZ_ASSERT(NS_IsMainThread());
2401 MOZ_ASSERT(!aDefault.IsNull());
2403 if (!sRegularRateTimer) {
2404 return aDefault;
2407 // For computing idleness of refresh drivers we only care about
2408 // sRegularRateTimer, since we consider refresh drivers attached to
2409 // sThrottledRateTimer to be inactive. This implies that tasks
2410 // resulting from a tick on the sRegularRateTimer counts as being
2411 // busy but tasks resulting from a tick on sThrottledRateTimer
2412 // counts as being idle.
2413 return sRegularRateTimer->GetIdleDeadlineHint(aDefault);
2416 /* static */
2417 Maybe<TimeStamp> nsRefreshDriver::GetNextTickHint() {
2418 MOZ_ASSERT(NS_IsMainThread());
2420 if (!sRegularRateTimer) {
2421 return Nothing();
2423 return sRegularRateTimer->GetNextTickHint();
2426 void nsRefreshDriver::Disconnect() {
2427 MOZ_ASSERT(NS_IsMainThread());
2429 StopTimer();
2431 if (mPresContext) {
2432 mPresContext = nullptr;
2433 if (--sRefreshDriverCount == 0) {
2434 Shutdown();
2439 /* static */
2440 bool nsRefreshDriver::IsJankCritical() {
2441 MOZ_ASSERT(NS_IsMainThread());
2442 return sActiveVsyncTimers > 0;
2445 /* static */
2446 bool nsRefreshDriver::GetJankLevels(Vector<uint64_t>& aJank) {
2447 aJank.clear();
2448 return aJank.append(sJankLevels, ArrayLength(sJankLevels));
2451 #undef LOG