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