Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp
Warning:line 1862, 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-19/lib/clang/19 -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 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-19/lib/clang/19/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-2024-09-22-115206-3586786-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 Telemetry::Accumulate(
771 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, sample);
772 } else if (mVsyncRate != TimeDuration::Forever()) {
773 TimeDuration contentDelay =
774 (TimeStamp::Now() - mLastTickStart) - mVsyncRate;
775 if (contentDelay.ToMilliseconds() < 0) {
776 // Vsyncs are noisy and some can come at a rate quicker than
777 // the reported hardware rate. In those cases, consider that we have 0
778 // delay.
779 contentDelay = TimeDuration::FromMilliseconds(0);
780 }
781 uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
782 Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
783 sample);
784 Telemetry::Accumulate(
785 Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, sample);
786 } else {
787 // Request the vsync rate which VsyncChild stored the last time it got a
788 // vsync notification.
789 mVsyncRate = mVsyncChild->GetVsyncRate();
790 }
791#endif
792 }
793
794 void OnTimerStart() {
795 mLastTickStart = TimeStamp::Now();
796 mLastTickEnd = TimeStamp();
797 mLastIdleTaskCount = 0;
798 }
799
800 void IdlePriorityNotify() {
801 if (mLastProcessedTick.IsNull() || mRecentVsync > mLastProcessedTick) {
802 // mSuspendVsyncPriorityTicksUntil is for high priority vsync
803 // notifications only.
804 mSuspendVsyncPriorityTicksUntil = TimeStamp();
805 TickRefreshDriver(mRecentVsyncId, mRecentVsync);
806 }
807
808 mProcessedVsync = true;
809 }
810
811 hal::PerformanceHintSession* GetPerformanceHintSession() {
812 // The ContentChild creates/destroys the PerformanceHintSession in response
813 // to the process' priority being foregrounded/backgrounded. We can only use
814 // this session when using a single vsync source for the process, otherwise
815 // these threads may be performing work for multiple
816 // VsyncRefreshDriverTimers and we will misreport the work duration.
817 const ContentChild* contentChild = ContentChild::GetSingleton();
818 if (contentChild && mVsyncChild) {
819 return contentChild->PerformanceHintSession();
820 }
821
822 return nullptr;
823 }
824
825 void TickRefreshDriver(VsyncId aId, TimeStamp aVsyncTimestamp) {
826 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"
, 826); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 826; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
827
828 RecordTelemetryProbes(aVsyncTimestamp);
829
830 TimeStamp tickStart = TimeStamp::Now();
831
832 const TimeDuration previousRate = mVsyncRate;
833 const TimeDuration rate = GetTimerRate();
834
835 if (rate != previousRate) {
836 if (auto* const performanceHintSession = GetPerformanceHintSession()) {
837 performanceHintSession->UpdateTargetWorkDuration(
838 ContentChild::GetPerformanceHintTarget(rate));
839 }
840 }
841
842 if (TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval()) >
843 rate) {
844 sMostRecentHighRateVsync = tickStart;
845 sMostRecentHighRate = rate;
846 }
847
848 // On 32-bit Windows we sometimes get times where TimeStamp::Now() is not
849 // monotonic because the underlying system apis produce non-monontonic
850 // results. (bug 1306896)
851#if !defined(_WIN32)
852 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"
, 852); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aVsyncTimestamp <= tickStart"
")"); do { *((volatile int*)__null) = 852; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
853#endif
854
855 bool shouldGiveNonVSyncTasksMoreTime = ShouldGiveNonVsyncTasksMoreTime();
856
857 // Set these variables before calling RunRefreshDrivers so that they are
858 // visible to any nested ticks.
859 mLastTickStart = tickStart;
860 mLastProcessedTick = aVsyncTimestamp;
861
862 RunRefreshDrivers(aId, aVsyncTimestamp);
863
864 TimeStamp tickEnd = TimeStamp::Now();
865
866 if (auto* const performanceHintSession = GetPerformanceHintSession()) {
867 performanceHintSession->ReportActualWorkDuration(tickEnd - tickStart);
868 }
869
870 // Re-read mLastTickStart in case there was a nested tick inside this
871 // tick.
872 TimeStamp mostRecentTickStart = mLastTickStart;
873
874 // Let also non-RefreshDriver code to run at least for awhile if we have
875 // a mVsyncRefreshDriverTimer.
876 // Always give a tiny bit, 5% of the vsync interval, time outside the
877 // tick
878 // In case there are both normal tasks and RefreshDrivers are doing
879 // work, mSuspendVsyncPriorityTicksUntil will be set to a timestamp in the
880 // future where the period between the previous tick start
881 // (mostRecentTickStart) and the next tick needs to be at least the amount
882 // of work normal tasks and RefreshDrivers did together (minus short grace
883 // period).
884 TimeDuration gracePeriod = rate / int64_t(20);
885
886 if (shouldGiveNonVSyncTasksMoreTime && !mLastTickEnd.IsNull() &&
887 XRE_IsContentProcess() &&
888 // For RefreshDriver scheduling during page load there is currently
889 // idle priority based setup.
890 // XXX Consider to remove the page load specific code paths.
891 !IsAnyToplevelContentPageLoading()) {
892 // In case normal tasks are doing lots of work, we still want to paint
893 // every now and then, so only at maximum 4 * rate of work is counted
894 // here.
895 // If we're giving extra time for tasks outside a tick, try to
896 // ensure the next vsync after that period is handled, so subtract
897 // a grace period.
898 TimeDuration timeForOutsideTick = clamped(
899 tickStart - mLastTickEnd - gracePeriod, gracePeriod, rate * 4);
900 mSuspendVsyncPriorityTicksUntil = tickEnd + timeForOutsideTick;
901 } else if (ShouldGiveNonVsyncTasksMoreTime(true)) {
902 // We've got some new tasks, give them some extra time.
903 // This handles also the case when mLastTickEnd.IsNull() above and we
904 // should give some more time for non-vsync tasks.
905 mSuspendVsyncPriorityTicksUntil = tickEnd + gracePeriod;
906 } else {
907 mSuspendVsyncPriorityTicksUntil = mostRecentTickStart + gracePeriod;
908 }
909
910 mLastIdleTaskCount =
911 TaskController::Get()->GetIdleTaskManager()->ProcessedTaskCount();
912 mLastRunOutOfMTTasksCount = TaskController::Get()->RunOutOfMTTasksCount();
913 mLastTickEnd = tickEnd;
914 }
915
916 void StartTimer() override {
917 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"
, 917); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 917; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
918
919 mLastFireTime = TimeStamp::Now();
920 mLastFireId = VsyncId();
921
922 if (mVsyncDispatcher) {
923 mVsyncDispatcher->AddVsyncObserver(mVsyncObserver);
924 } else if (mVsyncChild) {
925 mVsyncChild->AddChildRefreshTimer(mVsyncObserver);
926 OnTimerStart();
927 }
928 mIsTicking = true;
929 }
930
931 void StopTimer() override {
932 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"
, 932); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 932; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
933
934 if (mVsyncDispatcher) {
935 mVsyncDispatcher->RemoveVsyncObserver(mVsyncObserver);
936 } else if (mVsyncChild) {
937 mVsyncChild->RemoveChildRefreshTimer(mVsyncObserver);
938 }
939 mIsTicking = false;
940 }
941
942 public:
943 bool IsTicking() const override { return mIsTicking; }
944
945 protected:
946 void ScheduleNextTick(TimeStamp aNowTime) override {
947 // Do nothing since we just wait for the next vsync from
948 // RefreshDriverVsyncObserver.
949 }
950
951 void RunRefreshDrivers(VsyncId aId, TimeStamp aTimeStamp) {
952 Tick(aId, aTimeStamp);
953 for (auto& driver : mContentRefreshDrivers) {
954 driver->FinishedVsyncTick();
955 }
956 for (auto& driver : mRootRefreshDrivers) {
957 driver->FinishedVsyncTick();
958 }
959 }
960
961 // Always non-null. Has a weak pointer to us and notifies us of vsync.
962 RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
963
964 // Used in the parent process. We register mVsyncObserver with it for the
965 // duration during which we want to receive vsync notifications. We also
966 // use it to query the current vsync rate.
967 RefPtr<VsyncDispatcher> mVsyncDispatcher;
968 // Used it the content process. We register mVsyncObserver with it for the
969 // duration during which we want to receive vsync notifications. The
970 // mVsyncChild will be always available before VsyncChild::ActorDestroy().
971 // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
972 RefPtr<VsyncMainChild> mVsyncChild;
973
974 TimeDuration mVsyncRate;
975 bool mIsTicking = false;
976
977 TimeStamp mRecentVsync;
978 VsyncId mRecentVsyncId;
979 // The local start time when RefreshDrivers' Tick was called last time.
980 TimeStamp mLastTickStart;
981 // The local end time of the last RefreshDrivers' tick.
982 TimeStamp mLastTickEnd;
983 // The number of idle tasks the main thread has processed. It is updated
984 // right after RefreshDrivers' tick.
985 uint64_t mLastIdleTaskCount;
986 // If there were no idle tasks, we need to check if the main event queue
987 // was totally empty at times.
988 uint64_t mLastRunOutOfMTTasksCount;
989 // Note, mLastProcessedTick stores the vsync timestamp, which may be coming
990 // from a different process.
991 TimeStamp mLastProcessedTick;
992 // mSuspendVsyncPriorityTicksUntil is used to block too high refresh rate in
993 // case the main thread has also other non-idle tasks to process.
994 // The timestamp is effectively mLastTickEnd + some duration.
995 TimeStamp mSuspendVsyncPriorityTicksUntil;
996 bool mProcessedVsync;
997
998 TimeStamp mPendingVsync;
999 VsyncId mPendingVsyncId;
1000 bool mHasPendingLowPrioTask;
1001}; // VsyncRefreshDriverTimer
1002
1003/**
1004 * Since the content process takes some time to setup
1005 * the vsync IPC connection, this timer is used
1006 * during the intial startup process.
1007 * During initial startup, the refresh drivers
1008 * are ticked off this timer, and are swapped out once content
1009 * vsync IPC connection is established.
1010 */
1011class StartupRefreshDriverTimer : public SimpleTimerBasedRefreshDriverTimer {
1012 public:
1013 explicit StartupRefreshDriverTimer(double aRate)
1014 : SimpleTimerBasedRefreshDriverTimer(aRate) {}
1015
1016 protected:
1017 void ScheduleNextTick(TimeStamp aNowTime) override {
1018 // Since this is only used for startup, it isn't super critical
1019 // that we tick at consistent intervals.
1020 TimeStamp newTarget = aNowTime + mRateDuration;
1021 uint32_t delay =
1022 static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
1023 mTimer->InitWithNamedFuncCallback(
1024 TimerTick, this, delay, nsITimer::TYPE_ONE_SHOT,
1025 "StartupRefreshDriverTimer::ScheduleNextTick");
1026 mTargetTime = newTarget;
1027 }
1028
1029 public:
1030 bool IsTicking() const override { return true; }
1031};
1032
1033/*
1034 * A RefreshDriverTimer for inactive documents. When a new refresh driver is
1035 * added, the rate is reset to the base (normally 1s/1fps). Every time
1036 * it ticks, a single refresh driver is poked. Once they have all been poked,
1037 * the duration between ticks doubles, up to mDisableAfterMilliseconds. At that
1038 * point, the timer is quiet and doesn't tick (until something is added to it
1039 * again).
1040 *
1041 * When a timer is removed, there is a possibility of another timer
1042 * being skipped for one cycle. We could avoid this by adjusting
1043 * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
1044 * add that complexity. All we want is for inactive drivers to tick
1045 * at some point, but we don't care too much about how often.
1046 */
1047class InactiveRefreshDriverTimer final
1048 : public SimpleTimerBasedRefreshDriverTimer {
1049 public:
1050 explicit InactiveRefreshDriverTimer(double aRate)
1051 : SimpleTimerBasedRefreshDriverTimer(aRate),
1052 mNextTickDuration(aRate),
1053 mDisableAfterMilliseconds(-1.0),
1054 mNextDriverIndex(0) {}
1055
1056 InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
1057 : SimpleTimerBasedRefreshDriverTimer(aRate),
1058 mNextTickDuration(aRate),
1059 mDisableAfterMilliseconds(aDisableAfterMilliseconds),
1060 mNextDriverIndex(0) {}
1061
1062 void AddRefreshDriver(nsRefreshDriver* aDriver) override {
1063 RefreshDriverTimer::AddRefreshDriver(aDriver);
1064
1065 LOG("[%p] inactive timer got new refresh driver %p, resetting rate", this,
1066 aDriver);
1067
1068 // reset the timer, and start with the newly added one next time.
1069 mNextTickDuration = mRateMilliseconds;
1070
1071 // we don't really have to start with the newly added one, but we may as
1072 // well not tick the old ones at the fastest rate any more than we need to.
1073 mNextDriverIndex = GetRefreshDriverCount() - 1;
1074
1075 StopTimer();
1076 StartTimer();
1077 }
1078
1079 TimeDuration GetTimerRate() override {
1080 return TimeDuration::FromMilliseconds(mNextTickDuration);
1081 }
1082
1083 protected:
1084 uint32_t GetRefreshDriverCount() {
1085 return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
1086 }
1087
1088 void StartTimer() override {
1089 mLastFireTime = TimeStamp::Now();
1090 mLastFireId = VsyncId();
1091
1092 mTargetTime = mLastFireTime + mRateDuration;
1093
1094 uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
1095 mTimer->InitWithNamedFuncCallback(TimerTickOne, this, delay,
1096 nsITimer::TYPE_ONE_SHOT,
1097 "InactiveRefreshDriverTimer::StartTimer");
1098 mIsTicking = true;
1099 }
1100
1101 void StopTimer() override {
1102 mTimer->Cancel();
1103 mIsTicking = false;
1104 }
1105
1106 void ScheduleNextTick(TimeStamp aNowTime) override {
1107 if (mDisableAfterMilliseconds > 0.0 &&
1108 mNextTickDuration > mDisableAfterMilliseconds) {
1109 // We hit the time after which we should disable
1110 // inactive window refreshes; don't schedule anything
1111 // until we get kicked by an AddRefreshDriver call.
1112 return;
1113 }
1114
1115 // double the next tick time if we've already gone through all of them once
1116 if (mNextDriverIndex >= GetRefreshDriverCount()) {
1117 mNextTickDuration *= 2.0;
1118 mNextDriverIndex = 0;
1119 }
1120
1121 // this doesn't need to be precise; do a simple schedule
1122 uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
1123 mTimer->InitWithNamedFuncCallback(
1124 TimerTickOne, this, delay, nsITimer::TYPE_ONE_SHOT,
1125 "InactiveRefreshDriverTimer::ScheduleNextTick");
1126
1127 LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this,
1128 mNextTickDuration, mNextDriverIndex, GetRefreshDriverCount());
1129 }
1130
1131 public:
1132 bool IsTicking() const override { return mIsTicking; }
1133
1134 protected:
1135 /* Runs just one driver's tick. */
1136 void TickOne() {
1137 TimeStamp now = TimeStamp::Now();
1138
1139 ScheduleNextTick(now);
1140
1141 mLastFireTime = now;
1142 mLastFireId = VsyncId();
1143
1144 nsTArray<RefPtr<nsRefreshDriver>> drivers(mContentRefreshDrivers.Clone());
1145 drivers.AppendElements(mRootRefreshDrivers);
1146 size_t index = mNextDriverIndex;
1147
1148 if (index < drivers.Length() &&
1149 !drivers[index]->IsTestControllingRefreshesEnabled()) {
1150 TickDriver(drivers[index], VsyncId(), now);
1151 }
1152
1153 mNextDriverIndex++;
1154 }
1155
1156 static void TimerTickOne(nsITimer* aTimer, void* aClosure) {
1157 RefPtr<InactiveRefreshDriverTimer> timer =
1158 static_cast<InactiveRefreshDriverTimer*>(aClosure);
1159 timer->TickOne();
1160 }
1161
1162 double mNextTickDuration;
1163 double mDisableAfterMilliseconds;
1164 uint32_t mNextDriverIndex;
1165 bool mIsTicking = false;
1166};
1167
1168} // namespace mozilla
1169
1170static StaticRefPtr<RefreshDriverTimer> sRegularRateTimer;
1171static StaticAutoPtr<nsTArray<RefreshDriverTimer*>> sRegularRateTimerList;
1172static StaticRefPtr<InactiveRefreshDriverTimer> sThrottledRateTimer;
1173
1174void nsRefreshDriver::CreateVsyncRefreshTimer() {
1175 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"
, 1175); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 1175; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1176
1177 if (gfxPlatform::IsInLayoutAsapMode()) {
1178 return;
1179 }
1180
1181 if (!mOwnTimer) {
1182 // If available, we fetch the widget-specific vsync source.
1183 nsPresContext* pc = GetPresContext();
1184 nsCOMPtr<nsIWidget> widget = pc->GetRootWidget();
1185 if (widget) {
1186 if (RefPtr<VsyncDispatcher> vsyncDispatcher =
1187 widget->GetVsyncDispatcher()) {
1188 mOwnTimer = VsyncRefreshDriverTimer::
1189 CreateForParentProcessWithLocalVsyncDispatcher(
1190 std::move(vsyncDispatcher));
1191 sRegularRateTimerList->AppendElement(mOwnTimer.get());
1192 return;
1193 }
1194 if (BrowserChild* browserChild = widget->GetOwningBrowserChild()) {
1195 if (RefPtr<VsyncMainChild> vsyncChildViaPBrowser =
1196 browserChild->GetVsyncChild()) {
1197 mOwnTimer = VsyncRefreshDriverTimer::CreateForContentProcess(
1198 std::move(vsyncChildViaPBrowser));
1199 sRegularRateTimerList->AppendElement(mOwnTimer.get());
1200 return;
1201 }
1202 }
1203 }
1204 }
1205 if (!sRegularRateTimer) {
1206 if (XRE_IsParentProcess()) {
1207 // Make sure all vsync systems are ready.
1208 gfxPlatform::GetPlatform();
1209 // In parent process, we can create the VsyncRefreshDriverTimer directly.
1210 sRegularRateTimer =
1211 VsyncRefreshDriverTimer::CreateForParentProcessWithGlobalVsync();
1212 } else {
1213 PBackgroundChild* actorChild =
1214 BackgroundChild::GetOrCreateForCurrentThread();
1215 if (NS_WARN_IF(!actorChild)NS_warn_if_impl(!actorChild, "!actorChild", "/var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp"
, 1215)
) {
1216 return;
1217 }
1218
1219 auto vsyncChildViaPBackground = MakeRefPtr<dom::VsyncMainChild>();
1220 dom::PVsyncChild* actor =
1221 actorChild->SendPVsyncConstructor(vsyncChildViaPBackground);
1222 if (NS_WARN_IF(!actor)NS_warn_if_impl(!actor, "!actor", "/var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp"
, 1222)
) {
1223 return;
1224 }
1225
1226 RefPtr<RefreshDriverTimer> vsyncRefreshDriverTimer =
1227 VsyncRefreshDriverTimer::CreateForContentProcess(
1228 std::move(vsyncChildViaPBackground));
1229
1230 sRegularRateTimer = std::move(vsyncRefreshDriverTimer);
1231 }
1232 }
1233}
1234
1235static uint32_t GetFirstFrameDelay(imgIRequest* req) {
1236 nsCOMPtr<imgIContainer> container;
1237 if (NS_FAILED(req->GetImage(getter_AddRefs(container)))((bool)(__builtin_expect(!!(NS_FAILED_impl(req->GetImage(getter_AddRefs
(container)))), 0)))
|| !container) {
1238 return 0;
1239 }
1240
1241 // If this image isn't animated, there isn't a first frame delay.
1242 int32_t delay = container->GetFirstFrameDelay();
1243 if (delay < 0) return 0;
1244
1245 return static_cast<uint32_t>(delay);
1246}
1247
1248/* static */
1249void nsRefreshDriver::Shutdown() {
1250 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"
, 1250); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 1250; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1251 // clean up our timers
1252 sRegularRateTimer = nullptr;
1253 sRegularRateTimerList = nullptr;
1254 sThrottledRateTimer = nullptr;
1255}
1256
1257/* static */
1258int32_t nsRefreshDriver::DefaultInterval() {
1259 return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
1260}
1261
1262/* static */
1263double nsRefreshDriver::HighRateMultiplier() {
1264 // We're in high rate mode if we've gotten a fast rate during the last
1265 // DefaultInterval().
1266 bool inHighRateMode =
1267 !gfxPlatform::IsInLayoutAsapMode() &&
1268 StaticPrefs::layout_expose_high_rate_mode_from_refreshdriver() &&
1269 !sMostRecentHighRateVsync.IsNull() &&
1270 (sMostRecentHighRateVsync +
1271 TimeDuration::FromMilliseconds(DefaultInterval())) > TimeStamp::Now();
1272 if (!inHighRateMode) {
1273 // Clear the timestamp so that the next call is faster.
1274 sMostRecentHighRateVsync = TimeStamp();
1275 sMostRecentHighRate = TimeDuration();
1276 return 1.0;
1277 }
1278
1279 return sMostRecentHighRate.ToMilliseconds() / DefaultInterval();
1280}
1281
1282// Compute the interval to use for the refresh driver timer, in milliseconds.
1283// outIsDefault indicates that rate was not explicitly set by the user
1284// so we might choose other, more appropriate rates (e.g. vsync, etc)
1285// layout.frame_rate=0 indicates "ASAP mode".
1286// In ASAP mode rendering is iterated as fast as possible (typically for stress
1287// testing). A target rate of 10k is used internally instead of special-handling
1288// 0. Backends which block on swap/present/etc should try to not block when
1289// layout.frame_rate=0 - to comply with "ASAP" as much as possible.
1290double nsRefreshDriver::GetRegularTimerInterval() const {
1291 int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
1292 if (rate < 0) {
1293 rate = gfxPlatform::GetDefaultFrameRate();
1294 } else if (rate == 0) {
1295 rate = 10000;
1296 }
1297
1298 return 1000.0 / rate;
1299}
1300
1301/* static */
1302double nsRefreshDriver::GetThrottledTimerInterval() {
1303 uint32_t rate = StaticPrefs::layout_throttled_frame_rate();
1304 return 1000.0 / rate;
1305}
1306
1307/* static */
1308TimeDuration nsRefreshDriver::GetMinRecomputeVisibilityInterval() {
1309 return TimeDuration::FromMilliseconds(
1310 StaticPrefs::layout_visibility_min_recompute_interval_ms());
1311}
1312
1313RefreshDriverTimer* nsRefreshDriver::ChooseTimer() {
1314 if (mThrottled) {
1315 if (!sThrottledRateTimer) {
1316 sThrottledRateTimer = new InactiveRefreshDriverTimer(
1317 GetThrottledTimerInterval(),
1318 DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS600 * 1000.0);
1319 }
1320 return sThrottledRateTimer;
1321 }
1322
1323 if (!mOwnTimer) {
1324 CreateVsyncRefreshTimer();
1325 }
1326
1327 if (mOwnTimer) {
1328 return mOwnTimer.get();
1329 }
1330
1331 if (!sRegularRateTimer) {
1332 double rate = GetRegularTimerInterval();
1333 sRegularRateTimer = new StartupRefreshDriverTimer(rate);
1334 }
1335
1336 return sRegularRateTimer;
1337}
1338
1339static nsDocShell* GetDocShell(nsPresContext* aPresContext) {
1340 if (!aPresContext) {
1341 return nullptr;
1342 }
1343 return static_cast<nsDocShell*>(aPresContext->GetDocShell());
1344}
1345
1346nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
1347 : mActiveTimer(nullptr),
1348 mOwnTimer(nullptr),
1349 mPresContext(aPresContext),
1350 mRootRefresh(nullptr),
1351 mNextTransactionId{0},
1352 mFreezeCount(0),
1353 mThrottledFrameRequestInterval(
1354 TimeDuration::FromMilliseconds(GetThrottledTimerInterval())),
1355 mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
1356 mThrottled(false),
1357 mNeedToRecomputeVisibility(false),
1358 mTestControllingRefreshes(false),
1359 mViewManagerFlushIsPending(false),
1360 mHasScheduleFlush(false),
1361 mInRefresh(false),
1362 mWaitingForTransaction(false),
1363 mSkippedPaints(false),
1364 mResizeSuppressed(false),
1365 mNeedToUpdateIntersectionObservations(false),
1366 mNeedToUpdateResizeObservers(false),
1367 mNeedToUpdateViewTransitions(false),
1368 mNeedToRunFrameRequestCallbacks(false),
1369 mNeedToUpdateAnimations(false),
1370 mMightNeedMediaQueryListenerUpdate(false),
1371 mNeedToUpdateContentRelevancy(false),
1372 mInNormalTick(false),
1373 mAttemptedExtraTickSinceLastVsync(false),
1374 mHasExceededAfterLoadTickPeriod(false),
1375 mHasStartedTimerAtLeastOnce(false) {
1376 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"
, 1376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 1376; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1377 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"
, 1379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mPresContext"
") (" "Need a pres context to tell us to call Disconnect() later "
"and decrement sRefreshDriverCount." ")"); do { *((volatile int
*)__null) = 1379; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
1378 "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"
, 1379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mPresContext"
") (" "Need a pres context to tell us to call Disconnect() later "
"and decrement sRefreshDriverCount." ")"); do { *((volatile int
*)__null) = 1379; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
1379 "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"
, 1379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mPresContext"
") (" "Need a pres context to tell us to call Disconnect() later "
"and decrement sRefreshDriverCount." ")"); do { *((volatile int
*)__null) = 1379; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1380 mMostRecentRefresh = TimeStamp::Now();
1381 mNextThrottledFrameRequestTick = mMostRecentRefresh;
1382 mNextRecomputeVisibilityTick = mMostRecentRefresh;
1383
1384 if (!sRegularRateTimerList) {
1385 sRegularRateTimerList = new nsTArray<RefreshDriverTimer*>();
1386 }
1387 ++sRefreshDriverCount;
1388}
1389
1390nsRefreshDriver::~nsRefreshDriver() {
1391 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"
, 1391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 1391; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1392 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"
, 1394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ObserverCount() == mEarlyRunners.Length()"
") (" "observers, except pending selection scrolls, " "should have been unregistered"
")"); do { *((volatile int*)__null) = 1394; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1393 "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"
, 1394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ObserverCount() == mEarlyRunners.Length()"
") (" "observers, except pending selection scrolls, " "should have been unregistered"
")"); do { *((volatile int*)__null) = 1394; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1394 "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"
, 1394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ObserverCount() == mEarlyRunners.Length()"
") (" "observers, except pending selection scrolls, " "should have been unregistered"
")"); do { *((volatile int*)__null) = 1394; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1395 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"
, 1395); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mActiveTimer"
") (" "timer should be gone" ")"); do { *((volatile int*)__null
) = 1395; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
1396 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"
, 1398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mPresContext"
") (" "Should have called Disconnect() and decremented " "sRefreshDriverCount!"
")"); do { *((volatile int*)__null) = 1398; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1397 "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"
, 1398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mPresContext"
") (" "Should have called Disconnect() and decremented " "sRefreshDriverCount!"
")"); do { *((volatile int*)__null) = 1398; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1398 "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"
, 1398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mPresContext"
") (" "Should have called Disconnect() and decremented " "sRefreshDriverCount!"
")"); do { *((volatile int*)__null) = 1398; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1399
1400 if (mRootRefresh) {
1401 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1402 mRootRefresh = nullptr;
1403 }
1404 if (mOwnTimer && sRegularRateTimerList) {
1405 sRegularRateTimerList->RemoveElement(mOwnTimer.get());
1406 }
1407}
1408
1409// Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
1410// for description.
1411void nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds) {
1412 // ensure that we're removed from our driver
1413 StopTimer();
1414
1415 if (!mTestControllingRefreshes) {
1416 mMostRecentRefresh = TimeStamp::Now();
1417
1418 mTestControllingRefreshes = true;
1419 if (mWaitingForTransaction) {
1420 // Disable any refresh driver throttling when entering test mode
1421 mWaitingForTransaction = false;
1422 mSkippedPaints = false;
1423 }
1424 }
1425
1426 mMostRecentRefresh += TimeDuration::FromMilliseconds((double)aMilliseconds);
1427
1428 mozilla::dom::AutoNoJSAPI nojsapi;
1429 DoTick();
1430}
1431
1432void nsRefreshDriver::RestoreNormalRefresh() {
1433 mTestControllingRefreshes = false;
1434 EnsureTimerStarted(eAllowTimeToGoBackwards);
1
Calling 'nsRefreshDriver::EnsureTimerStarted'
1435 mPendingTransactions.Clear();
1436}
1437
1438TimeStamp nsRefreshDriver::MostRecentRefresh(bool aEnsureTimerStarted) const {
1439 // In case of stylo traversal, we have already activated the refresh driver in
1440 // RestyleManager::ProcessPendingRestyles().
1441 if (aEnsureTimerStarted && !ServoStyleSet::IsInServoTraversal()) {
1442 const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
1443 }
1444
1445 return mMostRecentRefresh;
1446}
1447
1448void nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
1449 FlushType aFlushType,
1450 const char* aObserverDescription) {
1451 ObserverArray& array = ArrayFor(aFlushType);
1452 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"
, 1453); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!array.Contains(aObserver)"
") (" "We don't want to redundantly register the same observer"
")"); do { *((volatile int*)__null) = 1453; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1453 "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"
, 1453); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!array.Contains(aObserver)"
") (" "We don't want to redundantly register the same observer"
")"); do { *((volatile int*)__null) = 1453; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1454 array.AppendElement(
1455 ObserverData{aObserver, aObserverDescription, TimeStamp::Now(),
1456 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)),
1457 profiler_capture_backtrace(), aFlushType});
1458#ifdef DEBUG1
1459 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"
, 1460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aObserver->mRegistrationCount >= 0"
") (" "Registration count shouldn't be able to go negative" ")"
); do { *((volatile int*)__null) = 1460; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1460 "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"
, 1460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aObserver->mRegistrationCount >= 0"
") (" "Registration count shouldn't be able to go negative" ")"
); do { *((volatile int*)__null) = 1460; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1461 aObserver->mRegistrationCount++;
1462#endif
1463 EnsureTimerStarted();
1464}
1465
1466bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
1467 FlushType aFlushType) {
1468 ObserverArray& array = ArrayFor(aFlushType);
1469 auto index = array.IndexOf(aObserver);
1470 if (index == ObserverArray::array_type::NoIndex) {
1471 return false;
1472 }
1473
1474 if (profiler_thread_is_being_profiled_for_markers()) {
1475 auto& data = array.ElementAt(index);
1476 nsPrintfCString str("%s [%s]", data.mDescription,
1477 kFlushTypeNames[aFlushType]);
1478 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)
1479 "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)
1480 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)
1481 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)
1482 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)
1483 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)
;
1484 }
1485
1486 array.RemoveElementAt(index);
1487#ifdef DEBUG1
1488 aObserver->mRegistrationCount--;
1489 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"
, 1490); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aObserver->mRegistrationCount >= 0"
") (" "Registration count shouldn't be able to go negative" ")"
); do { *((volatile int*)__null) = 1490; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1490 "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"
, 1490); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aObserver->mRegistrationCount >= 0"
") (" "Registration count shouldn't be able to go negative" ")"
); do { *((volatile int*)__null) = 1490; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1491#endif
1492 return true;
1493}
1494
1495void nsRefreshDriver::PostVisualViewportResizeEvent(
1496 VVPResizeEvent* aResizeEvent) {
1497 mVisualViewportResizeEvents.AppendElement(aResizeEvent);
1498 EnsureTimerStarted();
1499}
1500
1501void nsRefreshDriver::DispatchVisualViewportResizeEvents() {
1502 // We're taking a hint from scroll events and only dispatch the current set
1503 // of queued resize events. If additional events are posted in response to
1504 // the current events being dispatched, we'll dispatch them on the next tick.
1505 VisualViewportResizeEventArray events =
1506 std::move(mVisualViewportResizeEvents);
1507 for (auto& event : events) {
1508 event->Run();
1509 }
1510}
1511
1512void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent,
1513 bool aDelayed) {
1514 if (aDelayed) {
1515 mDelayedScrollEvents.AppendElement(aScrollEvent);
1516 } else {
1517 mScrollEvents.AppendElement(aScrollEvent);
1518 EnsureTimerStarted();
1519 }
1520}
1521
1522void nsRefreshDriver::PostScrollEndEvent(mozilla::Runnable* aScrollEndEvent,
1523 bool aDelayed) {
1524 if (aDelayed) {
1525 mDelayedScrollEndEvents.AppendElement(aScrollEndEvent);
1526 } else {
1527 mScrollEndEvents.AppendElement(aScrollEndEvent);
1528 EnsureTimerStarted();
1529 }
1530}
1531
1532void nsRefreshDriver::DispatchScrollEvents() {
1533 // Scroll events are one-shot, so after running them we can drop them.
1534 // However, dispatching a scroll event can potentially cause more scroll
1535 // events to be posted, so we move the initial set into a temporary array
1536 // first. (Newly posted scroll events will be dispatched on the next tick.)
1537 ScrollEventArray events = std::move(mScrollEvents);
1538 for (auto& event : events) {
1539 event->Run();
1540 }
1541}
1542
1543void nsRefreshDriver::DispatchScrollEndEvents() {
1544 ScrollEventArray events = std::move(mScrollEndEvents);
1545 for (auto& event : events) {
1546 event->Run();
1547 }
1548}
1549
1550void nsRefreshDriver::PostVisualViewportScrollEvent(
1551 VVPScrollEvent* aScrollEvent) {
1552 mVisualViewportScrollEvents.AppendElement(aScrollEvent);
1553 EnsureTimerStarted();
1554}
1555
1556void nsRefreshDriver::DispatchVisualViewportScrollEvents() {
1557 // Scroll events are one-shot, so after running them we can drop them.
1558 // However, dispatching a scroll event can potentially cause more scroll
1559 // events to be posted, so we move the initial set into a temporary array
1560 // first. (Newly posted scroll events will be dispatched on the next tick.)
1561 VisualViewportScrollEventArray events =
1562 std::move(mVisualViewportScrollEvents);
1563 for (auto& event : events) {
1564 event->Run();
1565 }
1566}
1567
1568// https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
1569void nsRefreshDriver::EvaluateMediaQueriesAndReportChanges() {
1570 if (!mMightNeedMediaQueryListenerUpdate) {
1571 return;
1572 }
1573 mMightNeedMediaQueryListenerUpdate = false;
1574 if (!mPresContext) {
1575 return;
1576 }
1577 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS(mozilla::AutoProfilerLabel raiiObject1578( "Evaluate media queries and report changes"
, nullptr, JS::ProfilingCategoryPair::LAYOUT, uint32_t(js::ProfilingStackFrame
::Flags::RELEVANT_FOR_JS))
1578 "Evaluate media queries and report changes", LAYOUT)mozilla::AutoProfilerLabel raiiObject1578( "Evaluate media queries and report changes"
, nullptr, JS::ProfilingCategoryPair::LAYOUT, uint32_t(js::ProfilingStackFrame
::Flags::RELEVANT_FOR_JS))
;
1579 RefPtr<Document> doc = mPresContext->Document();
1580 doc->EvaluateMediaQueriesAndReportChanges(/* aRecurse = */ true);
1581}
1582
1583void nsRefreshDriver::AddPostRefreshObserver(
1584 nsAPostRefreshObserver* aObserver) {
1585 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"
, 1585); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mPostRefreshObservers.Contains(aObserver)"
")"); do { *((volatile int*)__null) = 1585; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1586 mPostRefreshObservers.AppendElement(aObserver);
1587}
1588
1589void nsRefreshDriver::RemovePostRefreshObserver(
1590 nsAPostRefreshObserver* aObserver) {
1591 bool removed = mPostRefreshObservers.RemoveElement(aObserver);
1592 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"
, 1592); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "removed"
")"); do { *((volatile int*)__null) = 1592; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1593 Unused << removed;
1594}
1595
1596void nsRefreshDriver::AddImageRequest(imgIRequest* aRequest) {
1597 uint32_t delay = GetFirstFrameDelay(aRequest);
1598 if (delay == 0) {
1599 mRequests.Insert(aRequest);
1600 } else {
1601 auto* const start = mStartTable.GetOrInsertNew(delay);
1602 start->mEntries.Insert(aRequest);
1603 }
1604
1605 EnsureTimerStarted();
1606
1607 if (profiler_thread_is_being_profiled_for_markers()) {
1608 nsCOMPtr<nsIURI> uri = aRequest->GetURI();
1609
1610 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)
1611 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)
1612 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)
1613 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)
1614 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)
;
1615 }
1616}
1617
1618void nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest) {
1619 // Try to remove from both places, just in case.
1620 bool removed = mRequests.EnsureRemoved(aRequest);
1621 uint32_t delay = GetFirstFrameDelay(aRequest);
1622 if (delay != 0) {
1623 ImageStartData* start = mStartTable.Get(delay);
1624 if (start) {
1625 removed = removed | start->mEntries.EnsureRemoved(aRequest);
1626 }
1627 }
1628
1629 if (removed && profiler_thread_is_being_profiled_for_markers()) {
1630 nsCOMPtr<nsIURI> uri = aRequest->GetURI();
1631
1632 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)
1633 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)
1634 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)
1635 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)
1636 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)
;
1637 }
1638}
1639
1640void nsRefreshDriver::RegisterCompositionPayload(
1641 const mozilla::layers::CompositionPayload& aPayload) {
1642 mCompositionPayloads.AppendElement(aPayload);
1643}
1644
1645void nsRefreshDriver::AddForceNotifyContentfulPaintPresContext(
1646 nsPresContext* aPresContext) {
1647 mForceNotifyContentfulPaintPresContexts.AppendElement(aPresContext);
1648}
1649
1650void nsRefreshDriver::FlushForceNotifyContentfulPaintPresContext() {
1651 while (!mForceNotifyContentfulPaintPresContexts.IsEmpty()) {
1652 WeakPtr<nsPresContext> presContext =
1653 mForceNotifyContentfulPaintPresContexts.PopLastElement();
1654 if (presContext) {
1655 presContext->NotifyContentfulPaint();
1656 }
1657 }
1658}
1659
1660void nsRefreshDriver::RunDelayedEventsSoon() {
1661 // Place entries for delayed events into their corresponding normal list,
1662 // and schedule a refresh. When these delayed events run, if their document
1663 // still has events suppressed then they will be readded to the delayed
1664 // events list.
1665
1666 mScrollEvents.AppendElements(mDelayedScrollEvents);
1667 mDelayedScrollEvents.Clear();
1668
1669 mScrollEndEvents.AppendElements(mDelayedScrollEvents);
1670 mDelayedScrollEndEvents.Clear();
1671
1672 mResizeEventFlushObservers.AppendElements(mDelayedResizeEventFlushObservers);
1673 mDelayedResizeEventFlushObservers.Clear();
1674
1675 EnsureTimerStarted();
1676}
1677
1678bool nsRefreshDriver::CanDoCatchUpTick() {
1679 if (mTestControllingRefreshes || !mActiveTimer) {
1680 return false;
1681 }
1682
1683 // If we've already ticked for the current timer refresh (or more recently
1684 // than that), then we don't need to do any catching up.
1685 if (mMostRecentRefresh >= mActiveTimer->MostRecentRefresh()) {
1686 return false;
1687 }
1688
1689 if (mActiveTimer->IsBlocked()) {
1690 return false;
1691 }
1692
1693 if (mTickVsyncTime.IsNull()) {
1694 // Don't try to run a catch-up tick before there has been at least one
1695 // normal tick. The catch-up tick could negatively affect page load
1696 // performance.
1697 return false;
1698 }
1699
1700 if (mPresContext && mPresContext->Document()->GetReadyStateEnum() <
1701 Document::READYSTATE_COMPLETE) {
1702 // Don't try to run a catch-up tick before the page has finished loading.
1703 // The catch-up tick could negatively affect page load performance.
1704 return false;
1705 }
1706
1707 return true;
1708}
1709
1710bool nsRefreshDriver::CanDoExtraTick() {
1711 // Only allow one extra tick per normal vsync tick.
1712 if (mAttemptedExtraTickSinceLastVsync) {
1713 return false;
1714 }
1715
1716 // If we don't have a timer, or we didn't tick on the timer's
1717 // refresh then we can't do an 'extra' tick (but we may still
1718 // do a catch up tick).
1719 if (!mActiveTimer ||
1720 mActiveTimer->MostRecentRefresh() != mMostRecentRefresh) {
1721 return false;
1722 }
1723
1724 // Grab the current timestamp before checking the tick hint to be sure
1725 // sure that it's equal or smaller than the value used within checking
1726 // the tick hint.
1727 TimeStamp now = TimeStamp::Now();
1728 Maybe<TimeStamp> nextTick = mActiveTimer->GetNextTickHint();
1729 int32_t minimumRequiredTime = StaticPrefs::layout_extra_tick_minimum_ms();
1730 // If there's less than 4 milliseconds until the next tick, it's probably
1731 // not worth trying to catch up.
1732 if (minimumRequiredTime < 0 || !nextTick ||
1733 (*nextTick - now) < TimeDuration::FromMilliseconds(minimumRequiredTime)) {
1734 return false;
1735 }
1736
1737 return true;
1738}
1739
1740void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
1741 // FIXME: Bug 1346065: We should also assert the case where we have no
1742 // stylo-threads.
1743 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"
, 1745); 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) = 1745; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
2
Assuming the condition is true
3
Taking false branch
4
Loop condition is false. Exiting loop
1744 "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"
, 1745); 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) = 1745; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
1745 "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"
, 1745); 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) = 1745; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
1746
1747 if (mTestControllingRefreshes
4.1
Field 'mTestControllingRefreshes' is false
) return;
5
Taking false branch
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) mActiveTimer->RemoveRefreshDriver(this);
1803 mActiveTimer = newTimer;
1804 mActiveTimer->AddRefreshDriver(this);
1805
1806 if (!mHasStartedTimerAtLeastOnce) {
1807 mHasStartedTimerAtLeastOnce = true;
1808 if (profiler_thread_is_being_profiled_for_markers()) {
1809 nsCString text = "initial timer start "_ns;
1810 if (mPresContext->Document()->GetDocumentURI()) {
1811 text.Append(nsContentUtils::TruncatedURLForDisplay(
1812 mPresContext->Document()->GetDocumentURI()));
1813 }
1814
1815 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)
1816 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)
1817 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)
1818 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)
;
1819 }
1820 }
1821
1822 // If the timer has ticked since we last ticked, consider doing a 'catch-up'
1823 // tick immediately.
1824 if (CanDoCatchUpTick()) {
1825 RefPtr<nsRefreshDriver> self = this;
1826 NS_DispatchToCurrentThreadQueue(
1827 NS_NewRunnableFunction(
1828 "RefreshDriver::EnsureTimerStarted::catch-up",
1829 [self]() -> void {
1830 // Re-check if we can still do a catch-up, in case anything
1831 // changed while the runnable was pending.
1832 if (self->CanDoCatchUpTick()) {
1833 LOG("[%p] Doing catch up tick", self.get());
1834 self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
1835 self->mActiveTimer->MostRecentRefresh());
1836 }
1837 }),
1838 EventQueuePriority::Vsync);
1839 }
1840 }
1841
1842 // When switching from an inactive timer to an active timer, the root
1843 // refresh driver is skipped due to being set to the content refresh
1844 // driver's timestamp. In case of EnsureTimerStarted is called from
1845 // ScheduleViewManagerFlush, we should avoid this behavior to flush
1846 // a paint in the same tick on the root refresh driver.
1847 if (aFlags & eNeverAdjustTimer) {
14
Taking false branch
1848 return;
1849 }
1850
1851 // Since the different timers are sampled at different rates, when switching
1852 // timers, the most recent refresh of the new timer may be *before* the
1853 // most recent refresh of the old timer.
1854 // If we are restoring the refresh driver from test control, the time is
1855 // expected to go backwards (see bug 1043078), otherwise we just keep the most
1856 // recent tick of this driver (which may be older than the most recent tick of
1857 // the timer).
1858 if (!(aFlags & eAllowTimeToGoBackwards)) {
15
Taking false branch
1859 return;
1860 }
1861
1862 if (mMostRecentRefresh != mActiveTimer->MostRecentRefresh()) {
16
Called C++ object pointer is null
1863 mMostRecentRefresh = mActiveTimer->MostRecentRefresh();
1864 }
1865}
1866
1867void nsRefreshDriver::StopTimer() {
1868 if (!mActiveTimer) return;
1869
1870 mActiveTimer->RemoveRefreshDriver(this);
1871 mActiveTimer = nullptr;
1872 mRefreshTimerStartedCause = nullptr;
1873}
1874
1875uint32_t nsRefreshDriver::ObserverCount() const {
1876 uint32_t sum = 0;
1877 for (const ObserverArray& array : mObservers) {
1878 sum += array.Length();
1879 }
1880
1881 // Even while throttled, we need to process layout and style changes. Style
1882 // changes can trigger transitions which fire events when they complete, and
1883 // layout changes can affect media queries on child documents, triggering
1884 // style changes, etc.
1885 sum += mAnimationEventFlushObservers.Length();
1886 sum += mResizeEventFlushObservers.Length();
1887 sum += mStyleFlushObservers.Length();
1888 sum += mPendingFullscreenEvents.Length();
1889 sum += mViewManagerFlushIsPending;
1890 sum += mEarlyRunners.Length();
1891 sum += mAutoFocusFlushDocuments.Length();
1892 return sum;
1893}
1894
1895bool nsRefreshDriver::HasObservers() const {
1896 for (const ObserverArray& array : mObservers) {
1897 if (!array.IsEmpty()) {
1898 return true;
1899 }
1900 }
1901
1902 return (mViewManagerFlushIsPending && !mThrottled) ||
1903 !mStyleFlushObservers.IsEmpty() ||
1904 !mAnimationEventFlushObservers.IsEmpty() ||
1905 !mResizeEventFlushObservers.IsEmpty() ||
1906 !mPendingFullscreenEvents.IsEmpty() ||
1907 !mAutoFocusFlushDocuments.IsEmpty() || !mEarlyRunners.IsEmpty();
1908}
1909
1910void nsRefreshDriver::AppendObserverDescriptionsToString(
1911 nsACString& aStr) const {
1912 for (const ObserverArray& array : mObservers) {
1913 for (const auto& observer : array.EndLimitedRange()) {
1914 aStr.AppendPrintf("%s [%s], ", observer.mDescription,
1915 kFlushTypeNames[observer.mFlushType]);
1916 }
1917 }
1918 if (mViewManagerFlushIsPending && !mThrottled) {
1919 aStr.AppendLiteral("View manager flush pending, ");
1920 }
1921 if (!mAnimationEventFlushObservers.IsEmpty()) {
1922 aStr.AppendPrintf("%zux Animation event flush observer, ",
1923 mAnimationEventFlushObservers.Length());
1924 }
1925 if (!mResizeEventFlushObservers.IsEmpty()) {
1926 aStr.AppendPrintf("%zux Resize event flush observer, ",
1927 mResizeEventFlushObservers.Length());
1928 }
1929 if (!mStyleFlushObservers.IsEmpty()) {
1930 aStr.AppendPrintf("%zux Style flush observer, ",
1931 mStyleFlushObservers.Length());
1932 }
1933 if (!mPendingFullscreenEvents.IsEmpty()) {
1934 aStr.AppendPrintf("%zux Pending fullscreen event, ",
1935 mPendingFullscreenEvents.Length());
1936 }
1937 if (!mAutoFocusFlushDocuments.IsEmpty()) {
1938 aStr.AppendPrintf("%zux AutoFocus flush doc, ",
1939 mAutoFocusFlushDocuments.Length());
1940 }
1941 if (!mEarlyRunners.IsEmpty()) {
1942 aStr.AppendPrintf("%zux Early runner, ", mEarlyRunners.Length());
1943 }
1944 // Remove last ", "
1945 aStr.Truncate(aStr.Length() - 2);
1946}
1947
1948bool nsRefreshDriver::HasImageRequests() const {
1949 for (const auto& data : mStartTable.Values()) {
1950 if (!data->mEntries.IsEmpty()) {
1951 return true;
1952 }
1953 }
1954
1955 return !mRequests.IsEmpty();
1956}
1957
1958auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons {
1959 TickReasons reasons = TickReasons::eNone;
1960 if (HasObservers()) {
1961 reasons |= TickReasons::eHasObservers;
1962 }
1963 if (HasImageRequests() && !mThrottled) {
1964 reasons |= TickReasons::eHasImageRequests;
1965 }
1966 if (mNeedToUpdateResizeObservers) {
1967 reasons |= TickReasons::eNeedsToNotifyResizeObservers;
1968 }
1969 if (mNeedToUpdateViewTransitions) {
1970 reasons |= TickReasons::eNeedsToUpdateViewTransitions;
1971 }
1972 if (mNeedToUpdateAnimations) {
1973 reasons |= TickReasons::eNeedsToUpdateAnimations;
1974 }
1975 if (mNeedToUpdateIntersectionObservations) {
1976 reasons |= TickReasons::eNeedsToUpdateIntersectionObservations;
1977 }
1978 if (mMightNeedMediaQueryListenerUpdate) {
1979 reasons |= TickReasons::eHasPendingMediaQueryListeners;
1980 }
1981 if (mNeedToUpdateContentRelevancy) {
1982 reasons |= TickReasons::eNeedsToUpdateContentRelevancy;
1983 }
1984 if (mNeedToRunFrameRequestCallbacks) {
1985 reasons |= TickReasons::eNeedsToRunFrameRequestCallbacks;
1986 }
1987 if (!mVisualViewportResizeEvents.IsEmpty()) {
1988 reasons |= TickReasons::eHasVisualViewportResizeEvents;
1989 }
1990 if (!mScrollEvents.IsEmpty() || !mScrollEndEvents.IsEmpty()) {
1991 reasons |= TickReasons::eHasScrollEvents;
1992 }
1993 if (!mVisualViewportScrollEvents.IsEmpty()) {
1994 reasons |= TickReasons::eHasVisualViewportScrollEvents;
1995 }
1996 if (mPresContext && mPresContext->IsRoot() &&
1997 mPresContext->NeedsMoreTicksForUserInput()) {
1998 reasons |= TickReasons::eRootNeedsMoreTicksForUserInput;
1999 }
2000 return reasons;
2001}
2002
2003void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons,
2004 nsACString& aStr) const {
2005 if (aReasons == TickReasons::eNone) {
2006 aStr.AppendLiteral(" <none>");
2007 return;
2008 }
2009
2010 if (aReasons & TickReasons::eHasObservers) {
2011 aStr.AppendLiteral(" HasObservers (");
2012 AppendObserverDescriptionsToString(aStr);
2013 aStr.AppendLiteral(")");
2014 }
2015 if (aReasons & TickReasons::eHasImageRequests) {
2016 aStr.AppendLiteral(" HasImageAnimations");
2017 }
2018 if (aReasons & TickReasons::eNeedsToNotifyResizeObservers) {
2019 aStr.AppendLiteral(" NeedsToNotifyResizeObservers");
2020 }
2021 if (aReasons & TickReasons::eNeedsToUpdateViewTransitions) {
2022 aStr.AppendLiteral(" NeedsToUpdateViewTransitions");
2023 }
2024 if (aReasons & TickReasons::eNeedsToUpdateAnimations) {
2025 aStr.AppendLiteral(" NeedsToUpdateAnimations");
2026 }
2027 if (aReasons & TickReasons::eNeedsToUpdateIntersectionObservations) {
2028 aStr.AppendLiteral(" NeedsToUpdateIntersectionObservations");
2029 }
2030 if (aReasons & TickReasons::eHasPendingMediaQueryListeners) {
2031 aStr.AppendLiteral(" HasPendingMediaQueryListeners");
2032 }
2033 if (aReasons & TickReasons::eNeedsToUpdateContentRelevancy) {
2034 aStr.AppendLiteral(" NeedsToUpdateContentRelevancy");
2035 }
2036 if (aReasons & TickReasons::eNeedsToRunFrameRequestCallbacks) {
2037 aStr.AppendLiteral(" NeedsToRunFrameRequestCallbacks");
2038 }
2039 if (aReasons & TickReasons::eHasVisualViewportResizeEvents) {
2040 aStr.AppendLiteral(" HasVisualViewportResizeEvents");
2041 }
2042 if (aReasons & TickReasons::eHasScrollEvents) {
2043 aStr.AppendLiteral(" HasScrollEvents");
2044 }
2045 if (aReasons & TickReasons::eHasVisualViewportScrollEvents) {
2046 aStr.AppendLiteral(" HasVisualViewportScrollEvents");
2047 }
2048 if (aReasons & TickReasons::eRootNeedsMoreTicksForUserInput) {
2049 aStr.AppendLiteral(" RootNeedsMoreTicksForUserInput");
2050 }
2051}
2052
2053bool nsRefreshDriver::
2054 ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint() {
2055 // On top level content pages keep the timer running initially so that we
2056 // paint the page soon enough.
2057 if (mThrottled || mTestControllingRefreshes || !XRE_IsContentProcess() ||
2058 !mPresContext->Document()->IsTopLevelContentDocument() ||
2059 mPresContext->Document()->IsInitialDocument() ||
2060 gfxPlatform::IsInLayoutAsapMode() ||
2061 mPresContext->HadFirstContentfulPaint() ||
2062 mPresContext->Document()->GetReadyStateEnum() ==
2063 Document::READYSTATE_COMPLETE) {
2064 return false;
2065 }
2066 if (mBeforeFirstContentfulPaintTimerRunningLimit.IsNull()) {
2067 // Don't let the timer to run forever, so limit to 4s for now.
2068 mBeforeFirstContentfulPaintTimerRunningLimit =
2069 TimeStamp::Now() + TimeDuration::FromSeconds(4.0f);
2070 }
2071
2072 return TimeStamp::Now() <= mBeforeFirstContentfulPaintTimerRunningLimit;
2073}
2074
2075bool nsRefreshDriver::ShouldKeepTimerRunningAfterPageLoad() {
2076 if (mHasExceededAfterLoadTickPeriod ||
2077 !StaticPrefs::layout_keep_ticking_after_load_ms() || mThrottled ||
2078 mTestControllingRefreshes || !XRE_IsContentProcess() ||
2079 !mPresContext->Document()->IsTopLevelContentDocument() ||
2080 TaskController::Get()->PendingMainthreadTaskCountIncludingSuspended() ==
2081 0 ||
2082 gfxPlatform::IsInLayoutAsapMode()) {
2083 // Make the next check faster.
2084 mHasExceededAfterLoadTickPeriod = true;
2085 return false;
2086 }
2087
2088 nsPIDOMWindowInner* innerWindow = mPresContext->Document()->GetInnerWindow();
2089 if (!innerWindow) {
2090 return false;
2091 }
2092 auto* perf =
2093 static_cast<PerformanceMainThread*>(innerWindow->GetPerformance());
2094 if (!perf) {
2095 return false;
2096 }
2097 nsDOMNavigationTiming* timing = perf->GetDOMTiming();
2098 if (!timing) {
2099 return false;
2100 }
2101 TimeStamp loadend = timing->LoadEventEnd();
2102 if (!loadend) {
2103 return false;
2104 }
2105 // Keep ticking after the page load for some time.
2106 const bool retval =
2107 (loadend + TimeDuration::FromMilliseconds(
2108 StaticPrefs::layout_keep_ticking_after_load_ms())) >
2109 TimeStamp::Now();
2110 if (!retval) {
2111 mHasExceededAfterLoadTickPeriod = true;
2112 }
2113 return retval;
2114}
2115
2116nsRefreshDriver::ObserverArray& nsRefreshDriver::ArrayFor(
2117 FlushType aFlushType) {
2118 switch (aFlushType) {
2119 case FlushType::Event:
2120 return mObservers[0];
2121 case FlushType::Style:
2122 return mObservers[1];
2123 case FlushType::Display:
2124 return mObservers[2];
2125 default:
2126 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"
, 2126); AnnotateMozCrashReason("MOZ_CRASH(" "We don't track refresh observers for this flush type"
")"); do { *((volatile int*)__null) = 2126; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2127 }
2128}
2129
2130/*
2131 * nsITimerCallback implementation
2132 */
2133
2134void nsRefreshDriver::DoTick() {
2135 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"
, 2135); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsFrozen()"
") (" "Why are we notified while frozen?" ")"); do { *((volatile
int*)__null) = 2135; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
2136 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"
, 2136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mPresContext"
") (" "Why are we notified after disconnection?" ")"); do { *
((volatile int*)__null) = 2136; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
2137 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"
, 2138); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!nsContentUtils::GetCurrentJSContext()"
") (" "Shouldn't have a JSContext on the stack" ")"); do { *
((volatile int*)__null) = 2138; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
2138 "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"
, 2138); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!nsContentUtils::GetCurrentJSContext()"
") (" "Shouldn't have a JSContext on the stack" ")"); do { *
((volatile int*)__null) = 2138; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
2139
2140 if (mTestControllingRefreshes) {
2141 Tick(VsyncId(), mMostRecentRefresh);
2142 } else {
2143 Tick(VsyncId(), TimeStamp::Now());
2144 }
2145}
2146
2147void nsRefreshDriver::ScheduleAutoFocusFlush(Document* aDocument) {
2148 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"
, 2148); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mAutoFocusFlushDocuments.Contains(aDocument)"
")"); do { *((volatile int*)__null) = 2148; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2149 mAutoFocusFlushDocuments.AppendElement(aDocument);
2150 EnsureTimerStarted();
2151}
2152
2153void nsRefreshDriver::FlushAutoFocusDocuments() {
2154 nsTArray<RefPtr<Document>> docs(std::move(mAutoFocusFlushDocuments));
2155
2156 for (const auto& doc : docs) {
2157 MOZ_KnownLive(doc)(doc)->FlushAutoFocusCandidates();
2158 }
2159}
2160
2161void nsRefreshDriver::DispatchResizeEvents() {
2162 AutoTArray<RefPtr<PresShell>, 16> observers;
2163 observers.AppendElements(mResizeEventFlushObservers);
2164 for (RefPtr<PresShell>& presShell : Reversed(observers)) {
2165 if (!mPresContext || !mPresContext->GetPresShell()) {
2166 break;
2167 }
2168 // Make sure to not process observers which might have been removed during
2169 // previous iterations.
2170 if (!mResizeEventFlushObservers.RemoveElement(presShell)) {
2171 continue;
2172 }
2173 // MOZ_KnownLive because 'observers' is guaranteed to keep it alive.
2174 //
2175 // Fixing https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 on its own
2176 // won't help here, because 'observers' is non-const and we have the
2177 // Reversed() going on too...
2178 MOZ_KnownLive(presShell)(presShell)->FireResizeEvent();
2179 }
2180}
2181
2182void nsRefreshDriver::FlushLayoutOnPendingDocsAndFixUpFocus() {
2183 AutoTArray<RefPtr<PresShell>, 16> observers;
2184 observers.AppendElements(mStyleFlushObservers);
2185 for (RefPtr<PresShell>& presShell : Reversed(observers)) {
2186 if (!mPresContext || !mPresContext->GetPresShell()) {
2187 break;
2188 }
2189 // Make sure to not process observers which might have been removed during
2190 // previous iterations.
2191 if (!mStyleFlushObservers.RemoveElement(presShell)) {
2192 continue;
2193 }
2194
2195 LogPresShellObserver::Run run(presShell, this);
2196 presShell->mWasLastReflowInterrupted = false;
2197 const ChangesToFlush ctf(FlushType::InterruptibleLayout, false);
2198 // MOZ_KnownLive because 'observers' is guaranteed to keep it alive.
2199 MOZ_KnownLive(presShell)(presShell)->FlushPendingNotifications(ctf);
2200 const bool fixedUpFocus = MOZ_KnownLive(presShell)(presShell)->FixUpFocus();
2201 if (fixedUpFocus) {
2202 MOZ_KnownLive(presShell)(presShell)->FlushPendingNotifications(ctf);
2203 }
2204 // This is a bit subtle: We intentionally mark the pres shell as not
2205 // observing style flushes here, rather than above the flush, so that
2206 // reflows scheduled from the style flush, but processed by the (same)
2207 // layout flush, don't end up needlessly scheduling another tick.
2208 // Instead, we re-observe only if after a flush we still need a style /
2209 // layout flush / focus fix-up. These should generally never happen, but
2210 // the later can for example if you have focus shifts during the focus
2211 // fixup event listeners etc.
2212 presShell->mObservingStyleFlushes = false;
2213 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"
, 2213)
||
2214 NS_WARN_IF(presShell->NeedLayoutFlush())NS_warn_if_impl(presShell->NeedLayoutFlush(), "presShell->NeedLayoutFlush()"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp"
, 2214)
||
2215 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"
, 2215)
) {
2216 presShell->ObserveStyleFlushes();
2217 }
2218
2219 // Inform the FontFaceSet that we ticked, so that it can resolve its ready
2220 // promise if it needs to.
2221 presShell->NotifyFontFaceSetOnRefresh();
2222 mNeedToRecomputeVisibility = true;
2223 }
2224}
2225
2226void nsRefreshDriver::MaybeIncreaseMeasuredTicksSinceLoading() {
2227 if (mPresContext && mPresContext->IsRoot()) {
2228 mPresContext->MaybeIncreaseMeasuredTicksSinceLoading();
2229 }
2230}
2231
2232void nsRefreshDriver::CancelFlushAutoFocus(Document* aDocument) {
2233 mAutoFocusFlushDocuments.RemoveElement(aDocument);
2234}
2235
2236// https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps
2237void nsRefreshDriver::RunFullscreenSteps() {
2238 // Swap out the current pending events
2239 nsTArray<UniquePtr<PendingFullscreenEvent>> pendings(
2240 std::move(mPendingFullscreenEvents));
2241 for (UniquePtr<PendingFullscreenEvent>& event : pendings) {
2242 event->Dispatch();
2243 }
2244}
2245
2246void nsRefreshDriver::PerformPendingViewTransitionOperations() {
2247 if (!mNeedToUpdateViewTransitions) {
2248 return;
2249 }
2250 mNeedToUpdateViewTransitions = false;
2251 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("View Transitions", LAYOUT)mozilla::AutoProfilerLabel raiiObject2251( "View Transitions"
, nullptr, JS::ProfilingCategoryPair::LAYOUT, uint32_t(js::ProfilingStackFrame
::Flags::RELEVANT_FOR_JS))
;
2252 mPresContext->Document()->PerformPendingViewTransitionOperations();
2253}
2254
2255void nsRefreshDriver::UpdateIntersectionObservations(TimeStamp aNowTime) {
2256 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Compute intersections", LAYOUT)mozilla::AutoProfilerLabel raiiObject2256( "Compute intersections"
, nullptr, JS::ProfilingCategoryPair::LAYOUT, uint32_t(js::ProfilingStackFrame
::Flags::RELEVANT_FOR_JS))
;
2257 mPresContext->Document()->UpdateIntersections(aNowTime);
2258 mNeedToUpdateIntersectionObservations = false;
2259}
2260
2261void nsRefreshDriver::UpdateRemoteFrameEffects() {
2262 mPresContext->Document()->UpdateRemoteFrameEffects();
2263}
2264
2265void nsRefreshDriver::UpdateRelevancyOfContentVisibilityAutoFrames() {
2266 if (!mNeedToUpdateContentRelevancy) {
2267 return;
2268 }
2269
2270 if (RefPtr<PresShell> topLevelPresShell = mPresContext->GetPresShell()) {
2271 topLevelPresShell->UpdateRelevancyOfContentVisibilityAutoFrames();
2272 }
2273
2274 mPresContext->Document()->EnumerateSubDocuments([](Document& aSubDoc) {
2275 if (PresShell* presShell = aSubDoc.GetPresShell()) {
2276 presShell->UpdateRelevancyOfContentVisibilityAutoFrames();
2277 }
2278 return CallState::Continue;
2279 });
2280
2281 mNeedToUpdateContentRelevancy = false;
2282}
2283
2284void nsRefreshDriver::DetermineProximityToViewportAndNotifyResizeObservers() {
2285 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Update the rendering: step 14", LAYOUT)mozilla::AutoProfilerLabel raiiObject2285( "Update the rendering: step 14"
, nullptr, JS::ProfilingCategoryPair::LAYOUT, uint32_t(js::ProfilingStackFrame
::Flags::RELEVANT_FOR_JS))
;
2286 // NotifyResizeObservers might re-schedule us for next tick.
2287 mNeedToUpdateResizeObservers = false;
2288
2289 if (MOZ_UNLIKELY(!mPresContext)(__builtin_expect(!!(!mPresContext), 0))) {
2290 return;
2291 }
2292
2293 auto ShouldCollect = [](const Document* aDocument) {
2294 PresShell* ps = aDocument->GetPresShell();
2295 if (!ps || !ps->DidInitialize()) {
2296 // If there's no shell or it didn't initialize, then we'll run this code
2297 // when the pres shell does the initial reflow.
2298 return false;
2299 }
2300 return ps->HasContentVisibilityAutoFrames() ||
2301 aDocument->HasResizeObservers() ||
2302 aDocument->HasElementsWithLastRememberedSize();
2303 };
2304
2305 AutoTArray<RefPtr<Document>, 32> documents;
2306 if (ShouldCollect(mPresContext->Document())) {
2307 documents.AppendElement(mPresContext->Document());
2308 }
2309 mPresContext->Document()->CollectDescendantDocuments(documents,
2310 ShouldCollect);
2311
2312 for (const RefPtr<Document>& doc : documents) {
2313 MOZ_KnownLive(doc)(doc)->DetermineProximityToViewportAndNotifyResizeObservers();
2314 }
2315}
2316
2317static CallState UpdateAndReduceAnimations(Document& aDocument) {
2318 for (DocumentTimeline* timeline : aDocument.Timelines()) {
2319 timeline->WillRefresh();
2320 }
2321
2322 if (nsPresContext* pc = aDocument.GetPresContext()) {
2323 if (pc->EffectCompositor()->NeedsReducing()) {
2324 pc->EffectCompositor()->ReduceAnimations();
2325 }
2326 }
2327 aDocument.EnumerateSubDocuments(UpdateAndReduceAnimations);
2328 return CallState::Continue;
2329}
2330
2331void nsRefreshDriver::UpdateAnimationsAndSendEvents() {
2332 // TODO(emilio): Can we early-return here if mNeedToUpdateAnimations is
2333 // already false?
2334 mNeedToUpdateAnimations = false;
2335 if (!mPresContext) {
2336 return;
2337 }
2338
2339 {
2340 // Animation updates may queue Promise resolution microtasks. We shouldn't
2341 // run these, however, until we have fully updated the animation state. As
2342 // per the "update animations and send events" procedure[1], we should
2343 // remove replaced animations and then run these microtasks before
2344 // dispatching the corresponding animation events.
2345 //
2346 // [1]:
2347 // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
2348 nsAutoMicroTask mt;
2349 UpdateAndReduceAnimations(*mPresContext->Document());
2350 }
2351
2352 // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
2353 // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
2354 // during processing the previous dispatcher.
2355 AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
2356 dispatchers.AppendElements(mAnimationEventFlushObservers);
2357 mAnimationEventFlushObservers.Clear();
2358
2359 for (auto& dispatcher : dispatchers) {
2360 dispatcher->DispatchEvents();
2361 }
2362}
2363
2364void nsRefreshDriver::RunVideoFrameCallbacks(
2365 const nsTArray<RefPtr<Document>>& aDocs, TimeStamp aNowTime) {
2366 // For each fully active Document in docs, for each associated video element
2367 // for that Document, run the video frame request callbacks passing now as the
2368 // timestamp.
2369 Maybe<TimeStamp> nextTickHint;
2370 for (Document* doc : aDocs) {
2371 nsTArray<RefPtr<HTMLVideoElement>> videoElms;
2372 doc->TakeVideoFrameRequestCallbacks(videoElms);
2373 if (videoElms.IsEmpty()) {
2374 continue;
2375 }
2376
2377 DOMHighResTimeStamp timeStamp = 0;
2378 DOMHighResTimeStamp nextTickTimeStamp = 0;
2379 if (auto* innerWindow = doc->GetInnerWindow()) {
2380 if (Performance* perf = innerWindow->GetPerformance()) {
2381 if (!nextTickHint) {
2382 nextTickHint = GetNextTickHint();
2383 }
2384 timeStamp = perf->TimeStampToDOMHighResForRendering(aNowTime);
2385 nextTickTimeStamp =
2386 nextTickHint
2387 ? perf->TimeStampToDOMHighResForRendering(*nextTickHint)
2388 : timeStamp;
2389 }
2390 // else window is partially torn down already
2391 }
2392
2393 AUTO_PROFILER_TRACING_MARKER_INNERWINDOWID(AutoProfilerTracing raiiObject2394("Paint", "requestVideoFrame callbacks"
, geckoprofiler::category::GRAPHICS, mozilla::Some(doc->InnerWindowID
()))
2394 "Paint", "requestVideoFrame callbacks", GRAPHICS, doc->InnerWindowID())AutoProfilerTracing raiiObject2394("Paint", "requestVideoFrame callbacks"
, geckoprofiler::category::GRAPHICS, mozilla::Some(doc->InnerWindowID
()))
;
2395 for (const auto& videoElm : videoElms) {
2396 nsTArray<VideoFrameRequest> callbacks;
2397 VideoFrameCallbackMetadata metadata;
2398
2399 // Presentation time is our best estimate of when the video frame was
2400 // submitted for compositing. Given that we decode frames in advance,
2401 // this can be most closely estimated as the vsync time (aNowTime), as
2402 // that is when the compositor samples the ImageHost to get the next
2403 // frame to present.
2404 metadata.mPresentationTime = timeStamp;
2405
2406 // Expected display time is our best estimate of when the video frame we
2407 // are submitting for compositing this cycle is shown to the user's eye.
2408 // This will generally be when the next vsync triggers, assuming we do
2409 // not fall behind on compositing.
2410 metadata.mExpectedDisplayTime = nextTickTimeStamp;
2411
2412 // TakeVideoFrameRequestCallbacks is responsible for populating the rest
2413 // of the metadata fields. If it is not ready, or there has been no
2414 // change, it will not populate metadata nor yield any callbacks.
2415 videoElm->TakeVideoFrameRequestCallbacks(aNowTime, nextTickHint, metadata,
2416 callbacks);
2417
2418 for (auto& callback : callbacks) {
2419 if (videoElm->IsVideoFrameCallbackCancelled(callback.mHandle)) {
2420 continue;
2421 }
2422
2423 // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
2424 // keeps callback alive and the mCallback strong reference can't be
2425 // mutated by the call.
2426 LogVideoFrameRequestCallback::Run run(callback.mCallback);
2427 MOZ_KnownLive(callback.mCallback)(callback.mCallback)->Call(timeStamp, metadata);
2428 }
2429 }
2430 }
2431}
2432
2433void nsRefreshDriver::RunFrameRequestCallbacks(
2434 const nsTArray<RefPtr<Document>>& aDocs, TimeStamp aNowTime) {
2435 for (Document* doc : aDocs) {
2436 nsTArray<FrameRequest> callbacks;
2437 doc->TakeFrameRequestCallbacks(callbacks);
2438 if (callbacks.IsEmpty()) {
2439 continue;
2440 }
2441
2442 DOMHighResTimeStamp timeStamp = 0;
2443 RefPtr innerWindow = nsGlobalWindowInner::Cast(doc->GetInnerWindow());
2444 if (innerWindow) {
2445 if (Performance* perf = innerWindow->GetPerformance()) {
2446 timeStamp = perf->TimeStampToDOMHighResForRendering(aNowTime);
2447 }
2448 // else window is partially torn down already
2449 }
2450
2451 AUTO_PROFILER_TRACING_MARKER_INNERWINDOWID(AutoProfilerTracing raiiObject2453("Paint", "requestAnimationFrame callbacks"
, geckoprofiler::category::GRAPHICS, mozilla::Some(doc->InnerWindowID
()))
2452 "Paint", "requestAnimationFrame callbacks", GRAPHICS,AutoProfilerTracing raiiObject2453("Paint", "requestAnimationFrame callbacks"
, geckoprofiler::category::GRAPHICS, mozilla::Some(doc->InnerWindowID
()))
2453 doc->InnerWindowID())AutoProfilerTracing raiiObject2453("Paint", "requestAnimationFrame callbacks"
, geckoprofiler::category::GRAPHICS, mozilla::Some(doc->InnerWindowID
()))
;
2454 const TimeStamp startTime = TimeStamp::Now();
2455 for (const auto& callback : callbacks) {
2456 if (doc->IsCanceledFrameRequestCallback(callback.mHandle)) {
2457 continue;
2458 }
2459
2460 CallbackDebuggerNotificationGuard guard(
2461 innerWindow, DebuggerNotificationType::RequestAnimationFrameCallback);
2462
2463 // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
2464 // keeps callback alive and the mCallback strong reference can't be
2465 // mutated by the call.
2466 LogFrameRequestCallback::Run run(callback.mCallback);
2467 MOZ_KnownLive(callback.mCallback)(callback.mCallback)->Call(timeStamp);
2468 }
2469
2470 if (doc->GetReadyStateEnum() == Document::READYSTATE_COMPLETE) {
2471 glean::performance_responsiveness::req_anim_frame_callback
2472 .AccumulateRawDuration(TimeStamp::Now() - startTime);
2473 } else {
2474 glean::performance_pageload::req_anim_frame_callback
2475 .AccumulateRawDuration(TimeStamp::Now() - startTime);
2476 }
2477 }
2478}
2479
2480void nsRefreshDriver::RunVideoAndFrameRequestCallbacks(TimeStamp aNowTime) {
2481 if (!mNeedToRunFrameRequestCallbacks) {
2482 return;
2483 }
2484 mNeedToRunFrameRequestCallbacks = false;
2485 const bool tickThrottledFrameRequests = [&] {
2486 if (mThrottled) {
2487 // We always tick throttled frame requests if the entire refresh driver is
2488 // throttled, because in that situation throttled frame requests tick at
2489 // the same frequency as non-throttled frame requests.
2490 return true;
2491 }
2492 if (aNowTime >= mNextThrottledFrameRequestTick) {
2493 mNextThrottledFrameRequestTick =
2494 aNowTime + mThrottledFrameRequestInterval;
2495 return true;
2496 }
2497 return false;
2498 }();
2499
2500 if (NS_WARN_IF(!mPresContext)NS_warn_if_impl(!mPresContext, "!mPresContext", "/var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp"
, 2500)
) {
2501 return;
2502 }
2503 // Grab all of our documents that can fire frame request callbacks up front.
2504 AutoTArray<RefPtr<Document>, 8> docs;
2505 auto ShouldCollect = [](const Document* aDoc) {
2506 // TODO(emilio): Consider removing HasFrameRequestCallbacks() to deal with
2507 // callbacks posted from other documents more per spec?
2508 //
2509 // If we do that we also need to tweak the throttling code to not set
2510 // mNeedToRunFrameRequestCallbacks unnecessarily... Check what other engines
2511 // do too.
2512 return aDoc->HasFrameRequestCallbacks() &&
2513 aDoc->ShouldFireFrameRequestCallbacks();
2514 };
2515 if (ShouldCollect(mPresContext->Document())) {
2516 docs.AppendElement(mPresContext->Document());
2517 }
2518 mPresContext->Document()->CollectDescendantDocuments(docs, ShouldCollect);
2519 // Skip throttled docs if it's not time to un-throttle them yet.
2520 if (!tickThrottledFrameRequests) {
2521 const size_t sizeBefore = docs.Length();
2522 docs.RemoveElementsBy(
2523 [](Document* aDoc) { return aDoc->ShouldThrottleFrameRequests(); });
2524 if (sizeBefore != docs.Length()) {
2525 // FIXME(emilio): It's a bit subtle to just set this to true here, but
2526 // matches pre-existing behavior for throttled docs. It seems at least we
2527 // should EnsureTimerStarted too? But that kinda defeats the throttling, a
2528 // little bit? For now, preserve behavior.
2529 mNeedToRunFrameRequestCallbacks = true;
2530 }
2531 }
2532
2533 if (docs.IsEmpty()) {
2534 return;
2535 }
2536
2537 RunVideoFrameCallbacks(docs, aNowTime);
2538 RunFrameRequestCallbacks(docs, aNowTime);
2539}
2540
2541static StaticAutoPtr<AutoTArray<RefPtr<Task>, 8>> sPendingIdleTasks;
2542
2543void nsRefreshDriver::DispatchIdleTaskAfterTickUnlessExists(Task* aTask) {
2544 if (!sPendingIdleTasks) {
2545 sPendingIdleTasks = new AutoTArray<RefPtr<Task>, 8>();
2546 } else {
2547 if (sPendingIdleTasks->Contains(aTask)) {
2548 return;
2549 }
2550 }
2551
2552 sPendingIdleTasks->AppendElement(aTask);
2553}
2554
2555void nsRefreshDriver::CancelIdleTask(Task* aTask) {
2556 if (!sPendingIdleTasks) {
2557 return;
2558 }
2559
2560 sPendingIdleTasks->RemoveElement(aTask);
2561
2562 if (sPendingIdleTasks->IsEmpty()) {
2563 sPendingIdleTasks = nullptr;
2564 }
2565}
2566
2567bool nsRefreshDriver::TickObserverArray(uint32_t aIdx, TimeStamp aNowTime) {
2568 MOZ_ASSERT(aIdx < ArrayLength(mObservers))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aIdx < ArrayLength(mObservers))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aIdx < ArrayLength(mObservers
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aIdx < ArrayLength(mObservers)", "/var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp"
, 2568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aIdx < ArrayLength(mObservers)"
")"); do { *((volatile int*)__null) = 2568; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2569 for (RefPtr<nsARefreshObserver> obs : mObservers[aIdx].EndLimitedRange()) {
2570 obs->WillRefresh(aNowTime);
2571
2572 if (!mPresContext || !mPresContext->GetPresShell()) {
2573 return false;
2574 }
2575 }
2576 return true;
2577}
2578
2579void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
2580 IsExtraTick aIsExtraTick /* = No */) {
2581 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"
, 2582); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!nsContentUtils::GetCurrentJSContext()"
") (" "Shouldn't have a JSContext on the stack" ")"); do { *
((volatile int*)__null) = 2582; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
2582 "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"
, 2582); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!nsContentUtils::GetCurrentJSContext()"
") (" "Shouldn't have a JSContext on the stack" ")"); do { *
((volatile int*)__null) = 2582; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
2583
2584 // We're either frozen or we were disconnected (likely in the middle
2585 // of a tick iteration). Just do nothing here, since our
2586 // prescontext went away.
2587 if (IsFrozen() || !mPresContext) {
2588 return;
2589 }
2590
2591 // We can have a race condition where the vsync timestamp
2592 // is before the most recent refresh due to a forced refresh.
2593 // The underlying assumption is that the refresh driver tick can only
2594 // go forward in time, not backwards. To prevent the refresh
2595 // driver from going back in time, just skip this tick and
2596 // wait until the next tick.
2597 // If this is an 'extra' tick, then we expect it to be using the same
2598 // vsync id and timestamp as the original tick, so also allow those.
2599 if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes &&
2600 aIsExtraTick == IsExtraTick::No) {
2601 return;
2602 }
2603 auto cleanupInExtraTick = MakeScopeExit([&] { mInNormalTick = false; });
2604 mInNormalTick = aIsExtraTick != IsExtraTick::Yes;
2605
2606 bool isPresentingInVR = false;
2607#if defined(MOZ_WIDGET_ANDROID)
2608 isPresentingInVR = gfx::VRManagerChild::IsPresenting();
2609#endif // defined(MOZ_WIDGET_ANDROID)
2610
2611 if (!isPresentingInVR && IsWaitingForPaint(aNowTime)) {
2612 // In immersive VR mode, we do not get notifications when frames are
2613 // presented, so we do not wait for the compositor in that mode.
2614
2615 // We're currently suspended waiting for earlier Tick's to
2616 // be completed (on the Compositor). Mark that we missed the paint
2617 // and keep waiting.
2618 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)
2619 "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)
2620 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)
;
2621 return;
2622 }
2623
2624 const TimeStamp previousRefresh = mMostRecentRefresh;
2625 mMostRecentRefresh = aNowTime;
2626
2627 if (mRootRefresh) {
2628 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2629 mRootRefresh = nullptr;
2630 }
2631 mSkippedPaints = false;
2632
2633 RefPtr<PresShell> presShell = mPresContext->GetPresShell();
2634 if (!presShell) {
2635 StopTimer();
2636 return;
2637 }
2638
2639 TickReasons tickReasons = GetReasonsToTick();
2640 if (tickReasons == TickReasons::eNone) {
2641 // We no longer have any observers.
2642 // Discard composition payloads because there is no paint.
2643 mCompositionPayloads.Clear();
2644
2645 // We don't want to stop the timer when observers are initially
2646 // removed, because sometimes observers can be added and removed
2647 // often depending on what other things are going on and in that
2648 // situation we don't want to thrash our timer. So instead we
2649 // wait until we get a Notify() call when we have no observers
2650 // before stopping the timer.
2651 // On top level content pages keep the timer running initially so that we
2652 // paint the page soon enough.
2653 if (ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint()) {
2654 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)
2655 "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)
2656 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)
2657 "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)
;
2658 } else if (ShouldKeepTimerRunningAfterPageLoad()) {
2659 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)
2660 "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)
2661 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)
2662 "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)
;
2663 } else {
2664 StopTimer();
2665 }
2666 return;
2667 }
2668
2669 if (StaticPrefs::layout_skip_ticks_while_page_suspended()) {
2670 Document* doc = mPresContext->Document();
2671 nsPIDOMWindowInner* win = doc ? doc->GetInnerWindow() : nullptr;
2672 // Synchronous DOM operations mark the document being in such. Window's
2673 // suspend can be used also by external code. So we check here them both
2674 // in order to limit rAF skipping to only those synchronous DOM APIs which
2675 // also suspend window.
2676 if (win && win->IsSuspended() && doc->IsInSyncOperation()) {
2677 return;
2678 }
2679 }
2680
2681 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("RefreshDriver tick", LAYOUT)mozilla::AutoProfilerLabel raiiObject2681( "RefreshDriver tick"
, nullptr, JS::ProfilingCategoryPair::LAYOUT, uint32_t(js::ProfilingStackFrame
::Flags::RELEVANT_FOR_JS))
;
2682
2683 nsAutoCString profilerStr;
2684 if (profiler_thread_is_being_profiled_for_markers()) {
2685 profilerStr.AppendLiteral("Tick reasons:");
2686 AppendTickReasonsToString(tickReasons, profilerStr);
2687 }
2688 AUTO_PROFILER_MARKER_TEXT(AutoProfilerTextMarker raiiObject2693( "RefreshDriverTick", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerStack
::TakeBacktrace(std::move(mRefreshTimerStartedCause)), MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext))), profilerStr)
2689 "RefreshDriverTick", GRAPHICS,AutoProfilerTextMarker raiiObject2693( "RefreshDriverTick", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerStack
::TakeBacktrace(std::move(mRefreshTimerStartedCause)), MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext))), profilerStr)
2690 MarkerOptions(AutoProfilerTextMarker raiiObject2693( "RefreshDriverTick", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerStack
::TakeBacktrace(std::move(mRefreshTimerStartedCause)), MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext))), profilerStr)
2691 MarkerStack::TakeBacktrace(std::move(mRefreshTimerStartedCause)),AutoProfilerTextMarker raiiObject2693( "RefreshDriverTick", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerStack
::TakeBacktrace(std::move(mRefreshTimerStartedCause)), MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext))), profilerStr)
2692 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext))),AutoProfilerTextMarker raiiObject2693( "RefreshDriverTick", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerStack
::TakeBacktrace(std::move(mRefreshTimerStartedCause)), MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext))), profilerStr)
2693 profilerStr)AutoProfilerTextMarker raiiObject2693( "RefreshDriverTick", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerStack
::TakeBacktrace(std::move(mRefreshTimerStartedCause)), MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext))), profilerStr)
;
2694
2695 mResizeSuppressed = false;
2696
2697 bool oldInRefresh = mInRefresh;
2698 auto restoreInRefresh = MakeScopeExit([&] { mInRefresh = oldInRefresh; });
2699 mInRefresh = true;
2700
2701 AutoRestore<TimeStamp> restoreTickStart(mTickStart);
2702 mTickStart = TimeStamp::Now();
2703 mTickVsyncId = aId;
2704 mTickVsyncTime = aNowTime;
2705
2706 gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
2707
2708 FlushForceNotifyContentfulPaintPresContext();
2709
2710 AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners = std::move(mEarlyRunners);
2711 for (auto& runner : earlyRunners) {
2712 runner->Run();
2713 // Early runners might destroy this pres context.
2714 if (!mPresContext || !mPresContext->GetPresShell()) {
2715 return StopTimer();
2716 }
2717 }
2718
2719 // Dispatch coalesced input events.
2720 if (!TickObserverArray(0, aNowTime)) {
2721 return StopTimer();
2722 }
2723
2724 // Notify style flush observers.
2725 if (!TickObserverArray(1, aNowTime)) {
2726 return StopTimer();
2727 }
2728
2729 // Check if running the microtask checkpoint above caused the pres context to
2730 // be destroyed.
2731 if (!mPresContext || !mPresContext->GetPresShell()) {
2732 return StopTimer();
2733 }
2734
2735 // Step 7. For each doc of docs, flush autofocus candidates for doc if its
2736 // node navigable is a top-level traversable.
2737 FlushAutoFocusDocuments();
2738
2739 // Step 8. For each doc of docs, run the resize steps for doc.
2740 DispatchResizeEvents();
2741 DispatchVisualViewportResizeEvents();
2742
2743 // Step 9. For each doc of docs, run the scroll steps for doc.
2744 DispatchScrollEvents();
2745 DispatchVisualViewportScrollEvents();
2746 DispatchScrollEndEvents();
2747
2748 // Step 10. For each doc of docs, evaluate media queries and report changes
2749 // for doc.
2750 EvaluateMediaQueriesAndReportChanges();
2751
2752 // Step 11. For each doc of docs, update animations and send events for doc.
2753 UpdateAnimationsAndSendEvents();
2754
2755 // Step 12. For each doc of docs, run the fullscreen steps for doc.
2756 RunFullscreenSteps();
2757
2758 // TODO: Step 13. For each doc of docs, if the user agent detects that the
2759 // backing storage associated with a CanvasRenderingContext2D or an
2760 // OffscreenCanvasRenderingContext2D, context, has been lost, then it must run
2761 // the context lost steps for each such context.
2762
2763 // Step 13.5. (https://wicg.github.io/video-rvfc/#video-rvfc-procedures):
2764 //
2765 // For each fully active Document in docs, for each associated video element
2766 // for that Document, run the video frame request callbacks passing now as
2767 // the timestamp.
2768 //
2769 // Step 14. For each doc of docs, run the animation frame callbacks for doc,
2770 // passing in the relative high resolution time given frameTimestamp and doc's
2771 // relevant global object as the timestamp.
2772 RunVideoAndFrameRequestCallbacks(aNowTime);
2773
2774 MaybeIncreaseMeasuredTicksSinceLoading();
2775
2776 // Step 17. For each doc of docs, if the focused area of doc is not a
2777 // focusable area, then run the focusing steps for doc's viewport [..].
2778 //
2779 // FIXME(emilio, bug 1788741): This should happen after resize observer
2780 // handling. Also, Step 16 is supposed to be what updates layout (as part of
2781 // ResizeObserver handling), not quite this. Try to consolidate it.
2782 FlushLayoutOnPendingDocsAndFixUpFocus();
2783
2784 if (!mPresContext || !mPresContext->GetPresShell()) {
2785 return StopTimer();
2786 }
2787
2788 // Recompute approximate frame visibility if it's necessary and enough time
2789 // has passed since the last time we did it.
2790 if (mNeedToRecomputeVisibility && !mThrottled &&
2791 aNowTime >= mNextRecomputeVisibilityTick &&
2792 !presShell->IsPaintingSuppressed()) {
2793 mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
2794 mNeedToRecomputeVisibility = false;
2795
2796 presShell->ScheduleApproximateFrameVisibilityUpdateNow();
2797 }
2798
2799 // Update any popups that may need to be moved or hidden due to their
2800 // anchor changing.
2801 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
2802 pm->UpdatePopupPositions(this);
2803 }
2804
2805 // Update the relevancy of the content of any `content-visibility: auto`
2806 // elements. The specification says: "Specifically, such changes will
2807 // take effect between steps 13 and 14 of Update the Rendering step of
2808 // the Processing Model (between “run the animation frame callbacks” and
2809 // “run the update intersection observations steps”)."
2810 // https://drafts.csswg.org/css-contain/#cv-notes
2811 //
2812 // FIXME(emilio): There are more steps in between now, the content-visibility
2813 // stuff should probably be integrated into the HTML spec.
2814 UpdateRelevancyOfContentVisibilityAutoFrames();
2815
2816 // Step 16.
2817 DetermineProximityToViewportAndNotifyResizeObservers();
2818 if (MOZ_UNLIKELY(!mPresContext || !mPresContext->GetPresShell())(__builtin_expect(!!(!mPresContext || !mPresContext->GetPresShell
()), 0))
) {
2819 return StopTimer();
2820 }
2821
2822 // TODO(emilio): Step 17, focus fix-up should happen here.
2823
2824 // Step 18: For each doc of docs, perform pending transition operations for
2825 // doc.
2826 PerformPendingViewTransitionOperations();
2827
2828 // Step 19. For each doc of docs, run the update intersection observations
2829 // steps for doc.
2830 UpdateIntersectionObservations(aNowTime);
2831
2832 // Notify display flush observers (like a11y).
2833 if (!TickObserverArray(2, aNowTime)) {
2834 return StopTimer();
2835 }
2836
2837 UpdateAnimatedImages(previousRefresh, aNowTime);
2838
2839 bool dispatchTasksAfterTick = false;
2840 if (mViewManagerFlushIsPending && !mThrottled) {
2841 nsCString transactionId;
2842 if (profiler_thread_is_being_profiled_for_markers()) {
2843 transactionId.AppendLiteral("Transaction ID: ");
2844 transactionId.AppendInt((uint64_t)mNextTransactionId);
2845 }
2846 AUTO_PROFILER_MARKER_TEXT(AutoProfilerTextMarker raiiObject2851( "ViewManagerFlush", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext)), MarkerStack::TakeBacktrace(std::
move(mViewManagerFlushCause))), transactionId)
2847 "ViewManagerFlush", GRAPHICS,AutoProfilerTextMarker raiiObject2851( "ViewManagerFlush", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext)), MarkerStack::TakeBacktrace(std::
move(mViewManagerFlushCause))), transactionId)
2848 MarkerOptions(AutoProfilerTextMarker raiiObject2851( "ViewManagerFlush", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext)), MarkerStack::TakeBacktrace(std::
move(mViewManagerFlushCause))), transactionId)
2849 MarkerInnerWindowIdFromDocShell(GetDocShell(mPresContext)),AutoProfilerTextMarker raiiObject2851( "ViewManagerFlush", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext)), MarkerStack::TakeBacktrace(std::
move(mViewManagerFlushCause))), transactionId)
2850 MarkerStack::TakeBacktrace(std::move(mViewManagerFlushCause))),AutoProfilerTextMarker raiiObject2851( "ViewManagerFlush", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext)), MarkerStack::TakeBacktrace(std::
move(mViewManagerFlushCause))), transactionId)
2851 transactionId)AutoProfilerTextMarker raiiObject2851( "ViewManagerFlush", ::
mozilla::baseprofiler::category::GRAPHICS, MarkerOptions( MarkerInnerWindowIdFromDocShell
(GetDocShell(mPresContext)), MarkerStack::TakeBacktrace(std::
move(mViewManagerFlushCause))), transactionId)
;
2852
2853 // Forward our composition payloads to the layer manager.
2854 if (!mCompositionPayloads.IsEmpty()) {
2855 nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
2856 WindowRenderer* renderer = widget ? widget->GetWindowRenderer() : nullptr;
2857 if (renderer && renderer->AsWebRender()) {
2858 renderer->AsWebRender()->RegisterPayloads(mCompositionPayloads);
2859 }
2860 mCompositionPayloads.Clear();
2861 }
2862
2863#ifdef MOZ_DUMP_PAINTING1
2864 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2865 printf_stderr("Starting ProcessPendingUpdates\n");
2866 }
2867#endif
2868
2869 mViewManagerFlushIsPending = false;
2870 RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
2871 const bool skipPaint = isPresentingInVR;
2872 // Skip the paint in immersive VR mode because whatever we paint here will
2873 // not end up on the screen. The screen is displaying WebGL content from a
2874 // single canvas in that mode.
2875 if (!skipPaint) {
2876 PaintTelemetry::AutoRecordPaint record;
2877 vm->ProcessPendingUpdates();
2878 }
2879
2880#ifdef MOZ_DUMP_PAINTING1
2881 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2882 printf_stderr("Ending ProcessPendingUpdates\n");
2883 }
2884#endif
2885
2886 dispatchTasksAfterTick = true;
2887 mHasScheduleFlush = false;
2888 } else {
2889 // No paint happened, discard composition payloads.
2890 mCompositionPayloads.Clear();
2891 }
2892
2893 // This needs to happen after DL building since we rely on the raster scales
2894 // being stored in nsSubDocumentFrame.
2895 UpdateRemoteFrameEffects();
2896
2897#ifndef ANDROID /* bug 1142079 */
2898 double totalMs = (TimeStamp::Now() - mTickStart).ToMilliseconds();
2899 mozilla::Telemetry::Accumulate(mozilla::Telemetry::REFRESH_DRIVER_TICK,
2900 static_cast<uint32_t>(totalMs));
2901#endif
2902
2903 for (nsAPostRefreshObserver* observer :
2904 mPostRefreshObservers.ForwardRange()) {
2905 observer->DidRefresh();
2906 }
2907
2908 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"
, 2908); MOZ_PretendNoReturn(); } } while (0)
;
2909
2910 if (mPresContext->IsRoot() && XRE_IsContentProcess() &&
2911 StaticPrefs::gfx_content_always_paint()) {
2912 ScheduleViewManagerFlush();
2913 }
2914
2915 if (dispatchTasksAfterTick && sPendingIdleTasks) {
2916 UniquePtr<AutoTArray<RefPtr<Task>, 8>> tasks(sPendingIdleTasks.forget());
2917 for (RefPtr<Task>& taskWithDelay : *tasks) {
2918 TaskController::Get()->AddTask(taskWithDelay.forget());
2919 }
2920 }
2921}
2922
2923void nsRefreshDriver::UpdateAnimatedImages(TimeStamp aPreviousRefresh,
2924 TimeStamp aNowTime) {
2925 if (mThrottled) {
2926 // Don't do this when throttled, as the compositor might be paused and we
2927 // don't want to queue a lot of paints, see bug 1828587.
2928 return;
2929 }
2930 // Perform notification to imgIRequests subscribed to listen for refresh
2931 // events.
2932 for (const auto& entry : mStartTable) {
2933 const uint32_t& delay = entry.GetKey();
2934 ImageStartData* data = entry.GetWeak();
2935
2936 if (data->mEntries.IsEmpty()) {
2937 continue;
2938 }
2939
2940 if (data->mStartTime) {
2941 TimeStamp& start = *data->mStartTime;
2942
2943 if (aPreviousRefresh >= start && aNowTime >= start) {
2944 TimeDuration prev = aPreviousRefresh - start;
2945 TimeDuration curr = aNowTime - start;
2946 uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
2947
2948 // We want to trigger images' refresh if we've just crossed over a
2949 // multiple of the first image's start time. If so, set the animation
2950 // start time to the nearest multiple of the delay and move all the
2951 // images in this table to the main requests table.
2952 if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
2953 mozilla::TimeStamp desired =
2954 start + TimeDuration::FromMilliseconds(prevMultiple * delay);
2955 BeginRefreshingImages(data->mEntries, desired);
2956 }
2957 } else {
2958 // Sometimes the start time can be in the future if we spin a nested
2959 // event loop and re-entrantly tick. In that case, setting the
2960 // animation start time to the start time seems like the least bad
2961 // thing we can do.
2962 mozilla::TimeStamp desired = start;
2963 BeginRefreshingImages(data->mEntries, desired);
2964 }
2965 } else {
2966 // This is the very first time we've drawn images with this time delay.
2967 // Set the animation start time to "now" and move all the images in this
2968 // table to the main requests table.
2969 mozilla::TimeStamp desired = aNowTime;
2970 BeginRefreshingImages(data->mEntries, desired);
2971 data->mStartTime.emplace(aNowTime);
2972 }
2973 }
2974
2975 if (!mRequests.IsEmpty()) {
2976 // RequestRefresh may run scripts, so it's not safe to directly call it
2977 // while using a hashtable enumerator to enumerate mRequests in case
2978 // script modifies the hashtable. Instead, we build a (local) array of
2979 // images to refresh, and then we refresh each image in that array.
2980 nsTArray<nsCOMPtr<imgIContainer>> imagesToRefresh(mRequests.Count());
2981
2982 for (const auto& req : mRequests) {
2983 nsCOMPtr<imgIContainer> image;
2984 if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))((bool)(__builtin_expect(!!(!NS_FAILED_impl(req->GetImage(
getter_AddRefs(image)))), 1)))
) {
2985 imagesToRefresh.AppendElement(image.forget());
2986 }
2987 }
2988
2989 for (const auto& image : imagesToRefresh) {
2990 image->RequestRefresh(aNowTime);
2991 }
2992 }
2993}
2994
2995void nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
2996 mozilla::TimeStamp aDesired) {
2997 for (const auto& req : aEntries) {
2998 mRequests.Insert(req);
2999
3000 nsCOMPtr<imgIContainer> image;
3001 if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))((bool)(__builtin_expect(!!(!NS_FAILED_impl(req->GetImage(
getter_AddRefs(image)))), 1)))
) {
3002 image->SetAnimationStartTime(aDesired);
3003 }
3004 }
3005 aEntries.Clear();
3006}
3007
3008void nsRefreshDriver::Freeze() {
3009 StopTimer();
3010 mFreezeCount++;
3011}
3012
3013void nsRefreshDriver::Thaw() {
3014 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"
, 3014); MOZ_PretendNoReturn(); } } while (0)
;
3015
3016 if (mFreezeCount > 0) {
3017 mFreezeCount--;
3018 }
3019
3020 if (mFreezeCount == 0 && HasReasonsToTick()) {
3021 // FIXME: This isn't quite right, since our EnsureTimerStarted call
3022 // updates our mMostRecentRefresh, but the DoRefresh call won't run
3023 // and notify our observers until we get back to the event loop.
3024 // Thus MostRecentRefresh() will lie between now and the DoRefresh.
3025 RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
3026 "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
3027 if (nsPresContext* pc = GetPresContext()) {
3028 pc->Document()->Dispatch(event.forget());
3029 EnsureTimerStarted();
3030 } else {
3031 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"
, 3031); MOZ_PretendNoReturn(); } while (0)
;
3032 }
3033 }
3034}
3035
3036void nsRefreshDriver::FinishedWaitingForTransaction() {
3037 if (mSkippedPaints && !IsInRefresh() && HasReasonsToTick() &&
3038 CanDoCatchUpTick()) {
3039 NS_DispatchToCurrentThreadQueue(
3040 NS_NewRunnableFunction(
3041 "nsRefreshDriver::FinishedWaitingForTransaction",
3042 [self = RefPtr{this}]() {
3043 if (self->CanDoCatchUpTick()) {
3044 self->Tick(self->mActiveTimer->MostRecentRefreshVsyncId(),
3045 self->mActiveTimer->MostRecentRefresh());
3046 }
3047 }),
3048 EventQueuePriority::Vsync);
3049 }
3050 mWaitingForTransaction = false;
3051 mSkippedPaints = false;
3052}
3053
3054mozilla::layers::TransactionId nsRefreshDriver::GetTransactionId(
3055 bool aThrottle) {
3056 mNextTransactionId = mNextTransactionId.Next();
3057 LOG("[%p] Allocating transaction id %" PRIu64"l" "u", this, mNextTransactionId.mId);
3058
3059 // If this a paint from within a normal tick, and the caller hasn't explicitly
3060 // asked for it to skip being throttled, then record this transaction as
3061 // pending and maybe disable painting until some transactions are processed.
3062 if (aThrottle && mInNormalTick) {
3063 mPendingTransactions.AppendElement(mNextTransactionId);
3064 if (TooManyPendingTransactions() && !mWaitingForTransaction &&
3065 !mTestControllingRefreshes) {
3066 LOG("[%p] Hit max pending transaction limit, entering wait mode", this);
3067 mWaitingForTransaction = true;
3068 mSkippedPaints = false;
3069 }
3070 }
3071
3072 return mNextTransactionId;
3073}
3074
3075mozilla::layers::TransactionId nsRefreshDriver::LastTransactionId() const {
3076 return mNextTransactionId;
3077}
3078
3079void nsRefreshDriver::RevokeTransactionId(
3080 mozilla::layers::TransactionId aTransactionId) {
3081 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"
, 3081); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aTransactionId == mNextTransactionId"
")"); do { *((volatile int*)__null) = 3081; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3082 LOG("[%p] Revoking transaction id %" PRIu64"l" "u", this, aTransactionId.mId);
3083 if (AtPendingTransactionLimit() &&
3084 mPendingTransactions.Contains(aTransactionId) && mWaitingForTransaction) {
3085 LOG("[%p] No longer over pending transaction limit, leaving wait state",
3086 this);
3087 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"
, 3088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mSkippedPaints"
") (" "How did we skip a paint when we're in the middle of one?"
")"); do { *((volatile int*)__null) = 3088; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3088 "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"
, 3088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mSkippedPaints"
") (" "How did we skip a paint when we're in the middle of one?"
")"); do { *((volatile int*)__null) = 3088; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3089 FinishedWaitingForTransaction();
3090 }
3091
3092 // Notify the pres context so that it can deliver MozAfterPaint for this
3093 // id if any caller was expecting it.
3094 nsPresContext* pc = GetPresContext();
3095 if (pc) {
3096 pc->NotifyRevokingDidPaint(aTransactionId);
3097 }
3098 // Remove aTransactionId from the set of outstanding transactions since we're
3099 // no longer waiting on it to be completed, but don't revert
3100 // mNextTransactionId since we can't use the id again.
3101 mPendingTransactions.RemoveElement(aTransactionId);
3102}
3103
3104void nsRefreshDriver::ClearPendingTransactions() {
3105 LOG("[%p] ClearPendingTransactions", this);
3106 mPendingTransactions.Clear();
3107 mWaitingForTransaction = false;
3108}
3109
3110void nsRefreshDriver::ResetInitialTransactionId(
3111 mozilla::layers::TransactionId aTransactionId) {
3112 mNextTransactionId = aTransactionId;
3113}
3114
3115mozilla::TimeStamp nsRefreshDriver::GetTransactionStart() { return mTickStart; }
3116
3117VsyncId nsRefreshDriver::GetVsyncId() { return mTickVsyncId; }
3118
3119mozilla::TimeStamp nsRefreshDriver::GetVsyncStart() { return mTickVsyncTime; }
3120
3121void nsRefreshDriver::NotifyTransactionCompleted(
3122 mozilla::layers::TransactionId aTransactionId) {
3123 LOG("[%p] Completed transaction id %" PRIu64"l" "u", this, aTransactionId.mId);
3124 mPendingTransactions.RemoveElement(aTransactionId);
3125 if (mWaitingForTransaction && !TooManyPendingTransactions()) {
3126 LOG("[%p] No longer over pending transaction limit, leaving wait state",
3127 this);
3128 FinishedWaitingForTransaction();
3129 }
3130}
3131
3132void nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime) {
3133 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
3134 mRootRefresh = nullptr;
3135 if (mSkippedPaints) {
3136 DoRefresh();
3137 }
3138}
3139
3140bool nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime) {
3141 if (mTestControllingRefreshes) {
3142 return false;
3143 }
3144
3145 if (mWaitingForTransaction) {
3146 LOG("[%p] Over max pending transaction limit when trying to paint, "
3147 "skipping",
3148 this);
3149 mSkippedPaints = true;
3150 return true;
3151 }
3152
3153 // Try find the 'root' refresh driver for the current window and check
3154 // if that is waiting for a paint.
3155 nsPresContext* pc = GetPresContext();
3156 nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
3157 if (rootContext) {
3158 nsRefreshDriver* rootRefresh = rootContext->RefreshDriver();
3159 if (rootRefresh && rootRefresh != this) {
3160 if (rootRefresh->IsWaitingForPaint(aTime)) {
3161 if (mRootRefresh != rootRefresh) {
3162 if (mRootRefresh) {
3163 mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
3164 }
3165 rootRefresh->AddRefreshObserver(this, FlushType::Style,
3166 "Waiting for paint");
3167 mRootRefresh = rootRefresh;
3168 }
3169 mSkippedPaints = true;
3170 return true;
3171 }
3172 }
3173 }
3174 return false;
3175}
3176
3177void nsRefreshDriver::SetActivity(bool aIsActive) {
3178 const bool shouldThrottle = !aIsActive;
3179 if (mThrottled == shouldThrottle) {
3180 return;
3181 }
3182 mThrottled = shouldThrottle;
3183 if (mActiveTimer || GetReasonsToTick() != TickReasons::eNone) {
3184 // We want to switch our timer type here, so just stop and restart the
3185 // timer.
3186 EnsureTimerStarted(eForceAdjustTimer);
3187 }
3188}
3189
3190nsPresContext* nsRefreshDriver::GetPresContext() const { return mPresContext; }
3191
3192void nsRefreshDriver::DoRefresh() {
3193 // Don't do a refresh unless we're in a state where we should be refreshing.
3194 if (!IsFrozen() && mPresContext && mActiveTimer) {
3195 DoTick();
3196 }
3197}
3198
3199#ifdef DEBUG1
3200bool nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
3201 FlushType aFlushType) {
3202 ObserverArray& array = ArrayFor(aFlushType);
3203 return array.Contains(aObserver);
3204}
3205#endif
3206
3207void nsRefreshDriver::ScheduleViewManagerFlush() {
3208 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"
, 3209); MOZ_PretendNoReturn(); } } while (0)
3209 "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"
, 3209); MOZ_PretendNoReturn(); } } while (0)
;
3210 mViewManagerFlushIsPending = true;
3211 if (!mViewManagerFlushCause) {
3212 mViewManagerFlushCause = profiler_capture_backtrace();
3213 }
3214 mHasScheduleFlush = true;
3215 EnsureTimerStarted(eNeverAdjustTimer);
3216}
3217
3218void nsRefreshDriver::ScheduleFullscreenEvent(
3219 UniquePtr<PendingFullscreenEvent> aEvent) {
3220 mPendingFullscreenEvents.AppendElement(std::move(aEvent));
3221 // make sure that the timer is running
3222 EnsureTimerStarted();
3223}
3224
3225void nsRefreshDriver::CancelPendingFullscreenEvents(Document* aDocument) {
3226 for (auto i : Reversed(IntegerRange(mPendingFullscreenEvents.Length()))) {
3227 if (mPendingFullscreenEvents[i]->Document() == aDocument) {
3228 mPendingFullscreenEvents.RemoveElementAt(i);
3229 }
3230 }
3231}
3232
3233void nsRefreshDriver::CancelPendingAnimationEvents(
3234 AnimationEventDispatcher* aDispatcher) {
3235 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"
, 3235); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDispatcher"
")"); do { *((volatile int*)__null) = 3235; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3236 aDispatcher->ClearEventQueue();
3237 mAnimationEventFlushObservers.RemoveElement(aDispatcher);
3238}
3239
3240/* static */
3241TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
3242 IdleCheck aCheckType) {
3243 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"
, 3243); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 3243; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3244 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"
, 3244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aDefault.IsNull()"
")"); do { *((volatile int*)__null) = 3244; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3245
3246 // For computing idleness of refresh drivers we only care about
3247 // sRegularRateTimerList, since we consider refresh drivers attached to
3248 // sThrottledRateTimer to be inactive. This implies that tasks
3249 // resulting from a tick on the sRegularRateTimer counts as being
3250 // busy but tasks resulting from a tick on sThrottledRateTimer
3251 // counts as being idle.
3252 if (sRegularRateTimer) {
3253 TimeStamp retVal = sRegularRateTimer->GetIdleDeadlineHint(aDefault);
3254 if (retVal != aDefault) {
3255 return retVal;
3256 }
3257 }
3258
3259 TimeStamp hint = TimeStamp();
3260 if (sRegularRateTimerList) {
3261 for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
3262 TimeStamp newHint = timer->GetIdleDeadlineHint(aDefault);
3263 if (newHint < aDefault && (hint.IsNull() || newHint < hint)) {
3264 hint = newHint;
3265 }
3266 }
3267 }
3268
3269 if (!hint.IsNull()) {
3270 return hint;
3271 }
3272
3273 if (aCheckType == IdleCheck::AllVsyncListeners && XRE_IsParentProcess()) {
3274 Maybe<TimeDuration> maybeRate =
3275 mozilla::gfx::VsyncSource::GetFastestVsyncRate();
3276 if (maybeRate.isSome()) {
3277 TimeDuration minIdlePeriod =
3278 TimeDuration::FromMilliseconds(StaticPrefs::idle_period_min());
3279 TimeDuration layoutIdleLimit = TimeDuration::FromMilliseconds(
3280 StaticPrefs::layout_idle_period_time_limit());
3281 TimeDuration rate = *maybeRate - layoutIdleLimit;
3282
3283 // If the rate is very short, don't let it affect idle processing in the
3284 // parent process too much.
3285 rate = std::max(rate, minIdlePeriod + minIdlePeriod);
3286
3287 TimeStamp newHint = TimeStamp::Now() + rate;
3288 if (newHint < aDefault) {
3289 return newHint;
3290 }
3291 }
3292 }
3293
3294 return aDefault;
3295}
3296
3297/* static */
3298Maybe<TimeStamp> nsRefreshDriver::GetNextTickHint() {
3299 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"
, 3299); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 3299; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3300
3301 if (sRegularRateTimer) {
3302 return sRegularRateTimer->GetNextTickHint();
3303 }
3304
3305 Maybe<TimeStamp> hint = Nothing();
3306 if (sRegularRateTimerList) {
3307 for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
3308 if (Maybe<TimeStamp> newHint = timer->GetNextTickHint()) {
3309 if (!hint || newHint.value() < hint.value()) {
3310 hint = newHint;
3311 }
3312 }
3313 }
3314 }
3315 return hint;
3316}
3317
3318/* static */
3319bool nsRefreshDriver::IsRegularRateTimerTicking() {
3320 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"
, 3320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 3320; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3321
3322 if (sRegularRateTimer) {
3323 if (sRegularRateTimer->IsTicking()) {
3324 return true;
3325 }
3326 }
3327
3328 if (sRegularRateTimerList) {
3329 for (RefreshDriverTimer* timer : *sRegularRateTimerList) {
3330 if (timer->IsTicking()) {
3331 return true;
3332 }
3333 }
3334 }
3335
3336 return false;
3337}
3338
3339void nsRefreshDriver::Disconnect() {
3340 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"
, 3340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 3340; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3341
3342 StopTimer();
3343
3344 mEarlyRunners.Clear();
3345
3346 if (mPresContext) {
3347 mPresContext = nullptr;
3348 if (--sRefreshDriverCount == 0) {
3349 Shutdown();
3350 }
3351 }
3352}
3353
3354#undef LOG