Bug 1415780 - De-templatize AnimationEventDispatcher. r?birtles,masayuki draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Sat, 27 Jan 2018 21:17:27 +0900
changeset 748023 d4eb8e0df51b62114d48566ce1a52102ae0b6ffa
parent 748022 d3fa8509b00b6c0ec70d4bf44fc96e17b25a1b39
child 748024 3eba7ed427a1b6a5bef1ca2e6affb167d5e66976
push id97048
push userhikezoe@mozilla.com
push dateSat, 27 Jan 2018 12:23:10 +0000
reviewersbirtles, masayuki
bugs1415780, 1433008
milestone60.0a1
Bug 1415780 - De-templatize AnimationEventDispatcher. r?birtles,masayuki Now single AnimationEventDispatcher can handle both CSS animation/transition events simultaneously. To do this we had to change AnimationEventInfo and TransitionEventInfo into a single struct, the struct is also named AnimationEventInfo. It results we can sort both CSS animation/transition events altogether. Thus we make sure CSS animation/transition events are sorted by scheduled event time prior to their composite order (i.e. transitions is prior to animations). At this moment, we don't sort both events altogether since nsAnimationManager and nsTransitionManager has an AnimationEventDispatcher respectively. In the next patch we move AnimationEventDispatcher into nsPresContext, i.e. each document has an AnimationEventDispatcher without the distinction between CSS animations and transitions. Note that, after this patch, we copy all members in InternalTransitionEvent and InternalAnimationEvent in the copy-constructor of AnimationEventInfo, this will be fixed once WidgetEvent has move-constructor and move-assignment (bug 1433008). MozReview-Commit-ID: 5JAh6N7C6ee
dom/animation/AnimationEventDispatcher.h
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
--- a/dom/animation/AnimationEventDispatcher.h
+++ b/dom/animation/AnimationEventDispatcher.h
@@ -4,49 +4,126 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_AnimationEventDispatcher_h
 #define mozilla_AnimationEventDispatcher_h
 
 #include <algorithm> // For <std::stable_sort>
 #include "mozilla/AnimationComparator.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/Variant.h"
+#include "nsCSSProps.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsPresContext;
 
 namespace mozilla {
 
-template <class EventInfo>
+struct AnimationEventInfo
+{
+  RefPtr<dom::Element> mElement;
+  RefPtr<dom::Animation> mAnimation;
+  TimeStamp mTimeStamp;
+
+  typedef Variant<InternalTransitionEvent, InternalAnimationEvent> EventVariant;
+  EventVariant mEvent;
+
+  // For CSS animation events
+  AnimationEventInfo(nsAtom* aAnimationName,
+                     const NonOwningAnimationTarget& aTarget,
+                     EventMessage aMessage,
+                     double aElapsedTime,
+                     const TimeStamp& aTimeStamp,
+                     dom::Animation* aAnimation)
+    : mElement(aTarget.mElement)
+    , mAnimation(aAnimation)
+    , mTimeStamp(aTimeStamp)
+    , mEvent(EventVariant(InternalAnimationEvent(true, aMessage)))
+  {
+    InternalAnimationEvent& event = mEvent.as<InternalAnimationEvent>();
+
+    aAnimationName->ToString(event.mAnimationName);
+    // XXX Looks like nobody initialize WidgetEvent::time
+    event.mElapsedTime = aElapsedTime;
+    event.mPseudoElement =
+      nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType);
+  }
+
+  // For CSS transition events
+  AnimationEventInfo(nsCSSPropertyID aProperty,
+                     const NonOwningAnimationTarget& aTarget,
+                     EventMessage aMessage,
+                     double aElapsedTime,
+                     const TimeStamp& aTimeStamp,
+                     dom::Animation* aAnimation)
+    : mElement(aTarget.mElement)
+    , mAnimation(aAnimation)
+    , mTimeStamp(aTimeStamp)
+    , mEvent(EventVariant(InternalTransitionEvent(true, aMessage)))
+  {
+    InternalTransitionEvent& event = mEvent.as<InternalTransitionEvent>();
+
+    event.mPropertyName =
+      NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty));
+    // XXX Looks like nobody initialize WidgetEvent::time
+    event.mElapsedTime = aElapsedTime;
+    event.mPseudoElement =
+      nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType);
+  }
+
+  // InternalAnimationEvent and InternalTransitionEvent don't support
+  // copy-construction, so we need to ourselves in order to work with nsTArray.
+  //
+  // FIXME: Drop this copy constructor and copy assignment below once
+  // WidgetEvent have move constructor and move assignment (bug 1433008).
+  AnimationEventInfo(const AnimationEventInfo& aOther) = default;
+  AnimationEventInfo& operator=(const AnimationEventInfo& aOther) = default;
+
+  WidgetEvent* AsWidgetEvent()
+  {
+    if (mEvent.is<InternalTransitionEvent>()) {
+      return &mEvent.as<InternalTransitionEvent>();
+    }
+    if (mEvent.is<InternalAnimationEvent>()) {
+      return &mEvent.as<InternalAnimationEvent>();
+    }
+
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected event type");
+    return nullptr;
+  }
+};
+
 class AnimationEventDispatcher final
 {
 public:
   AnimationEventDispatcher() : mIsSorted(true) { }
 
-  void QueueEvents(nsTArray<EventInfo>&& aEvents)
+  void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents)
   {
-    mPendingEvents.AppendElements(Forward<nsTArray<EventInfo>>(aEvents));
+    mPendingEvents.AppendElements(Move(aEvents));
     mIsSorted = false;
   }
 
   // This is exposed as a separate method so that when we are dispatching
   // *both* transition events and animation events we can sort both lists
   // once using the current state of the document before beginning any
   // dispatch.
   void SortEvents()
   {
     if (mIsSorted) {
       return;
     }
 
     // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
     // fixed.
     std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(),
-                     EventInfoLessThan());
+                     AnimationEventInfoLessThan());
     mIsSorted = true;
   }
 
   // Takes a reference to the owning manager's pres context so it can
   // detect if the pres context is destroyed while dispatching one of
   // the events.
   //
   // This will call SortEvents automatically if it has not already been
