Bug Summary

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