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/. */
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"
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"
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/CycleCollectedJSContext.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/DisplayPortUtils.h"
38 #include "mozilla/InputTaskManager.h"
39 #include "mozilla/IntegerRange.h"
40 #include "mozilla/PresShell.h"
41 #include "mozilla/dom/FontTableURIProtocolHandler.h"
43 #include "nsLayoutUtils.h"
44 #include "nsPresContext.h"
45 #include "nsComponentManagerUtils.h"
46 #include "mozilla/Logging.h"
47 #include "mozilla/dom/Document.h"
48 #include "mozilla/dom/DocumentInlines.h"
49 #include "nsIXULRuntime.h"
51 #include "nsContentUtils.h"
52 #include "mozilla/PendingAnimationTracker.h"
53 #include "mozilla/PendingFullscreenEvent.h"
54 #include "mozilla/dom/PerformanceMainThread.h"
55 #include "mozilla/Preferences.h"
56 #include "mozilla/StaticPrefs_apz.h"
57 #include "mozilla/StaticPrefs_gfx.h"
58 #include "mozilla/StaticPrefs_layout.h"
59 #include "mozilla/StaticPrefs_page_load.h"
60 #include "nsViewManager.h"
61 #include "GeckoProfiler.h"
62 #include "mozilla/dom/BrowserChild.h"
63 #include "mozilla/dom/CallbackDebuggerNotification.h"
64 #include "mozilla/dom/Event.h"
65 #include "mozilla/dom/Performance.h"
66 #include "mozilla/dom/Selection.h"
67 #include "mozilla/dom/VsyncChild.h"
68 #include "mozilla/dom/WindowBinding.h"
69 #include "mozilla/RestyleManager.h"
70 #include "mozilla/TaskController.h"
72 #include "imgIContainer.h"
73 #include "mozilla/dom/ScriptSettings.h"
74 #include "nsDocShell.h"
75 #include "nsISimpleEnumerator.h"
76 #include "nsJSEnvironment.h"
77 #include "mozilla/ScopeExit.h"
78 #include "mozilla/Telemetry.h"
80 #include "mozilla/ipc/BackgroundChild.h"
81 #include "mozilla/ipc/PBackgroundChild.h"
82 #include "VsyncSource.h"
83 #include "mozilla/VsyncDispatcher.h"
84 #include "mozilla/Unused.h"
85 #include "mozilla/TimelineConsumers.h"
86 #include "nsAnimationManager.h"
87 #include "nsDisplayList.h"
88 #include "nsDOMNavigationTiming.h"
89 #include "nsTransitionManager.h"
91 #if defined(MOZ_WIDGET_ANDROID)
92 # include "VRManagerChild.h"
93 #endif // defined(MOZ_WIDGET_ANDROID)
96 # include "nsXULPopupManager.h"
101 using namespace mozilla
;
102 using namespace mozilla::widget
;
103 using namespace mozilla::ipc
;
104 using namespace mozilla::dom
;
105 using namespace mozilla::layout
;
107 static mozilla::LazyLogModule
sRefreshDriverLog("nsRefreshDriver");
109 MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
111 #define DEFAULT_THROTTLED_FRAME_RATE 1
112 #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
113 #define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
114 // after 10 minutes, stop firing off inactive timers
115 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
117 // The number of seconds spent skipping frames because we are waiting for the
118 // compositor before logging.
119 #if defined(MOZ_ASAN)
120 # define REFRESH_WAIT_WARNING 5
121 #elif defined(DEBUG) && !defined(MOZ_VALGRIND)
122 # define REFRESH_WAIT_WARNING 5
123 #elif defined(DEBUG) && defined(MOZ_VALGRIND)
124 # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
125 #elif defined(MOZ_VALGRIND)
126 # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
128 # define REFRESH_WAIT_WARNING 1
131 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsRefreshDriver::TickReasons
);
134 // The number outstanding nsRefreshDrivers (that have been created but not
135 // disconnected). When this reaches zero we will call
136 // nsRefreshDriver::Shutdown.
137 static uint32_t sRefreshDriverCount
= 0;
139 // RAII-helper for recording elapsed duration for refresh tick phases.
140 class AutoRecordPhase
{
142 explicit AutoRecordPhase(double* aResultMs
)
143 : mTotalMs(aResultMs
), mStartTime(TimeStamp::Now()) {
144 MOZ_ASSERT(mTotalMs
);
147 *mTotalMs
= (TimeStamp::Now() - mStartTime
).ToMilliseconds();
152 mozilla::TimeStamp mStartTime
;
160 * The base class for all global refresh driver timers. It takes care
161 * of managing the list of refresh drivers attached to them and
162 * provides interfaces for querying/setting the rate and actually
163 * running a timer 'Tick'. Subclasses must implement StartTimer(),
164 * StopTimer(), and ScheduleNextTick() -- the first two just
165 * start/stop whatever timer mechanism is in use, and ScheduleNextTick
166 * is called at the start of the Tick() implementation to set a time
169 class RefreshDriverTimer
{
171 RefreshDriverTimer() = default;
173 NS_INLINE_DECL_REFCOUNTING(RefreshDriverTimer
)
175 virtual void AddRefreshDriver(nsRefreshDriver
* aDriver
) {
176 LOG("[%p] AddRefreshDriver %p", this, aDriver
);
179 mContentRefreshDrivers
.IsEmpty() && mRootRefreshDrivers
.IsEmpty();
180 if (IsRootRefreshDriver(aDriver
)) {
181 NS_ASSERTION(!mRootRefreshDrivers
.Contains(aDriver
),
182 "Adding a duplicate root refresh driver!");
183 mRootRefreshDrivers
.AppendElement(aDriver
);
185 NS_ASSERTION(!mContentRefreshDrivers
.Contains(aDriver
),
186 "Adding a duplicate content refresh driver!");
187 mContentRefreshDrivers
.AppendElement(aDriver
);
195 void RemoveRefreshDriver(nsRefreshDriver
* aDriver
) {
196 LOG("[%p] RemoveRefreshDriver %p", this, aDriver
);
198 if (IsRootRefreshDriver(aDriver
)) {
199 NS_ASSERTION(mRootRefreshDrivers
.Contains(aDriver
),
200 "RemoveRefreshDriver for a refresh driver that's not in the "
201 "root refresh list!");
202 mRootRefreshDrivers
.RemoveElement(aDriver
);
204 nsPresContext
* pc
= aDriver
->GetPresContext();
205 nsPresContext
* rootContext
= pc
? pc
->GetRootPresContext() : nullptr;
206 // During PresContext shutdown, we can't accurately detect
207 // if a root refresh driver exists or not. Therefore, we have to
208 // search and find out which list this driver exists in.
210 if (mRootRefreshDrivers
.Contains(aDriver
)) {
211 mRootRefreshDrivers
.RemoveElement(aDriver
);
213 NS_ASSERTION(mContentRefreshDrivers
.Contains(aDriver
),
214 "RemoveRefreshDriver without a display root for a "
215 "driver that is not in the content refresh list");
216 mContentRefreshDrivers
.RemoveElement(aDriver
);
219 NS_ASSERTION(mContentRefreshDrivers
.Contains(aDriver
),
220 "RemoveRefreshDriver for a driver that is not in the "
221 "content refresh list");
222 mContentRefreshDrivers
.RemoveElement(aDriver
);
227 mContentRefreshDrivers
.IsEmpty() && mRootRefreshDrivers
.IsEmpty();
233 TimeStamp
MostRecentRefresh() const { return mLastFireTime
; }
234 VsyncId
MostRecentRefreshVsyncId() const { return mLastFireId
; }
236 virtual TimeDuration
GetTimerRate() = 0;
238 TimeStamp
GetIdleDeadlineHint(TimeStamp aDefault
) {
239 MOZ_ASSERT(NS_IsMainThread());
241 TimeStamp mostRecentRefresh
= MostRecentRefresh();
242 TimeDuration refreshPeriod
= GetTimerRate();
243 TimeStamp idleEnd
= mostRecentRefresh
+ refreshPeriod
;
245 // If we haven't painted for some time, then guess that we won't paint
246 // again for a while, so the refresh driver is not a good way to predict
250 StaticPrefs::layout_idle_period_required_quiescent_frames() <
255 // End the predicted idle time a little early, the amount controlled by a
256 // pref, to prevent overrunning the idle time and delaying a frame.
257 idleEnd
= idleEnd
- TimeDuration::FromMilliseconds(
258 StaticPrefs::layout_idle_period_time_limit());
259 return idleEnd
< aDefault
? idleEnd
: aDefault
;
262 Maybe
<TimeStamp
> GetNextTickHint() {
263 MOZ_ASSERT(NS_IsMainThread());
264 TimeStamp nextTick
= MostRecentRefresh() + GetTimerRate();
265 return nextTick
< TimeStamp::Now() ? Nothing() : Some(nextTick
);
268 // Returns null if the RefreshDriverTimer is attached to several
269 // RefreshDrivers. That may happen for example when there are
270 // several windows open.
271 nsPresContext
* GetPresContextForOnlyRefreshDriver() {
272 if (mRootRefreshDrivers
.Length() == 1 && mContentRefreshDrivers
.IsEmpty()) {
273 return mRootRefreshDrivers
[0]->GetPresContext();
275 if (mContentRefreshDrivers
.Length() == 1 && mRootRefreshDrivers
.IsEmpty()) {
276 return mContentRefreshDrivers
[0]->GetPresContext();
282 virtual ~RefreshDriverTimer() {
284 mContentRefreshDrivers
.Length() == 0,
285 "Should have removed all content refresh drivers from here by now!");
287 mRootRefreshDrivers
.Length() == 0,
288 "Should have removed all root refresh drivers from here by now!");
291 virtual void StartTimer() = 0;
292 virtual void StopTimer() = 0;
293 virtual void ScheduleNextTick(TimeStamp aNowTime
) = 0;
295 bool IsRootRefreshDriver(nsRefreshDriver
* aDriver
) {
296 nsPresContext
* pc
= aDriver
->GetPresContext();
297 nsPresContext
* rootContext
= pc
? pc
->GetRootPresContext() : nullptr;
302 return aDriver
== rootContext
->RefreshDriver();
306 * Actually runs a tick, poking all the attached RefreshDrivers.
307 * Grabs the "now" time via TimeStamp::Now().
310 TimeStamp now
= TimeStamp::Now();
311 Tick(VsyncId(), now
);
314 void TickRefreshDrivers(VsyncId aId
, TimeStamp aNow
,
315 nsTArray
<RefPtr
<nsRefreshDriver
>>& aDrivers
) {
316 if (aDrivers
.IsEmpty()) {
320 for (nsRefreshDriver
* driver
: aDrivers
.Clone()) {
321 // don't poke this driver if it's in test mode
322 if (driver
->IsTestControllingRefreshesEnabled()) {
326 TickDriver(driver
, aId
, aNow
);
331 * Tick the refresh drivers based on the given timestamp.
333 void Tick(VsyncId aId
, TimeStamp now
) {
334 ScheduleNextTick(now
);
339 LOG("[%p] ticking drivers...", this);
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
;
353 TimeStamp mTargetTime
;
355 nsTArray
<RefPtr
<nsRefreshDriver
>> mContentRefreshDrivers
;
356 nsTArray
<RefPtr
<nsRefreshDriver
>> mRootRefreshDrivers
;
358 // useful callback for nsITimer-based derived classes, here
359 // because of c++ protected shenanigans
360 static void TimerTick(nsITimer
* aTimer
, void* aClosure
) {
361 RefPtr
<RefreshDriverTimer
> timer
=
362 static_cast<RefreshDriverTimer
*>(aClosure
);
368 * A RefreshDriverTimer that uses a nsITimer as the underlying timer. Note that
369 * this is a ONE_SHOT timer, not a repeating one! Subclasses are expected to
370 * implement ScheduleNextTick and intelligently calculate the next time to tick,
371 * and to reset mTimer. Using a repeating nsITimer gets us into a lot of pain
372 * with its attempt at intelligent slack removal and such, so we don't do it.
374 class SimpleTimerBasedRefreshDriverTimer
: public RefreshDriverTimer
{
377 * aRate -- the delay, in milliseconds, requested between timer firings
379 explicit SimpleTimerBasedRefreshDriverTimer(double aRate
) {
381 mTimer
= NS_NewTimer();
384 virtual ~SimpleTimerBasedRefreshDriverTimer() override
{ StopTimer(); }
386 // will take effect at next timer tick
387 virtual void SetRate(double aNewRate
) {
388 mRateMilliseconds
= aNewRate
;
389 mRateDuration
= TimeDuration::FromMilliseconds(mRateMilliseconds
);
392 double GetRate() const { return mRateMilliseconds
; }
394 TimeDuration
GetTimerRate() override
{ return mRateDuration
; }
397 void StartTimer() override
{
398 // pretend we just fired, and we schedule the next tick normally
399 mLastFireTime
= TimeStamp::Now();
400 mLastFireId
= VsyncId();
402 mTargetTime
= mLastFireTime
+ mRateDuration
;
404 uint32_t delay
= static_cast<uint32_t>(mRateMilliseconds
);
405 mTimer
->InitWithNamedFuncCallback(
406 TimerTick
, this, delay
, nsITimer::TYPE_ONE_SHOT
,
407 "SimpleTimerBasedRefreshDriverTimer::StartTimer");
410 void StopTimer() override
{ mTimer
->Cancel(); }
412 double mRateMilliseconds
;
413 TimeDuration mRateDuration
;
414 RefPtr
<nsITimer
> mTimer
;
418 * A refresh driver that listens to vsync events and ticks the refresh driver
419 * on vsync intervals. We throttle the refresh driver if we get too many
420 * vsync events and wait to catch up again.
422 class VsyncRefreshDriverTimer
: public RefreshDriverTimer
{
424 VsyncRefreshDriverTimer()
425 : mVsyncDispatcher(nullptr),
426 mVsyncChild(nullptr),
427 mVsyncRate(TimeDuration::Forever()) {
428 MOZ_ASSERT(XRE_IsParentProcess());
429 MOZ_ASSERT(NS_IsMainThread());
430 mVsyncSource
= gfxPlatform::GetPlatform()->GetHardwareVsync();
431 mVsyncObserver
= new RefreshDriverVsyncObserver(this);
432 MOZ_ALWAYS_TRUE(mVsyncDispatcher
=
433 mVsyncSource
->GetRefreshTimerVsyncDispatcher());
436 // Constructor for when we have a local vsync source. As it is local, we do
437 // not have to worry about it being re-inited by gfxPlatform on frame rate
438 // change on the global source.
439 explicit VsyncRefreshDriverTimer(const RefPtr
<gfx::VsyncSource
>& aVsyncSource
)
440 : mVsyncSource(aVsyncSource
),
441 mVsyncDispatcher(nullptr),
442 mVsyncChild(nullptr),
443 mVsyncRate(TimeDuration::Forever()) {
444 MOZ_ASSERT(XRE_IsParentProcess());
445 MOZ_ASSERT(NS_IsMainThread());
446 mVsyncObserver
= new RefreshDriverVsyncObserver(this);
447 MOZ_ALWAYS_TRUE(mVsyncDispatcher
=
448 aVsyncSource
->GetRefreshTimerVsyncDispatcher());
451 explicit VsyncRefreshDriverTimer(const RefPtr
<VsyncChild
>& aVsyncChild
)
452 : mVsyncSource(nullptr),
453 mVsyncDispatcher(nullptr),
454 mVsyncChild(aVsyncChild
),
455 mVsyncRate(TimeDuration::Forever()) {
456 MOZ_ASSERT(XRE_IsContentProcess());
457 MOZ_ASSERT(NS_IsMainThread());
458 mVsyncObserver
= new RefreshDriverVsyncObserver(this);
461 TimeDuration
GetTimerRate() override
{
463 mVsyncRate
= mVsyncSource
->GetGlobalDisplay().GetVsyncRate();
464 } else if (mVsyncChild
) {
465 mVsyncRate
= mVsyncChild
->GetVsyncRate();
468 // If hardware queries fail / are unsupported, we have to just guess.
469 return mVsyncRate
!= TimeDuration::Forever()
471 : TimeDuration::FromMilliseconds(1000.0 / 60.0);
475 // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
476 // explicitly shutdown. We create an inner class that has the VsyncObserver
477 // and is shutdown when the RefreshDriverTimer is deleted.
478 class RefreshDriverVsyncObserver final
: public VsyncObserver
{
480 explicit RefreshDriverVsyncObserver(
481 VsyncRefreshDriverTimer
* aVsyncRefreshDriverTimer
)
482 : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer
),
483 mParentProcessRefreshTickLock("RefreshTickLock"),
484 mPendingParentProcessVsync(false),
485 mRecentVsync(TimeStamp::Now()),
486 mLastTick(TimeStamp::Now()),
487 mVsyncRate(TimeDuration::Forever()),
488 mProcessedVsync(true) {
489 MOZ_ASSERT(NS_IsMainThread());
492 class ParentProcessVsyncNotifier final
: public Runnable
,
493 public nsIRunnablePriority
{
495 explicit ParentProcessVsyncNotifier(RefreshDriverVsyncObserver
* aObserver
)
497 "VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::"
498 "ParentProcessVsyncNotifier"),
499 mObserver(aObserver
) {}
501 NS_DECL_ISUPPORTS_INHERITED
503 NS_IMETHOD
Run() override
{
504 MOZ_ASSERT(NS_IsMainThread());
505 sVsyncPriorityEnabled
= mozilla::BrowserTabsRemoteAutostart();
507 mObserver
->NotifyParentProcessVsync();
511 NS_IMETHOD
GetPriority(uint32_t* aPriority
) override
{
512 *aPriority
= sVsyncPriorityEnabled
513 ? nsIRunnablePriority::PRIORITY_VSYNC
514 : nsIRunnablePriority::PRIORITY_NORMAL
;
519 ~ParentProcessVsyncNotifier() = default;
520 RefPtr
<RefreshDriverVsyncObserver
> mObserver
;
521 static mozilla::Atomic
<bool> sVsyncPriorityEnabled
;
524 bool NotifyVsync(const VsyncEvent
& aVsync
) override
{
525 // Compress vsync notifications such that only 1 may run at a time
526 // This is so that we don't flood the refresh driver with vsync messages
527 // if the main thread is blocked for long periods of time
529 MonitorAutoLock
lock(mParentProcessRefreshTickLock
);
530 mRecentParentProcessVsync
= aVsync
;
531 if (mPendingParentProcessVsync
) {
534 mPendingParentProcessVsync
= true;
537 if (XRE_IsContentProcess()) {
538 NotifyParentProcessVsync();
542 nsCOMPtr
<nsIRunnable
> vsyncEvent
= new ParentProcessVsyncNotifier(this);
543 NS_DispatchToMainThread(vsyncEvent
);
547 void NotifyParentProcessVsync() {
548 // IMPORTANT: All paths through this method MUST hold a strong ref on
549 // |this| for the duration of the TickRefreshDriver callback.
550 MOZ_ASSERT(NS_IsMainThread());
552 // This clears the input handling start time.
553 InputTaskManager::Get()->SetInputHandlingStartTime(TimeStamp());
557 MonitorAutoLock
lock(mParentProcessRefreshTickLock
);
558 aVsync
= mRecentParentProcessVsync
;
559 mPendingParentProcessVsync
= false;
562 mRecentVsync
= aVsync
.mTime
;
563 mRecentVsyncId
= aVsync
.mId
;
564 if (!mBlockUntil
.IsNull() && mBlockUntil
> aVsync
.mTime
) {
565 if (mProcessedVsync
) {
566 // Re-post vsync update as a normal priority runnable. This way
567 // runnables already in normal priority queue get processed.
568 mProcessedVsync
= false;
569 nsCOMPtr
<nsIRunnable
> vsyncEvent
= NewRunnableMethod
<>(
570 "RefreshDriverVsyncObserver::NormalPriorityNotify", this,
571 &RefreshDriverVsyncObserver::NormalPriorityNotify
);
572 NS_DispatchToMainThread(vsyncEvent
);
578 if (StaticPrefs::layout_lower_priority_refresh_driver_during_load() &&
579 mVsyncRefreshDriverTimer
) {
580 nsPresContext
* pctx
=
581 mVsyncRefreshDriverTimer
->GetPresContextForOnlyRefreshDriver();
582 if (pctx
&& pctx
->HadContentfulPaint() && pctx
->Document() &&
583 pctx
->Document()->GetReadyStateEnum() <
584 Document::READYSTATE_COMPLETE
) {
585 nsPIDOMWindowInner
* win
= pctx
->Document()->GetInnerWindow();
586 uint32_t frameRateMultiplier
= pctx
->GetNextFrameRateMultiplier();
587 if (!frameRateMultiplier
) {
588 pctx
->DidUseFrameRateMultiplier();
590 if (win
&& frameRateMultiplier
) {
591 dom::Performance
* perf
= win
->GetPerformance();
592 // Limit slower refresh rate to 5 seconds between the
593 // first contentful paint and page load.
594 if (perf
&& perf
->Now() <
595 StaticPrefs::page_load_deprioritization_period()) {
596 if (mProcessedVsync
) {
597 mProcessedVsync
= false;
598 // Handle this case similarly to the code above, but just
600 TimeDuration rate
= mVsyncRefreshDriverTimer
->GetTimerRate();
601 uint32_t slowRate
= static_cast<uint32_t>(
602 rate
.ToMilliseconds() * frameRateMultiplier
);
603 pctx
->DidUseFrameRateMultiplier();
604 nsCOMPtr
<nsIRunnable
> vsyncEvent
= NewRunnableMethod
<>(
605 "RefreshDriverVsyncObserver::NormalPriorityNotify[IDLE]",
606 this, &RefreshDriverVsyncObserver::NormalPriorityNotify
);
607 NS_DispatchToCurrentThreadQueue(vsyncEvent
.forget(), slowRate
,
608 EventQueuePriority::Idle
);
616 RefPtr
<RefreshDriverVsyncObserver
> kungFuDeathGrip(this);
617 TickRefreshDriver(aVsync
.mId
, aVsync
.mTime
);
621 MOZ_ASSERT(NS_IsMainThread());
622 mVsyncRefreshDriverTimer
= nullptr;
625 void OnTimerStart() { mLastTick
= TimeStamp::Now(); }
627 void NormalPriorityNotify() {
628 if (mLastProcessedTick
.IsNull() || mRecentVsync
> mLastProcessedTick
) {
629 // mBlockUntil is for high priority vsync notifications only.
630 mBlockUntil
= TimeStamp();
631 TickRefreshDriver(mRecentVsyncId
, mRecentVsync
);
634 mProcessedVsync
= true;
638 ~RefreshDriverVsyncObserver() = default;
640 void RecordTelemetryProbes(TimeStamp aVsyncTimestamp
) {
641 MOZ_ASSERT(NS_IsMainThread());
642 #ifndef ANDROID /* bug 1142079 */
643 if (XRE_IsParentProcess()) {
644 TimeDuration vsyncLatency
= TimeStamp::Now() - aVsyncTimestamp
;
645 uint32_t sample
= (uint32_t)vsyncLatency
.ToMilliseconds();
646 Telemetry::Accumulate(
647 Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS
, sample
);
648 Telemetry::Accumulate(
649 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS
, sample
);
650 } else if (mVsyncRate
!= TimeDuration::Forever()) {
651 TimeDuration contentDelay
= (TimeStamp::Now() - mLastTick
) - mVsyncRate
;
652 if (contentDelay
.ToMilliseconds() < 0) {
653 // Vsyncs are noisy and some can come at a rate quicker than
654 // the reported hardware rate. In those cases, consider that we have 0
656 contentDelay
= TimeDuration::FromMilliseconds(0);
658 uint32_t sample
= (uint32_t)contentDelay
.ToMilliseconds();
659 Telemetry::Accumulate(
660 Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS
, sample
);
661 Telemetry::Accumulate(
662 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS
, sample
);
664 // Request the vsync rate from the parent process. Might be a few vsyncs
665 // until the parent responds.
666 if (mVsyncRefreshDriverTimer
) {
667 mVsyncRate
= mVsyncRefreshDriverTimer
->mVsyncChild
->GetVsyncRate();
673 void TickRefreshDriver(VsyncId aId
, TimeStamp aVsyncTimestamp
) {
674 MOZ_ASSERT(NS_IsMainThread());
676 RecordTelemetryProbes(aVsyncTimestamp
);
677 mLastTick
= TimeStamp::Now();
678 mLastProcessedTick
= aVsyncTimestamp
;
680 // On 32-bit Windows we sometimes get times where TimeStamp::Now() is not
681 // monotonic because the underlying system apis produce non-monontonic
682 // results. (bug 1306896)
684 MOZ_ASSERT(aVsyncTimestamp
<= TimeStamp::Now());
687 // Let also non-RefreshDriver code to run at least for awhile if we have
688 // a mVsyncRefreshDriverTimer. Note, if nothing else is running,
689 // RefreshDriver will still run as fast as possible, some ticks will
690 // just be triggered from a normal priority runnable.
691 TimeDuration timeForOutsideTick
= TimeDuration::FromMilliseconds(0.0f
);
693 // We might have a problem that we call ~VsyncRefreshDriverTimer() before
694 // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
696 if (mVsyncRefreshDriverTimer
) {
697 timeForOutsideTick
= TimeDuration::FromMilliseconds(
698 mVsyncRefreshDriverTimer
->GetTimerRate().ToMilliseconds() / 100.0f
);
699 RefPtr
<VsyncRefreshDriverTimer
> timer
= mVsyncRefreshDriverTimer
;
700 timer
->RunRefreshDrivers(aId
, aVsyncTimestamp
);
701 // Note: mVsyncRefreshDriverTimer might be null now.
704 TimeDuration tickDuration
= TimeStamp::Now() - mLastTick
;
705 mBlockUntil
= aVsyncTimestamp
+ tickDuration
+ timeForOutsideTick
;
708 // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
709 // be always available before Shutdown(). We can just use the raw pointer
711 VsyncRefreshDriverTimer
* mVsyncRefreshDriverTimer
;
713 Monitor mParentProcessRefreshTickLock
;
714 VsyncEvent mRecentParentProcessVsync
;
715 bool mPendingParentProcessVsync
;
717 TimeStamp mRecentVsync
;
718 VsyncId mRecentVsyncId
;
720 TimeStamp mLastProcessedTick
;
721 TimeStamp mBlockUntil
;
722 TimeDuration mVsyncRate
;
723 bool mProcessedVsync
;
724 }; // RefreshDriverVsyncObserver
726 ~VsyncRefreshDriverTimer() override
{
727 if (mVsyncDispatcher
) {
728 mVsyncDispatcher
->RemoveChildRefreshTimer(mVsyncObserver
);
729 mVsyncDispatcher
= nullptr;
730 } else if (mVsyncChild
) {
731 mVsyncChild
->RemoveChildRefreshTimer(mVsyncObserver
);
732 mVsyncChild
= nullptr;
735 // Detach current vsync timer from this VsyncObserver. The observer will no
736 // longer tick this timer.
737 mVsyncObserver
->Shutdown();
738 mVsyncObserver
= nullptr;
741 void StartTimer() override
{
742 MOZ_ASSERT(NS_IsMainThread());
744 mLastFireTime
= TimeStamp::Now();
745 mLastFireId
= VsyncId();
747 if (mVsyncDispatcher
) {
748 mVsyncDispatcher
->AddChildRefreshTimer(mVsyncObserver
);
749 } else if (mVsyncChild
) {
750 mVsyncChild
->AddChildRefreshTimer(mVsyncObserver
);
751 mVsyncObserver
->OnTimerStart();
755 void StopTimer() override
{
756 MOZ_ASSERT(NS_IsMainThread());
758 if (mVsyncDispatcher
) {
759 mVsyncDispatcher
->RemoveChildRefreshTimer(mVsyncObserver
);
760 } else if (mVsyncChild
) {
761 mVsyncChild
->RemoveChildRefreshTimer(mVsyncObserver
);
765 void ScheduleNextTick(TimeStamp aNowTime
) override
{
766 // Do nothing since we just wait for the next vsync from
767 // RefreshDriverVsyncObserver.
770 void RunRefreshDrivers(VsyncId aId
, TimeStamp aTimeStamp
) {
771 Tick(aId
, aTimeStamp
);
772 for (auto& driver
: mContentRefreshDrivers
) {
773 driver
->FinishedVsyncTick();
775 for (auto& driver
: mRootRefreshDrivers
) {
776 driver
->FinishedVsyncTick();
780 // When using local vsync source, we keep a strong ref to it here to ensure
781 // that the weak ref in the vsync dispatcher does not end up dangling.
782 // As this is a local vsync source, it is not affected by gfxPlatform vsync
784 RefPtr
<gfx::VsyncSource
> mVsyncSource
;
785 RefPtr
<RefreshDriverVsyncObserver
> mVsyncObserver
;
786 // Used for parent process.
787 RefPtr
<RefreshTimerVsyncDispatcher
> mVsyncDispatcher
;
788 // Used for child process.
789 // The mVsyncChild will be always available before VsncChild::ActorDestroy().
790 // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
791 RefPtr
<VsyncChild
> mVsyncChild
;
792 TimeDuration mVsyncRate
;
793 }; // VsyncRefreshDriverTimer
795 NS_IMPL_ISUPPORTS_INHERITED(
796 VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::
797 ParentProcessVsyncNotifier
,
798 Runnable
, nsIRunnablePriority
)
800 mozilla::Atomic
<bool> VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::
801 ParentProcessVsyncNotifier::sVsyncPriorityEnabled(false);
804 * Since the content process takes some time to setup
805 * the vsync IPC connection, this timer is used
806 * during the intial startup process.
807 * During initial startup, the refresh drivers
808 * are ticked off this timer, and are swapped out once content
809 * vsync IPC connection is established.
811 class StartupRefreshDriverTimer
: public SimpleTimerBasedRefreshDriverTimer
{
813 explicit StartupRefreshDriverTimer(double aRate
)
814 : SimpleTimerBasedRefreshDriverTimer(aRate
) {}
817 void ScheduleNextTick(TimeStamp aNowTime
) override
{
818 // Since this is only used for startup, it isn't super critical
819 // that we tick at consistent intervals.
820 TimeStamp newTarget
= aNowTime
+ mRateDuration
;
822 static_cast<uint32_t>((newTarget
- aNowTime
).ToMilliseconds());
823 mTimer
->InitWithNamedFuncCallback(
824 TimerTick
, this, delay
, nsITimer::TYPE_ONE_SHOT
,
825 "StartupRefreshDriverTimer::ScheduleNextTick");
826 mTargetTime
= newTarget
;
831 * A RefreshDriverTimer for inactive documents. When a new refresh driver is
832 * added, the rate is reset to the base (normally 1s/1fps). Every time
833 * it ticks, a single refresh driver is poked. Once they have all been poked,
834 * the duration between ticks doubles, up to mDisableAfterMilliseconds. At that
835 * point, the timer is quiet and doesn't tick (until something is added to it
838 * When a timer is removed, there is a possibility of another timer
839 * being skipped for one cycle. We could avoid this by adjusting
840 * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
841 * add that complexity. All we want is for inactive drivers to tick
842 * at some point, but we don't care too much about how often.
844 class InactiveRefreshDriverTimer final
845 : public SimpleTimerBasedRefreshDriverTimer
{
847 explicit InactiveRefreshDriverTimer(double aRate
)
848 : SimpleTimerBasedRefreshDriverTimer(aRate
),
849 mNextTickDuration(aRate
),
850 mDisableAfterMilliseconds(-1.0),
851 mNextDriverIndex(0) {}
853 InactiveRefreshDriverTimer(double aRate
, double aDisableAfterMilliseconds
)
854 : SimpleTimerBasedRefreshDriverTimer(aRate
),
855 mNextTickDuration(aRate
),
856 mDisableAfterMilliseconds(aDisableAfterMilliseconds
),
857 mNextDriverIndex(0) {}
859 void AddRefreshDriver(nsRefreshDriver
* aDriver
) override
{
860 RefreshDriverTimer::AddRefreshDriver(aDriver
);
862 LOG("[%p] inactive timer got new refresh driver %p, resetting rate", this,
865 // reset the timer, and start with the newly added one next time.
866 mNextTickDuration
= mRateMilliseconds
;
868 // we don't really have to start with the newly added one, but we may as
869 // well not tick the old ones at the fastest rate any more than we need to.
870 mNextDriverIndex
= GetRefreshDriverCount() - 1;
876 TimeDuration
GetTimerRate() override
{
877 return TimeDuration::FromMilliseconds(mNextTickDuration
);
881 uint32_t GetRefreshDriverCount() {
882 return mContentRefreshDrivers
.Length() + mRootRefreshDrivers
.Length();
885 void StartTimer() override
{
886 mLastFireTime
= TimeStamp::Now();
887 mLastFireId
= VsyncId();
889 mTargetTime
= mLastFireTime
+ mRateDuration
;
891 uint32_t delay
= static_cast<uint32_t>(mRateMilliseconds
);
892 mTimer
->InitWithNamedFuncCallback(TimerTickOne
, this, delay
,
893 nsITimer::TYPE_ONE_SHOT
,
894 "InactiveRefreshDriverTimer::StartTimer");
897 void StopTimer() override
{ mTimer
->Cancel(); }
899 void ScheduleNextTick(TimeStamp aNowTime
) override
{
900 if (mDisableAfterMilliseconds
> 0.0 &&
901 mNextTickDuration
> mDisableAfterMilliseconds
) {
902 // We hit the time after which we should disable
903 // inactive window refreshes; don't schedule anything
904 // until we get kicked by an AddRefreshDriver call.
908 // double the next tick time if we've already gone through all of them once
909 if (mNextDriverIndex
>= GetRefreshDriverCount()) {
910 mNextTickDuration
*= 2.0;
911 mNextDriverIndex
= 0;
914 // this doesn't need to be precise; do a simple schedule
915 uint32_t delay
= static_cast<uint32_t>(mNextTickDuration
);
916 mTimer
->InitWithNamedFuncCallback(
917 TimerTickOne
, this, delay
, nsITimer::TYPE_ONE_SHOT
,
918 "InactiveRefreshDriverTimer::ScheduleNextTick");
920 LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this,
921 mNextTickDuration
, mNextDriverIndex
, GetRefreshDriverCount());
924 /* Runs just one driver's tick. */
926 TimeStamp now
= TimeStamp::Now();
928 ScheduleNextTick(now
);
931 mLastFireId
= VsyncId();
933 nsTArray
<RefPtr
<nsRefreshDriver
>> drivers(mContentRefreshDrivers
.Clone());
934 drivers
.AppendElements(mRootRefreshDrivers
);
935 size_t index
= mNextDriverIndex
;
937 if (index
< drivers
.Length() &&
938 !drivers
[index
]->IsTestControllingRefreshesEnabled()) {
939 TickDriver(drivers
[index
], VsyncId(), now
);
945 static void TimerTickOne(nsITimer
* aTimer
, void* aClosure
) {
946 RefPtr
<InactiveRefreshDriverTimer
> timer
=
947 static_cast<InactiveRefreshDriverTimer
*>(aClosure
);
951 double mNextTickDuration
;
952 double mDisableAfterMilliseconds
;
953 uint32_t mNextDriverIndex
;
956 } // namespace mozilla
958 static StaticRefPtr
<RefreshDriverTimer
> sRegularRateTimer
;
959 static nsTArray
<RefreshDriverTimer
*>* sRegularRateTimerList
;
960 static StaticRefPtr
<InactiveRefreshDriverTimer
> sThrottledRateTimer
;
962 void nsRefreshDriver::CreateVsyncRefreshTimer() {
963 MOZ_ASSERT(NS_IsMainThread());
965 if (gfxPlatform::IsInLayoutAsapMode()) {
970 // If available, we fetch the widget-specific vsync source.
971 nsPresContext
* pc
= GetPresContext();
972 nsIWidget
* widget
= pc
->GetRootWidget();
974 if (RefPtr
<gfx::VsyncSource
> localVsyncSource
=
975 widget
->GetVsyncSource()) {
976 mOwnTimer
= new VsyncRefreshDriverTimer(localVsyncSource
);
977 sRegularRateTimerList
->AppendElement(mOwnTimer
.get());
980 if (BrowserChild
* browserChild
= widget
->GetOwningBrowserChild()) {
981 if (RefPtr
<VsyncChild
> localVsyncSource
=
982 browserChild
->GetVsyncChild()) {
983 mOwnTimer
= new VsyncRefreshDriverTimer(localVsyncSource
);
984 sRegularRateTimerList
->AppendElement(mOwnTimer
.get());
990 if (!sRegularRateTimer
) {
991 if (XRE_IsParentProcess()) {
992 // Make sure all vsync systems are ready.
993 gfxPlatform::GetPlatform();
994 // In parent process, we can create the VsyncRefreshDriverTimer directly.
995 sRegularRateTimer
= new VsyncRefreshDriverTimer();
997 PBackgroundChild
* actorChild
=
998 BackgroundChild::GetOrCreateForCurrentThread();
999 if (NS_WARN_IF(!actorChild
)) {
1003 dom::PVsyncChild
* actor
= actorChild
->SendPVsyncConstructor();
1004 if (NS_WARN_IF(!actor
)) {
1008 dom::VsyncChild
* child
= static_cast<dom::VsyncChild
*>(actor
);
1010 RefPtr
<RefreshDriverTimer
> vsyncRefreshDriverTimer
=
1011 new VsyncRefreshDriverTimer(child
);
1013 sRegularRateTimer
= std::move(vsyncRefreshDriverTimer
);
1018 static uint32_t GetFirstFrameDelay(imgIRequest
* req
) {
1019 nsCOMPtr
<imgIContainer
> container
;
1020 if (NS_FAILED(req
->GetImage(getter_AddRefs(container
))) || !container
) {
1024 // If this image isn't animated, there isn't a first frame delay.
1025 int32_t delay
= container
->GetFirstFrameDelay();
1026 if (delay
< 0) return 0;
1028 return static_cast<uint32_t>(delay
);
1032 void nsRefreshDriver::Shutdown() {
1033 MOZ_ASSERT(NS_IsMainThread());
1034 // clean up our timers
1035 sRegularRateTimer
= nullptr;
1036 delete sRegularRateTimerList
;
1037 sRegularRateTimerList
= nullptr;
1038 sThrottledRateTimer
= nullptr;
1042 int32_t nsRefreshDriver::DefaultInterval() {
1043 return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
1046 // Compute the interval to use for the refresh driver timer, in milliseconds.
1047 // outIsDefault indicates that rate was not explicitly set by the user
1048 // so we might choose other, more appropriate rates (e.g. vsync, etc)
1049 // layout.frame_rate=0 indicates "ASAP mode".
1050 // In ASAP mode rendering is iterated as fast as possible (typically for stress
1051 // testing). A target rate of 10k is used internally instead of special-handling
1052 // 0. Backends which block on swap/present/etc should try to not block when
1053 // layout.frame_rate=0 - to comply with "ASAP" as much as possible.
1054 double nsRefreshDriver::GetRegularTimerInterval() const {
1055 int32_t rate
= Preferences::GetInt("layout.frame_rate", -1);
1057 rate
= gfxPlatform::GetDefaultFrameRate();
1058 } else if (rate
== 0) {
1062 return 1000.0 / rate
;
1066 double nsRefreshDriver::GetThrottledTimerInterval() {
1067 int32_t rate
= Preferences::GetInt("layout.throttled_frame_rate", -1);
1069 rate
= DEFAULT_THROTTLED_FRAME_RATE
;
1071 return 1000.0 / rate
;
1074 /* static */ mozilla::TimeDuration
1075 nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
1077 Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
1078 if (interval
<= 0) {
1079 interval
= DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS
;
1081 return TimeDuration::FromMilliseconds(interval
);
1084 RefreshDriverTimer
* nsRefreshDriver::ChooseTimer() {
1086 if (!sThrottledRateTimer
)
1087 sThrottledRateTimer
= new InactiveRefreshDriverTimer(
1088 GetThrottledTimerInterval(),
1089 DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS
* 1000.0);
1090 return sThrottledRateTimer
;
1094 CreateVsyncRefreshTimer();
1098 return mOwnTimer
.get();
1101 if (!sRegularRateTimer
) {
1102 double rate
= GetRegularTimerInterval();
1103 sRegularRateTimer
= new StartupRefreshDriverTimer(rate
);
1106 return sRegularRateTimer
;
1109 nsRefreshDriver::nsRefreshDriver(nsPresContext
* aPresContext
)
1110 : mActiveTimer(nullptr),
1112 mPresContext(aPresContext
),
1113 mRootRefresh(nullptr),
1114 mNextTransactionId
{0},
1116 mThrottledFrameRequestInterval(
1117 TimeDuration::FromMilliseconds(GetThrottledTimerInterval())),
1118 mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
1120 mNeedToRecomputeVisibility(false),
1121 mTestControllingRefreshes(false),
1122 mViewManagerFlushIsPending(false),
1123 mHasScheduleFlush(false),
1125 mWaitingForTransaction(false),
1126 mSkippedPaints(false),
1127 mResizeSuppressed(false),
1128 mNotifyDOMContentFlushed(false),
1129 mNeedToUpdateIntersectionObservations(false),
1130 mInNormalTick(false),
1131 mAttemptedExtraTickSinceLastVsync(false),
1132 mHasExceededAfterLoadTickPeriod(false),
1133 mWarningThreshold(REFRESH_WAIT_WARNING
) {
1134 MOZ_ASSERT(NS_IsMainThread());
1135 MOZ_ASSERT(mPresContext
,
1136 "Need a pres context to tell us to call Disconnect() later "
1137 "and decrement sRefreshDriverCount.");
1138 mMostRecentRefresh
= TimeStamp::Now();
1139 mNextThrottledFrameRequestTick
= mMostRecentRefresh
;
1140 mNextRecomputeVisibilityTick
= mMostRecentRefresh
;
1142 if (!sRegularRateTimerList
) {
1143 sRegularRateTimerList
= new nsTArray
<RefreshDriverTimer
*>();
1145 ++sRefreshDriverCount
;
1148 nsRefreshDriver::~nsRefreshDriver() {
1149 MOZ_ASSERT(NS_IsMainThread());
1150 MOZ_ASSERT(ObserverCount() == mEarlyRunners
.Length(),
1151 "observers, except pending selection scrolls, "
1152 "should have been unregistered");
1153 MOZ_ASSERT(!mActiveTimer
, "timer should be gone");
1154 MOZ_ASSERT(!mPresContext
,
1155 "Should have called Disconnect() and decremented "
1156 "sRefreshDriverCount!");
1159 mRootRefresh
->RemoveRefreshObserver(this, FlushType::Style
);
1160 mRootRefresh
= nullptr;
1162 if (mOwnTimer
&& sRegularRateTimerList
) {
1163 sRegularRateTimerList
->RemoveElement(mOwnTimer
.get());
1167 // Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
1169 void nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds
) {
1170 // ensure that we're removed from our driver
1173 if (!mTestControllingRefreshes
) {
1174 mMostRecentRefresh
= TimeStamp::Now();
1176 mTestControllingRefreshes
= true;
1177 if (mWaitingForTransaction
) {
1178 // Disable any refresh driver throttling when entering test mode
1179 mWaitingForTransaction
= false;
1180 mSkippedPaints
= false;
1181 mWarningThreshold
= REFRESH_WAIT_WARNING
;
1185 mMostRecentRefresh
+= TimeDuration::FromMilliseconds((double)aMilliseconds
);
1187 mozilla::dom::AutoNoJSAPI nojsapi
;
1191 void nsRefreshDriver::RestoreNormalRefresh() {
1192 mTestControllingRefreshes
= false;
1193 EnsureTimerStarted(eAllowTimeToGoBackwards
);
1194 mPendingTransactions
.Clear();
1197 TimeStamp
nsRefreshDriver::MostRecentRefresh(bool aEnsureTimerStarted
) const {
1198 // In case of stylo traversal, we have already activated the refresh driver in
1199 // RestyleManager::ProcessPendingRestyles().
1200 if (aEnsureTimerStarted
&& !ServoStyleSet::IsInServoTraversal()) {
1201 const_cast<nsRefreshDriver
*>(this)->EnsureTimerStarted();
1204 return mMostRecentRefresh
;
1207 void nsRefreshDriver::AddRefreshObserver(nsARefreshObserver
* aObserver
,
1208 FlushType aFlushType
,
1209 const char* aObserverDescription
) {
1210 ObserverArray
& array
= ArrayFor(aFlushType
);
1211 array
.AppendElement(ObserverData
{
1212 aObserver
, aObserverDescription
, TimeStamp::Now(),
1214 ? MarkerInnerWindowIdFromDocShell(mPresContext
->GetDocShell())
1215 : MarkerInnerWindowId::NoId(),
1216 profiler_capture_backtrace(), aFlushType
});
1217 EnsureTimerStarted();
1220 bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver
* aObserver
,
1221 FlushType aFlushType
) {
1222 ObserverArray
& array
= ArrayFor(aFlushType
);
1223 auto index
= array
.IndexOf(aObserver
);
1224 if (index
== ObserverArray::array_type::NoIndex
) {
1228 if (profiler_can_accept_markers()) {
1229 auto& data
= array
.ElementAt(index
);
1230 nsPrintfCString
str("%s [%s]", data
.mDescription
,
1231 kFlushTypeNames
[aFlushType
]);
1232 PROFILER_MARKER_TEXT(
1233 "RefreshObserver", GRAPHICS
,
1234 MarkerOptions(MarkerStack::TakeBacktrace(std::move(data
.mCause
)),
1235 MarkerTiming::IntervalUntilNowFrom(data
.mRegisterTime
),
1236 std::move(data
.mInnerWindowId
)),
1240 array
.RemoveElementAt(index
);
1244 void nsRefreshDriver::AddTimerAdjustmentObserver(
1245 nsATimerAdjustmentObserver
* aObserver
) {
1246 MOZ_ASSERT(!mTimerAdjustmentObservers
.Contains(aObserver
));
1247 mTimerAdjustmentObservers
.AppendElement(aObserver
);
1250 void nsRefreshDriver::RemoveTimerAdjustmentObserver(
1251 nsATimerAdjustmentObserver
* aObserver
) {
1252 MOZ_ASSERT(mTimerAdjustmentObservers
.Contains(aObserver
));
1253 mTimerAdjustmentObservers
.RemoveElement(aObserver
);
1256 void nsRefreshDriver::PostVisualViewportResizeEvent(
1257 VVPResizeEvent
* aResizeEvent
) {
1258 mVisualViewportResizeEvents
.AppendElement(aResizeEvent
);
1259 EnsureTimerStarted();
1262 void nsRefreshDriver::DispatchVisualViewportResizeEvents() {
1263 // We're taking a hint from scroll events and only dispatch the current set
1264 // of queued resize events. If additional events are posted in response to
1265 // the current events being dispatched, we'll dispatch them on the next tick.
1266 VisualViewportResizeEventArray events
=
1267 std::move(mVisualViewportResizeEvents
);
1268 for (auto& event
: events
) {
1273 void nsRefreshDriver::PostScrollEvent(mozilla::Runnable
* aScrollEvent
,
1276 mDelayedScrollEvents
.AppendElement(aScrollEvent
);
1278 mScrollEvents
.AppendElement(aScrollEvent
);
1279 EnsureTimerStarted();
1283 void nsRefreshDriver::DispatchScrollEvents() {
1284 // Scroll events are one-shot, so after running them we can drop them.
1285 // However, dispatching a scroll event can potentially cause more scroll
1286 // events to be posted, so we move the initial set into a temporary array
1287 // first. (Newly posted scroll events will be dispatched on the next tick.)
1288 ScrollEventArray events
= std::move(mScrollEvents
);
1289 for (auto& event
: events
) {
1294 void nsRefreshDriver::PostVisualViewportScrollEvent(
1295 VVPScrollEvent
* aScrollEvent
) {
1296 mVisualViewportScrollEvents
.AppendElement(aScrollEvent
);
1297 EnsureTimerStarted();
1300 void nsRefreshDriver::DispatchVisualViewportScrollEvents() {
1301 // Scroll events are one-shot, so after running them we can drop them.
1302 // However, dispatching a scroll event can potentially cause more scroll
1303 // events to be posted, so we move the initial set into a temporary array
1304 // first. (Newly posted scroll events will be dispatched on the next tick.)
1305 VisualViewportScrollEventArray events
=
1306 std::move(mVisualViewportScrollEvents
);
1307 for (auto& event
: events
) {
1312 void nsRefreshDriver::AddPostRefreshObserver(
1313 nsAPostRefreshObserver
* aObserver
) {
1314 MOZ_DIAGNOSTIC_ASSERT(!mPostRefreshObservers
.Contains(aObserver
));
1315 mPostRefreshObservers
.AppendElement(aObserver
);
1318 void nsRefreshDriver::RemovePostRefreshObserver(
1319 nsAPostRefreshObserver
* aObserver
) {
1320 bool removed
= mPostRefreshObservers
.RemoveElement(aObserver
);
1321 MOZ_DIAGNOSTIC_ASSERT(removed
);
1325 bool nsRefreshDriver::AddImageRequest(imgIRequest
* aRequest
) {
1326 uint32_t delay
= GetFirstFrameDelay(aRequest
);
1328 mRequests
.Insert(aRequest
);
1330 auto* const start
= mStartTable
.GetOrInsertNew(delay
);
1331 start
->mEntries
.Insert(aRequest
);
1334 EnsureTimerStarted();
1339 void nsRefreshDriver::RemoveImageRequest(imgIRequest
* aRequest
) {
1340 // Try to remove from both places, just in case, because we can't tell
1341 // whether RemoveEntry() succeeds.
1342 mRequests
.Remove(aRequest
);
1343 uint32_t delay
= GetFirstFrameDelay(aRequest
);
1345 ImageStartData
* start
= mStartTable
.Get(delay
);
1347 start
->mEntries
.Remove(aRequest
);
1352 void nsRefreshDriver::NotifyDOMContentLoaded() {
1353 // If the refresh driver is going to tick, we mark the timestamp after
1354 // everything is flushed in the next tick. If it isn't, mark ourselves as
1356 if (!HasObservers()) {
1357 GetPresContext()->NotifyDOMContentFlushed();
1359 mNotifyDOMContentFlushed
= true;
1363 void nsRefreshDriver::RegisterCompositionPayload(
1364 const mozilla::layers::CompositionPayload
& aPayload
) {
1365 mCompositionPayloads
.AppendElement(aPayload
);
1368 void nsRefreshDriver::AddForceNotifyContentfulPaintPresContext(
1369 nsPresContext
* aPresContext
) {
1370 mForceNotifyContentfulPaintPresContexts
.AppendElement(aPresContext
);
1373 void nsRefreshDriver::FlushForceNotifyContentfulPaintPresContext() {
1374 while (!mForceNotifyContentfulPaintPresContexts
.IsEmpty()) {
1375 WeakPtr
<nsPresContext
> presContext
=
1376 mForceNotifyContentfulPaintPresContexts
.PopLastElement();
1378 presContext
->NotifyContentfulPaint();
1383 void nsRefreshDriver::RunDelayedEventsSoon() {
1384 // Place entries for delayed events into their corresponding normal list,
1385 // and schedule a refresh. When these delayed events run, if their document
1386 // still has events suppressed then they will be readded to the delayed
1389 mScrollEvents
.AppendElements(mDelayedScrollEvents
);
1390 mDelayedScrollEvents
.Clear();
1392 mResizeEventFlushObservers
.AppendElements(mDelayedResizeEventFlushObservers
);
1393 mDelayedResizeEventFlushObservers
.Clear();
1395 EnsureTimerStarted();
1398 bool nsRefreshDriver::CanDoCatchUpTick() {
1399 if (mTestControllingRefreshes
|| !mActiveTimer
) {
1403 // If we've already ticked for the current timer refresh (or more recently
1404 // than that), then we don't need to do any catching up.
1405 if (mMostRecentRefresh
>= mActiveTimer
->MostRecentRefresh()) {
1412 bool nsRefreshDriver::CanDoExtraTick() {
1413 // Only allow one extra tick per normal vsync tick.
1414 if (mAttemptedExtraTickSinceLastVsync
) {
1418 // If we don't have a timer, or we didn't tick on the timer's
1419 // refresh then we can't do an 'extra' tick (but we may still
1420 // do a catch up tick).
1421 if (!mActiveTimer
||
1422 mActiveTimer
->MostRecentRefresh() != mMostRecentRefresh
) {
1426 // Grab the current timestamp before checking the tick hint to be sure
1427 // sure that it's equal or smaller than the value used within checking
1429 TimeStamp now
= TimeStamp::Now();
1430 Maybe
<TimeStamp
> nextTick
= mActiveTimer
->GetNextTickHint();
1431 int32_t minimumRequiredTime
= StaticPrefs::layout_extra_tick_minimum_ms();
1432 // If there's less than 4 milliseconds until the next tick, it's probably
1433 // not worth trying to catch up.
1434 if (minimumRequiredTime
< 0 || !nextTick
||
1435 (*nextTick
- now
) < TimeDuration::FromMilliseconds(minimumRequiredTime
)) {
1442 void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags
) {
1443 // FIXME: Bug 1346065: We should also assert the case where we have
1445 MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
1446 "EnsureTimerStarted should be called only when we are not "
1447 "in servo traversal or on the main-thread");
1449 if (mTestControllingRefreshes
) return;
1451 if (!mRefreshTimerStartedCause
) {
1452 mRefreshTimerStartedCause
= profiler_capture_backtrace();
1455 // will it already fire, and no other changes needed?
1456 if (mActiveTimer
&& !(aFlags
& eForceAdjustTimer
)) {
1457 // If we're being called from within a user input handler, and we think
1458 // there's time to rush an extra tick immediately, then schedule a runnable
1459 // to run the extra tick.
1460 if (mUserInputProcessingCount
&& CanDoExtraTick()) {
1461 RefPtr
<nsRefreshDriver
> self
= this;
1462 NS_DispatchToCurrentThreadQueue(
1463 NS_NewRunnableFunction(
1464 "RefreshDriver::EnsureTimerStarted::extra",
1466 // Re-check if we can still do an extra tick, in case anything
1467 // changed while the runnable was pending.
1468 if (self
->CanDoExtraTick()) {
1469 PROFILER_MARKER_UNTYPED("ExtraRefreshDriverTick", GRAPHICS
);
1470 LOG("[%p] Doing extra tick for user input", self
.get());
1471 self
->mAttemptedExtraTickSinceLastVsync
= true;
1472 self
->Tick(self
->mActiveTimer
->MostRecentRefreshVsyncId(),
1473 self
->mActiveTimer
->MostRecentRefresh(),
1477 EventQueuePriority::Vsync
);
1482 if (IsFrozen() || !mPresContext
) {
1483 // If we don't want to start it now, or we've been disconnected.
1488 if (mPresContext
->Document()->IsBeingUsedAsImage()) {
1489 // Image documents receive ticks from clients' refresh drivers.
1490 // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
1491 // they receive refresh-driver ticks from their client docs (bug 1107252).
1492 nsIURI
* uri
= mPresContext
->Document()->GetDocumentURI();
1493 if (!uri
|| !mozilla::dom::IsFontTableURI(uri
)) {
1494 MOZ_ASSERT(!mActiveTimer
,
1495 "image doc refresh driver should never have its own timer");
1500 // We got here because we're either adjusting the time *or* we're
1501 // starting it for the first time. Add to the right timer,
1502 // prehaps removing it from a previously-set one.
1503 RefreshDriverTimer
* newTimer
= ChooseTimer();
1504 if (newTimer
!= mActiveTimer
) {
1505 if (mActiveTimer
) mActiveTimer
->RemoveRefreshDriver(this);
1506 mActiveTimer
= newTimer
;
1507 mActiveTimer
->AddRefreshDriver(this);
1509 // If the timer has ticked since we last ticked, consider doing a 'catch-up'
1510 // tick immediately.
1511 if (CanDoCatchUpTick()) {
1512 RefPtr
<nsRefreshDriver
> self
= this;
1513 NS_DispatchToCurrentThreadQueue(
1514 NS_NewRunnableFunction(
1515 "RefreshDriver::EnsureTimerStarted::catch-up",
1517 // Re-check if we can still do a catch-up, in case anything
1518 // changed while the runnable was pending.
1519 if (self
->CanDoCatchUpTick()) {
1520 LOG("[%p] Doing catch up tick", self
.get());
1521 self
->Tick(self
->mActiveTimer
->MostRecentRefreshVsyncId(),
1522 self
->mActiveTimer
->MostRecentRefresh());
1525 EventQueuePriority::Vsync
);
1529 // When switching from an inactive timer to an active timer, the root
1530 // refresh driver is skipped due to being set to the content refresh
1531 // driver's timestamp. In case of EnsureTimerStarted is called from
1532 // ScheduleViewManagerFlush, we should avoid this behavior to flush
1533 // a paint in the same tick on the root refresh driver.
1534 if (aFlags
& eNeverAdjustTimer
) {
1538 // Since the different timers are sampled at different rates, when switching
1539 // timers, the most recent refresh of the new timer may be *before* the
1540 // most recent refresh of the old timer.
1541 // If we are restoring the refresh driver from test control, the time is
1542 // expected to go backwards (see bug 1043078), otherwise we just keep the most
1543 // recent tick of this driver (which may be older than the most recent tick of
1545 if (!(aFlags
& eAllowTimeToGoBackwards
)) {
1549 if (mMostRecentRefresh
!= mActiveTimer
->MostRecentRefresh()) {
1550 mMostRecentRefresh
= mActiveTimer
->MostRecentRefresh();
1552 for (nsATimerAdjustmentObserver
* obs
:
1553 mTimerAdjustmentObservers
.EndLimitedRange()) {
1554 obs
->NotifyTimerAdjusted(mMostRecentRefresh
);
1559 void nsRefreshDriver::StopTimer() {
1560 if (!mActiveTimer
) return;
1562 mActiveTimer
->RemoveRefreshDriver(this);
1563 mActiveTimer
= nullptr;
1564 mRefreshTimerStartedCause
= nullptr;
1567 uint32_t nsRefreshDriver::ObserverCount() const {
1569 for (const ObserverArray
& array
: mObservers
) {
1570 sum
+= array
.Length();
1573 // Even while throttled, we need to process layout and style changes. Style
1574 // changes can trigger transitions which fire events when they complete, and
1575 // layout changes can affect media queries on child documents, triggering
1576 // style changes, etc.
1577 sum
+= mAnimationEventFlushObservers
.Length();
1578 sum
+= mResizeEventFlushObservers
.Length();
1579 sum
+= mStyleFlushObservers
.Length();
1580 sum
+= mLayoutFlushObservers
.Length();
1581 sum
+= mPendingFullscreenEvents
.Length();
1582 sum
+= mFrameRequestCallbackDocs
.Length();
1583 sum
+= mThrottledFrameRequestCallbackDocs
.Length();
1584 sum
+= mViewManagerFlushIsPending
;
1585 sum
+= mEarlyRunners
.Length();
1586 sum
+= mTimerAdjustmentObservers
.Length();
1590 bool nsRefreshDriver::HasObservers() const {
1591 for (const ObserverArray
& array
: mObservers
) {
1592 if (!array
.IsEmpty()) {
1597 // We should NOT count mTimerAdjustmentObservers here since this method is
1598 // used to determine whether or not to stop the timer or re-start it and timer
1599 // adjustment observers should not influence timer starting or stopping.
1600 return mViewManagerFlushIsPending
|| !mStyleFlushObservers
.IsEmpty() ||
1601 !mLayoutFlushObservers
.IsEmpty() ||
1602 !mAnimationEventFlushObservers
.IsEmpty() ||
1603 !mResizeEventFlushObservers
.IsEmpty() ||
1604 !mPendingFullscreenEvents
.IsEmpty() ||
1605 !mFrameRequestCallbackDocs
.IsEmpty() ||
1606 !mThrottledFrameRequestCallbackDocs
.IsEmpty() ||
1607 !mEarlyRunners
.IsEmpty();
1610 void nsRefreshDriver::AppendObserverDescriptionsToString(
1611 nsACString
& aStr
) const {
1612 for (const ObserverArray
& array
: mObservers
) {
1613 for (const auto& observer
: array
.EndLimitedRange()) {
1614 aStr
.AppendPrintf("%s [%s], ", observer
.mDescription
,
1615 kFlushTypeNames
[observer
.mFlushType
]);
1618 if (mViewManagerFlushIsPending
) {
1619 aStr
.AppendLiteral("View manager flush pending, ");
1621 if (!mAnimationEventFlushObservers
.IsEmpty()) {
1622 aStr
.AppendPrintf("%zux Animation event flush observer, ",
1623 mAnimationEventFlushObservers
.Length());
1625 if (!mResizeEventFlushObservers
.IsEmpty()) {
1626 aStr
.AppendPrintf("%zux Resize event flush observer, ",
1627 mResizeEventFlushObservers
.Length());
1629 if (!mStyleFlushObservers
.IsEmpty()) {
1630 aStr
.AppendPrintf("%zux Style flush observer, ",
1631 mStyleFlushObservers
.Length());
1633 if (!mLayoutFlushObservers
.IsEmpty()) {
1634 aStr
.AppendPrintf("%zux Layout flush observer, ",
1635 mLayoutFlushObservers
.Length());
1637 if (!mPendingFullscreenEvents
.IsEmpty()) {
1638 aStr
.AppendPrintf("%zux Pending fullscreen event, ",
1639 mPendingFullscreenEvents
.Length());
1641 if (!mFrameRequestCallbackDocs
.IsEmpty()) {
1642 aStr
.AppendPrintf("%zux Frame request callback doc, ",
1643 mFrameRequestCallbackDocs
.Length());
1645 if (!mThrottledFrameRequestCallbackDocs
.IsEmpty()) {
1646 aStr
.AppendPrintf("%zux Throttled frame request callback doc, ",
1647 mThrottledFrameRequestCallbackDocs
.Length());
1649 if (!mEarlyRunners
.IsEmpty()) {
1650 aStr
.AppendPrintf("%zux Early runner, ", mEarlyRunners
.Length());
1653 aStr
.Truncate(aStr
.Length() - 2);
1656 bool nsRefreshDriver::HasImageRequests() const {
1657 for (const auto& data
: mStartTable
.Values()) {
1658 if (!data
->mEntries
.IsEmpty()) {
1663 return !mRequests
.IsEmpty();
1666 auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons
{
1667 TickReasons reasons
= TickReasons::eNone
;
1668 if (HasObservers()) {
1669 reasons
|= TickReasons::eHasObservers
;
1671 if (HasImageRequests()) {
1672 reasons
|= TickReasons::eHasImageRequests
;
1674 if (mNeedToUpdateIntersectionObservations
) {
1675 reasons
|= TickReasons::eNeedsToUpdateIntersectionObservations
;
1677 if (!mVisualViewportResizeEvents
.IsEmpty()) {
1678 reasons
|= TickReasons::eHasVisualViewportResizeEvents
;
1680 if (!mScrollEvents
.IsEmpty()) {
1681 reasons
|= TickReasons::eHasScrollEvents
;
1683 if (!mVisualViewportScrollEvents
.IsEmpty()) {
1684 reasons
|= TickReasons::eHasVisualViewportScrollEvents
;
1689 void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons
,
1690 nsACString
& aStr
) const {
1691 if (aReasons
== TickReasons::eNone
) {
1692 aStr
.AppendLiteral(" <none>");
1696 if (aReasons
& TickReasons::eHasObservers
) {
1697 aStr
.AppendLiteral(" HasObservers (");
1698 AppendObserverDescriptionsToString(aStr
);
1699 aStr
.AppendLiteral(")");
1701 if (aReasons
& TickReasons::eHasImageRequests
) {
1702 aStr
.AppendLiteral(" HasImageRequests");
1704 if (aReasons
& TickReasons::eNeedsToUpdateIntersectionObservations
) {
1705 aStr
.AppendLiteral(" NeedsToUpdateIntersectionObservations");
1707 if (aReasons
& TickReasons::eHasVisualViewportResizeEvents
) {
1708 aStr
.AppendLiteral(" HasVisualViewportResizeEvents");
1710 if (aReasons
& TickReasons::eHasScrollEvents
) {
1711 aStr
.AppendLiteral(" HasScrollEvents");
1713 if (aReasons
& TickReasons::eHasVisualViewportScrollEvents
) {
1714 aStr
.AppendLiteral(" HasVisualViewportScrollEvents");
1718 bool nsRefreshDriver::
1719 ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint() {
1720 // On top level content pages keep the timer running initially so that we
1721 // paint the page soon enough.
1722 if (mThrottled
|| mTestControllingRefreshes
|| !XRE_IsContentProcess() ||
1723 !mPresContext
->Document()->IsTopLevelContentDocument() ||
1724 gfxPlatform::IsInLayoutAsapMode() || mPresContext
->HadContentfulPaint() ||
1725 mPresContext
->Document()->GetReadyStateEnum() ==
1726 Document::READYSTATE_COMPLETE
) {
1729 if (mBeforeFirstContentfulPaintTimerRunningLimit
.IsNull()) {
1730 // Don't let the timer to run forever, so limit to 4s for now.
1731 mBeforeFirstContentfulPaintTimerRunningLimit
=
1732 TimeStamp::Now() + TimeDuration::FromSeconds(4.0f
);
1735 return TimeStamp::Now() <= mBeforeFirstContentfulPaintTimerRunningLimit
;
1738 bool nsRefreshDriver::ShouldKeepTimerRunningAfterPageLoad() {
1739 if (mHasExceededAfterLoadTickPeriod
||
1740 !StaticPrefs::layout_keep_ticking_after_load_ms() || mThrottled
||
1741 mTestControllingRefreshes
|| !XRE_IsContentProcess() ||
1742 !mPresContext
->Document()->IsTopLevelContentDocument() ||
1743 gfxPlatform::IsInLayoutAsapMode()) {
1744 // Make the next check faster.
1745 mHasExceededAfterLoadTickPeriod
= true;
1749 nsPIDOMWindowInner
* innerWindow
= mPresContext
->Document()->GetInnerWindow();
1751 if (PerformanceMainThread
* perf
= static_cast<PerformanceMainThread
*>(
1752 innerWindow
->GetPerformance())) {
1753 nsDOMNavigationTiming
* timing
= perf
->GetDOMTiming();
1755 TimeStamp loadend
= timing
->LoadEventEnd();
1757 // Keep ticking after the page load for some time.
1760 TimeDuration::FromMilliseconds(
1761 StaticPrefs::layout_keep_ticking_after_load_ms())) >
1764 mHasExceededAfterLoadTickPeriod
= true;
1775 nsRefreshDriver::ObserverArray
& nsRefreshDriver::ArrayFor(
1776 FlushType aFlushType
) {
1777 switch (aFlushType
) {
1778 case FlushType::Event
:
1779 return mObservers
[0];
1780 case FlushType::Style
:
1781 case FlushType::Frames
:
1782 return mObservers
[1];
1783 case FlushType::Layout
:
1784 return mObservers
[2];
1785 case FlushType::Display
:
1786 return mObservers
[3];
1788 MOZ_CRASH("We don't track refresh observers for this flush type");
1793 * nsITimerCallback implementation
1796 void nsRefreshDriver::DoTick() {
1797 MOZ_ASSERT(!IsFrozen(), "Why are we notified while frozen?");
1798 MOZ_ASSERT(mPresContext
, "Why are we notified after disconnection?");
1799 MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
1800 "Shouldn't have a JSContext on the stack");
1802 if (mTestControllingRefreshes
) {
1803 Tick(VsyncId(), mMostRecentRefresh
);
1805 Tick(VsyncId(), TimeStamp::Now());
1809 struct DocumentFrameCallbacks
{
1810 explicit DocumentFrameCallbacks(Document
* aDocument
) : mDocument(aDocument
) {}
1812 RefPtr
<Document
> mDocument
;
1813 nsTArray
<Document::FrameRequest
> mCallbacks
;
1816 static nsDocShell
* GetDocShell(nsPresContext
* aPresContext
) {
1817 return static_cast<nsDocShell
*>(aPresContext
->GetDocShell());
1820 static bool HasPendingAnimations(PresShell
* aPresShell
) {
1821 Document
* doc
= aPresShell
->GetDocument();
1826 PendingAnimationTracker
* tracker
= doc
->GetPendingAnimationTracker();
1827 return tracker
&& tracker
->HasPendingAnimations();
1831 * Return a list of all the child docShells in a given root docShell that are
1832 * visible and are recording markers for the profilingTimeline
1834 static void GetProfileTimelineSubDocShells(nsDocShell
* aRootDocShell
,
1835 nsTArray
<nsDocShell
*>& aShells
) {
1836 if (!aRootDocShell
) {
1840 RefPtr
<TimelineConsumers
> timelines
= TimelineConsumers::Get();
1841 if (!timelines
|| timelines
->IsEmpty()) {
1845 RefPtr
<BrowsingContext
> bc
= aRootDocShell
->GetBrowsingContext();
1850 bc
->PostOrderWalk([&](BrowsingContext
* aContext
) {
1851 if (!aContext
->IsActive()) {
1855 nsDocShell
* shell
= nsDocShell::Cast(aContext
->GetDocShell());
1856 if (!shell
|| !shell
->GetRecordProfileTimelineMarkers()) {
1857 // This process isn't painting OOP iframes so we ignore
1858 // docshells that are OOP.
1862 aShells
.AppendElement(shell
);
1866 static void TakeFrameRequestCallbacksFrom(
1867 Document
* aDocument
, nsTArray
<DocumentFrameCallbacks
>& aTarget
) {
1868 aTarget
.AppendElement(aDocument
);
1869 aDocument
->TakeFrameRequestCallbacks(aTarget
.LastElement().mCallbacks
);
1872 // https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps
1873 void nsRefreshDriver::RunFullscreenSteps() {
1874 // Swap out the current pending events
1875 nsTArray
<UniquePtr
<PendingFullscreenEvent
>> pendings(
1876 std::move(mPendingFullscreenEvents
));
1877 for (UniquePtr
<PendingFullscreenEvent
>& event
: pendings
) {
1882 void nsRefreshDriver::UpdateIntersectionObservations(TimeStamp aNowTime
) {
1883 AutoTArray
<RefPtr
<Document
>, 32> documents
;
1885 if (mPresContext
->Document()->HasIntersectionObservers()) {
1886 documents
.AppendElement(mPresContext
->Document());
1889 mPresContext
->Document()->CollectDescendantDocuments(
1890 documents
, [](const Document
* document
) -> bool {
1891 return document
->HasIntersectionObservers();
1894 for (uint32_t i
= 0; i
< documents
.Length(); ++i
) {
1895 Document
* doc
= documents
[i
];
1896 doc
->UpdateIntersectionObservations(aNowTime
);
1897 doc
->ScheduleIntersectionObserverNotification();
1900 mNeedToUpdateIntersectionObservations
= false;
1903 void nsRefreshDriver::DispatchAnimationEvents() {
1904 if (!mPresContext
) {
1908 // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
1909 // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
1910 // during processing the previous dispatcher.
1911 AutoTArray
<RefPtr
<AnimationEventDispatcher
>, 16> dispatchers
;
1912 dispatchers
.AppendElements(mAnimationEventFlushObservers
);
1913 mAnimationEventFlushObservers
.Clear();
1915 for (auto& dispatcher
: dispatchers
) {
1916 dispatcher
->DispatchEvents();
1920 void nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime
) {
1921 // Grab all of our frame request callbacks up front.
1922 nsTArray
<DocumentFrameCallbacks
> frameRequestCallbacks(
1923 mFrameRequestCallbackDocs
.Length() +
1924 mThrottledFrameRequestCallbackDocs
.Length());
1926 // First, grab throttled frame request callbacks.
1928 nsTArray
<Document
*> docsToRemove
;
1930 // We always tick throttled frame requests if the entire refresh driver is
1931 // throttled, because in that situation throttled frame requests tick at the
1932 // same frequency as non-throttled frame requests.
1933 bool tickThrottledFrameRequests
= mThrottled
;
1935 if (!tickThrottledFrameRequests
&&
1936 aNowTime
>= mNextThrottledFrameRequestTick
) {
1937 mNextThrottledFrameRequestTick
=
1938 aNowTime
+ mThrottledFrameRequestInterval
;
1939 tickThrottledFrameRequests
= true;
1942 for (Document
* doc
: mThrottledFrameRequestCallbackDocs
) {
1943 if (tickThrottledFrameRequests
) {
1944 // We're ticking throttled documents, so grab this document's requests.
1945 // We don't bother appending to docsToRemove because we're going to
1946 // clear mThrottledFrameRequestCallbackDocs anyway.
1947 TakeFrameRequestCallbacksFrom(doc
, frameRequestCallbacks
);
1948 } else if (!doc
->ShouldThrottleFrameRequests()) {
1949 // This document is no longer throttled, so grab its requests even
1950 // though we're not ticking throttled frame requests right now. If
1951 // this is the first unthrottled document with frame requests, we'll
1952 // enter high precision mode the next time the callback is scheduled.
1953 TakeFrameRequestCallbacksFrom(doc
, frameRequestCallbacks
);
1954 docsToRemove
.AppendElement(doc
);
1958 // Remove all the documents we're ticking from
1959 // mThrottledFrameRequestCallbackDocs so they can be readded as needed.
1960 if (tickThrottledFrameRequests
) {
1961 mThrottledFrameRequestCallbackDocs
.Clear();
1963 // XXX(seth): We're using this approach to avoid concurrent modification
1964 // of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
1965 // zero elements or a very small number, so this should be OK in practice.
1966 for (Document
* doc
: docsToRemove
) {
1967 mThrottledFrameRequestCallbackDocs
.RemoveElement(doc
);
1972 // Now grab unthrottled frame request callbacks.
1973 for (Document
* doc
: mFrameRequestCallbackDocs
) {
1974 TakeFrameRequestCallbacksFrom(doc
, frameRequestCallbacks
);
1977 // Reset mFrameRequestCallbackDocs so they can be readded as needed.
1978 mFrameRequestCallbackDocs
.Clear();
1980 if (!frameRequestCallbacks
.IsEmpty()) {
1981 AUTO_PROFILER_TRACING_MARKER_DOCSHELL("Paint",
1982 "requestAnimationFrame callbacks",
1983 GRAPHICS
, GetDocShell(mPresContext
));
1984 for (const DocumentFrameCallbacks
& docCallbacks
: frameRequestCallbacks
) {
1985 TimeStamp startTime
= TimeStamp::Now();
1987 // XXXbz Bug 863140: GetInnerWindow can return the outer
1988 // window in some cases.
1989 nsPIDOMWindowInner
* innerWindow
=
1990 docCallbacks
.mDocument
->GetInnerWindow();
1991 DOMHighResTimeStamp timeStamp
= 0;
1993 if (Performance
* perf
= innerWindow
->GetPerformance()) {
1994 timeStamp
= perf
->TimeStampToDOMHighResForRendering(aNowTime
);
1996 // else window is partially torn down already
1998 for (auto& callback
: docCallbacks
.mCallbacks
) {
1999 if (docCallbacks
.mDocument
->IsCanceledFrameRequestCallback(
2000 callback
.mHandle
)) {
2004 nsCOMPtr
<nsIGlobalObject
> global(innerWindow
? innerWindow
->AsGlobal()
2006 CallbackDebuggerNotificationGuard
guard(
2007 global
, DebuggerNotificationType::RequestAnimationFrameCallback
);
2009 // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
2010 // keeps callback alive and the mCallback strong reference can't be
2011 // mutated by the call.
2012 LogFrameRequestCallback::Run
run(callback
.mCallback
);
2013 MOZ_KnownLive(callback
.mCallback
)->Call(timeStamp
);
2016 if (docCallbacks
.mDocument
->GetReadyStateEnum() ==
2017 Document::READYSTATE_COMPLETE
) {
2018 Telemetry::AccumulateTimeDelta(
2019 Telemetry::PERF_REQUEST_ANIMATION_CALLBACK_NON_PAGELOAD_MS
,
2020 startTime
, TimeStamp::Now());
2022 Telemetry::AccumulateTimeDelta(
2023 Telemetry::PERF_REQUEST_ANIMATION_CALLBACK_PAGELOAD_MS
, startTime
,
2030 static AutoTArray
<RefPtr
<Task
>, 8>* sPendingIdleTasks
= nullptr;
2032 void nsRefreshDriver::DispatchIdleTaskAfterTickUnlessExists(Task
* aTask
) {
2033 if (!sPendingIdleTasks
) {
2034 sPendingIdleTasks
= new AutoTArray
<RefPtr
<Task
>, 8>();
2036 if (sPendingIdleTasks
->Contains(aTask
)) {
2041 sPendingIdleTasks
->AppendElement(aTask
);
2044 void nsRefreshDriver::CancelIdleTask(Task
* aTask
) {
2045 if (!sPendingIdleTasks
) {
2049 sPendingIdleTasks
->RemoveElement(aTask
);
2051 if (sPendingIdleTasks
->IsEmpty()) {
2052 delete sPendingIdleTasks
;
2053 sPendingIdleTasks
= nullptr;
2057 static CallState
ReduceAnimations(Document
& aDocument
) {
2058 if (nsPresContext
* pc
= aDocument
.GetPresContext()) {
2059 if (pc
->EffectCompositor()->NeedsReducing()) {
2060 pc
->EffectCompositor()->ReduceAnimations();
2063 aDocument
.EnumerateSubDocuments(ReduceAnimations
);
2064 return CallState::Continue
;
2067 void nsRefreshDriver::Tick(VsyncId aId
, TimeStamp aNowTime
,
2068 IsExtraTick aIsExtraTick
/* = No */) {
2069 MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
2070 "Shouldn't have a JSContext on the stack");
2072 // We're either frozen or we were disconnected (likely in the middle
2073 // of a tick iteration). Just do nothing here, since our
2074 // prescontext went away.
2075 if (IsFrozen() || !mPresContext
) {
2079 // We can have a race condition where the vsync timestamp
2080 // is before the most recent refresh due to a forced refresh.
2081 // The underlying assumption is that the refresh driver tick can only
2082 // go forward in time, not backwards. To prevent the refresh
2083 // driver from going back in time, just skip this tick and
2084 // wait until the next tick.
2085 // If this is an 'extra' tick, then we expect it to be using the same
2086 // vsync id and timestamp as the original tick, so also allow those.
2087 if ((aNowTime
<= mMostRecentRefresh
) && !mTestControllingRefreshes
&&
2088 aIsExtraTick
== IsExtraTick::No
) {
2091 auto cleanupInExtraTick
= MakeScopeExit([&] { mInNormalTick
= false; });
2092 mInNormalTick
= aIsExtraTick
!= IsExtraTick::Yes
;
2094 bool isPresentingInVR
= false;
2095 #if defined(MOZ_WIDGET_ANDROID)
2096 isPresentingInVR
= gfx::VRManagerChild::IsPresenting();
2097 #endif // defined(MOZ_WIDGET_ANDROID)
2099 if (!isPresentingInVR
&& IsWaitingForPaint(aNowTime
)) {
2100 // In immersive VR mode, we do not get notifications when frames are
2101 // presented, so we do not wait for the compositor in that mode.
2103 // We're currently suspended waiting for earlier Tick's to
2104 // be completed (on the Compositor). Mark that we missed the paint
2105 // and keep waiting.
2106 PROFILER_MARKER_UNTYPED(
2107 "RefreshDriverTick waiting for paint", GRAPHICS
,
2108 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext
)));
2112 TimeStamp previousRefresh
= mMostRecentRefresh
;
2113 mMostRecentRefresh
= aNowTime
;
2116 mRootRefresh
->RemoveRefreshObserver(this, FlushType::Style
);
2117 mRootRefresh
= nullptr;
2119 mSkippedPaints
= false;
2120 mWarningThreshold
= 1;
2122 RefPtr
<PresShell
> presShell
= mPresContext
->GetPresShell();
2128 TickReasons tickReasons
= GetReasonsToTick();
2129 if (tickReasons
== TickReasons::eNone
) {
2130 // We no longer have any observers.
2131 // Discard composition payloads because there is no paint.
2132 mCompositionPayloads
.Clear();
2134 // We don't want to stop the timer when observers are initially
2135 // removed, because sometimes observers can be added and removed
2136 // often depending on what other things are going on and in that
2137 // situation we don't want to thrash our timer. So instead we
2138 // wait until we get a Notify() call when we have no observers
2139 // before stopping the timer.
2140 // On top level content pages keep the timer running initially so that we
2141 // paint the page soon enough.
2142 if (ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint()) {
2144 "RefreshDriverTick waiting for first contentful paint", GRAPHICS
,
2145 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext
)), Tracing
,
2147 } else if (ShouldKeepTimerRunningAfterPageLoad()) {
2149 "RefreshDriverTick after page load", GRAPHICS
,
2150 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext
)), Tracing
,
2158 AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", LAYOUT
);
2160 nsAutoCString profilerStr
;
2161 if (profiler_can_accept_markers()) {
2162 profilerStr
.AppendLiteral("Tick reasons:");
2163 AppendTickReasonsToString(tickReasons
, profilerStr
);
2165 AUTO_PROFILER_MARKER_TEXT(
2166 "RefreshDriverTick", GRAPHICS
,
2168 MarkerStack::TakeBacktrace(std::move(mRefreshTimerStartedCause
)),
2169 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext
))),
2172 mResizeSuppressed
= false;
2174 bool oldInRefresh
= mInRefresh
;
2175 auto restoreInRefresh
= MakeScopeExit([&] { mInRefresh
= oldInRefresh
; });
2178 AutoRestore
<TimeStamp
> restoreTickStart(mTickStart
);
2179 mTickStart
= TimeStamp::Now();
2181 mTickVsyncTime
= aNowTime
;
2183 gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
2185 // We want to process any pending APZ metrics ahead of their positions
2186 // in the queue. This will prevent us from spending precious time
2187 // painting a stale displayport.
2188 if (StaticPrefs::apz_peek_messages_enabled()) {
2189 DisplayPortUtils::UpdateDisplayPortMarginsFromPendingMessages();
2192 FlushForceNotifyContentfulPaintPresContext();
2194 AutoTArray
<nsCOMPtr
<nsIRunnable
>, 16> earlyRunners
= std::move(mEarlyRunners
);
2195 for (auto& runner
: earlyRunners
) {
2199 // Resize events should be fired before layout flushes or
2200 // calling animation frame callbacks.
2201 AutoTArray
<RefPtr
<PresShell
>, 16> observers
;
2202 observers
.AppendElements(mResizeEventFlushObservers
);
2203 for (RefPtr
<PresShell
>& presShell
: Reversed(observers
)) {
2204 if (!mPresContext
|| !mPresContext
->GetPresShell()) {
2208 // Make sure to not process observers which might have been removed
2209 // during previous iterations.
2210 if (!mResizeEventFlushObservers
.RemoveElement(presShell
)) {
2213 // MOZ_KnownLive because 'observers' is guaranteed to
2216 // Fixing https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 on its own
2217 // won't help here, because 'observers' is non-const and we have the
2218 // Reversed() going on too...
2219 MOZ_KnownLive(presShell
)->FireResizeEvent();
2221 DispatchVisualViewportResizeEvents();
2223 double phaseMetrics
[MOZ_ARRAY_LENGTH(mObservers
)] = {
2228 * The timer holds a reference to |this| while calling |Notify|.
2229 * However, implementations of |WillRefresh| are permitted to destroy
2230 * the pres context, which will cause our |mPresContext| to become
2231 * null. If this happens, we must stop notifying observers.
2233 for (uint32_t i
= 0; i
< ArrayLength(mObservers
); ++i
) {
2234 AutoRecordPhase
phaseRecord(&phaseMetrics
[i
]);
2236 for (RefPtr
<nsARefreshObserver
> obs
: mObservers
[i
].EndLimitedRange()) {
2237 obs
->WillRefresh(aNowTime
);
2239 if (!mPresContext
|| !mPresContext
->GetPresShell()) {
2245 // Any animation timelines updated above may cause animations to queue
2246 // Promise resolution microtasks. We shouldn't run these, however, until we
2247 // have fully updated the animation state.
2249 // As per the "update animations and send events" procedure[1], we should
2250 // remove replaced animations and then run these microtasks before
2251 // dispatching the corresponding animation events.
2254 // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
2257 ReduceAnimations(*mPresContext
->Document());
2260 // Check if running the microtask checkpoint caused the pres context to
2262 if (i
== 1 && (!mPresContext
|| !mPresContext
->GetPresShell())) {
2268 // This is the FlushType::Style case.
2270 DispatchScrollEvents();
2271 DispatchVisualViewportScrollEvents();
2272 DispatchAnimationEvents();
2273 RunFullscreenSteps();
2274 RunFrameRequestCallbacks(aNowTime
);
2276 if (mPresContext
&& mPresContext
->GetPresShell()) {
2277 AutoTArray
<PresShell
*, 16> observers
;
2278 observers
.AppendElements(mStyleFlushObservers
);
2279 for (uint32_t j
= observers
.Length();
2280 j
&& mPresContext
&& mPresContext
->GetPresShell(); --j
) {
2281 // Make sure to not process observers which might have been removed
2282 // during previous iterations.
2283 PresShell
* rawPresShell
= observers
[j
- 1];
2284 if (!mStyleFlushObservers
.RemoveElement(rawPresShell
)) {
2288 LogPresShellObserver::Run
run(rawPresShell
, this);
2290 RefPtr
<PresShell
> presShell
= rawPresShell
;
2291 presShell
->mObservingStyleFlushes
= false;
2292 presShell
->FlushPendingNotifications(
2293 ChangesToFlush(FlushType::Style
, false));
2294 // Inform the FontFaceSet that we ticked, so that it can resolve its
2295 // ready promise if it needs to (though it might still be waiting on
2297 presShell
->NotifyFontFaceSetOnRefresh();
2298 mNeedToRecomputeVisibility
= true;
2300 // Record the telemetry for events that occurred between ticks.
2301 presShell
->PingPerTickTelemetry(FlushType::Style
);
2304 } else if (i
== 2) {
2305 // This is the FlushType::Layout case.
2306 AutoTArray
<PresShell
*, 16> observers
;
2307 observers
.AppendElements(mLayoutFlushObservers
);
2308 for (uint32_t j
= observers
.Length();
2309 j
&& mPresContext
&& mPresContext
->GetPresShell(); --j
) {
2310 // Make sure to not process observers which might have been removed
2311 // during previous iterations.
2312 PresShell
* rawPresShell
= observers
[j
- 1];
2313 if (!mLayoutFlushObservers
.RemoveElement(rawPresShell
)) {
2317 LogPresShellObserver::Run
run(rawPresShell
, this);
2319 RefPtr
<PresShell
> presShell
= rawPresShell
;
2320 presShell
->mObservingLayoutFlushes
= false;
2321 presShell
->mWasLastReflowInterrupted
= false;
2322 FlushType flushType
= HasPendingAnimations(presShell
)
2324 : FlushType::InterruptibleLayout
;
2325 presShell
->FlushPendingNotifications(ChangesToFlush(flushType
, false));
2326 // Inform the FontFaceSet that we ticked, so that it can resolve its
2327 // ready promise if it needs to.
2328 presShell
->NotifyFontFaceSetOnRefresh();
2329 mNeedToRecomputeVisibility
= true;
2331 // Record the telemetry for events that occurred between ticks.
2332 presShell
->PingPerTickTelemetry(FlushType::Layout
);
2336 // The pres context may be destroyed during we do the flushing.
2337 if (!mPresContext
|| !mPresContext
->GetPresShell()) {
2343 // Recompute approximate frame visibility if it's necessary and enough time
2344 // has passed since the last time we did it.
2345 if (mNeedToRecomputeVisibility
&& !mThrottled
&&
2346 aNowTime
>= mNextRecomputeVisibilityTick
&&
2347 !presShell
->IsPaintingSuppressed()) {
2348 mNextRecomputeVisibilityTick
= aNowTime
+ mMinRecomputeVisibilityInterval
;
2349 mNeedToRecomputeVisibility
= false;
2351 presShell
->ScheduleApproximateFrameVisibilityUpdateNow();
2355 // Update any popups that may need to be moved or hidden due to their
2357 if (nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance()) {
2358 pm
->UpdatePopupPositions(this);
2362 UpdateIntersectionObservations(aNowTime
);
2365 * Perform notification to imgIRequests subscribed to listen
2366 * for refresh events.
2369 for (const auto& entry
: mStartTable
) {
2370 const uint32_t& delay
= entry
.GetKey();
2371 ImageStartData
* data
= entry
.GetWeak();
2373 if (data
->mStartTime
) {
2374 TimeStamp
& start
= *data
->mStartTime
;
2375 TimeDuration prev
= previousRefresh
- start
;
2376 TimeDuration curr
= aNowTime
- start
;
2377 uint32_t prevMultiple
= uint32_t(prev
.ToMilliseconds()) / delay
;
2379 // We want to trigger images' refresh if we've just crossed over a
2380 // multiple of the first image's start time. If so, set the animation
2381 // start time to the nearest multiple of the delay and move all the
2382 // images in this table to the main requests table.
2383 if (prevMultiple
!= uint32_t(curr
.ToMilliseconds()) / delay
) {
2384 mozilla::TimeStamp desired
=
2385 start
+ TimeDuration::FromMilliseconds(prevMultiple
* delay
);
2386 BeginRefreshingImages(data
->mEntries
, desired
);
2389 // This is the very first time we've drawn images with this time delay.
2390 // Set the animation start time to "now" and move all the images in this
2391 // table to the main requests table.
2392 mozilla::TimeStamp desired
= aNowTime
;
2393 BeginRefreshingImages(data
->mEntries
, desired
);
2394 data
->mStartTime
.emplace(aNowTime
);
2398 if (mRequests
.Count()) {
2399 // RequestRefresh may run scripts, so it's not safe to directly call it
2400 // while using a hashtable enumerator to enumerate mRequests in case
2401 // script modifies the hashtable. Instead, we build a (local) array of
2402 // images to refresh, and then we refresh each image in that array.
2403 nsCOMArray
<imgIContainer
> imagesToRefresh(mRequests
.Count());
2405 for (nsISupports
* entry
: mRequests
) {
2406 auto* req
= static_cast<imgIRequest
*>(entry
);
2407 MOZ_ASSERT(req
, "Unable to retrieve the image request");
2408 nsCOMPtr
<imgIContainer
> image
;
2409 if (NS_SUCCEEDED(req
->GetImage(getter_AddRefs(image
)))) {
2410 imagesToRefresh
.AppendElement(image
.forget());
2414 for (uint32_t i
= 0; i
< imagesToRefresh
.Length(); i
++) {
2415 imagesToRefresh
[i
]->RequestRefresh(aNowTime
);
2419 double phasePaint
= 0.0;
2420 bool dispatchTasksAfterTick
= false;
2421 if (mViewManagerFlushIsPending
) {
2422 AutoRecordPhase
paintRecord(&phasePaint
);
2423 nsCString transactionId
;
2424 if (profiler_can_accept_markers()) {
2425 transactionId
.AppendLiteral("Transaction ID: ");
2426 transactionId
.AppendInt((uint64_t)mNextTransactionId
);
2428 AUTO_PROFILER_MARKER_TEXT(
2429 "ViewManagerFlush", GRAPHICS
,
2431 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext
)),
2432 MarkerStack::TakeBacktrace(std::move(mViewManagerFlushCause
))),
2435 // Forward our composition payloads to the layer manager.
2436 if (!mCompositionPayloads
.IsEmpty()) {
2437 nsIWidget
* widget
= mPresContext
->GetRootWidget();
2438 layers::LayerManager
* lm
= widget
? widget
->GetLayerManager() : nullptr;
2440 lm
->RegisterPayloads(mCompositionPayloads
);
2442 mCompositionPayloads
.Clear();
2445 RefPtr
<TimelineConsumers
> timelines
= TimelineConsumers::Get();
2447 nsTArray
<nsDocShell
*> profilingDocShells
;
2448 GetProfileTimelineSubDocShells(GetDocShell(mPresContext
),
2449 profilingDocShells
);
2450 for (nsDocShell
* docShell
: profilingDocShells
) {
2451 // For the sake of the profile timeline's simplicity, this is flagged as
2452 // paint even if it includes creating display lists
2453 MOZ_ASSERT(timelines
);
2454 MOZ_ASSERT(timelines
->HasConsumer(docShell
));
2455 timelines
->AddMarkerForDocShell(docShell
, "Paint",
2456 MarkerTracingType::START
);
2459 #ifdef MOZ_DUMP_PAINTING
2460 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2461 printf_stderr("Starting ProcessPendingUpdates\n");
2465 mViewManagerFlushIsPending
= false;
2466 RefPtr
<nsViewManager
> vm
= mPresContext
->GetPresShell()->GetViewManager();
2467 const bool skipPaint
= isPresentingInVR
;
2468 // Skip the paint in immersive VR mode because whatever we paint here will
2469 // not end up on the screen. The screen is displaying WebGL content from a
2470 // single canvas in that mode.
2472 PaintTelemetry::AutoRecordPaint record
;
2473 vm
->ProcessPendingUpdates();
2476 #ifdef MOZ_DUMP_PAINTING
2477 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2478 printf_stderr("Ending ProcessPendingUpdates\n");
2482 for (nsDocShell
* docShell
: profilingDocShells
) {
2483 MOZ_ASSERT(timelines
);
2484 MOZ_ASSERT(timelines
->HasConsumer(docShell
));
2485 timelines
->AddMarkerForDocShell(docShell
, "Paint",
2486 MarkerTracingType::END
);
2489 dispatchTasksAfterTick
= true;
2490 mHasScheduleFlush
= false;
2492 // No paint happened, discard composition payloads.
2493 mCompositionPayloads
.Clear();
2496 double totalMs
= (TimeStamp::Now() - mTickStart
).ToMilliseconds();
2498 #ifndef ANDROID /* bug 1142079 */
2499 mozilla::Telemetry::Accumulate(mozilla::Telemetry::REFRESH_DRIVER_TICK
,
2500 static_cast<uint32_t>(totalMs
));
2503 // Bug 1568107: If the totalMs is greater than 1/60th second (ie. 1000/60 ms)
2504 // then record, via telemetry, the percentage of time spent in each
2506 if (totalMs
> 1000.0 / 60.0) {
2507 auto record
= [=](const nsCString
& aKey
, double aDurationMs
) -> void {
2508 MOZ_ASSERT(aDurationMs
<= totalMs
);
2509 auto phasePercent
= static_cast<uint32_t>(aDurationMs
* 100.0 / totalMs
);
2510 Telemetry::Accumulate(Telemetry::REFRESH_DRIVER_TICK_PHASE_WEIGHT
, aKey
,
2514 record("Event"_ns
, phaseMetrics
[0]);
2515 record("Style"_ns
, phaseMetrics
[1]);
2516 record("Reflow"_ns
, phaseMetrics
[2]);
2517 record("Display"_ns
, phaseMetrics
[3]);
2518 record("Paint"_ns
, phasePaint
);
2520 // Explicitly record the time unaccounted for.
2521 double other
= totalMs
-
2522 std::accumulate(phaseMetrics
, ArrayEnd(phaseMetrics
), 0.0) -
2524 record("Other"_ns
, other
);
2527 if (mNotifyDOMContentFlushed
) {
2528 mNotifyDOMContentFlushed
= false;
2529 mPresContext
->NotifyDOMContentFlushed();
2532 for (nsAPostRefreshObserver
* observer
:
2533 mPostRefreshObservers
.ForwardRange()) {
2534 observer
->DidRefresh();
2537 NS_ASSERTION(mInRefresh
, "Still in refresh");
2539 if (mPresContext
->IsRoot() && XRE_IsContentProcess() &&
2540 StaticPrefs::gfx_content_always_paint()) {
2541 ScheduleViewManagerFlush();
2544 if (dispatchTasksAfterTick
&& sPendingIdleTasks
) {
2545 AutoTArray
<RefPtr
<Task
>, 8>* tasks
= sPendingIdleTasks
;
2546 sPendingIdleTasks
= nullptr;
2547 for (RefPtr
<Task
>& taskWithDelay
: *tasks
) {
2548 TaskController::Get()->AddTask(taskWithDelay
.forget());
2554 void nsRefreshDriver::BeginRefreshingImages(RequestTable
& aEntries
,
2555 mozilla::TimeStamp aDesired
) {
2556 for (const auto& key
: aEntries
) {
2557 auto* req
= static_cast<imgIRequest
*>(key
);
2558 MOZ_ASSERT(req
, "Unable to retrieve the image request");
2560 mRequests
.Insert(req
);
2562 nsCOMPtr
<imgIContainer
> image
;
2563 if (NS_SUCCEEDED(req
->GetImage(getter_AddRefs(image
)))) {
2564 image
->SetAnimationStartTime(aDesired
);
2570 void nsRefreshDriver::Freeze() {
2575 void nsRefreshDriver::Thaw() {
2576 NS_ASSERTION(mFreezeCount
> 0, "Thaw() called on an unfrozen refresh driver");
2578 if (mFreezeCount
> 0) {
2582 if (mFreezeCount
== 0) {
2583 if (HasObservers() || HasImageRequests()) {
2584 // FIXME: This isn't quite right, since our EnsureTimerStarted call
2585 // updates our mMostRecentRefresh, but the DoRefresh call won't run
2586 // and notify our observers until we get back to the event loop.
2587 // Thus MostRecentRefresh() will lie between now and the DoRefresh.
2588 RefPtr
<nsRunnableMethod
<nsRefreshDriver
>> event
= NewRunnableMethod(
2589 "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh
);
2590 nsPresContext
* pc
= GetPresContext();
2592 pc
->Document()->Dispatch(TaskCategory::Other
, event
.forget());
2593 EnsureTimerStarted();
2595 NS_ERROR("Thawing while document is being destroyed");
2601 void nsRefreshDriver::FinishedWaitingForTransaction() {
2602 mWaitingForTransaction
= false;
2603 mSkippedPaints
= false;
2604 mWarningThreshold
= 1;
2607 mozilla::layers::TransactionId
nsRefreshDriver::GetTransactionId(
2609 mNextTransactionId
= mNextTransactionId
.Next();
2610 LOG("[%p] Allocating transaction id %" PRIu64
, this, mNextTransactionId
.mId
);
2612 // If this a paint from within a normal tick, and the caller hasn't explicitly
2613 // asked for it to skip being throttled, then record this transaction as
2614 // pending and maybe disable painting until some transactions are processed.
2615 if (aThrottle
&& mInNormalTick
) {
2616 mPendingTransactions
.AppendElement(mNextTransactionId
);
2617 if (TooManyPendingTransactions() && !mWaitingForTransaction
&&
2618 !mTestControllingRefreshes
) {
2619 LOG("[%p] Hit max pending transaction limit, entering wait mode", this);
2620 mWaitingForTransaction
= true;
2621 mSkippedPaints
= false;
2622 mWarningThreshold
= 1;
2626 return mNextTransactionId
;
2629 mozilla::layers::TransactionId
nsRefreshDriver::LastTransactionId() const {
2630 return mNextTransactionId
;
2633 void nsRefreshDriver::RevokeTransactionId(
2634 mozilla::layers::TransactionId aTransactionId
) {
2635 MOZ_ASSERT(aTransactionId
== mNextTransactionId
);
2636 LOG("[%p] Revoking transaction id %" PRIu64
, this, aTransactionId
.mId
);
2637 if (AtPendingTransactionLimit() &&
2638 mPendingTransactions
.Contains(aTransactionId
) && mWaitingForTransaction
) {
2639 LOG("[%p] No longer over pending transaction limit, leaving wait state",
2641 MOZ_ASSERT(!mSkippedPaints
,
2642 "How did we skip a paint when we're in the middle of one?");
2643 FinishedWaitingForTransaction();
2646 // Notify the pres context so that it can deliver MozAfterPaint for this
2647 // id if any caller was expecting it.
2648 nsPresContext
* pc
= GetPresContext();
2650 pc
->NotifyRevokingDidPaint(aTransactionId
);
2652 // Remove aTransactionId from the set of outstanding transactions since we're
2653 // no longer waiting on it to be completed, but don't revert
2654 // mNextTransactionId since we can't use the id again.
2655 mPendingTransactions
.RemoveElement(aTransactionId
);
2658 void nsRefreshDriver::ClearPendingTransactions() {
2659 LOG("[%p] ClearPendingTransactions", this);
2660 mPendingTransactions
.Clear();
2661 mWaitingForTransaction
= false;
2664 void nsRefreshDriver::ResetInitialTransactionId(
2665 mozilla::layers::TransactionId aTransactionId
) {
2666 mNextTransactionId
= aTransactionId
;
2669 mozilla::TimeStamp
nsRefreshDriver::GetTransactionStart() { return mTickStart
; }
2671 VsyncId
nsRefreshDriver::GetVsyncId() { return mTickVsyncId
; }
2673 mozilla::TimeStamp
nsRefreshDriver::GetVsyncStart() { return mTickVsyncTime
; }
2675 void nsRefreshDriver::NotifyTransactionCompleted(
2676 mozilla::layers::TransactionId aTransactionId
) {
2677 LOG("[%p] Completed transaction id %" PRIu64
, this, aTransactionId
.mId
);
2678 mPendingTransactions
.RemoveElement(aTransactionId
);
2679 if (mWaitingForTransaction
&& !TooManyPendingTransactions()) {
2680 LOG("[%p] No longer over pending transaction limit, leaving wait state",
2682 FinishedWaitingForTransaction();
2686 void nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime
) {
2687 mRootRefresh
->RemoveRefreshObserver(this, FlushType::Style
);
2688 mRootRefresh
= nullptr;
2689 if (mSkippedPaints
) {
2694 bool nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime
) {
2695 if (mTestControllingRefreshes
) {
2699 if (mWaitingForTransaction
) {
2700 if (mSkippedPaints
&&
2701 aTime
> (mMostRecentRefresh
+
2702 TimeDuration::FromMilliseconds(mWarningThreshold
* 1000))) {
2703 // XXX - Bug 1303369 - too many false positives.
2704 // gfxCriticalNote << "Refresh driver waiting for the compositor for "
2705 // << (aTime - mMostRecentRefresh).ToSeconds()
2707 mWarningThreshold
*= 2;
2710 LOG("[%p] Over max pending transaction limit when trying to paint, "
2713 mSkippedPaints
= true;
2717 // Try find the 'root' refresh driver for the current window and check
2718 // if that is waiting for a paint.
2719 nsPresContext
* pc
= GetPresContext();
2720 nsPresContext
* rootContext
= pc
? pc
->GetRootPresContext() : nullptr;
2722 nsRefreshDriver
* rootRefresh
= rootContext
->RefreshDriver();
2723 if (rootRefresh
&& rootRefresh
!= this) {
2724 if (rootRefresh
->IsWaitingForPaint(aTime
)) {
2725 if (mRootRefresh
!= rootRefresh
) {
2727 mRootRefresh
->RemoveRefreshObserver(this, FlushType::Style
);
2729 rootRefresh
->AddRefreshObserver(this, FlushType::Style
,
2730 "Waiting for paint");
2731 mRootRefresh
= rootRefresh
;
2733 mSkippedPaints
= true;
2741 void nsRefreshDriver::SetThrottled(bool aThrottled
) {
2742 if (aThrottled
!= mThrottled
) {
2743 mThrottled
= aThrottled
;
2745 // We want to switch our timer type here, so just stop and
2746 // restart the timer.
2747 EnsureTimerStarted(eForceAdjustTimer
);
2752 nsPresContext
* nsRefreshDriver::GetPresContext() const { return mPresContext
; }
2754 void nsRefreshDriver::DoRefresh() {
2755 // Don't do a refresh unless we're in a state where we should be refreshing.
2756 if (!IsFrozen() && mPresContext
&& mActiveTimer
) {
2762 bool nsRefreshDriver::IsRefreshObserver(nsARefreshObserver
* aObserver
,
2763 FlushType aFlushType
) {
2764 ObserverArray
& array
= ArrayFor(aFlushType
);
2765 return array
.Contains(aObserver
);
2769 void nsRefreshDriver::ScheduleViewManagerFlush() {
2770 NS_ASSERTION(mPresContext
->IsRoot(),
2771 "Should only schedule view manager flush on root prescontexts");
2772 mViewManagerFlushIsPending
= true;
2773 if (!mViewManagerFlushCause
) {
2774 mViewManagerFlushCause
= profiler_capture_backtrace();
2776 mHasScheduleFlush
= true;
2777 EnsureTimerStarted(eNeverAdjustTimer
);
2780 void nsRefreshDriver::ScheduleFrameRequestCallbacks(Document
* aDocument
) {
2781 NS_ASSERTION(mFrameRequestCallbackDocs
.IndexOf(aDocument
) ==
2782 mFrameRequestCallbackDocs
.NoIndex
&&
2783 mThrottledFrameRequestCallbackDocs
.IndexOf(aDocument
) ==
2784 mThrottledFrameRequestCallbackDocs
.NoIndex
,
2785 "Don't schedule the same document multiple times");
2786 if (aDocument
->ShouldThrottleFrameRequests()) {
2787 mThrottledFrameRequestCallbackDocs
.AppendElement(aDocument
);
2789 mFrameRequestCallbackDocs
.AppendElement(aDocument
);
2792 // make sure that the timer is running
2793 EnsureTimerStarted();
2796 void nsRefreshDriver::RevokeFrameRequestCallbacks(Document
* aDocument
) {
2797 mFrameRequestCallbackDocs
.RemoveElement(aDocument
);
2798 mThrottledFrameRequestCallbackDocs
.RemoveElement(aDocument
);
2799 // No need to worry about restarting our timer in slack mode if it's already
2800 // running; that will happen automatically when it fires.
2803 void nsRefreshDriver::ScheduleFullscreenEvent(
2804 UniquePtr
<PendingFullscreenEvent
> aEvent
) {
2805 mPendingFullscreenEvents
.AppendElement(std::move(aEvent
));
2806 // make sure that the timer is running
2807 EnsureTimerStarted();
2810 void nsRefreshDriver::CancelPendingFullscreenEvents(Document
* aDocument
) {
2811 for (auto i
: Reversed(IntegerRange(mPendingFullscreenEvents
.Length()))) {
2812 if (mPendingFullscreenEvents
[i
]->Document() == aDocument
) {
2813 mPendingFullscreenEvents
.RemoveElementAt(i
);
2818 void nsRefreshDriver::CancelPendingAnimationEvents(
2819 AnimationEventDispatcher
* aDispatcher
) {
2820 MOZ_ASSERT(aDispatcher
);
2821 aDispatcher
->ClearEventQueue();
2822 mAnimationEventFlushObservers
.RemoveElement(aDispatcher
);
2826 TimeStamp
nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault
) {
2827 MOZ_ASSERT(NS_IsMainThread());
2828 MOZ_ASSERT(!aDefault
.IsNull());
2830 // For computing idleness of refresh drivers we only care about
2831 // sRegularRateTimerList, since we consider refresh drivers attached to
2832 // sThrottledRateTimer to be inactive. This implies that tasks
2833 // resulting from a tick on the sRegularRateTimer counts as being
2834 // busy but tasks resulting from a tick on sThrottledRateTimer
2835 // counts as being idle.
2836 if (sRegularRateTimer
) {
2837 return sRegularRateTimer
->GetIdleDeadlineHint(aDefault
);
2840 // The following calculation is only used on platform using per-BrowserChild
2841 // Vsync. This is hard to properly map on static calls such as this -
2842 // optimally we'd only want to query the timers that are relevant for the
2843 // caller, not all in this process. Further more, in this scenario we often
2844 // hit cases where timers would return their fallback value that is aDefault,
2845 // giving us a much higher value than intended.
2846 // For now we use a somewhat simplistic approach that in many situations
2847 // gives us similar behaviour to what we would get using sRegularRateTimer:
2848 // use the highest result that is still lower than the aDefault fallback.
2849 TimeStamp hint
= TimeStamp();
2850 if (sRegularRateTimerList
) {
2851 for (RefreshDriverTimer
* timer
: *sRegularRateTimerList
) {
2852 TimeStamp newHint
= timer
->GetIdleDeadlineHint(aDefault
);
2853 if (newHint
< aDefault
&& (hint
.IsNull() || newHint
> hint
)) {
2859 return hint
.IsNull() ? aDefault
: hint
;
2863 Maybe
<TimeStamp
> nsRefreshDriver::GetNextTickHint() {
2864 MOZ_ASSERT(NS_IsMainThread());
2866 if (sRegularRateTimer
) {
2867 return sRegularRateTimer
->GetNextTickHint();
2870 Maybe
<TimeStamp
> hint
= Nothing();
2871 if (sRegularRateTimerList
) {
2872 for (RefreshDriverTimer
* timer
: *sRegularRateTimerList
) {
2873 if (Maybe
<TimeStamp
> newHint
= timer
->GetNextTickHint()) {
2874 if (!hint
|| newHint
.value() < hint
.value()) {
2883 void nsRefreshDriver::Disconnect() {
2884 MOZ_ASSERT(NS_IsMainThread());
2889 mPresContext
= nullptr;
2890 if (--sRefreshDriverCount
== 0) {