Coverage Report

Created: 2018-09-25 14:53

/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