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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_AnimationEventDispatcher_h
8 #define mozilla_AnimationEventDispatcher_h
10 #include <algorithm> // For <std::stable_sort>
11 #include "mozilla/AnimationComparator.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/ContentEvents.h"
14 #include "mozilla/EventDispatcher.h"
15 #include "mozilla/Variant.h"
16 #include "mozilla/dom/AnimationPlaybackEvent.h"
17 #include "mozilla/ProfilerMarkers.h"
18 #include "nsCSSProps.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsPresContext.h"
22 class nsRefreshDriver
;
26 struct AnimationEventInfo
{
27 RefPtr
<dom::EventTarget
> mTarget
;
28 RefPtr
<dom::Animation
> mAnimation
;
29 TimeStamp mScheduledEventTimeStamp
;
31 typedef Variant
<InternalTransitionEvent
, InternalAnimationEvent
,
32 RefPtr
<dom::AnimationPlaybackEvent
>>
36 // For CSS animation events
37 AnimationEventInfo(nsAtom
* aAnimationName
,
38 const NonOwningAnimationTarget
& aTarget
,
39 EventMessage aMessage
, double aElapsedTime
,
40 const TimeStamp
& aScheduledEventTimeStamp
,
41 dom::Animation
* aAnimation
)
42 : mTarget(aTarget
.mElement
),
43 mAnimation(aAnimation
),
44 mScheduledEventTimeStamp(aScheduledEventTimeStamp
),
45 mEvent(EventVariant(InternalAnimationEvent(true, aMessage
))) {
46 InternalAnimationEvent
& event
= mEvent
.as
<InternalAnimationEvent
>();
48 aAnimationName
->ToString(event
.mAnimationName
);
49 // XXX Looks like nobody initialize WidgetEvent::time
50 event
.mElapsedTime
= aElapsedTime
;
51 event
.mPseudoElement
=
52 nsCSSPseudoElements::PseudoTypeAsString(aTarget
.mPseudoType
);
54 if ((aMessage
== eAnimationCancel
|| aMessage
== eAnimationEnd
) &&
55 profiler_can_accept_markers()) {
57 aAnimationName
->ToUTF8String(markerText
);
60 MarkerTiming::Interval(aScheduledEventTimeStamp
-
61 TimeDuration::FromSeconds(aElapsedTime
),
62 aScheduledEventTimeStamp
),
67 // For CSS transition events
68 AnimationEventInfo(nsCSSPropertyID aProperty
,
69 const NonOwningAnimationTarget
& aTarget
,
70 EventMessage aMessage
, double aElapsedTime
,
71 const TimeStamp
& aScheduledEventTimeStamp
,
72 dom::Animation
* aAnimation
)
73 : mTarget(aTarget
.mElement
),
74 mAnimation(aAnimation
),
75 mScheduledEventTimeStamp(aScheduledEventTimeStamp
),
76 mEvent(EventVariant(InternalTransitionEvent(true, aMessage
))) {
77 InternalTransitionEvent
& event
= mEvent
.as
<InternalTransitionEvent
>();
80 NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty
));
81 // XXX Looks like nobody initialize WidgetEvent::time
82 event
.mElapsedTime
= aElapsedTime
;
83 event
.mPseudoElement
=
84 nsCSSPseudoElements::PseudoTypeAsString(aTarget
.mPseudoType
);
86 if ((aMessage
== eTransitionEnd
|| aMessage
== eTransitionCancel
) &&
87 profiler_can_accept_markers()) {
89 markerText
.Assign(nsCSSProps::GetStringValue(aProperty
));
90 if (aMessage
== eTransitionCancel
) {
91 markerText
.AppendLiteral(" (canceled)");
94 "CSS transition", DOM
,
95 MarkerTiming::Interval(aScheduledEventTimeStamp
-
96 TimeDuration::FromSeconds(aElapsedTime
),
97 aScheduledEventTimeStamp
),
102 // For web animation events
103 AnimationEventInfo(const nsAString
& aName
,
104 RefPtr
<dom::AnimationPlaybackEvent
>&& aEvent
,
105 TimeStamp
&& aScheduledEventTimeStamp
,
106 dom::Animation
* aAnimation
)
107 : mTarget(aAnimation
),
108 mAnimation(aAnimation
),
109 mScheduledEventTimeStamp(std::move(aScheduledEventTimeStamp
)),
110 mEvent(std::move(aEvent
)) {}
112 AnimationEventInfo(const AnimationEventInfo
& aOther
) = delete;
113 AnimationEventInfo
& operator=(const AnimationEventInfo
& aOther
) = delete;
114 AnimationEventInfo(AnimationEventInfo
&& aOther
) = default;
115 AnimationEventInfo
& operator=(AnimationEventInfo
&& aOther
) = default;
117 bool IsWebAnimationEvent() const {
118 return mEvent
.is
<RefPtr
<dom::AnimationPlaybackEvent
>>();
122 bool IsStale() const {
123 const WidgetEvent
* widgetEvent
= AsWidgetEvent();
124 return widgetEvent
->mFlags
.mIsBeingDispatched
||
125 widgetEvent
->mFlags
.mDispatchedAtLeastOnce
;
128 const WidgetEvent
* AsWidgetEvent() const {
129 return const_cast<AnimationEventInfo
*>(this)->AsWidgetEvent();
133 WidgetEvent
* AsWidgetEvent() {
134 if (mEvent
.is
<InternalTransitionEvent
>()) {
135 return &mEvent
.as
<InternalTransitionEvent
>();
137 if (mEvent
.is
<InternalAnimationEvent
>()) {
138 return &mEvent
.as
<InternalAnimationEvent
>();
140 if (mEvent
.is
<RefPtr
<dom::AnimationPlaybackEvent
>>()) {
141 return mEvent
.as
<RefPtr
<dom::AnimationPlaybackEvent
>>()->WidgetEventPtr();
144 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected event type");
148 void Dispatch(nsPresContext
* aPresContext
) {
149 if (mEvent
.is
<RefPtr
<dom::AnimationPlaybackEvent
>>()) {
150 EventDispatcher::DispatchDOMEvent(
151 mTarget
, nullptr /* WidgetEvent */,
152 mEvent
.as
<RefPtr
<dom::AnimationPlaybackEvent
>>(), aPresContext
,
153 nullptr /* nsEventStatus */);
157 MOZ_ASSERT(mEvent
.is
<InternalTransitionEvent
>() ||
158 mEvent
.is
<InternalAnimationEvent
>());
160 EventDispatcher::Dispatch(mTarget
, aPresContext
, AsWidgetEvent());
164 class AnimationEventDispatcher final
{
166 explicit AnimationEventDispatcher(nsPresContext
* aPresContext
)
167 : mPresContext(aPresContext
), mIsSorted(true), mIsObserving(false) {}
169 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher
)
170 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher
)
174 void QueueEvent(AnimationEventInfo
&& aEvent
);
175 void QueueEvents(nsTArray
<AnimationEventInfo
>&& aEvents
);
177 // This will call SortEvents automatically if it has not already been
179 void DispatchEvents() {
180 mIsObserving
= false;
181 if (!mPresContext
|| mPendingEvents
.IsEmpty()) {
187 EventArray events
= std::move(mPendingEvents
);
188 // mIsSorted will be set to true by SortEvents above, and we leave it
189 // that way since mPendingEvents is now empty
190 for (AnimationEventInfo
& info
: events
) {
191 MOZ_ASSERT(!info
.IsStale(), "The event shouldn't be stale");
192 info
.Dispatch(mPresContext
);
194 // Bail out if our mPresContext was nullified due to destroying the pres
202 void ClearEventQueue() {
203 mPendingEvents
.Clear();
206 bool HasQueuedEvents() const { return !mPendingEvents
.IsEmpty(); }
210 ~AnimationEventDispatcher() = default;
212 ~AnimationEventDispatcher() {
213 MOZ_ASSERT(!mIsObserving
,
214 "AnimationEventDispatcher should have disassociated from "
219 class AnimationEventInfoLessThan
{
221 bool operator()(const AnimationEventInfo
& a
,
222 const AnimationEventInfo
& b
) const {
223 if (a
.mScheduledEventTimeStamp
!= b
.mScheduledEventTimeStamp
) {
224 // Null timestamps sort first
225 if (a
.mScheduledEventTimeStamp
.IsNull() ||
226 b
.mScheduledEventTimeStamp
.IsNull()) {
227 return a
.mScheduledEventTimeStamp
.IsNull();
229 return a
.mScheduledEventTimeStamp
< b
.mScheduledEventTimeStamp
;
233 // Events in the Web Animations spec are prior to CSS events.
234 if (a
.IsWebAnimationEvent() != b
.IsWebAnimationEvent()) {
235 return a
.IsWebAnimationEvent();
238 AnimationPtrComparator
<RefPtr
<dom::Animation
>> comparator
;
239 return comparator
.LessThan(a
.mAnimation
, b
.mAnimation
);
243 // Sort all pending CSS animation/transition events by scheduled event time
244 // and composite order.
245 // https://drafts.csswg.org/web-animations/#update-animations-and-send-events
251 for (auto& pending
: mPendingEvents
) {
252 pending
.mAnimation
->CachedChildIndexRef() = -1;
255 // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
257 std::stable_sort(mPendingEvents
.begin(), mPendingEvents
.end(),
258 AnimationEventInfoLessThan());
261 void ScheduleDispatch();
263 nsPresContext
* mPresContext
;
264 typedef nsTArray
<AnimationEventInfo
> EventArray
;
265 EventArray mPendingEvents
;
270 } // namespace mozilla
272 #endif // mozilla_AnimationEventDispatcher_h