/work/obj-fuzz/dist/include/mozilla/AnimationEventDispatcher.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef mozilla_AnimationEventDispatcher_h |
8 | | #define mozilla_AnimationEventDispatcher_h |
9 | | |
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 "nsCSSProps.h" |
18 | | #include "nsCycleCollectionParticipant.h" |
19 | | |
20 | | class nsPresContext; |
21 | | class nsRefreshDriver; |
22 | | |
23 | | namespace mozilla { |
24 | | |
25 | | struct AnimationEventInfo |
26 | | { |
27 | | RefPtr<dom::EventTarget> mTarget; |
28 | | RefPtr<dom::Animation> mAnimation; |
29 | | TimeStamp mScheduledEventTimeStamp; |
30 | | |
31 | | typedef Variant<InternalTransitionEvent, |
32 | | InternalAnimationEvent, |
33 | | RefPtr<dom::AnimationPlaybackEvent>> EventVariant; |
34 | | EventVariant mEvent; |
35 | | |
36 | | // For CSS animation events |
37 | | AnimationEventInfo(nsAtom* aAnimationName, |
38 | | const NonOwningAnimationTarget& aTarget, |
39 | | EventMessage aMessage, |
40 | | double aElapsedTime, |
41 | | const TimeStamp& aScheduledEventTimeStamp, |
42 | | dom::Animation* aAnimation) |
43 | | : mTarget(aTarget.mElement) |
44 | | , mAnimation(aAnimation) |
45 | | , mScheduledEventTimeStamp(aScheduledEventTimeStamp) |
46 | | , mEvent(EventVariant(InternalAnimationEvent(true, aMessage))) |
47 | 0 | { |
48 | 0 | InternalAnimationEvent& event = mEvent.as<InternalAnimationEvent>(); |
49 | 0 |
|
50 | 0 | aAnimationName->ToString(event.mAnimationName); |
51 | 0 | // XXX Looks like nobody initialize WidgetEvent::time |
52 | 0 | event.mElapsedTime = aElapsedTime; |
53 | 0 | event.mPseudoElement = |
54 | 0 | nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType); |
55 | 0 | } |
56 | | |
57 | | // For CSS transition events |
58 | | AnimationEventInfo(nsCSSPropertyID aProperty, |
59 | | const NonOwningAnimationTarget& aTarget, |
60 | | EventMessage aMessage, |
61 | | double aElapsedTime, |
62 | | const TimeStamp& aScheduledEventTimeStamp, |
63 | | dom::Animation* aAnimation) |
64 | | : mTarget(aTarget.mElement) |
65 | | , mAnimation(aAnimation) |
66 | | , mScheduledEventTimeStamp(aScheduledEventTimeStamp) |
67 | | , mEvent(EventVariant(InternalTransitionEvent(true, aMessage))) |
68 | 0 | { |
69 | 0 | InternalTransitionEvent& event = mEvent.as<InternalTransitionEvent>(); |
70 | 0 |
|
71 | 0 | event.mPropertyName = |
72 | 0 | NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)); |
73 | 0 | // XXX Looks like nobody initialize WidgetEvent::time |
74 | 0 | event.mElapsedTime = aElapsedTime; |
75 | 0 | event.mPseudoElement = |
76 | 0 | nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType); |
77 | 0 | } |
78 | | |
79 | | // For web animation events |
80 | | AnimationEventInfo(const nsAString& aName, |
81 | | RefPtr<dom::AnimationPlaybackEvent>&& aEvent, |
82 | | TimeStamp&& aScheduledEventTimeStamp, |
83 | | dom::Animation* aAnimation) |
84 | | : mTarget(aAnimation) |
85 | | , mAnimation(aAnimation) |
86 | | , mScheduledEventTimeStamp(std::move(aScheduledEventTimeStamp)) |
87 | | , mEvent(std::move(aEvent)) |
88 | 0 | { |
89 | 0 | } |
90 | | |
91 | | AnimationEventInfo(const AnimationEventInfo& aOther) = delete; |
92 | | AnimationEventInfo& operator=(const AnimationEventInfo& aOther) = delete; |
93 | 0 | AnimationEventInfo(AnimationEventInfo&& aOther) = default; |
94 | 0 | AnimationEventInfo& operator=(AnimationEventInfo&& aOther) = default; |
95 | | |
96 | | bool IsWebAnimationEvent() const |
97 | 0 | { |
98 | 0 | return mEvent.is<RefPtr<dom::AnimationPlaybackEvent>>(); |
99 | 0 | } |
100 | | |
101 | | #ifdef DEBUG |
102 | | bool IsStale() const |
103 | | { |
104 | | const WidgetEvent* widgetEvent = AsWidgetEvent(); |
105 | | return widgetEvent->mFlags.mIsBeingDispatched || |
106 | | widgetEvent->mFlags.mDispatchedAtLeastOnce; |
107 | | } |
108 | | |
109 | | const WidgetEvent* AsWidgetEvent() const |
110 | | { |
111 | | return const_cast<AnimationEventInfo*>(this)->AsWidgetEvent(); |
112 | | } |
113 | | #endif |
114 | | |
115 | | WidgetEvent* AsWidgetEvent() |
116 | 0 | { |
117 | 0 | if (mEvent.is<InternalTransitionEvent>()) { |
118 | 0 | return &mEvent.as<InternalTransitionEvent>(); |
119 | 0 | } |
120 | 0 | if (mEvent.is<InternalAnimationEvent>()) { |
121 | 0 | return &mEvent.as<InternalAnimationEvent>(); |
122 | 0 | } |
123 | 0 | if (mEvent.is<RefPtr<dom::AnimationPlaybackEvent>>()) { |
124 | 0 | return mEvent.as<RefPtr<dom::AnimationPlaybackEvent>>() |
125 | 0 | ->WidgetEventPtr(); |
126 | 0 | } |
127 | 0 | |
128 | 0 | MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected event type"); |
129 | 0 | return nullptr; |
130 | 0 | } |
131 | | |
132 | | void Dispatch(nsPresContext* aPresContext) |
133 | 0 | { |
134 | 0 | if (mEvent.is<RefPtr<dom::AnimationPlaybackEvent>>()) { |
135 | 0 | EventDispatcher::DispatchDOMEvent( |
136 | 0 | mTarget, |
137 | 0 | nullptr /* WidgetEvent */, |
138 | 0 | mEvent.as<RefPtr<dom::AnimationPlaybackEvent>>(), |
139 | 0 | aPresContext, |
140 | 0 | nullptr /* nsEventStatus */); |
141 | 0 | return; |
142 | 0 | } |
143 | 0 | |
144 | 0 | MOZ_ASSERT(mEvent.is<InternalTransitionEvent>() || |
145 | 0 | mEvent.is<InternalAnimationEvent>()); |
146 | 0 |
|
147 | 0 | EventDispatcher::Dispatch(mTarget, aPresContext, AsWidgetEvent()); |
148 | 0 | } |
149 | | }; |
150 | | |
151 | | class AnimationEventDispatcher final |
152 | | { |
153 | | public: |
154 | | explicit AnimationEventDispatcher(nsPresContext* aPresContext) |
155 | | : mPresContext(aPresContext) |
156 | | , mIsSorted(true) |
157 | | , mIsObserving(false) |
158 | 0 | { |
159 | 0 | } |
160 | | |
161 | | NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher) |
162 | | NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher) |
163 | | |
164 | | void Disconnect(); |
165 | | |
166 | | void QueueEvent(AnimationEventInfo&& aEvent); |
167 | | void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents); |
168 | | |
169 | | // This will call SortEvents automatically if it has not already been |
170 | | // called. |
171 | | void DispatchEvents() |
172 | 0 | { |
173 | 0 | mIsObserving = false; |
174 | 0 | if (!mPresContext || mPendingEvents.IsEmpty()) { |
175 | 0 | return; |
176 | 0 | } |
177 | 0 | |
178 | 0 | SortEvents(); |
179 | 0 |
|
180 | 0 | EventArray events; |
181 | 0 | mPendingEvents.SwapElements(events); |
182 | 0 | // mIsSorted will be set to true by SortEvents above, and we leave it |
183 | 0 | // that way since mPendingEvents is now empty |
184 | 0 | for (AnimationEventInfo& info : events) { |
185 | 0 | MOZ_ASSERT(!info.IsStale(), "The event shouldn't be stale"); |
186 | 0 | info.Dispatch(mPresContext); |
187 | 0 |
|
188 | 0 | // Bail out if our mPresContext was nullified due to destroying the pres |
189 | 0 | // context. |
190 | 0 | if (!mPresContext) { |
191 | 0 | break; |
192 | 0 | } |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | void ClearEventQueue() |
197 | 0 | { |
198 | 0 | mPendingEvents.Clear(); |
199 | 0 | mIsSorted = true; |
200 | 0 | } |
201 | 0 | bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); } |
202 | | |
203 | | private: |
204 | | #ifndef DEBUG |
205 | 0 | ~AnimationEventDispatcher() = default; |
206 | | #else |
207 | | ~AnimationEventDispatcher() |
208 | | { |
209 | | MOZ_ASSERT(!mIsObserving, |
210 | | "AnimationEventDispatcher should have disassociated from " |
211 | | "nsRefreshDriver"); |
212 | | } |
213 | | #endif |
214 | | |
215 | | class AnimationEventInfoLessThan |
216 | | { |
217 | | public: |
218 | | bool operator()(const AnimationEventInfo& a, |
219 | | const AnimationEventInfo& b) const |
220 | 0 | { |
221 | 0 | if (a.mScheduledEventTimeStamp != b.mScheduledEventTimeStamp) { |
222 | 0 | // Null timestamps sort first |
223 | 0 | if (a.mScheduledEventTimeStamp.IsNull() || |
224 | 0 | b.mScheduledEventTimeStamp.IsNull()) { |
225 | 0 | return a.mScheduledEventTimeStamp.IsNull(); |
226 | 0 | } else { |
227 | 0 | return a.mScheduledEventTimeStamp < b.mScheduledEventTimeStamp; |
228 | 0 | } |
229 | 0 | } |
230 | 0 | |
231 | 0 | // Events in the Web Animations spec are prior to CSS events. |
232 | 0 | if (a.IsWebAnimationEvent() != b.IsWebAnimationEvent()) { |
233 | 0 | return a.IsWebAnimationEvent(); |
234 | 0 | } |
235 | 0 | |
236 | 0 | AnimationPtrComparator<RefPtr<dom::Animation>> comparator; |
237 | 0 | return comparator.LessThan(a.mAnimation, b.mAnimation); |
238 | 0 | } |
239 | | }; |
240 | | |
241 | | // Sort all pending CSS animation/transition events by scheduled event time |
242 | | // and composite order. |
243 | | // https://drafts.csswg.org/web-animations/#update-animations-and-send-events |
244 | | void SortEvents() |
245 | 0 | { |
246 | 0 | if (mIsSorted) { |
247 | 0 | return; |
248 | 0 | } |
249 | 0 | |
250 | 0 | for (auto& pending : mPendingEvents) { |
251 | 0 | pending.mAnimation->CachedChildIndexRef() = -1; |
252 | 0 | } |
253 | 0 |
|
254 | 0 | // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is |
255 | 0 | // fixed. |
256 | 0 | std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(), |
257 | 0 | AnimationEventInfoLessThan()); |
258 | 0 | mIsSorted = true; |
259 | 0 | } |
260 | | void ScheduleDispatch(); |
261 | | |
262 | | nsPresContext* mPresContext; |
263 | | typedef nsTArray<AnimationEventInfo> EventArray; |
264 | | EventArray mPendingEvents; |
265 | | bool mIsSorted; |
266 | | bool mIsObserving; |
267 | | }; |
268 | | |
269 | | } // namespace mozilla |
270 | | |
271 | | #endif // mozilla_AnimationEventDispatcher_h |