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/. */
7 #include "mozilla/AnimationEventDispatcher.h"
9 #include "mozilla/EventDispatcher.h"
10 #include "nsPresContext.h"
11 #include "nsRefreshDriver.h"
12 #include "nsCSSProps.h"
13 #include "mozilla/dom/AnimationEffect.h"
14 #include "nsGlobalWindowInner.h"
16 using namespace mozilla
;
18 namespace geckoprofiler::markers
{
20 struct CSSAnimationMarker
{
21 static constexpr Span
<const char> MarkerTypeName() {
22 return MakeStringSpan("CSSAnimation");
24 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter
& aWriter
,
25 const nsCString
& aName
,
26 const nsCString
& aTarget
,
27 const nsCString
& aProperties
,
28 const nsCString
& aOnCompositor
) {
29 aWriter
.StringProperty("Name", aName
);
30 aWriter
.StringProperty("Target", aTarget
);
31 aWriter
.StringProperty("properties", aProperties
);
32 aWriter
.StringProperty("oncompositor", aOnCompositor
);
34 static MarkerSchema
MarkerTypeDisplay() {
35 using MS
= MarkerSchema
;
36 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
37 schema
.AddKeyFormatSearchable("Name", MS::Format::String
,
38 MS::Searchable::Searchable
);
39 schema
.AddKeyLabelFormat("properties", "Animated Properties",
41 schema
.AddKeyLabelFormat("oncompositor", "Can Run on Compositor",
43 schema
.AddKeyFormat("Target", MS::Format::String
);
44 schema
.SetChartLabel("{marker.data.Name}");
46 "{marker.name} - {marker.data.Name}: {marker.data.properties}");
51 struct CSSTransitionMarker
{
52 static constexpr Span
<const char> MarkerTypeName() {
53 return MakeStringSpan("CSSTransition");
55 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter
& aWriter
,
56 const nsCString
& aTarget
,
57 const nsCString
& aProperty
,
58 bool aOnCompositor
, bool aCanceled
) {
59 aWriter
.StringProperty("Target", aTarget
);
60 aWriter
.StringProperty("property", aProperty
);
61 aWriter
.BoolProperty("oncompositor", aOnCompositor
);
63 aWriter
.BoolProperty("Canceled", aCanceled
);
66 static MarkerSchema
MarkerTypeDisplay() {
67 using MS
= MarkerSchema
;
68 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
69 schema
.AddKeyLabelFormat("property", "Animated Property",
71 schema
.AddKeyLabelFormat("oncompositor", "Can Run on Compositor",
73 schema
.AddKeyFormat("Canceled", MS::Format::String
);
74 schema
.AddKeyFormat("Target", MS::Format::String
);
75 schema
.SetChartLabel("{marker.data.property}");
76 schema
.SetTableLabel("{marker.name} - {marker.data.property}");
81 } // namespace geckoprofiler::markers
85 NS_IMPL_CYCLE_COLLECTION_CLASS(AnimationEventDispatcher
)
86 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AnimationEventDispatcher
)
87 tmp
->ClearEventQueue();
88 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEventDispatcher
)
90 for (auto& info
: tmp
->mPendingEvents
) {
91 if (OwningAnimationTarget
* target
= info
.GetOwningAnimationTarget()) {
92 ImplCycleCollectionTraverse(
94 "mozilla::AnimationEventDispatcher.mPendingEvents.mTarget");
96 ImplCycleCollectionTraverse(
98 "mozilla::AnimationEventDispatcher.mPendingEvents.mAnimation");
100 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
102 void AnimationEventDispatcher::Disconnect() {
104 MOZ_ASSERT(mPresContext
&& mPresContext
->RefreshDriver(),
105 "The pres context and the refresh driver should be still "
106 "alive if we haven't disassociated from the refresh driver");
107 mPresContext
->RefreshDriver()->CancelPendingAnimationEvents(this);
108 mIsObserving
= false;
110 mPresContext
= nullptr;
113 void AnimationEventDispatcher::QueueEvent(AnimationEventInfo
&& aEvent
) {
114 mPendingEvents
.AppendElement(std::move(aEvent
));
119 void AnimationEventDispatcher::QueueEvents(
120 nsTArray
<AnimationEventInfo
>&& aEvents
) {
121 mPendingEvents
.AppendElements(std::move(aEvents
));
126 void AnimationEventDispatcher::ScheduleDispatch() {
127 MOZ_ASSERT(mPresContext
, "The pres context should be valid");
129 mPresContext
->RefreshDriver()->ScheduleAnimationEventDispatch(this);
134 void AnimationEventInfo::MaybeAddMarker() const {
135 if (mData
.is
<CssAnimationData
>()) {
136 const auto& data
= mData
.as
<CssAnimationData
>();
137 const EventMessage message
= data
.mMessage
;
138 if (message
!= eAnimationCancel
&& message
!= eAnimationEnd
&&
139 message
!= eAnimationIteration
) {
143 data
.mAnimationName
->ToUTF8String(name
);
144 const TimeStamp startTime
= [&] {
145 if (message
== eAnimationIteration
) {
146 if (auto* effect
= mAnimation
->GetEffect()) {
147 return mScheduledEventTimeStamp
-
148 TimeDuration(effect
->GetComputedTiming().mDuration
);
151 return mScheduledEventTimeStamp
-
152 TimeDuration::FromSeconds(data
.mElapsedTime
);
155 AnimatedPropertyIDSet propertySet
;
157 if (dom::AnimationEffect
* effect
= mAnimation
->GetEffect()) {
158 if (dom::KeyframeEffect
* keyFrameEffect
= effect
->AsKeyframeEffect()) {
159 keyFrameEffect
->GetTarget()->Describe(target
, true);
160 for (const AnimationProperty
& property
: keyFrameEffect
->Properties()) {
161 propertySet
.AddProperty(property
.mProperty
);
165 nsAutoCString properties
;
166 nsAutoCString oncompositor
;
167 for (const AnimatedPropertyID
& property
: propertySet
) {
168 if (!properties
.IsEmpty()) {
169 properties
.AppendLiteral(", ");
170 oncompositor
.AppendLiteral(", ");
173 property
.ToString(prop
);
174 properties
.Append(prop
);
176 !property
.IsCustom() &&
177 nsCSSProps::PropHasFlags(property
.mID
,
178 CSSPropFlags::CanAnimateOnCompositor
)
183 message
== eAnimationIteration
184 ? ProfilerString8View("CSS animation iteration")
185 : ProfilerString8View("CSS animation"),
188 MarkerTiming::Interval(startTime
, mScheduledEventTimeStamp
),
189 mAnimation
->GetOwnerWindow()
190 ? MarkerInnerWindowId(mAnimation
->GetOwnerWindow()->WindowID())
191 : MarkerInnerWindowId::NoId()),
192 CSSAnimationMarker
, name
, NS_ConvertUTF16toUTF8(target
), properties
,
197 if (!mData
.is
<CssTransitionData
>()) {
201 const auto& data
= mData
.as
<CssTransitionData
>();
202 const EventMessage message
= data
.mMessage
;
203 if (message
!= eTransitionEnd
&& message
!= eTransitionCancel
) {
208 if (dom::AnimationEffect
* effect
= mAnimation
->GetEffect()) {
209 if (dom::KeyframeEffect
* keyFrameEffect
= effect
->AsKeyframeEffect()) {
210 keyFrameEffect
->GetTarget()->Describe(target
, true);
213 nsAutoCString property
;
214 data
.mProperty
.ToString(property
);
216 // FIXME: This doesn't _really_ reflect whether the animation is actually run
217 // in the compositor. The effect has that information and we should use it
219 const bool onCompositor
=
220 !data
.mProperty
.IsCustom() &&
221 nsCSSProps::PropHasFlags(data
.mProperty
.mID
,
222 CSSPropFlags::CanAnimateOnCompositor
);
224 "CSS transition", DOM
,
226 MarkerTiming::Interval(
227 mScheduledEventTimeStamp
-
228 TimeDuration::FromSeconds(data
.mElapsedTime
),
229 mScheduledEventTimeStamp
),
230 mAnimation
->GetOwnerWindow()
231 ? MarkerInnerWindowId(mAnimation
->GetOwnerWindow()->WindowID())
232 : MarkerInnerWindowId::NoId()),
233 CSSTransitionMarker
, NS_ConvertUTF16toUTF8(target
), property
,
234 onCompositor
, message
== eTransitionCancel
);
237 } // namespace mozilla