@@ -58,18 +135,23 @@ public:
     }
 
     SortEvents();
 
     EventArray events;
     mPendingEvents.SwapElements(events);
     // mIsSorted will be set to true by SortEvents above, and we leave it
     // that way since mPendingEvents is now empty
-    for (EventInfo& info : events) {
-      EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent);
+    for (AnimationEventInfo& info : events) {
+      MOZ_ASSERT(!info.AsWidgetEvent()->mFlags.mIsBeingDispatched &&
+                 !info.AsWidgetEvent()->mFlags.mDispatchedAtLeastOnce,
+                 "The WidgetEvent should be fresh");
+      EventDispatcher::Dispatch(info.mElement,
+                                aPresContext,
+                                info.AsWidgetEvent());
 
       if (!aPresContext) {
         break;
       }
     }
   }
 
   void ClearEventQueue()
@@ -78,59 +160,57 @@ public:
     mIsSorted = true;
   }
   bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
 
   // Methods for supporting cycle-collection
   void Traverse(nsCycleCollectionTraversalCallback* aCallback,
                 const char* aName)
   {
-    for (EventInfo& info : mPendingEvents) {
+    for (AnimationEventInfo& info : mPendingEvents) {
       ImplCycleCollectionTraverse(*aCallback, info.mElement, aName);
       ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName);
     }
   }
   void Unlink() { ClearEventQueue(); }
 
 protected:
-  class EventInfoLessThan
+  class AnimationEventInfoLessThan
   {
   public:
-    bool operator()(const EventInfo& a, const EventInfo& b) const
+    bool operator()(const AnimationEventInfo& a, const AnimationEventInfo& b) const
     {
       if (a.mTimeStamp != b.mTimeStamp) {
         // Null timestamps sort first
         if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) {
           return a.mTimeStamp.IsNull();
         } else {
           return a.mTimeStamp < b.mTimeStamp;
         }
       }
 
       AnimationPtrComparator<RefPtr<dom::Animation>> comparator;
       return comparator.LessThan(a.mAnimation, b.mAnimation);
     }
   };
 
-  typedef nsTArray<EventInfo> EventArray;
+  typedef nsTArray<AnimationEventInfo> EventArray;
   EventArray mPendingEvents;
   bool mIsSorted;
 };
 
-template <class EventInfo>
 inline void
-ImplCycleCollectionUnlink(AnimationEventDispatcher<EventInfo>& aField)
+ImplCycleCollectionUnlink(AnimationEventDispatcher& aField)
 {
   aField.Unlink();
 }
 
-template <class EventInfo>
 inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
-                            AnimationEventDispatcher<EventInfo>& aField,
+                            AnimationEventDispatcher& aField,
                             const char* aName,
                             uint32_t aFlags = 0)
 {
   aField.Traverse(&aCallback, aName);
 }
 
 } // namespace mozilla
 
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -16,23 +16,23 @@
 #include "mozilla/TimingParams.h"
 #include "nsContentUtils.h"
 
 class nsIFrame;
 class nsPresContext;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
-template <class EventInfo> class AnimationEventDispatcher;
+class AnimationEventDispatcher;
 
 namespace dom {
 class Element;
 }
 
