Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/UniquePtr.h
Warning:line 287, column 25
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nsRefreshDriver.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/layout/base -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/layout/base -resource-dir /usr/lib/llvm-20/lib/clang/20 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/layout/base -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/layout/base -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/layout/forms -I /var/lib/jenkins/workspace/firefox-scan-build/layout/generic -I /var/lib/jenkins/workspace/firefox-scan-build/layout/mathml -I /var/lib/jenkins/workspace/firefox-scan-build/layout/painting -I /var/lib/jenkins/workspace/firefox-scan-build/layout/printing -I /var/lib/jenkins/workspace/firefox-scan-build/layout/style -I /var/lib/jenkins/workspace/firefox-scan-build/layout/tables -I /var/lib/jenkins/workspace/firefox-scan-build/layout/xul -I /var/lib/jenkins/workspace/firefox-scan-build/layout/xul/tree -I /var/lib/jenkins/workspace/firefox-scan-build/docshell/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/base -I /var/lib/jenkins/workspace/firefox-scan-build/dom/html -I /var/lib/jenkins/workspace/firefox-scan-build/dom/svg -I /var/lib/jenkins/workspace/firefox-scan-build/dom/xul -I /var/lib/jenkins/workspace/firefox-scan-build/view -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-01-20-090804-167946-1 -x c++ /var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp

/var/lib/jenkins/workspace/firefox-scan-build/layout/base/nsRefreshDriver.cpp

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

/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h

