Bug 1698238 return default dictionary from GetUserMediaRequest#getConstraints() if...
[gecko.git] / dom / animation / AnimationEventDispatcher.h
blobec3f730b9b33ce6fa22128deae35966eb26dca45
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;
24 namespace mozilla {
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>>
33 EventVariant;
34 EventVariant mEvent;
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()) {
56 nsCString markerText;
57 aAnimationName->ToUTF8String(markerText);
58 PROFILER_MARKER_TEXT(
59 "CSS animation", DOM,
60 MarkerTiming::Interval(aScheduledEventTimeStamp -
61 TimeDuration::FromSeconds(aElapsedTime),
62 aScheduledEventTimeStamp),
63 markerText);
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>();
79 event.mPropertyName =
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()) {
88 nsCString markerText;
89 markerText.Assign(nsCSSProps::GetStringValue(aProperty));
90 if (aMessage == eTransitionCancel) {
91 markerText.AppendLiteral(" (canceled)");
93 PROFILER_MARKER_TEXT(
94 "CSS transition", DOM,
95 MarkerTiming::Interval(aScheduledEventTimeStamp -
96 TimeDuration::FromSeconds(aElapsedTime),
97 aScheduledEventTimeStamp),
98 markerText);
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>>();
121 #ifdef DEBUG
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();
131 #endif
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");
145 return nullptr;
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 */);
154 return;
157 MOZ_ASSERT(mEvent.is<InternalTransitionEvent>() ||
158 mEvent.is<InternalAnimationEvent>());
160 EventDispatcher::Dispatch(mTarget, aPresContext, AsWidgetEvent());
164 class AnimationEventDispatcher final {
165 public:
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)
172 void Disconnect();
174 void QueueEvent(AnimationEventInfo&& aEvent);
175 void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents);
177 // This will call SortEvents automatically if it has not already been
178 // called.
179 void DispatchEvents() {
180 mIsObserving = false;
181 if (!mPresContext || mPendingEvents.IsEmpty()) {
182 return;
185 SortEvents();
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
195 // context.
196 if (!mPresContext) {
197 break;
202 void ClearEventQueue() {
203 mPendingEvents.Clear();
204 mIsSorted = true;
206 bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
208 private:
209 #ifndef DEBUG
210 ~AnimationEventDispatcher() = default;
211 #else
212 ~AnimationEventDispatcher() {
213 MOZ_ASSERT(!mIsObserving,
214 "AnimationEventDispatcher should have disassociated from "
215 "nsRefreshDriver");
217 #endif
219 class AnimationEventInfoLessThan {
220 public:
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();
228 } else {
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
246 void SortEvents() {
247 if (mIsSorted) {
248 return;
251 for (auto& pending : mPendingEvents) {
252 pending.mAnimation->CachedChildIndexRef() = -1;
255 // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
256 // fixed.
257 std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(),
258 AnimationEventInfoLessThan());
259 mIsSorted = true;
261 void ScheduleDispatch();
263 nsPresContext* mPresContext;
264 typedef nsTArray<AnimationEventInfo> EventArray;
265 EventArray mPendingEvents;
266 bool mIsSorted;
267 bool mIsObserving;
270 } // namespace mozilla
272 #endif // mozilla_AnimationEventDispatcher_h