-template <class AnimationType, class AnimationEventType>
+template <class AnimationType>
 class CommonAnimationManager {
 public:
   explicit CommonAnimationManager(nsPresContext *aPresContext)
     : mPresContext(aPresContext)
   {
   }
 
   // NOTE:  This can return null after Disconnect().
@@ -67,20 +67,19 @@ public:
 
     nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());
     collection->Destroy();
   }
 
   /**
    * Add pending events.
    */
-  void QueueEvents(nsTArray<AnimationEventType>&& aEvents)
+  void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents)
   {
-    mEventDispatcher.QueueEvents(
-      mozilla::Forward<nsTArray<AnimationEventType>>(aEvents));
+    mEventDispatcher.QueueEvents(Move(aEvents));
   }
 
   void SortEvents()      { mEventDispatcher.SortEvents(); }
   void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
 
 protected:
   virtual ~CommonAnimationManager()
   {
@@ -97,17 +96,17 @@ protected:
            mElementCollections.getFirst()) {
       head->Destroy(); // Note: this removes 'head' from mElementCollections.
     }
   }
 
   LinkedList<AnimationCollection<AnimationType>> mElementCollections;
   nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
 
-  mozilla::AnimationEventDispatcher<AnimationEventType> mEventDispatcher;
+  mozilla::AnimationEventDispatcher mEventDispatcher;
 };
 
 /**
  * Utility class for referencing the element that created a CSS animation or
  * transition. It is non-owning (i.e. it uses a raw pointer) since it is only
  * expected to be set by the owned animation while it actually being managed
  * by the owning element.
  *
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -237,19 +237,19 @@ CSSAnimation::QueueEvents(const StickyTi
 
   auto appendAnimationEvent = [&](EventMessage aMessage,
                                   const StickyTimeDuration& aElapsedTime,
                                   const TimeStamp& aTimeStamp) {
     double elapsedTime = aElapsedTime.ToSeconds();
     if (aMessage == eAnimationCancel) {
       elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime);
     }
-    events.AppendElement(AnimationEventInfo(mOwningElement.Target(),
+    events.AppendElement(AnimationEventInfo(mAnimationName,
+                                            mOwningElement.Target(),
                                             aMessage,
-                                            mAnimationName,
                                             elapsedTime,
                                             aTimeStamp,
                                             this));
   };
 
   // Handle cancel event first
   if ((mPreviousPhase != AnimationPhase::Idle &&
        mPreviousPhase != AnimationPhase::After) &&
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -29,52 +29,16 @@ class KeyframeEffectReadOnly;
 class Promise;
 } /* namespace dom */
 
 class GeckoStyleContext;
 class ServoStyleContext;
 enum class CSSPseudoElementType : uint8_t;
 struct NonOwningAnimationTarget;
 
-struct AnimationEventInfo {
-  RefPtr<dom::Element> mElement;
-  RefPtr<dom::Animation> mAnimation;
-  InternalAnimationEvent mEvent;
-  TimeStamp mTimeStamp;
-
-  AnimationEventInfo(const NonOwningAnimationTarget& aTarget,
-                     EventMessage aMessage,
-                     nsAtom* aAnimationName,
-                     double aElapsedTime,
-                     const TimeStamp& aTimeStamp,
-                     dom::Animation* aAnimation)
-    : mElement(aTarget.mElement)
-    , mAnimation(aAnimation)
-    , mEvent(true, aMessage)
-    , mTimeStamp(aTimeStamp)
-  {
-    // XXX Looks like nobody initialize WidgetEvent::time
-    aAnimationName->ToString(mEvent.mAnimationName);
-    mEvent.mElapsedTime = aElapsedTime;
-    mEvent.mPseudoElement =
-      nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType);
-  }
-
-  // InternalAnimationEvent doesn't support copy-construction, so we need
-  // to ourselves in order to work with nsTArray
-  AnimationEventInfo(const AnimationEventInfo& aOther)
-    : mElement(aOther.mElement)
-    , mAnimation(aOther.mAnimation)
-    , mEvent(true, aOther.mEvent.mMessage)
-    , mTimeStamp(aOther.mTimeStamp)
-  {
-    mEvent.AssignAnimationEventData(aOther.mEvent, false);
-  }
-};
-
 namespace dom {
 
 class CSSAnimation final : public Animation
 {
 public:
  explicit CSSAnimation(nsIGlobalObject* aGlobal,
                        nsAtom* aAnimationName)
     : dom::Animation(aGlobal)
@@ -305,23 +269,21 @@ struct AnimationTypeTraits<dom::CSSAnima
   {
     return nsGkAtoms::animationsOfAfterProperty;
   }
 };
 
 } /* namespace mozilla */
 
 class nsAnimationManager final
