Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp
Warning:line 1864, column 29
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

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