1/* -*- Mode: C++; tab-width: 2; 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// This header contains basic definitions required to create marker types, and
8// to add markers to the profiler buffers.
9//
10// In most cases, #include "mozilla/BaseProfilerMarkers.h" instead, or
11// #include "mozilla/BaseProfilerMarkerTypes.h" for common marker types.
12
13#ifndef BaseProfilerMarkersPrerequisites_h
14#define BaseProfilerMarkersPrerequisites_h
15
16namespace mozilla {
17
18enum class StackCaptureOptions {
19 NoStack, // No stack captured.
20 Full, // Capture a full stack, including label frames, JS frames and
21 // native frames.
22 NonNative, // Capture a stack without native frames for reduced overhead.
23};
24
25}
26
27#include "BaseProfileJSONWriter.h"
28#include "BaseProfilingCategory.h"
29#include "mozilla/Maybe.h"
30#include "mozilla/ProfileChunkedBuffer.h"
31#include "mozilla/BaseProfilerState.h"
32#include "mozilla/TimeStamp.h"
33#include "mozilla/UniquePtr.h"
34#include "mozilla/Variant.h"
35
36#include <initializer_list>
37#include <string_view>
38#include <string>
39#include <type_traits>
40#include <utility>
41#include <vector>
42
43namespace mozilla {
44
45// Return a NotNull<const CHAR*> pointing at the literal empty string `""`.
46template <typename CHAR>
47constexpr const CHAR* LiteralEmptyStringPointer() {
48 static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
49 "Only char and char16_t are supported in Firefox");
50 if constexpr (std::is_same_v<CHAR, char>) {
51 return "";
52 }
53 if constexpr (std::is_same_v<CHAR, char16_t>) {
54 return u"";
55 }
56}
57
58// Return a string_view<CHAR> pointing at the literal empty string.
59template <typename CHAR>
60constexpr std::basic_string_view<CHAR> LiteralEmptyStringView() {
61 static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
62 "Only char and char16_t are supported in Firefox");
63 // Use `operator""sv()` from <string_view>.
64 using namespace std::literals::string_view_literals;
65 if constexpr (std::is_same_v<CHAR, char>) {
66 return ""sv;
67 }
68 if constexpr (std::is_same_v<CHAR, char16_t>) {
69 return u""sv;
70 }
71}
72
73// General string view, optimized for short on-stack life before serialization,
74// and between deserialization and JSON-streaming.
75template <typename CHAR>
76class MOZ_STACK_CLASS ProfilerStringView {
77 public:
78 // Default constructor points at "" (literal empty string).
79 constexpr ProfilerStringView() = default;
80
81 // Don't allow copy.
82 ProfilerStringView(const ProfilerStringView&) = delete;
83 ProfilerStringView& operator=(const ProfilerStringView&) = delete;
84
85 // Allow move. For consistency the moved-from string is always reset to "".
86 constexpr ProfilerStringView(ProfilerStringView&& aOther)
87 : mStringView(std::move(aOther.mStringView)),
88 mOwnership(aOther.mOwnership) {
89 if (mOwnership == Ownership::OwnedThroughStringView) {
90 // We now own the buffer, make the other point at the literal "".
91 aOther.mStringView = LiteralEmptyStringView<CHAR>();
92 aOther.mOwnership = Ownership::Literal;
93 }
94 }
95 constexpr ProfilerStringView& operator=(ProfilerStringView&& aOther) {
96 mStringView = std::move(aOther.mStringView);
97 mOwnership = aOther.mOwnership;
98 if (mOwnership == Ownership::OwnedThroughStringView) {
99 // We now own the buffer, make the other point at the literal "".
100 aOther.mStringView = LiteralEmptyStringView<CHAR>();
101 aOther.mOwnership = Ownership::Literal;
102 }
103 return *this;
104 }
105
106 ~ProfilerStringView() {
107 if (MOZ_UNLIKELY(mOwnership == Ownership::OwnedThroughStringView)(__builtin_expect(!!(mOwnership == Ownership::OwnedThroughStringView
), 0))
) {
108 // We own the buffer pointed at by mStringView, destroy it.
109 // This is only used between deserialization and streaming.
110 delete mStringView.data();
111 }
112 }
113
114 // Implicit construction from nullptr, points at "" (literal empty string).
115 constexpr MOZ_IMPLICIT ProfilerStringView(decltype(nullptr)) {}
116
117 // Implicit constructor from a literal string.
118 template <size_t Np1>
119 constexpr MOZ_IMPLICIT ProfilerStringView(const CHAR (&aLiteralString)[Np1])
120 : ProfilerStringView(aLiteralString, Np1 - 1, Ownership::Literal) {}
121
122 // Constructor from a non-literal string.
123 constexpr ProfilerStringView(const CHAR* aString, size_t aLength)
124 : ProfilerStringView(aString, aLength, Ownership::Reference) {}
125
126 // Implicit constructor from a string_view.
127 constexpr MOZ_IMPLICIT ProfilerStringView(
128 const std::basic_string_view<CHAR>& aStringView)
129 : ProfilerStringView(aStringView.data(), aStringView.length(),
130 Ownership::Reference) {}
131
132 // Implicit constructor from an expiring string_view. We assume that the
133 // pointed-at string will outlive this ProfilerStringView.
134 constexpr MOZ_IMPLICIT ProfilerStringView(
135 std::basic_string_view<CHAR>&& aStringView)
136 : ProfilerStringView(aStringView.data(), aStringView.length(),
137 Ownership::Reference) {}
138
139 // Implicit constructor from std::string.
140 constexpr MOZ_IMPLICIT ProfilerStringView(
141 const std::basic_string<CHAR>& aString)
142 : ProfilerStringView(aString.data(), aString.length(),
143 Ownership::Reference) {}
144
145 // Construction from a raw pointer to a null-terminated string.
146 // This is a named class-static function to make it more obvious where work is
147 // being done (to determine the string length), and encourage users to instead
148 // provide a length, if already known.
149 // TODO: Find callers and convert them to constructor instead if possible.
150 static constexpr ProfilerStringView WrapNullTerminatedString(
151 const CHAR* aString) {
152 return ProfilerStringView(
153 aString, aString ? std::char_traits<CHAR>::length(aString) : 0,
154 Ownership::Reference);
155 }
156
157 // Implicit constructor for an object with member functions `Data()`
158 // `Length()`, and `IsLiteral()`, common in xpcom strings.
159 template <
160 typename String,
161 typename DataReturnType = decltype(std::declval<const String>().Data()),
162 typename LengthReturnType =
163 decltype(std::declval<const String>().Length()),
164 typename IsLiteralReturnType =
165 decltype(std::declval<const String>().IsLiteral()),
166 typename =
167 std::enable_if_t<std::is_convertible_v<DataReturnType, const CHAR*> &&
168 std::is_integral_v<LengthReturnType> &&
169 std::is_same_v<IsLiteralReturnType, bool>>>
170 constexpr MOZ_IMPLICIT ProfilerStringView(const String& aString)
171 : ProfilerStringView(
172 static_cast<const CHAR*>(aString.Data()), aString.Length(),
173 aString.IsLiteral() ? Ownership::Literal : Ownership::Reference) {}
174
175 [[nodiscard]] constexpr const std::basic_string_view<CHAR>& StringView()
176 const {
177 return mStringView;
178 }
179
180 [[nodiscard]] constexpr size_t Length() const { return mStringView.length(); }
181
182 [[nodiscard]] constexpr bool IsLiteral() const {
183 return mOwnership == Ownership::Literal;
184 }
185 [[nodiscard]] constexpr bool IsReference() const {
186 return mOwnership == Ownership::Reference;
187 }
188 // No `IsOwned...()` because it's a secret, only used internally!
189
190 [[nodiscard]] Span<const CHAR> AsSpan() const {
191 return Span<const CHAR>(mStringView.data(), mStringView.length());
192 }
193 [[nodiscard]] operator Span<const CHAR>() const { return AsSpan(); }
194
195 private:
196 enum class Ownership { Literal, Reference, OwnedThroughStringView };
197
198 // Allow deserializer to store anything here.
199 friend ProfileBufferEntryReader::Deserializer<ProfilerStringView>;
200
201 constexpr ProfilerStringView(const CHAR* aString, size_t aLength,
202 Ownership aOwnership)
203 : mStringView(aString ? std::basic_string_view<CHAR>(aString, aLength)
204 : LiteralEmptyStringView<CHAR>()),
205 mOwnership(aString ? aOwnership : Ownership::Literal) {}
206
207 // String view to an outside string (literal or reference).
208 // We may actually own the pointed-at buffer, but it is only used internally
209 // between deserialization and JSON streaming.
210 std::basic_string_view<CHAR> mStringView = LiteralEmptyStringView<CHAR>();
211
212 Ownership mOwnership = Ownership::Literal;
213};
214
215using ProfilerString8View = ProfilerStringView<char>;
216using ProfilerString16View = ProfilerStringView<char16_t>;
217
218// This compulsory marker parameter contains the required category information.
219class MarkerCategory {
220 public:
221 // Constructor from category pair (includes both super- and sub-categories).
222 constexpr explicit MarkerCategory(
223 baseprofiler::ProfilingCategoryPair aCategoryPair)
224 : mCategoryPair(aCategoryPair) {}
225
226 // Returns the stored category pair.
227 constexpr baseprofiler::ProfilingCategoryPair CategoryPair() const {
228 return mCategoryPair;
229 }
230
231 // Returns the super-category from the stored category pair.
232 baseprofiler::ProfilingCategory GetCategory() const {
233 return GetProfilingCategoryPairInfo(mCategoryPair).mCategory;
234 }
235
236 private:
237 baseprofiler::ProfilingCategoryPair mCategoryPair =
238 baseprofiler::ProfilingCategoryPair::OTHER;
239};
240
241namespace baseprofiler::category {
242
243// Each category pair name constructs a MarkerCategory.
244// E.g.: mozilla::baseprofiler::category::OTHER_Profiling
245// Profiler macros will take the category name alone without namespace.
246// E.g.: `PROFILER_MARKER_UNTYPED("name", OTHER_Profiling)`
247#define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
248#define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) \
249 static constexpr MarkerCategory name{ProfilingCategoryPair::name};
250#define CATEGORY_ENUM_END_CATEGORY
251MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY,CATEGORY_ENUM_BEGIN_CATEGORY(IDLE, "Idle", "transparent") CATEGORY_ENUM_SUBCATEGORY
(IDLE, IDLE, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(OTHER, "Other", "grey") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER
, "Other") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER_PreferenceRead
, "Preference Read") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER_Profiling
, "Profiling") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TEST, "Test", "darkgray") CATEGORY_ENUM_SUBCATEGORY(TEST, TEST
, "Test") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(LAYOUT, "Layout", "purple") CATEGORY_ENUM_SUBCATEGORY(LAYOUT
, LAYOUT, "Other") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_FrameConstruction
, "Frame construction") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Reflow
, "Reflow") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_CSSParsing
, "CSS parsing") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_SelectorQuery
, "Selector query") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_StyleComputation
, "Style computation") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Destroy
, "Layout cleanup") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Printing
, "Printing") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JS, "JavaScript", "yellow") CATEGORY_ENUM_SUBCATEGORY(JS, JS
, "Other") CATEGORY_ENUM_SUBCATEGORY(JS, JS_Parsing, "Parsing"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_BaselineCompilation, "JIT Compile (baseline)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_IonCompilation, "JIT Compile (ion)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_Interpreter, "Interpreter"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_BaselineInterpret, "JIT (baseline-interpreter)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_Baseline, "JIT (baseline)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_IonMonkey, "JIT (ion)") CATEGORY_ENUM_SUBCATEGORY
(JS, JS_Builtin, "Builtin API") CATEGORY_ENUM_SUBCATEGORY(JS,
JS_WasmIon, "Wasm (ion)") CATEGORY_ENUM_SUBCATEGORY(JS, JS_WasmBaseline
, "Wasm (baseline)") CATEGORY_ENUM_SUBCATEGORY(JS, JS_WasmOther
, "Wasm (other)") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(GCCC, "GC / CC", "orange") CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC
, "Other") CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MinorGC, "Minor GC"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC, "Major GC (Other)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Mark, "Major GC (Mark)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Sweep, "Major GC (Sweep)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Compact, "Major GC (Compact)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_UnmarkGray, "Unmark Gray"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_Barrier, "Barrier") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_FreeSnowWhite, "CC (Free Snow White)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_BuildGraph, "CC (Build Graph)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_ScanRoots, "CC (Scan Roots)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_CollectWhite, "CC (Collect White)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_Finalize, "CC (Finalize)") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(NETWORK, "Network", "lightblue"
) CATEGORY_ENUM_SUBCATEGORY(NETWORK, NETWORK, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(GRAPHICS, "Graphics", "green") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS, "Other") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS
, GRAPHICS_DisplayListBuilding, "DisplayList building") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS_DisplayListMerging, "DisplayList merging"
) CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_LayerBuilding,
"Layer building") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_TileAllocation
, "Tile allocation") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_WRDisplayList
, "WebRender display list") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS
, GRAPHICS_Rasterization, "Rasterization") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS_FlushingAsyncPaints, "Flushing async paints"
) CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_ImageDecoding,
"Image decoding") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(DOM, "DOM", "blue") CATEGORY_ENUM_SUBCATEGORY(DOM, DOM, "Other"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_ANDROID
, "Android", "yellow") CATEGORY_ENUM_SUBCATEGORY(JAVA_ANDROID
, JAVA_ANDROID, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JAVA_ANDROIDX, "AndroidX", "orange") CATEGORY_ENUM_SUBCATEGORY
(JAVA_ANDROIDX, JAVA_ANDROIDX, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_LANGUAGE, "Java", "blue") CATEGORY_ENUM_SUBCATEGORY
(JAVA_LANGUAGE, JAVA_LANGUAGE, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_MOZILLA, "Mozilla", "green"
) CATEGORY_ENUM_SUBCATEGORY(JAVA_MOZILLA, JAVA_MOZILLA, "Other"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_KOTLIN
, "Kotlin", "purple") CATEGORY_ENUM_SUBCATEGORY(JAVA_KOTLIN, JAVA_KOTLIN
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JAVA_BLOCKED, "Blocked", "lightblue") CATEGORY_ENUM_SUBCATEGORY
(JAVA_BLOCKED, JAVA_BLOCKED, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(IPC, "IPC", "lightgreen") CATEGORY_ENUM_SUBCATEGORY
(IPC, IPC, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(MEDIA, "Media", "orange") CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA
, "Other") CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_CUBEB, "Cubeb"
) CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_PLAYBACK, "Playback"
) CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_RT, "Real-time rendering"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(A11Y
, "Accessibility", "brown") CATEGORY_ENUM_SUBCATEGORY(A11Y, A11Y
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(PROFILER, "Profiler", "lightred") CATEGORY_ENUM_SUBCATEGORY(
PROFILER, PROFILER, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TIMER, "Timer", "grey") CATEGORY_ENUM_SUBCATEGORY(TIMER, TIMER
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(REMOTE_PROTOCOL, "Remote-Protocol", "grey") CATEGORY_ENUM_SUBCATEGORY
(REMOTE_PROTOCOL, REMOTE_PROTOCOL, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(SANDBOX, "Sandbox", "grey") CATEGORY_ENUM_SUBCATEGORY
(SANDBOX, SANDBOX, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TELEMETRY, "Telemetry", "grey") CATEGORY_ENUM_SUBCATEGORY(TELEMETRY
, TELEMETRY, "Other") CATEGORY_ENUM_END_CATEGORY
252 CATEGORY_ENUM_SUBCATEGORY,CATEGORY_ENUM_BEGIN_CATEGORY(IDLE, "Idle", "transparent") CATEGORY_ENUM_SUBCATEGORY
(IDLE, IDLE, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(OTHER, "Other", "grey") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER
, "Other") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER_PreferenceRead
, "Preference Read") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER_Profiling
, "Profiling") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TEST, "Test", "darkgray") CATEGORY_ENUM_SUBCATEGORY(TEST, TEST
, "Test") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(LAYOUT, "Layout", "purple") CATEGORY_ENUM_SUBCATEGORY(LAYOUT
, LAYOUT, "Other") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_FrameConstruction
, "Frame construction") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Reflow
, "Reflow") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_CSSParsing
, "CSS parsing") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_SelectorQuery
, "Selector query") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_StyleComputation
, "Style computation") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Destroy
, "Layout cleanup") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Printing
, "Printing") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JS, "JavaScript", "yellow") CATEGORY_ENUM_SUBCATEGORY(JS, JS
, "Other") CATEGORY_ENUM_SUBCATEGORY(JS, JS_Parsing, "Parsing"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_BaselineCompilation, "JIT Compile (baseline)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_IonCompilation, "JIT Compile (ion)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_Interpreter, "Interpreter"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_BaselineInterpret, "JIT (baseline-interpreter)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_Baseline, "JIT (baseline)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_IonMonkey, "JIT (ion)") CATEGORY_ENUM_SUBCATEGORY
(JS, JS_Builtin, "Builtin API") CATEGORY_ENUM_SUBCATEGORY(JS,
JS_WasmIon, "Wasm (ion)") CATEGORY_ENUM_SUBCATEGORY(JS, JS_WasmBaseline
, "Wasm (baseline)") CATEGORY_ENUM_SUBCATEGORY(JS, JS_WasmOther
, "Wasm (other)") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(GCCC, "GC / CC", "orange") CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC
, "Other") CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MinorGC, "Minor GC"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC, "Major GC (Other)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Mark, "Major GC (Mark)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Sweep, "Major GC (Sweep)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Compact, "Major GC (Compact)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_UnmarkGray, "Unmark Gray"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_Barrier, "Barrier") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_FreeSnowWhite, "CC (Free Snow White)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_BuildGraph, "CC (Build Graph)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_ScanRoots, "CC (Scan Roots)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_CollectWhite, "CC (Collect White)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_Finalize, "CC (Finalize)") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(NETWORK, "Network", "lightblue"
) CATEGORY_ENUM_SUBCATEGORY(NETWORK, NETWORK, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(GRAPHICS, "Graphics", "green") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS, "Other") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS
, GRAPHICS_DisplayListBuilding, "DisplayList building") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS_DisplayListMerging, "DisplayList merging"
) CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_LayerBuilding,
"Layer building") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_TileAllocation
, "Tile allocation") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_WRDisplayList
, "WebRender display list") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS
, GRAPHICS_Rasterization, "Rasterization") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS_FlushingAsyncPaints, "Flushing async paints"
) CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_ImageDecoding,
"Image decoding") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(DOM, "DOM", "blue") CATEGORY_ENUM_SUBCATEGORY(DOM, DOM, "Other"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_ANDROID
, "Android", "yellow") CATEGORY_ENUM_SUBCATEGORY(JAVA_ANDROID
, JAVA_ANDROID, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JAVA_ANDROIDX, "AndroidX", "orange") CATEGORY_ENUM_SUBCATEGORY
(JAVA_ANDROIDX, JAVA_ANDROIDX, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_LANGUAGE, "Java", "blue") CATEGORY_ENUM_SUBCATEGORY
(JAVA_LANGUAGE, JAVA_LANGUAGE, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_MOZILLA, "Mozilla", "green"
) CATEGORY_ENUM_SUBCATEGORY(JAVA_MOZILLA, JAVA_MOZILLA, "Other"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_KOTLIN
, "Kotlin", "purple") CATEGORY_ENUM_SUBCATEGORY(JAVA_KOTLIN, JAVA_KOTLIN
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JAVA_BLOCKED, "Blocked", "lightblue") CATEGORY_ENUM_SUBCATEGORY
(JAVA_BLOCKED, JAVA_BLOCKED, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(IPC, "IPC", "lightgreen") CATEGORY_ENUM_SUBCATEGORY
(IPC, IPC, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(MEDIA, "Media", "orange") CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA
, "Other") CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_CUBEB, "Cubeb"
) CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_PLAYBACK, "Playback"
) CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_RT, "Real-time rendering"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(A11Y
, "Accessibility", "brown") CATEGORY_ENUM_SUBCATEGORY(A11Y, A11Y
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(PROFILER, "Profiler", "lightred") CATEGORY_ENUM_SUBCATEGORY(
PROFILER, PROFILER, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TIMER, "Timer", "grey") CATEGORY_ENUM_SUBCATEGORY(TIMER, TIMER
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(REMOTE_PROTOCOL, "Remote-Protocol", "grey") CATEGORY_ENUM_SUBCATEGORY
(REMOTE_PROTOCOL, REMOTE_PROTOCOL, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(SANDBOX, "Sandbox", "grey") CATEGORY_ENUM_SUBCATEGORY
(SANDBOX, SANDBOX, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TELEMETRY, "Telemetry", "grey") CATEGORY_ENUM_SUBCATEGORY(TELEMETRY
, TELEMETRY, "Other") CATEGORY_ENUM_END_CATEGORY
253 CATEGORY_ENUM_END_CATEGORY)CATEGORY_ENUM_BEGIN_CATEGORY(IDLE, "Idle", "transparent") CATEGORY_ENUM_SUBCATEGORY
(IDLE, IDLE, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(OTHER, "Other", "grey") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER
, "Other") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER_PreferenceRead
, "Preference Read") CATEGORY_ENUM_SUBCATEGORY(OTHER, OTHER_Profiling
, "Profiling") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TEST, "Test", "darkgray") CATEGORY_ENUM_SUBCATEGORY(TEST, TEST
, "Test") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(LAYOUT, "Layout", "purple") CATEGORY_ENUM_SUBCATEGORY(LAYOUT
, LAYOUT, "Other") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_FrameConstruction
, "Frame construction") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Reflow
, "Reflow") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_CSSParsing
, "CSS parsing") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_SelectorQuery
, "Selector query") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_StyleComputation
, "Style computation") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Destroy
, "Layout cleanup") CATEGORY_ENUM_SUBCATEGORY(LAYOUT, LAYOUT_Printing
, "Printing") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JS, "JavaScript", "yellow") CATEGORY_ENUM_SUBCATEGORY(JS, JS
, "Other") CATEGORY_ENUM_SUBCATEGORY(JS, JS_Parsing, "Parsing"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_BaselineCompilation, "JIT Compile (baseline)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_IonCompilation, "JIT Compile (ion)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_Interpreter, "Interpreter"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_BaselineInterpret, "JIT (baseline-interpreter)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_Baseline, "JIT (baseline)"
) CATEGORY_ENUM_SUBCATEGORY(JS, JS_IonMonkey, "JIT (ion)") CATEGORY_ENUM_SUBCATEGORY
(JS, JS_Builtin, "Builtin API") CATEGORY_ENUM_SUBCATEGORY(JS,
JS_WasmIon, "Wasm (ion)") CATEGORY_ENUM_SUBCATEGORY(JS, JS_WasmBaseline
, "Wasm (baseline)") CATEGORY_ENUM_SUBCATEGORY(JS, JS_WasmOther
, "Wasm (other)") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(GCCC, "GC / CC", "orange") CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC
, "Other") CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MinorGC, "Minor GC"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC, "Major GC (Other)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Mark, "Major GC (Mark)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Sweep, "Major GC (Sweep)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_MajorGC_Compact, "Major GC (Compact)"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_UnmarkGray, "Unmark Gray"
) CATEGORY_ENUM_SUBCATEGORY(GCCC, GCCC_Barrier, "Barrier") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_FreeSnowWhite, "CC (Free Snow White)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_BuildGraph, "CC (Build Graph)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_ScanRoots, "CC (Scan Roots)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_CollectWhite, "CC (Collect White)") CATEGORY_ENUM_SUBCATEGORY
(GCCC, GCCC_Finalize, "CC (Finalize)") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(NETWORK, "Network", "lightblue"
) CATEGORY_ENUM_SUBCATEGORY(NETWORK, NETWORK, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(GRAPHICS, "Graphics", "green") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS, "Other") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS
, GRAPHICS_DisplayListBuilding, "DisplayList building") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS_DisplayListMerging, "DisplayList merging"
) CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_LayerBuilding,
"Layer building") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_TileAllocation
, "Tile allocation") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_WRDisplayList
, "WebRender display list") CATEGORY_ENUM_SUBCATEGORY(GRAPHICS
, GRAPHICS_Rasterization, "Rasterization") CATEGORY_ENUM_SUBCATEGORY
(GRAPHICS, GRAPHICS_FlushingAsyncPaints, "Flushing async paints"
) CATEGORY_ENUM_SUBCATEGORY(GRAPHICS, GRAPHICS_ImageDecoding,
"Image decoding") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(DOM, "DOM", "blue") CATEGORY_ENUM_SUBCATEGORY(DOM, DOM, "Other"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_ANDROID
, "Android", "yellow") CATEGORY_ENUM_SUBCATEGORY(JAVA_ANDROID
, JAVA_ANDROID, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JAVA_ANDROIDX, "AndroidX", "orange") CATEGORY_ENUM_SUBCATEGORY
(JAVA_ANDROIDX, JAVA_ANDROIDX, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_LANGUAGE, "Java", "blue") CATEGORY_ENUM_SUBCATEGORY
(JAVA_LANGUAGE, JAVA_LANGUAGE, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_MOZILLA, "Mozilla", "green"
) CATEGORY_ENUM_SUBCATEGORY(JAVA_MOZILLA, JAVA_MOZILLA, "Other"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(JAVA_KOTLIN
, "Kotlin", "purple") CATEGORY_ENUM_SUBCATEGORY(JAVA_KOTLIN, JAVA_KOTLIN
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(JAVA_BLOCKED, "Blocked", "lightblue") CATEGORY_ENUM_SUBCATEGORY
(JAVA_BLOCKED, JAVA_BLOCKED, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(IPC, "IPC", "lightgreen") CATEGORY_ENUM_SUBCATEGORY
(IPC, IPC, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(MEDIA, "Media", "orange") CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA
, "Other") CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_CUBEB, "Cubeb"
) CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_PLAYBACK, "Playback"
) CATEGORY_ENUM_SUBCATEGORY(MEDIA, MEDIA_RT, "Real-time rendering"
) CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY(A11Y
, "Accessibility", "brown") CATEGORY_ENUM_SUBCATEGORY(A11Y, A11Y
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(PROFILER, "Profiler", "lightred") CATEGORY_ENUM_SUBCATEGORY(
PROFILER, PROFILER, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TIMER, "Timer", "grey") CATEGORY_ENUM_SUBCATEGORY(TIMER, TIMER
, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(REMOTE_PROTOCOL, "Remote-Protocol", "grey") CATEGORY_ENUM_SUBCATEGORY
(REMOTE_PROTOCOL, REMOTE_PROTOCOL, "Other") CATEGORY_ENUM_END_CATEGORY
CATEGORY_ENUM_BEGIN_CATEGORY(SANDBOX, "Sandbox", "grey") CATEGORY_ENUM_SUBCATEGORY
(SANDBOX, SANDBOX, "Other") CATEGORY_ENUM_END_CATEGORY CATEGORY_ENUM_BEGIN_CATEGORY
(TELEMETRY, "Telemetry", "grey") CATEGORY_ENUM_SUBCATEGORY(TELEMETRY
, TELEMETRY, "Other") CATEGORY_ENUM_END_CATEGORY
254#undef CATEGORY_ENUM_BEGIN_CATEGORY
255#undef CATEGORY_ENUM_SUBCATEGORY
256#undef CATEGORY_ENUM_END_CATEGORY
257
258// Import `MarkerCategory` into this namespace. This will allow using this type
259// dynamically in macros that prepend `::mozilla::baseprofiler::category::` to
260// the given category, e.g.:
261// `PROFILER_MARKER_UNTYPED("name", MarkerCategory(...))`
262using MarkerCategory = ::mozilla::MarkerCategory;
263
264} // namespace baseprofiler::category
265
266// The classes below are all embedded in a `MarkerOptions` object.
267class MarkerOptions;
268
269// This marker option captures a given thread id.
270// If left unspecified (by default construction) during the add-marker call, the
271// current thread id will be used then.
272class MarkerThreadId {
273 public:
274 // Default constructor, keeps the thread id unspecified.
275 constexpr MarkerThreadId() = default;
276
277 // Constructor from a given thread id.
278 constexpr explicit MarkerThreadId(
279 baseprofiler::BaseProfilerThreadId aThreadId)
280 : mThreadId(aThreadId) {}
281
282 // Use the current thread's id.
283 static MarkerThreadId CurrentThread() {
284 return MarkerThreadId(baseprofiler::profiler_current_thread_id());
285 }
286
287 // Use the main thread's id. This can be useful to record a marker from a
288 // possibly-unregistered thread, and display it in the main thread track.
289 static MarkerThreadId MainThread() {
290 return MarkerThreadId(baseprofiler::profiler_main_thread_id());
291 }
292
293 [[nodiscard]] constexpr baseprofiler::BaseProfilerThreadId ThreadId() const {
294 return mThreadId;
295 }
296
297 [[nodiscard]] constexpr bool IsUnspecified() const {
298 return !mThreadId.IsSpecified();
299 }
300
301 private:
302 baseprofiler::BaseProfilerThreadId mThreadId;
303};
304
305// This marker option contains marker timing information.
306// This class encapsulates the logic for correctly storing a marker based on its
307// Use the static methods to create the MarkerTiming. This is a transient object
308// that is being used to enforce the constraints of the combinations of the
309// data.
310class MarkerTiming {
311 public:
312 // The following static methods are used to create the MarkerTiming based on
313 // the type that it is.
314
315 static MarkerTiming InstantAt(const TimeStamp& aTime) {
316 MOZ_ASSERT(!aTime.IsNull(), "Time is null for an instant marker.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aTime.IsNull()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!aTime.IsNull()"
" (" "Time is null for an instant marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 316); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aTime.IsNull()"
") (" "Time is null for an instant marker." ")"); do { *((volatile
int*)__null) = 316; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
317 return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::Instant};
318 }
319
320 static MarkerTiming InstantNow() { return InstantAt(TimeStamp::Now()); }
321
322 static MarkerTiming Interval(const TimeStamp& aStartTime,
323 const TimeStamp& aEndTime) {
324 MOZ_ASSERT(!aStartTime.IsNull(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aStartTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aStartTime.IsNull()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!aStartTime.IsNull()"
" (" "Start time is null for an interval marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aStartTime.IsNull()"
") (" "Start time is null for an interval marker." ")"); do {
*((volatile int*)__null) = 325; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
325 "Start time is null for an interval marker.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aStartTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aStartTime.IsNull()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!aStartTime.IsNull()"
" (" "Start time is null for an interval marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aStartTime.IsNull()"
") (" "Start time is null for an interval marker." ")"); do {
*((volatile int*)__null) = 325; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
326 MOZ_ASSERT(!aEndTime.IsNull(), "End time is null for an interval marker.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aEndTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aEndTime.IsNull()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!aEndTime.IsNull()"
" (" "End time is null for an interval marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 326); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aEndTime.IsNull()"
") (" "End time is null for an interval marker." ")"); do { *
((volatile int*)__null) = 326; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
327 return MarkerTiming{aStartTime, aEndTime, MarkerTiming::Phase::Interval};
328 }
329
330 static MarkerTiming IntervalUntilNowFrom(const TimeStamp& aStartTime) {
331 return Interval(aStartTime, TimeStamp::Now());
332 }
333
334 static MarkerTiming IntervalStart(const TimeStamp& aTime = TimeStamp::Now()) {
335 MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval start marker.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aTime.IsNull()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!aTime.IsNull()"
" (" "Time is null for an interval start marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 335); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aTime.IsNull()"
") (" "Time is null for an interval start marker." ")"); do {
*((volatile int*)__null) = 335; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
336 return MarkerTiming{aTime, TimeStamp{}, MarkerTiming::Phase::IntervalStart};
337 }
338
339 static MarkerTiming IntervalEnd(const TimeStamp& aTime = TimeStamp::Now()) {
340 MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aTime.IsNull()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!aTime.IsNull()"
" (" "Time is null for an interval end marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aTime.IsNull()"
") (" "Time is null for an interval end marker." ")"); do { *
((volatile int*)__null) = 340; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
341 return MarkerTiming{TimeStamp{}, aTime, MarkerTiming::Phase::IntervalEnd};
342 }
343
344 // Set the interval end in this timing.
345 // If there was already a start time, this makes it a full interval.
346 void SetIntervalEnd(const TimeStamp& aTime = TimeStamp::Now()) {
347 MOZ_ASSERT(!aTime.IsNull(), "Time is null for an interval end marker.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aTime.IsNull())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aTime.IsNull()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!aTime.IsNull()"
" (" "Time is null for an interval end marker." ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 347); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aTime.IsNull()"
") (" "Time is null for an interval end marker." ")"); do { *
((volatile int*)__null) = 347; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
348 mEndTime = aTime;
349 mPhase = mStartTime.IsNull() ? Phase::IntervalEnd : Phase::Interval;
350 }
351
352 [[nodiscard]] const TimeStamp& StartTime() const { return mStartTime; }
353 [[nodiscard]] const TimeStamp& EndTime() const { return mEndTime; }
354
355 // The phase differentiates Instant markers from Interval markers.
356 // Interval markers can either carry both timestamps on a single marker,
357 // or they can be split into individual Start and End markers, which are
358 // associated with each other via the marker name.
359 //
360 // The numeric representation of this enum value is also exposed in the
361 // ETW trace event's Phase field.
362 enum class Phase : uint8_t {
363 Instant = 0,
364 Interval = 1,
365 IntervalStart = 2,
366 IntervalEnd = 3,
367 };
368
369 [[nodiscard]] Phase MarkerPhase() const {
370 MOZ_ASSERT(!IsUnspecified())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsUnspecified())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsUnspecified()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!IsUnspecified()"
, "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsUnspecified()"
")"); do { *((volatile int*)__null) = 370; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
371 return mPhase;
372 }
373
374 // The following getter methods are used to put the value into the buffer for
375 // storage.
376 [[nodiscard]] double GetStartTime() const {
377 MOZ_ASSERT(!IsUnspecified())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsUnspecified())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsUnspecified()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!IsUnspecified()"
, "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsUnspecified()"
")"); do { *((volatile int*)__null) = 377; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
378 // If mStartTime is null (e.g., for IntervalEnd), this will output 0.0 as
379 // expected.
380 return MarkerTiming::timeStampToDouble(mStartTime);
381 }
382
383 [[nodiscard]] double GetEndTime() const {
384 MOZ_ASSERT(!IsUnspecified())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsUnspecified())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsUnspecified()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!IsUnspecified()"
, "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 384); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsUnspecified()"
")"); do { *((volatile int*)__null) = 384; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
385 // If mEndTime is null (e.g., for Instant or IntervalStart), this will
386 // output 0.0 as expected.
387 return MarkerTiming::timeStampToDouble(mEndTime);
388 }
389
390 [[nodiscard]] uint8_t GetPhase() const {
391 MOZ_ASSERT(!IsUnspecified())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsUnspecified())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsUnspecified()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!IsUnspecified()"
, "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsUnspecified()"
")"); do { *((volatile int*)__null) = 391; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
392 return static_cast<uint8_t>(mPhase);
393 }
394
395 // This is a constructor for Rust FFI bindings. It must not be used outside of
396 // this! Please see the other static constructors above.
397 static void UnsafeConstruct(MarkerTiming* aMarkerTiming,
398 const TimeStamp& aStartTime,
399 const TimeStamp& aEndTime, Phase aPhase) {
400 new (aMarkerTiming) MarkerTiming{aStartTime, aEndTime, aPhase};
401 }
402
403 private:
404 friend ProfileBufferEntryWriter::Serializer<MarkerTiming>;
405 friend ProfileBufferEntryReader::Deserializer<MarkerTiming>;
406 friend MarkerOptions;
407
408 // Default timing leaves it internally "unspecified", serialization getters
409 // and add-marker functions will default to `InstantNow()`.
410 constexpr MarkerTiming() = default;
411
412 // This should only be used by internal profiler code.
413 [[nodiscard]] bool IsUnspecified() const {
414 return mStartTime.IsNull() && mEndTime.IsNull();
415 }
416
417 // Full constructor, used by static factory functions.
418 constexpr MarkerTiming(const TimeStamp& aStartTime, const TimeStamp& aEndTime,
419 Phase aPhase)
420 : mStartTime(aStartTime), mEndTime(aEndTime), mPhase(aPhase) {}
421
422 static double timeStampToDouble(const TimeStamp& time) {
423 if (time.IsNull()) {
424 // The Phase lets us know not to use this value.
425 return 0;
426 }
427 return (time - TimeStamp::ProcessCreation()).ToMilliseconds();
428 }
429
430 TimeStamp mStartTime;
431 TimeStamp mEndTime;
432 Phase mPhase = Phase::Instant;
433};
434
435// This marker option allows three cases:
436// - By default, no stacks are captured.
437// - The caller can request a stack capture, and the add-marker code will take
438// care of it in the most efficient way.
439// - The caller can still provide an existing backtrace, for cases where a
440// marker reports something that happened elsewhere.
441class MarkerStack {
442 public:
443 // Default constructor, no capture.
444 constexpr MarkerStack() = default;
445
446 // Disallow copy.
447 MarkerStack(const MarkerStack&) = delete;
448 MarkerStack& operator=(const MarkerStack&) = delete;
449
450 // Allow move.
451 MarkerStack(MarkerStack&& aOther)
452 : mCaptureOptions(aOther.mCaptureOptions),
453 mOptionalChunkedBufferStorage(
454 std::move(aOther.mOptionalChunkedBufferStorage)),
455 mChunkedBuffer(aOther.mChunkedBuffer) {
456 AssertInvariants();
457 aOther.Clear();
458 }
459 MarkerStack& operator=(MarkerStack&& aOther) {
460 mCaptureOptions = aOther.mCaptureOptions;
461 mOptionalChunkedBufferStorage =
462 std::move(aOther.mOptionalChunkedBufferStorage);
463 mChunkedBuffer = aOther.mChunkedBuffer;
464 AssertInvariants();
465 aOther.Clear();
466 return *this;
467 }
468
469 // Take ownership of a backtrace. If null or empty, equivalent to NoStack().
470 explicit MarkerStack(UniquePtr<ProfileChunkedBuffer>&& aExternalChunkedBuffer)
471 : mOptionalChunkedBufferStorage(
472 (!aExternalChunkedBuffer || aExternalChunkedBuffer->IsEmpty())
8
Assuming the condition is false
9
'?' condition is false
473 ? nullptr
474 : std::move(aExternalChunkedBuffer)),
10
Calling '~UniquePtr'
19
Returning from '~UniquePtr'
475 mChunkedBuffer(mOptionalChunkedBufferStorage.get()) {
20
Calling 'UniquePtr::get'
476 AssertInvariants();
477 }
478
479 // Use an existing backtrace stored elsewhere, which the user must guarantee
480 // is alive during the add-marker call. If empty, equivalent to NoStack().
481 explicit MarkerStack(ProfileChunkedBuffer& aExternalChunkedBuffer)
482 : mChunkedBuffer(aExternalChunkedBuffer.IsEmpty()
483 ? nullptr
484 : &aExternalChunkedBuffer) {
485 AssertInvariants();
486 }
487
488 // Don't capture a stack in this marker.
489 static MarkerStack NoStack() {
490 return MarkerStack(StackCaptureOptions::NoStack);
491 }
492
493 // Capture a stack when adding this marker.
494 static MarkerStack Capture(
495 StackCaptureOptions aCaptureOptions = StackCaptureOptions::Full) {
496 // Actual capture will be handled inside profiler_add_marker.
497 return MarkerStack(aCaptureOptions);
498 }
499
500 // Optionally capture a stack, useful for avoiding long-winded ternaries.
501 static MarkerStack MaybeCapture(bool aDoCapture) {
502 return aDoCapture ? Capture() : NoStack();
503 }
504
505 // Use an existing backtrace stored elsewhere, which the user must guarantee
506 // is alive during the add-marker call. If empty, equivalent to NoStack().
507 static MarkerStack UseBacktrace(
508 ProfileChunkedBuffer& aExternalChunkedBuffer) {
509 return MarkerStack(aExternalChunkedBuffer);
510 }
511
512 // Take ownership of a backtrace previously captured with
513 // `profiler_capture_backtrace()`. If null, equivalent to NoStack().
514 static MarkerStack TakeBacktrace(
515 UniquePtr<ProfileChunkedBuffer>&& aExternalChunkedBuffer) {
516 return MarkerStack(std::move(aExternalChunkedBuffer));
7
Calling constructor for 'MarkerStack'
517 }
518
519 // Construct with the given capture options.
520 static MarkerStack WithCaptureOptions(StackCaptureOptions aCaptureOptions) {
521 return MarkerStack(aCaptureOptions);
522 }
523
524 [[nodiscard]] StackCaptureOptions CaptureOptions() const {
525 return mCaptureOptions;
526 }
527
528 ProfileChunkedBuffer* GetChunkedBuffer() const { return mChunkedBuffer; }
529
530 // Use backtrace after a request. If null, equivalent to NoStack().
531 void UseRequestedBacktrace(ProfileChunkedBuffer* aExternalChunkedBuffer) {
532 MOZ_RELEASE_ASSERT(mCaptureOptions != StackCaptureOptions::NoStack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mCaptureOptions != StackCaptureOptions::NoStack)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mCaptureOptions != StackCaptureOptions::NoStack))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mCaptureOptions != StackCaptureOptions::NoStack"
, "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 532); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mCaptureOptions != StackCaptureOptions::NoStack"
")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
533 mCaptureOptions = StackCaptureOptions::NoStack;
534 if (aExternalChunkedBuffer && !aExternalChunkedBuffer->IsEmpty()) {
535 // We only need to use the provided buffer if it is not empty.
536 mChunkedBuffer = aExternalChunkedBuffer;
537 }
538 AssertInvariants();
539 }
540
541 void Clear() {
542 mCaptureOptions = StackCaptureOptions::NoStack;
543 mOptionalChunkedBufferStorage.reset();
544 mChunkedBuffer = nullptr;
545 AssertInvariants();
546 }
547
548 private:
549 explicit MarkerStack(StackCaptureOptions aCaptureOptions)
550 : mCaptureOptions(aCaptureOptions) {
551 AssertInvariants();
552 }
553
554 // This should be called after every constructor and non-const function.
555 void AssertInvariants() const {
556#ifdef DEBUG1
557 if (mCaptureOptions != StackCaptureOptions::NoStack) {
558 MOZ_ASSERT(!mOptionalChunkedBufferStorage,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mOptionalChunkedBufferStorage)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mOptionalChunkedBufferStorage
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!mOptionalChunkedBufferStorage" " (" "We should not hold a buffer when capture is requested"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 559); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mOptionalChunkedBufferStorage"
") (" "We should not hold a buffer when capture is requested"
")"); do { *((volatile int*)__null) = 559; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
559 "We should not hold a buffer when capture is requested")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mOptionalChunkedBufferStorage)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mOptionalChunkedBufferStorage
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!mOptionalChunkedBufferStorage" " (" "We should not hold a buffer when capture is requested"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 559); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mOptionalChunkedBufferStorage"
") (" "We should not hold a buffer when capture is requested"
")"); do { *((volatile int*)__null) = 559; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
560 MOZ_ASSERT(!mChunkedBuffer,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mChunkedBuffer)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mChunkedBuffer))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!mChunkedBuffer"
" (" "We should not point at a buffer when capture is requested"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 561); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mChunkedBuffer"
") (" "We should not point at a buffer when capture is requested"
")"); do { *((volatile int*)__null) = 561; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
561 "We should not point at a buffer when capture is requested")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mChunkedBuffer)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mChunkedBuffer))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!mChunkedBuffer"
" (" "We should not point at a buffer when capture is requested"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 561); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mChunkedBuffer"
") (" "We should not point at a buffer when capture is requested"
")"); do { *((volatile int*)__null) = 561; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
562 } else {
563 if (mOptionalChunkedBufferStorage) {
564 MOZ_ASSERT(mChunkedBuffer == mOptionalChunkedBufferStorage.get(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mChunkedBuffer == mOptionalChunkedBufferStorage.get(
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mChunkedBuffer == mOptionalChunkedBufferStorage.get(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mChunkedBuffer == mOptionalChunkedBufferStorage.get()" " ("
"Non-null mOptionalChunkedBufferStorage must be pointed-at "
"by mChunkedBuffer" ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mChunkedBuffer == mOptionalChunkedBufferStorage.get()"
") (" "Non-null mOptionalChunkedBufferStorage must be pointed-at "
"by mChunkedBuffer" ")"); do { *((volatile int*)__null) = 566
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
565 "Non-null mOptionalChunkedBufferStorage must be pointed-at "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mChunkedBuffer == mOptionalChunkedBufferStorage.get(
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mChunkedBuffer == mOptionalChunkedBufferStorage.get(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mChunkedBuffer == mOptionalChunkedBufferStorage.get()" " ("
"Non-null mOptionalChunkedBufferStorage must be pointed-at "
"by mChunkedBuffer" ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mChunkedBuffer == mOptionalChunkedBufferStorage.get()"
") (" "Non-null mOptionalChunkedBufferStorage must be pointed-at "
"by mChunkedBuffer" ")"); do { *((volatile int*)__null) = 566
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
566 "by mChunkedBuffer")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mChunkedBuffer == mOptionalChunkedBufferStorage.get(
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mChunkedBuffer == mOptionalChunkedBufferStorage.get(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mChunkedBuffer == mOptionalChunkedBufferStorage.get()" " ("
"Non-null mOptionalChunkedBufferStorage must be pointed-at "
"by mChunkedBuffer" ")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mChunkedBuffer == mOptionalChunkedBufferStorage.get()"
") (" "Non-null mOptionalChunkedBufferStorage must be pointed-at "
"by mChunkedBuffer" ")"); do { *((volatile int*)__null) = 566
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
567 }
568 if (mChunkedBuffer) {
569 MOZ_ASSERT(!mChunkedBuffer->IsEmpty(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mChunkedBuffer->IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mChunkedBuffer->IsEmpty(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mChunkedBuffer->IsEmpty()" " (" "Non-null mChunkedBuffer must not be empty"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mChunkedBuffer->IsEmpty()"
") (" "Non-null mChunkedBuffer must not be empty" ")"); do {
*((volatile int*)__null) = 570; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
570 "Non-null mChunkedBuffer must not be empty")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mChunkedBuffer->IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mChunkedBuffer->IsEmpty(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mChunkedBuffer->IsEmpty()" " (" "Non-null mChunkedBuffer must not be empty"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/BaseProfilerMarkersPrerequisites.h"
, 570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mChunkedBuffer->IsEmpty()"
") (" "Non-null mChunkedBuffer must not be empty" ")"); do {
*((volatile int*)__null) = 570; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
571 }
572 }
573#endif // DEBUG
574 }
575
576 StackCaptureOptions mCaptureOptions = StackCaptureOptions::NoStack;
577
578 // Optional storage for the backtrace, in case it was captured before the
579 // add-marker call.
580 UniquePtr<ProfileChunkedBuffer> mOptionalChunkedBufferStorage;
581
582 // If not null, this points to the backtrace. It may point to a backtrace
583 // temporarily stored on the stack, or to mOptionalChunkedBufferStorage.
584 ProfileChunkedBuffer* mChunkedBuffer = nullptr;
585};
586
587// This marker option captures a given inner window id.
588class MarkerInnerWindowId {
589 public:
590 // Default constructor, it leaves the id unspecified.
591 constexpr MarkerInnerWindowId() = default;
592
593 // Constructor with a specified inner window id.
594 constexpr explicit MarkerInnerWindowId(uint64_t i) : mInnerWindowId(i) {}
595
596 // Constructor with either specified inner window id or Nothing.
597 constexpr explicit MarkerInnerWindowId(const Maybe<uint64_t>& i)
598 : mInnerWindowId(i.valueOr(scNoId)) {}
599
600 // Explicit option with unspecified id.
601 constexpr static MarkerInnerWindowId NoId() { return MarkerInnerWindowId{}; }
602
603 [[nodiscard]] bool IsUnspecified() const { return mInnerWindowId == scNoId; }
604
605 [[nodiscard]] constexpr uint64_t Id() const { return mInnerWindowId; }
606
607 private:
608 static constexpr uint64_t scNoId = 0;
609 uint64_t mInnerWindowId = scNoId;
610};
611
612// This class combines each of the possible marker options above.
613class MarkerOptions {
614 public:
615 // Constructor from individual options (including none).
616 // Implicit to allow `{}` and one option type as-is.
617 // Options that are not provided here are defaulted. In particular, timing
618 // defaults to `MarkerTiming::InstantNow()` when the marker is recorded.
619 template <typename... Options>
620 MOZ_IMPLICIT MarkerOptions(Options&&... aOptions) {
621 (Set(std::forward<Options>(aOptions)), ...);
622 }
623
624 // Disallow copy.
625 MarkerOptions(const MarkerOptions&) = delete;
626 MarkerOptions& operator=(const MarkerOptions&) = delete;
627
628 // Allow move.
629 MarkerOptions(MarkerOptions&&) = default;
630 MarkerOptions& operator=(MarkerOptions&&) = default;
631
632 // The embedded `MarkerTiming` hasn't been specified yet.
633 [[nodiscard]] bool IsTimingUnspecified() const {
634 return mTiming.IsUnspecified();
635 }
636
637 // Each option may be added in a chain by e.g.:
638 // `options.Set(MarkerThreadId(123)).Set(MarkerTiming::IntervalEnd())`.
639 // When passed to an add-marker function, it must be an rvalue, either created
640 // on the spot, or `std::move`d from storage, e.g.:
641 // `PROFILER_MARKER_UNTYPED("...", std::move(options).Set(...))`;
642 //
643 // Options can be read by their name (without "Marker"), e.g.: `o.ThreadId()`.
644 // Add "Ref" for a non-const reference, e.g.: `o.ThreadIdRef() = ...;`
645#define FUNCTIONS_ON_MEMBER(NAME) \
646 MarkerOptions& Set(Marker##NAME&& a##NAME)& { \
647 m##NAME = std::move(a##NAME); \
648 return *this; \
649 } \
650 \
651 MarkerOptions&& Set(Marker##NAME&& a##NAME)&& { \
652 m##NAME = std::move(a##NAME); \
653 return std::move(*this); \
654 } \
655 \
656 const Marker##NAME& NAME() const { return m##NAME; } \
657 \
658 Marker##NAME& NAME##Ref() { return m##NAME; }
659
660 FUNCTIONS_ON_MEMBER(ThreadId);
661 FUNCTIONS_ON_MEMBER(Timing);
662 FUNCTIONS_ON_MEMBER(Stack);
663 FUNCTIONS_ON_MEMBER(InnerWindowId);
664#undef FUNCTIONS_ON_MEMBER
665
666 private:
667 friend ProfileBufferEntryReader::Deserializer<MarkerOptions>;
668
669 MarkerThreadId mThreadId;
670 MarkerTiming mTiming;
671 MarkerStack mStack;
672 MarkerInnerWindowId mInnerWindowId;
673};
674
675} // namespace mozilla
676
677namespace mozilla::baseprofiler::markers {
678
679// Default marker payload types, with no extra information, not even a marker
680// type and payload. This is intended for label-only markers.
681struct NoPayload final {};
682
683} // namespace mozilla::baseprofiler::markers
684
685namespace mozilla {
686
687class JSONWriter;
688
689// This class collects all the information necessary to stream the JSON schema
690// that informs the front-end how to display a type of markers.
691// It will be created and populated in `MarkerTypeDisplay()` functions in each
692// marker type definition, see Add/Set functions.
693class MarkerSchema {
694 public:
695 // This is used to describe a C++ type that is expected to be specified to
696 // the marker and used in PayloadField. This type is the expected input type
697 // to the marker data.
698 enum class InputType {
699 Uint64,
700 Uint32,
701 Uint8,
702 Boolean,
703 CString,
704 String,
705 TimeStamp,
706 TimeDuration
707 };
708
709 enum class Location : unsigned {
710 MarkerChart,
711 MarkerTable,
712 // This adds markers to the main marker timeline in the header.
713 TimelineOverview,
714 // In the timeline, this is a section that breaks out markers that are
715 // related to memory. When memory counters are enabled, this is its own
716 // track, otherwise it is displayed with the main thread.
717 TimelineMemory,
718 // This adds markers to the IPC timeline area in the header.
719 TimelineIPC,
720 // This adds markers to the FileIO timeline area in the header.
721 TimelineFileIO,
722 // TODO - This is not supported yet.
723 StackChart
724 };
725
726 // Used as constructor parameter, to explicitly specify that the location (and
727 // other display options) are handled as a special case in the front-end.
728 // In this case, *no* schema will be output for this type.
729 struct SpecialFrontendLocation {};
730
731 enum class Format {
732 // ----------------------------------------------------
733 // String types.
734
735 // Show the URL, and handle PII sanitization
736 Url,
737 // Show the file path, and handle PII sanitization.
738 FilePath,
739 // Show arbitrary string and handle PII sanitization
740 SanitizedString,
741 // Important, do not put URL or file path information here, as it will not
742 // be sanitized. Please be careful with including other types of PII here as
743 // well.
744 // e.g. "Label: Some String"
745 String,
746
747 // Show a string from a UniqueStringArray given an index in the profile.
748 // e.g. 1, given string table ["hello", "world"] will show "world"
749 UniqueString,
750
751 // ----------------------------------------------------
752 // Numeric types
753
754 // For time data that represents a duration of time.
755 // e.g. "Label: 5s, 5ms, 5μs"
756 Duration,
757 // Data that happened at a specific time, relative to the start of the
758 // profile. e.g. "Label: 15.5s, 20.5ms, 30.5μs"
759 Time,
760 // The following are alternatives to display a time only in a specific unit
761 // of time.
762 Seconds, // "Label: 5s"
763 Milliseconds, // "Label: 5ms"
764 Microseconds, // "Label: 5μs"
765 Nanoseconds, // "Label: 5ns"
766 // e.g. "Label: 5.55mb, 5 bytes, 312.5kb"
767 Bytes,
768 // This should be a value between 0 and 1.
769 // "Label: 50%"
770 Percentage,
771 // The integer should be used for generic representations of numbers.
772 // Do not use it for time information.
773 // "Label: 52, 5,323, 1,234,567"
774 Integer,
775 // The decimal should be used for generic representations of numbers.
776 // Do not use it for time information.
777 // "Label: 52.23, 0.0054, 123,456.78"
778 Decimal,
779
780 // A flow is a u64 identifier that's unique across processes. All of
781 // the markers with same flow id before a terminating flow id will be
782 // considered part of the same "flow" and linked together.
783 Flow,
784 // A terminating flow ends a flow of a particular id and allows that id
785 // to be reused again. It often makes sense for destructors to create
786 // a marker with a field of this type.
787 TerminatingFlow
788 };
789
790 // This represents groups of markers which MarkerTypes can expose to indicate
791 // what group they belong to (multiple groups are allowed combined in bitwise
792 // or). This is currently only used for ETW filtering. In the long run this
793 // should be generalized to gecko markers.
794 enum class ETWMarkerGroup : uint64_t {
795 Generic = 1,
796 UserMarkers = 1 << 1,
797 Memory = 1 << 2,
798 Scheduling = 1 << 3,
799 Text = 1 << 4,
800 Tracing = 1 << 5
801 };
802
803 // Flags which describe additional information for a PayloadField.
804 enum class PayloadFlags : uint32_t { None = 0, Searchable = 1 };
805
806 // This is one field of payload to be used for additional marker data.
807 struct PayloadField {
808 // Key identifying the marker.
809 const char* Key;
810 // Input type, this represents the data type specified.
811 InputType InputTy;
812 // Label, additional description.
813 const char* Label = nullptr;
814 // Format as written to the JSON.
815 Format Fmt = Format::String;
816 // Optional PayloadFlags.
817 PayloadFlags Flags = PayloadFlags::None;
818 };
819
820 enum class Searchable { NotSearchable, Searchable };
821 enum class GraphType { Line, Bar, FilledLine };
822 enum class GraphColor {
823 Blue,
824 Green,
825 Grey,
826 Ink,
827 Magenta,
828 Orange,
829 Purple,
830 Red,
831 Teal,
832 Yellow
833 };
834
835 // Marker schema, with a non-empty list of locations where markers should be
836 // shown.
837 // Tech note: Even though `aLocations` are templated arguments, they are
838 // assigned to an `enum class` object, so they can only be of that enum type.
839 template <typename... Locations>
840 explicit MarkerSchema(Location aLocation, Locations... aLocations)
841 : mLocations{aLocation, aLocations...} {}
842
843 // Alternative constructor for MarkerSchema.
844 explicit MarkerSchema(const mozilla::MarkerSchema::Location* aLocations,
845 size_t aLength)
846 : mLocations(aLocations, aLocations + aLength) {}
847
848 // Marker schema for types that have special frontend handling.
849 // Nothing else should be set in this case.
850 // Implicit to allow quick return from MarkerTypeDisplay functions.
851 MOZ_IMPLICIT MarkerSchema(SpecialFrontendLocation) {}
852
853 // Caller must specify location(s) or SpecialFrontendLocation above.
854 MarkerSchema() = delete;
855
856 // Optional labels in the marker chart, the chart tooltip, and the marker
857 // table. If not provided, the marker "name" will be used. The given string
858 // can contain element keys in braces to include data elements streamed by
859 // `StreamJSONMarkerData()`. E.g.: "This is {text}"
860
861#define LABEL_SETTER(name) \
862 MarkerSchema& Set##name(std::string a##name) { \
863 m##name = std::move(a##name); \
864 return *this; \
865 }
866
867 LABEL_SETTER(ChartLabel)
868 LABEL_SETTER(TooltipLabel)
869 LABEL_SETTER(TableLabel)
870
871#undef LABEL_SETTER
872
873 MarkerSchema& SetAllLabels(std::string aText) {
874 // Here we set the same text in each label.
875 // TODO: Move to a single "label" field once the front-end allows it.
876 SetChartLabel(aText);
877 SetTooltipLabel(aText);
878 SetTableLabel(std::move(aText));
879 return *this;
880 }
881
882 MarkerSchema& SetIsStackBased() {
883 mIsStackBased = true;
884 return *this;
885 }
886
887 // Each data element that is streamed by `StreamJSONMarkerData()` can be
888 // displayed as indicated by using one of the `Add...` function below.
889 // Each `Add...` will add a line in the full marker description. Parameters:
890 // - `aKey`: Element property name as streamed by `StreamJSONMarkerData()`.
891 // - `aLabel`: Optional prefix. Defaults to the key name.
892 // - `aFormat`: How to format the data element value, see `Format` above.
893 // - `aSearchable`: Optional, indicates if the value is used in searches,
894 // defaults to false.
895
896 MarkerSchema& AddKeyFormat(std::string aKey, Format aFormat) {
897 mData.emplace_back(mozilla::VariantType<DynamicData>{},
898 DynamicData{std::move(aKey), mozilla::Nothing{}, aFormat,
899 mozilla::Nothing{}});
900 return *this;
901 }
902
903 MarkerSchema& AddKeyLabelFormat(std::string aKey, std::string aLabel,
904 Format aFormat) {
905 mData.emplace_back(
906 mozilla::VariantType<DynamicData>{},
907 DynamicData{std::move(aKey), mozilla::Some(std::move(aLabel)), aFormat,
908 mozilla::Nothing{}});
909 return *this;
910 }
911
912 MarkerSchema& AddKeyFormatSearchable(std::string aKey, Format aFormat,
913 Searchable aSearchable) {
914 mData.emplace_back(mozilla::VariantType<DynamicData>{},
915 DynamicData{std::move(aKey), mozilla::Nothing{}, aFormat,
916 mozilla::Some(aSearchable)});
917 return *this;
918 }
919
920 MarkerSchema& AddKeyLabelFormatSearchable(std::string aKey,
921 std::string aLabel, Format aFormat,
922 Searchable aSearchable) {
923 mData.emplace_back(
924 mozilla::VariantType<DynamicData>{},
925 DynamicData{std::move(aKey), mozilla::Some(std::move(aLabel)), aFormat,
926 mozilla::Some(aSearchable)});
927 return *this;
928 }
929
930 // The display may also include static rows.
931
932 MarkerSchema& AddStaticLabelValue(std::string aLabel, std::string aValue) {
933 mData.emplace_back(mozilla::VariantType<StaticData>{},
934 StaticData{std::move(aLabel), std::move(aValue)});
935 return *this;
936 }
937
938 // Markers can be shown as timeline tracks.
939
940 MarkerSchema& AddChart(std::string aKey, GraphType aType) {
941 mGraphs.emplace_back(GraphData{std::move(aKey), aType, mozilla::Nothing{}});
942 return *this;
943 }
944
945 MarkerSchema& AddChartColor(std::string aKey, GraphType aType,
946 GraphColor aColor) {
947 mGraphs.emplace_back(
948 GraphData{std::move(aKey), aType, mozilla::Some(aColor)});
949 return *this;
950 }
951
952 // Internal streaming function.
953 MFBT_API__attribute__((weak)) __attribute__((visibility("default"))) void Stream(JSONWriter& aWriter, const Span<const char>& aName) &&;
954
955 private:
956 MFBT_API__attribute__((weak)) __attribute__((visibility("default"))) static Span<const char> LocationToStringSpan(Location aLocation);
957 MFBT_API__attribute__((weak)) __attribute__((visibility("default"))) static Span<const char> FormatToStringSpan(Format aFormat);
958 MFBT_API__attribute__((weak)) __attribute__((visibility("default"))) static Span<const char> GraphTypeToStringSpan(GraphType aType);
959 MFBT_API__attribute__((weak)) __attribute__((visibility("default"))) static Span<const char> GraphColorToStringSpan(GraphColor aColor);
960
961 // List of marker display locations. Empty for SpecialFrontendLocation.
962 std::vector<Location> mLocations;
963 // Labels for different places.
964 std::string mChartLabel;
965 std::string mTooltipLabel;
966 std::string mTableLabel;
967 bool mIsStackBased = false;
968 // Main display, made of zero or more rows of key+label+format or label+value.
969 private:
970 struct DynamicData {
971 std::string mKey;
972 mozilla::Maybe<std::string> mLabel;
973 Format mFormat;
974 mozilla::Maybe<Searchable> mSearchable;
975 };
976 struct StaticData {
977 std::string mLabel;
978 std::string mValue;
979 };
980 using DataRow = mozilla::Variant<DynamicData, StaticData>;
981 using DataRowVector = std::vector<DataRow>;
982
983 DataRowVector mData;
984
985 struct GraphData {
986 std::string mKey;
987 GraphType mType;
988 mozilla::Maybe<GraphColor> mColor;
989 };
990 std::vector<GraphData> mGraphs;
991};
992
993namespace detail {
994// GCC doesn't allow this to live inside the class.
995template <typename PayloadType>
996static void StreamPayload(baseprofiler::SpliceableJSONWriter& aWriter,
997 const Span<const char> aKey,
998 const PayloadType& aPayload) {
999 aWriter.StringProperty(aKey, aPayload);
1000}
1001
1002template <typename PayloadType>
1003inline void StreamPayload(baseprofiler::SpliceableJSONWriter& aWriter,
1004 const Span<const char> aKey,
1005 const Maybe<PayloadType>& aPayload) {
1006 if (aPayload.isSome()) {
1007 StreamPayload(aWriter, aKey, *aPayload);
1008 } else {
1009 aWriter.NullProperty(aKey);
1010 }
1011}
1012
1013template <>
1014inline void StreamPayload<bool>(baseprofiler::SpliceableJSONWriter& aWriter,
1015 const Span<const char> aKey,
1016 const bool& aPayload) {
1017 aWriter.BoolProperty(aKey, aPayload);
1018}
1019
1020template <>
1021inline void StreamPayload<ProfilerString8View>(
1022 baseprofiler::SpliceableJSONWriter& aWriter, const Span<const char> aKey,
1023 const ProfilerString8View& aPayload) {
1024 aWriter.StringProperty(aKey, aPayload);
1025}
1026} // namespace detail
1027
1028// This helper class is used by MarkerTypes that want to support the general
1029// MarkerType object schema. When using this the markers will also transmit
1030// their payload to the ETW tracer as well as requiring less inline code.
1031// This is a curiously recurring template, the template argument is the child
1032// class itself.
1033template <typename T>
1034struct BaseMarkerType {
1035 static constexpr const char* AllLabels = nullptr;
1036 static constexpr const char* ChartLabel = nullptr;
1037 static constexpr const char* TableLabel = nullptr;
1038 static constexpr const char* TooltipLabel = nullptr;
1039
1040 // Setting this property to true is a promise that the the marker will nest
1041 // properly. i.e. it can't have a partially overlapping time range with any
1042 // other stack based markers on the same thread.
1043 static constexpr bool IsStackBased = false;
1044
1045 // This indicates whether this marker type wants the names passed to the
1046 // individual marker calls stores along with the marker.
1047 static constexpr bool StoreName = false;
1048
1049 static constexpr MarkerSchema::ETWMarkerGroup Group =
1050 MarkerSchema::ETWMarkerGroup::Generic;
1051
1052 static MarkerSchema MarkerTypeDisplay() {
1053 using MS = MarkerSchema;
1054 MS schema{T::Locations, std::size(T::Locations)};
1055 if (T::AllLabels) {
1056 schema.SetAllLabels(T::AllLabels);
1057 }
1058 if (T::ChartLabel) {
1059 schema.SetChartLabel(T::ChartLabel);
1060 }
1061 if (T::TableLabel) {
1062 schema.SetTableLabel(T::TableLabel);
1063 }
1064 if (T::TooltipLabel) {
1065 schema.SetTooltipLabel(T::TooltipLabel);
1066 }
1067 if (T::IsStackBased) {
1068 schema.SetIsStackBased();
1069 }
1070 for (const MS::PayloadField field : T::PayloadFields) {
1071 if (field.Label) {
1072 if (uint32_t(field.Flags) & uint32_t(MS::PayloadFlags::Searchable)) {
1073 schema.AddKeyLabelFormatSearchable(field.Key, field.Label, field.Fmt,
1074 MS::Searchable::Searchable);
1075 } else {
1076 schema.AddKeyLabelFormat(field.Key, field.Label, field.Fmt);
1077 }
1078 } else {
1079 if (uint32_t(field.Flags) & uint32_t(MS::PayloadFlags::Searchable)) {
1080 schema.AddKeyFormatSearchable(field.Key, field.Fmt,
1081 MS::Searchable::Searchable);
1082 } else {
1083 schema.AddKeyFormat(field.Key, field.Fmt);
1084 }
1085 }
1086 }
1087 if (T::Description) {
1088 schema.AddStaticLabelValue("Description", T::Description);
1089 }
1090 return schema;
1091 }
1092
1093 static constexpr Span<const char> MarkerTypeName() {
1094 return MakeStringSpan(T::Name);
1095 }
1096
1097 // This is called by the child class since the child class version of this
1098 // function is used to infer the argument types by the profile buffer and
1099 // allows the child to do any special data conversion it needs to do.
1100 // Optionally the child can opt not to use this at all and write the data
1101 // out itself.
1102 template <typename... PayloadArguments>
1103 static void StreamJSONMarkerDataImpl(
1104 baseprofiler::SpliceableJSONWriter& aWriter,
1105 const PayloadArguments&... aPayloadArguments) {
1106 size_t i = 0;
1107 (detail::StreamPayload(aWriter, MakeStringSpan(T::PayloadFields[i++].Key),
1108 aPayloadArguments),
1109 ...);
1110 }
1111};
1112} // namespace mozilla
1113
1114#endif // BaseProfilerMarkersPrerequisites_h

/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/UniquePtr.h

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/* Smart pointer managing sole ownership of a resource. */
8
9#ifndef mozilla_UniquePtr_h
10#define mozilla_UniquePtr_h
11
12#include <memory>
13#include <type_traits>
14#include <utility>
15
16#include "mozilla/Assertions.h"
17#include "mozilla/Attributes.h"
18#include "mozilla/CompactPair.h"
19#include "mozilla/Compiler.h"
20
21namespace mozilla {
22
23template <typename T>
24class DefaultDelete;
25template <typename T, class D = DefaultDelete<T>>
26class UniquePtr;
27
28} // namespace mozilla
29
30namespace mozilla {
31
32namespace detail {
33
34struct HasPointerTypeHelper {
35 template <class U>
36 static double Test(...);
37 template <class U>
38 static char Test(typename U::pointer* = 0);
39};
40
41template <class T>
42class HasPointerType
43 : public std::integral_constant<bool, sizeof(HasPointerTypeHelper::Test<T>(
44 0)) == 1> {};
45
46template <class T, class D, bool = HasPointerType<D>::value>
47struct PointerTypeImpl {
48 typedef typename D::pointer Type;
49};
50
51template <class T, class D>
52struct PointerTypeImpl<T, D, false> {
53 typedef T* Type;
54};
55
56template <class T, class D>
57struct PointerType {
58 typedef typename PointerTypeImpl<T, std::remove_reference_t<D>>::Type Type;
59};
60
61} // namespace detail
62
63/**
64 * UniquePtr is a smart pointer that wholly owns a resource. Ownership may be
65 * transferred out of a UniquePtr through explicit action, but otherwise the
66 * resource is destroyed when the UniquePtr is destroyed.
67 *
68 * UniquePtr is similar to C++98's std::auto_ptr, but it improves upon auto_ptr
69 * in one crucial way: it's impossible to copy a UniquePtr. Copying an auto_ptr
70 * obviously *can't* copy ownership of its singly-owned resource. So what
71 * happens if you try to copy one? Bizarrely, ownership is implicitly
72 * *transferred*, preserving single ownership but breaking code that assumes a
73 * copy of an object is identical to the original. (This is why auto_ptr is
74 * prohibited in STL containers.)
75 *
76 * UniquePtr solves this problem by being *movable* rather than copyable.
77 * Instead of passing a |UniquePtr u| directly to the constructor or assignment
78 * operator, you pass |Move(u)|. In doing so you indicate that you're *moving*
79 * ownership out of |u|, into the target of the construction/assignment. After
80 * the transfer completes, |u| contains |nullptr| and may be safely destroyed.
81 * This preserves single ownership but also allows UniquePtr to be moved by
82 * algorithms that have been made move-safe. (Note: if |u| is instead a
83 * temporary expression, don't use |Move()|: just pass the expression, because
84 * it's already move-ready. For more information see Move.h.)
85 *
86 * UniquePtr is also better than std::auto_ptr in that the deletion operation is
87 * customizable. An optional second template parameter specifies a class that
88 * (through its operator()(T*)) implements the desired deletion policy. If no
89 * policy is specified, mozilla::DefaultDelete<T> is used -- which will either
90 * |delete| or |delete[]| the resource, depending whether the resource is an
91 * array. Custom deletion policies ideally should be empty classes (no member
92 * fields, no member fields in base classes, no virtual methods/inheritance),
93 * because then UniquePtr can be just as efficient as a raw pointer.
94 *
95 * Use of UniquePtr proceeds like so:
96 *
97 * UniquePtr<int> g1; // initializes to nullptr
98 * g1.reset(new int); // switch resources using reset()
99 * g1 = nullptr; // clears g1, deletes the int
100 *
101 * UniquePtr<int> g2(new int); // owns that int
102 * int* p = g2.release(); // g2 leaks its int -- still requires deletion
103 * delete p; // now freed
104 *
105 * struct S { int x; S(int x) : x(x) {} };
106 * UniquePtr<S> g3, g4(new S(5));
107 * g3 = std::move(g4); // g3 owns the S, g4 cleared
108 * S* p = g3.get(); // g3 still owns |p|
109 * assert(g3->x == 5); // operator-> works (if .get() != nullptr)
110 * assert((*g3).x == 5); // also operator* (again, if not cleared)
111 * std::swap(g3, g4); // g4 now owns the S, g3 cleared
112 * g3.swap(g4); // g3 now owns the S, g4 cleared
113 * UniquePtr<S> g5(std::move(g3)); // g5 owns the S, g3 cleared
114 * g5.reset(); // deletes the S, g5 cleared
115 *
116 * struct FreePolicy { void operator()(void* p) { free(p); } };
117 * UniquePtr<int, FreePolicy> g6(static_cast<int*>(malloc(sizeof(int))));
118 * int* ptr = g6.get();
119 * g6 = nullptr; // calls free(ptr)
120 *
121 * Now, carefully note a few things you *can't* do:
122 *
123 * UniquePtr<int> b1;
124 * b1 = new int; // BAD: can only assign another UniquePtr
125 * int* ptr = b1; // BAD: no auto-conversion to pointer, use get()
126 *
127 * UniquePtr<int> b2(b1); // BAD: can't copy a UniquePtr
128 * UniquePtr<int> b3 = b1; // BAD: can't copy-assign a UniquePtr
129 *
130 * (Note that changing a UniquePtr to store a direct |new| expression is
131 * permitted, but usually you should use MakeUnique, defined at the end of this
132 * header.)
133 *
134 * A few miscellaneous notes:
135 *
136 * UniquePtr, when not instantiated for an array type, can be move-constructed
137 * and move-assigned, not only from itself but from "derived" UniquePtr<U, E>
138 * instantiations where U converts to T and E converts to D. If you want to use
139 * this, you're going to have to specify a deletion policy for both UniquePtr
140 * instantations, and T pretty much has to have a virtual destructor. In other
141 * words, this doesn't work:
142 *
143 * struct Base { virtual ~Base() {} };
144 * struct Derived : Base {};
145 *
146 * UniquePtr<Base> b1;
147 * // BAD: DefaultDelete<Base> and DefaultDelete<Derived> don't interconvert
148 * UniquePtr<Derived> d1(std::move(b));
149 *
150 * UniquePtr<Base> b2;
151 * UniquePtr<Derived, DefaultDelete<Base>> d2(std::move(b2)); // okay
152 *
153 * UniquePtr is specialized for array types. Specializing with an array type
154 * creates a smart-pointer version of that array -- not a pointer to such an
155 * array.
156 *
157 * UniquePtr<int[]> arr(new int[5]);
158 * arr[0] = 4;
159 *
160 * What else is different? Deletion of course uses |delete[]|. An operator[]
161 * is provided. Functionality that doesn't make sense for arrays is removed.
162 * The constructors and mutating methods only accept array pointers (not T*, U*
163 * that converts to T*, or UniquePtr<U[]> or UniquePtr<U>) or |nullptr|.
164 *
165 * It's perfectly okay for a function to return a UniquePtr. This transfers
166 * the UniquePtr's sole ownership of the data, to the fresh UniquePtr created
167 * in the calling function, that will then solely own that data. Such functions
168 * can return a local variable UniquePtr, |nullptr|, |UniquePtr(ptr)| where
169 * |ptr| is a |T*|, or a UniquePtr |Move()|'d from elsewhere.
170 *
171 * UniquePtr will commonly be a member of a class, with lifetime equivalent to
172 * that of that class. If you want to expose the related resource, you could
173 * expose a raw pointer via |get()|, but ownership of a raw pointer is
174 * inherently unclear. So it's better to expose a |const UniquePtr&| instead.
175 * This prohibits mutation but still allows use of |get()| when needed (but
176 * operator-> is preferred). Of course, you can only use this smart pointer as
177 * long as the enclosing class instance remains live -- no different than if you
178 * exposed the |get()| raw pointer.
179 *
180 * To pass a UniquePtr-managed resource as a pointer, use a |const UniquePtr&|
181 * argument. To specify an inout parameter (where the method may or may not
182 * take ownership of the resource, or reset it), or to specify an out parameter
183 * (where simply returning a |UniquePtr| isn't possible), use a |UniquePtr&|
184 * argument. To unconditionally transfer ownership of a UniquePtr
185 * into a method, use a |UniquePtr| argument. To conditionally transfer
186 * ownership of a resource into a method, should the method want it, use a
187 * |UniquePtr&&| argument.
188 */
189template <typename T, class D>
190class UniquePtr {
191 public:
192 typedef T ElementType;
193 typedef D DeleterType;
194 typedef typename detail::PointerType<T, DeleterType>::Type Pointer;
195
196 private:
197 mozilla::CompactPair<Pointer, DeleterType> mTuple;
198
199 Pointer& ptr() { return mTuple.first(); }
200 const Pointer& ptr() const { return mTuple.first(); }
201
202 DeleterType& del() { return mTuple.second(); }
203 const DeleterType& del() const { return mTuple.second(); }
204
205 public:
206 /**
207 * Construct a UniquePtr containing |nullptr|.
208 */
209 constexpr UniquePtr() : mTuple(static_cast<Pointer>(nullptr), DeleterType()) {
210 static_assert(!std::is_pointer_v<D>, "must provide a deleter instance");
211 static_assert(!std::is_reference_v<D>, "must provide a deleter instance");
212 }
213
214 /**
215 * Construct a UniquePtr containing |aPtr|.
216 */
217 explicit UniquePtr(Pointer aPtr) : mTuple(aPtr, DeleterType()) {
218 static_assert(!std::is_pointer_v<D>, "must provide a deleter instance");
219 static_assert(!std::is_reference_v<D>, "must provide a deleter instance");
220 }
221
222 UniquePtr(Pointer aPtr,
223 std::conditional_t<std::is_reference_v<D>, D, const D&> aD1)
224 : mTuple(aPtr, aD1) {}
225
226 UniquePtr(Pointer aPtr, std::remove_reference_t<D>&& aD2)
227 : mTuple(aPtr, std::move(aD2)) {
228 static_assert(!std::is_reference_v<D>,
229 "rvalue deleter can't be stored by reference");
230 }
231
232 UniquePtr(UniquePtr&& aOther)
233 : mTuple(aOther.release(),
234 std::forward<DeleterType>(aOther.get_deleter())) {}
235
236 MOZ_IMPLICIT constexpr UniquePtr(decltype(nullptr)) : UniquePtr() {}
237
238 template <typename U, class E>
239 MOZ_IMPLICIT UniquePtr(
240 UniquePtr<U, E>&& aOther,
241 std::enable_if_t<
242 std::is_convertible_v<typename UniquePtr<U, E>::Pointer, Pointer> &&
243 !std::is_array_v<U> &&
244 (std::is_reference_v<D> ? std::is_same_v<D, E>
245 : std::is_convertible_v<E, D>),
246 int>
247 aDummy = 0)
248 : mTuple(aOther.release(), std::forward<E>(aOther.get_deleter())) {}
249
250 ~UniquePtr() { reset(nullptr); }
11
Calling 'UniquePtr::reset'
18
Returning; memory was released
251
252 UniquePtr& operator=(UniquePtr&& aOther) {
253 reset(aOther.release());
254 get_deleter() = std::forward<DeleterType>(aOther.get_deleter());
255 return *this;
256 }
257
258 template <typename U, typename E>
259 UniquePtr& operator=(UniquePtr<U, E>&& aOther) {
260 static_assert(
261 std::is_convertible_v<typename UniquePtr<U, E>::Pointer, Pointer>,
262 "incompatible UniquePtr pointees");
263 static_assert(!std::is_array_v<U>,
264 "can't assign from UniquePtr holding an array");
265
266 reset(aOther.release());
267 get_deleter() = std::forward<E>(aOther.get_deleter());
268 return *this;
269 }
270
271 UniquePtr& operator=(decltype(nullptr)) {
272 reset(nullptr);
273 return *this;
274 }
275
276 std::add_lvalue_reference_t<T> operator*() const {
277 MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr with *")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(get())>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(get()))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("get()" " (" "dereferencing a UniquePtr containing nullptr with *"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/UniquePtr.h"
, 277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "get()" ") ("
"dereferencing a UniquePtr containing nullptr with *" ")"); do
{ *((volatile int*)__null) = 277; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
278 return *get();
279 }
280 Pointer operator->() const {
281 MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr with ->")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(get())>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(get()))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("get()" " (" "dereferencing a UniquePtr containing nullptr with ->"
")", "/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/UniquePtr.h"
, 281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "get()" ") ("
"dereferencing a UniquePtr containing nullptr with ->" ")"
); do { *((volatile int*)__null) = 281; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
282 return get();
283 }
284
285 explicit operator bool() const { return get() != nullptr; }
286
287 Pointer get() const { return ptr(); }
21
Use of memory after it is freed
288
289 DeleterType& get_deleter() { return del(); }
290 const DeleterType& get_deleter() const { return del(); }
291
292 [[nodiscard]] Pointer release() {
293 Pointer p = ptr();
294 ptr() = nullptr;
295 return p;
296 }
297
298 void reset(Pointer aPtr = Pointer()) {
299 Pointer old = ptr();
300 ptr() = aPtr;
301 if (old != nullptr) {
12
Taking true branch
302 get_deleter()(old);
13
Calling 'DefaultDelete::operator()'
17
Returning; memory was released via 2nd parameter
303 }
304 }
305
306 void swap(UniquePtr& aOther) { mTuple.swap(aOther.mTuple); }
307
308 UniquePtr(const UniquePtr& aOther) = delete; // construct using std::move()!
309 void operator=(const UniquePtr& aOther) =
310 delete; // assign using std::move()!
311};
312
313// In case you didn't read the comment by the main definition (you should!): the
314// UniquePtr<T[]> specialization exists to manage array pointers. It deletes
315// such pointers using delete[], it will reject construction and modification
316// attempts using U* or U[]. Otherwise it works like the normal UniquePtr.
317template <typename T, class D>
318class UniquePtr<T[], D> {
319 public:
320 typedef T* Pointer;
321 typedef T ElementType;
322 typedef D DeleterType;
323
324 private:
325 mozilla::CompactPair<Pointer, DeleterType> mTuple;
326
327 public:
328 /**
329 * Construct a UniquePtr containing nullptr.
330 */
331 constexpr UniquePtr() : mTuple(static_cast<Pointer>(nullptr), DeleterType()) {
332 static_assert(!std::is_pointer_v<D>, "must provide a deleter instance");
333 static_assert(!std::is_reference_v<D>, "must provide a deleter instance");
334 }
335
336 /**
337 * Construct a UniquePtr containing |aPtr|.
338 */
339 explicit UniquePtr(Pointer aPtr) : mTuple(aPtr, DeleterType()) {
340 static_assert(!std::is_pointer_v<D>, "must provide a deleter instance");
341 static_assert(!std::is_reference_v<D>, "must provide a deleter instance");
342 }
343
344 // delete[] knows how to handle *only* an array of a single class type. For
345 // delete[] to work correctly, it must know the size of each element, the
346 // fields and base classes of each element requiring destruction, and so on.
347 // So forbid all overloads which would end up invoking delete[] on a pointer
348 // of the wrong type.
349 template <typename U>
350 UniquePtr(U&& aU,
351 std::enable_if_t<
352 std::is_pointer_v<U> && std::is_convertible_v<U, Pointer>, int>
353 aDummy = 0) = delete;
354
355 UniquePtr(Pointer aPtr,
356 std::conditional_t<std::is_reference_v<D>, D, const D&> aD1)
357 : mTuple(aPtr, aD1) {}
358
359 UniquePtr(Pointer aPtr, std::remove_reference_t<D>&& aD2)
360 : mTuple(aPtr, std::move(aD2)) {
361 static_assert(!std::is_reference_v<D>,
362 "rvalue deleter can't be stored by reference");
363 }
364
365 // Forbidden for the same reasons as stated above.
366 template <typename U, typename V>
367 UniquePtr(U&& aU, V&& aV,
368 std::enable_if_t<
369 std::is_pointer_v<U> && std::is_convertible_v<U, Pointer>, int>
370 aDummy = 0) = delete;
371
372 UniquePtr(UniquePtr&& aOther)
373 : mTuple(aOther.release(),
374 std::forward<DeleterType>(aOther.get_deleter())) {}
375
376 MOZ_IMPLICIT
377 UniquePtr(decltype(nullptr)) : mTuple(nullptr, DeleterType()) {
378 static_assert(!std::is_pointer_v<D>, "must provide a deleter instance");
379 static_assert(!std::is_reference_v<D>, "must provide a deleter instance");
380 }
381
382 ~UniquePtr() { reset(nullptr); }
383
384 UniquePtr& operator=(UniquePtr&& aOther) {
385 reset(aOther.release());
386 get_deleter() = std::forward<DeleterType>(aOther.get_deleter());
387 return *this;
388 }
389
390 UniquePtr& operator=(decltype(nullptr)) {
391 reset();
392 return *this;
393 }
394
395 explicit operator bool() const { return get() != nullptr; }
396
397 T& operator[](decltype(sizeof(int)) aIndex) const { return get()[aIndex]; }
398 Pointer get() const { return mTuple.first(); }
399
400 DeleterType& get_deleter() { return mTuple.second(); }
401 const DeleterType& get_deleter() const { return mTuple.second(); }
402
403 [[nodiscard]] Pointer release() {
404 Pointer p = mTuple.first();
405 mTuple.first() = nullptr;
406 return p;
407 }
408
409 void reset(Pointer aPtr = Pointer()) {
410 Pointer old = mTuple.first();
411 mTuple.first() = aPtr;
412 if (old != nullptr) {
413 mTuple.second()(old);
414 }
415 }
416
417 void reset(decltype(nullptr)) {
418 Pointer old = mTuple.first();
419 mTuple.first() = nullptr;
420 if (old != nullptr) {
421 mTuple.second()(old);
422 }
423 }
424
425 template <typename U>
426 void reset(U) = delete;
427
428 void swap(UniquePtr& aOther) { mTuple.swap(aOther.mTuple); }
429
430 UniquePtr(const UniquePtr& aOther) = delete; // construct using std::move()!
431 void operator=(const UniquePtr& aOther) =
432 delete; // assign using std::move()!
433};
434
435/**
436 * A default deletion policy using plain old operator delete.
437 *
438 * Note that this type can be specialized, but authors should beware of the risk
439 * that the specialization may at some point cease to match (either because it
440 * gets moved to a different compilation unit or the signature changes). If the
441 * non-specialized (|delete|-based) version compiles for that type but does the
442 * wrong thing, bad things could happen.
443 *
444 * This is a non-issue for types which are always incomplete (i.e. opaque handle
445 * types), since |delete|-ing such a type will always trigger a compilation
446 * error.
447 */
448template <typename T>
449class DefaultDelete {
450 public:
451 constexpr DefaultDelete() = default;
452
453 template <typename U>
454 MOZ_IMPLICIT DefaultDelete(
455 const DefaultDelete<U>& aOther,
456 std::enable_if_t<std::is_convertible_v<U*, T*>, int> aDummy = 0) {}
457
458 void operator()(T* aPtr) const {
459 static_assert(sizeof(T) > 0, "T must be complete");
460 delete aPtr;
14
Calling 'operator delete'
16
Returning from 'operator delete'
461 }
462};
463
464/** A default deletion policy using operator delete[]. */
465template <typename T>
466class DefaultDelete<T[]> {
467 public:
468 constexpr DefaultDelete() = default;
469
470 void operator()(T* aPtr) const {
471 static_assert(sizeof(T) > 0, "T must be complete");
472 delete[] aPtr;
473 }
474
475 template <typename U>
476 void operator()(U* aPtr) const = delete;
477};
478
479template <typename T, class D, typename U, class E>
480bool operator==(const UniquePtr<T, D>& aX, const UniquePtr<U, E>& aY) {
481 return aX.get() == aY.get();
482}
483
484template <typename T, class D, typename U, class E>
485bool operator!=(const UniquePtr<T, D>& aX, const UniquePtr<U, E>& aY) {
486 return aX.get() != aY.get();
487}
488
489template <typename T, class D>
490bool operator==(const UniquePtr<T, D>& aX, const T* aY) {
491 return aX.get() == aY;
492}
493
494template <typename T, class D>
495bool operator==(const T* aY, const UniquePtr<T, D>& aX) {
496 return aY == aX.get();
497}
498
499template <typename T, class D>
500bool operator!=(const UniquePtr<T, D>& aX, const T* aY) {
501 return aX.get() != aY;
502}
503
504template <typename T, class D>
505bool operator!=(const T* aY, const UniquePtr<T, D>& aX) {
506 return aY != aX.get();
507}
508
509template <typename T, class D>
510bool operator==(const UniquePtr<T, D>& aX, decltype(nullptr)) {
511 return !aX;
512}
513
514template <typename T, class D>
515bool operator==(decltype(nullptr), const UniquePtr<T, D>& aX) {
516 return !aX;
517}
518
519template <typename T, class D>
520bool operator!=(const UniquePtr<T, D>& aX, decltype(nullptr)) {
521 return bool(aX);
522}
523
524template <typename T, class D>
525bool operator!=(decltype(nullptr), const UniquePtr<T, D>& aX) {
526 return bool(aX);
527}
528
529// No operator<, operator>, operator<=, operator>= for now because simplicity.
530
531namespace detail {
532
533template <typename T>
534struct UniqueSelector {
535 typedef UniquePtr<T> SingleObject;
536};
537
538template <typename T>
539struct UniqueSelector<T[]> {
540 typedef UniquePtr<T[]> UnknownBound;
541};
542
543template <typename T, decltype(sizeof(int)) N>
544struct UniqueSelector<T[N]> {
545 typedef UniquePtr<T[N]> KnownBound;
546};
547
548} // namespace detail
549
550/**
551 * MakeUnique is a helper function for allocating new'd objects and arrays,
552 * returning a UniquePtr containing the resulting pointer. The semantics of
553 * MakeUnique<Type>(...) are as follows.
554 *
555 * If Type is an array T[n]:
556 * Disallowed, deleted, no overload for you!
557 * If Type is an array T[]:
558 * MakeUnique<T[]>(size_t) is the only valid overload. The pointer returned
559 * is as if by |new T[n]()|, which value-initializes each element. (If T
560 * isn't a class type, this will zero each element. If T is a class type,
561 * then roughly speaking, each element will be constructed using its default
562 * constructor. See C++11 [dcl.init]p7 for the full gory details.)
563 * If Type is non-array T:
564 * The arguments passed to MakeUnique<T>(...) are forwarded into a
565 * |new T(...)| call, initializing the T as would happen if executing
566 * |T(...)|.
567 *
568 * There are various benefits to using MakeUnique instead of |new| expressions.
569 *
570 * First, MakeUnique eliminates use of |new| from code entirely. If objects are
571 * only created through UniquePtr, then (assuming all explicit release() calls
572 * are safe, including transitively, and no type-safety casting funniness)
573 * correctly maintained ownership of the UniquePtr guarantees no leaks are
574 * possible. (This pays off best if a class is only ever created through a
575 * factory method on the class, using a private constructor.)
576 *
577 * Second, initializing a UniquePtr using a |new| expression requires repeating
578 * the name of the new'd type, whereas MakeUnique in concert with the |auto|
579 * keyword names it only once:
580 *
581 * UniquePtr<char> ptr1(new char()); // repetitive
582 * auto ptr2 = MakeUnique<char>(); // shorter
583 *
584 * Of course this assumes the reader understands the operation MakeUnique
585 * performs. In the long run this is probably a reasonable assumption. In the
586 * short run you'll have to use your judgment about what readers can be expected
587 * to know, or to quickly look up.
588 *
589 * Third, a call to MakeUnique can be assigned directly to a UniquePtr. In
590 * contrast you can't assign a pointer into a UniquePtr without using the
591 * cumbersome reset().
592 *
593 * UniquePtr<char> p;
594 * p = new char; // ERROR
595 * p.reset(new char); // works, but fugly
596 * p = MakeUnique<char>(); // preferred
597 *
598 * (And third, although not relevant to Mozilla: MakeUnique is exception-safe.
599 * An exception thrown after |new T| succeeds will leak that memory, unless the
600 * pointer is assigned to an object that will manage its ownership. UniquePtr
601 * ably serves this function.)
602 */
603
604template <typename T, typename... Args>
605typename detail::UniqueSelector<T>::SingleObject MakeUnique(Args&&... aArgs) {
606 return UniquePtr<T>(new T(std::forward<Args>(aArgs)...));
607}
608
609template <typename T>
610typename detail::UniqueSelector<T>::UnknownBound MakeUnique(
611 decltype(sizeof(int)) aN) {
612 using ArrayType = std::remove_extent_t<T>;
613 return UniquePtr<T>(new ArrayType[aN]());
614}
615
616template <typename T, typename... Args>
617typename detail::UniqueSelector<T>::KnownBound MakeUnique(Args&&... aArgs) =
618 delete;
619
620/**
621 * WrapUnique is a helper function to transfer ownership from a raw pointer
622 * into a UniquePtr<T>. It can only be used with a single non-array type.
623 *
624 * It is generally used this way:
625 *
626 * auto p = WrapUnique(new char);
627 *
628 * It can be used when MakeUnique is not usable, for example, when the
629 * constructor you are using is private, or you want to use aggregate
630 * initialization.
631 */
632
633template <typename T>
634typename detail::UniqueSelector<T>::SingleObject WrapUnique(T* aPtr) {
635 return UniquePtr<T>(aPtr);
636}
637
638} // namespace mozilla
639
640namespace std {
641
642template <typename T, class D>
643void swap(mozilla::UniquePtr<T, D>& aX, mozilla::UniquePtr<T, D>& aY) {
644 aX.swap(aY);
645}
646
647} // namespace std
648
649/**
650TempPtrToSetter(UniquePtr<T>*) -> T**-ish
651TempPtrToSetter(std::unique_ptr<T>*) -> T**-ish
652
653Make a temporary class to support assigning to UniquePtr/unique_ptr via passing
654a pointer to the callee.
655
656Often, APIs will be shaped like this trivial example:
657```
658nsresult Foo::NewChildBar(Bar** out) {
659 if (!IsOk()) return NS_ERROR_FAILURE;
660 *out = new Bar(this);
661 return NS_OK;
662}
663```
664
665In order to make this work with unique ptrs, it's often either risky or
666overwrought:
667```
668Bar* bar = nullptr;
669const auto cleanup = MakeScopeExit([&]() {
670 if (bar) {
671 delete bar;
672 }
673});
674if (FAILED(foo->NewChildBar(&bar)) {
675 // handle it
676}
677```
678
679```
680UniquePtr<Bar> bar;
681{
682 Bar* raw = nullptr;
683 const auto res = foo->NewChildBar(&bar);
684 bar.reset(raw);
685 if (FAILED(res) {
686 // handle it
687 }
688}
689```
690TempPtrToSettable is a shorthand for the latter approach, allowing something
691cleaner but also safe:
692
693```
694UniquePtr<Bar> bar;
695if (FAILED(foo->NewChildBar(TempPtrToSetter(&bar))) {
696 // handle it
697}
698```
699*/
700
701namespace mozilla {
702namespace detail {
703
704template <class T, class UniquePtrT>
705class MOZ_TEMPORARY_CLASS TempPtrToSetterT final {
706 private:
707 UniquePtrT* const mDest;
708 T* mNewVal;
709
710 public:
711 explicit TempPtrToSetterT(UniquePtrT* dest)
712 : mDest(dest), mNewVal(mDest->get()) {}
713
714 operator T**() { return &mNewVal; }
715
716 ~TempPtrToSetterT() {
717 if (mDest->get() != mNewVal) {
718 mDest->reset(mNewVal);
719 }
720 }
721};
722
723} // namespace detail
724
725template <class T, class Deleter>
726auto TempPtrToSetter(UniquePtr<T, Deleter>* const p) {
727 return detail::TempPtrToSetterT<T, UniquePtr<T, Deleter>>{p};
728}
729
730template <class T, class Deleter>
731auto TempPtrToSetter(std::unique_ptr<T, Deleter>* const p) {
732 return detail::TempPtrToSetterT<T, std::unique_ptr<T, Deleter>>{p};
733}
734
735} // namespace mozilla
736
737#endif /* mozilla_UniquePtr_h */

/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/mozilla/cxxalloc.h

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#ifndef mozilla_cxxalloc_h
6#define mozilla_cxxalloc_h
7
8/*
9 * We implement the default operators new/delete as part of
10 * libmozalloc, replacing their definitions in libstdc++. The
11 * operator new* definitions in libmozalloc will never return a NULL
12 * pointer.
13 *
14 * Each operator new immediately below returns a pointer to memory
15 * that can be delete'd by any of
16 *
17 * (1) the matching infallible operator delete immediately below
18 * (2) the matching system |operator delete(void*, std::nothrow)|
19 * (3) the matching system |operator delete(void*) noexcept(false)|
20 *
21 * NB: these are declared |noexcept(false)|, though they will never
22 * throw that exception. This declaration is consistent with the rule
23 * that |::operator new() noexcept(false)| will never return NULL.
24 *
25 * NB: mozilla::fallible can be used instead of std::nothrow.
26 */
27
28#ifndef MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline
29# define MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline MFBT_API__attribute__((weak)) __attribute__((visibility("default")))
30#endif
31
32MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new(size_t size) noexcept(false) {
33 return moz_xmalloc(size);
34}
35
36MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new(size_t size,
37 const std::nothrow_t&) noexcept(true) {
38 return malloc_implmalloc(size);
39}
40
41MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new[](size_t size) noexcept(false) {
42 return moz_xmalloc(size);
43}
44
45MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new[](size_t size,
46 const std::nothrow_t&) noexcept(true) {
47 return malloc_implmalloc(size);
48}
49
50MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr) noexcept(true) {
51 return free_implfree(ptr);
15
Memory is released
52}
53
54MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr,
55 const std::nothrow_t&) noexcept(true) {
56 return free_implfree(ptr);
57}
58
59MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](void* ptr) noexcept(true) {
60 return free_implfree(ptr);
61}
62
63MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](
64 void* ptr, const std::nothrow_t&) noexcept(true) {
65 return free_implfree(ptr);
66}
67
68#if defined(XP_WIN)
69// We provide the global sized delete overloads unconditionally because the
70// MSVC runtime headers do, despite compiling with /Zc:sizedDealloc-
71MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr,
72 size_t /*size*/) noexcept(true) {
73 return free_implfree(ptr);
74}
75
76MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](void* ptr,
77 size_t /*size*/) noexcept(true) {
78 return free_implfree(ptr);
79}
80#endif
81
82#endif /* mozilla_cxxalloc_h */