-  : public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation,
-                                           mozilla::AnimationEventInfo>
+  : public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>
 {
 public:
   explicit nsAnimationManager(nsPresContext *aPresContext)
-    : mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation,
-                                      mozilla::AnimationEventInfo>(aPresContext)
+    : mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>(aPresContext)
   {
   }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager)
 
   typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation>
     CSSAnimationCollection;
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -244,31 +244,31 @@ CSSTransition::QueueEvents(const StickyT
 
   if (mPendingState != PendingState::NotPending &&
       (mPreviousTransitionPhase == TransitionPhase::Idle ||
        mPreviousTransitionPhase == TransitionPhase::Pending))
   {
     currentPhase = TransitionPhase::Pending;
   }
 
-  AutoTArray<TransitionEventInfo, 3> events;
+  AutoTArray<AnimationEventInfo, 3> events;
 
   auto appendTransitionEvent = [&](EventMessage aMessage,
                                    const StickyTimeDuration& aElapsedTime,
                                    const TimeStamp& aTimeStamp) {
     double elapsedTime = aElapsedTime.ToSeconds();
     if (aMessage == eTransitionCancel) {
       elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime);
     }
-    events.AppendElement(TransitionEventInfo(mOwningElement.Target(),
-                                             aMessage,
-                                             TransitionProperty(),
-                                             elapsedTime,
-                                             aTimeStamp,
-                                             this));
+    events.AppendElement(AnimationEventInfo(TransitionProperty(),
+                                            mOwningElement.Target(),
+                                            aMessage,
+                                            elapsedTime,
+                                            aTimeStamp,
+                                            this));
   };
 
   // Handle cancel events first
   if ((mPreviousTransitionPhase != TransitionPhase::Idle &&
        mPreviousTransitionPhase != TransitionPhase::After) &&
       currentPhase == TransitionPhase::Idle) {
     TimeStamp activeTimeStamp = ElapsedTimeToTimeStamp(aActiveTime);
     appendTransitionEvent(eTransitionCancel, aActiveTime, activeTimeStamp);
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -5,23 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Code to start and animate CSS transitions. */
 
 #ifndef nsTransitionManager_h_
 #define nsTransitionManager_h_
 
 #include "mozilla/ComputedTiming.h"
-#include "mozilla/ContentEvents.h"
 #include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
-#include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/KeyframeEffectReadOnly.h"
 #include "AnimationCommon.h"
-#include "nsCSSProps.h"
 
 class nsIGlobalObject;
 class nsStyleContext;
 class nsPresContext;
 class nsCSSPropertyIDSet;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
@@ -296,63 +293,24 @@ struct AnimationTypeTraits<dom::CSSTrans
     return nsGkAtoms::transitionsOfBeforeProperty;
   }
   static nsAtom* AfterPropertyAtom()
   {
     return nsGkAtoms::transitionsOfAfterProperty;
   }
 };
 
-struct TransitionEventInfo {
-  RefPtr<dom::Element> mElement;
-  RefPtr<dom::Animation> mAnimation;
-  InternalTransitionEvent mEvent;
-  TimeStamp mTimeStamp;
-
-  TransitionEventInfo(const NonOwningAnimationTarget& aTarget,
-                      EventMessage aMessage,
-                      nsCSSPropertyID aProperty,
-                      double aElapsedTime,
-                      const TimeStamp& aTimeStamp,
-                      dom::Animation* aAnimation)
-    : mElement(aTarget.mElement)
-    , mAnimation(aAnimation)
-    , mEvent(true, aMessage)
-    , mTimeStamp(aTimeStamp)
-  {
-    // XXX Looks like nobody initialize WidgetEvent::time
-    mEvent.mPropertyName =
-      NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty));
-    mEvent.mElapsedTime = aElapsedTime;
-    mEvent.mPseudoElement =
-      nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType);
-  }
-
-  // InternalTransitionEvent doesn't support copy-construction, so we need
-  // to ourselves in order to work with nsTArray
-  TransitionEventInfo(const TransitionEventInfo& aOther)
-    : mElement(aOther.mElement)
-    , mAnimation(aOther.mAnimation)
-    , mEvent(aOther.mEvent)
-    , mTimeStamp(aOther.mTimeStamp)
-  {
-    mEvent.AssignTransitionEventData(aOther.mEvent, false);
-  }
-};
-
 } // namespace mozilla
 
 class nsTransitionManager final
-  : public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition,
-                                           mozilla::TransitionEventInfo>
+  : public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>
 {
 public:
   explicit nsTransitionManager(nsPresContext *aPresContext)
-    : mozilla::CommonAnimationManager<mozilla::dom::CSSTransition,
-                                      mozilla::TransitionEventInfo>(aPresContext)
+    : mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>(aPresContext)
     , mInAnimationOnlyStyleUpdate(false)
   {
   }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsTransitionManager)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTransitionManager)
 
   typedef mozilla::AnimationCollection<mozilla::dom::CSSTransition>