Bug 1180125 part 3 - Extract DelayedEventDispatcher; r=dbaron
authorBrian Birtles <birtles@gmail.com>
Wed, 29 Jul 2015 10:57:39 +0900
changeset 288377 d9549506b9cffa8337ad44eb32e8fe3105ac1b48
parent 288376 6bd9620537f0c60625cd804cdac85406f1976fce
child 288378 50892e26a6ec6469407a654d4f242bafb32a7b95
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1180125
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1180125 part 3 - Extract DelayedEventDispatcher; r=dbaron This patch extracts a utility class for queueing up a series of EventInfo objects (of templated type) and then dispatching them. This covers the event queuing behavior in nsAnimationManager so that we can reuse it in nsTransitionManager.
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -8,16 +8,17 @@
 
 #include "nsIStyleRuleProcessor.h"
 #include "nsIStyleRule.h"
 #include "nsRefreshDriver.h"
 #include "prclist.h"
 #include "nsChangeHint.h"
 #include "nsCSSProperty.h"
 #include "nsDisplayList.h" // For nsDisplayItem::Type
+#include "mozilla/EventDispatcher.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsStyleStruct.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Assertions.h"
@@ -507,11 +508,77 @@ public:
 
   nsPresContext* GetRenderedPresContext() const;
 
 private:
   dom::Element* MOZ_NON_OWNING_REF mElement;
   nsCSSPseudoElements::Type        mPseudoType;
 };
 
+template <class EventInfo>
+class DelayedEventDispatcher
+{
+public:
+  void QueueEvent(EventInfo&& aEventInfo)
+  {
+    mPendingEvents.AppendElement(mozilla::Forward<EventInfo>(aEventInfo));
+  }
+
+  // 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.
+  void DispatchEvents(nsPresContext* const & aPresContext)
+  {
+    if (!aPresContext || mPendingEvents.IsEmpty()) {
+      return;
+    }
+
+    EventArray events;
+    mPendingEvents.SwapElements(events);
+    // FIXME: Sort events here in timeline order, then document order
+    for (EventInfo& info : events) {
+      EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent);
+
+      if (!aPresContext) {
+        break;
+      }
+    }
+  }
+
+  void ClearEventQueue() { mPendingEvents.Clear(); }
+  bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
+
+  // Methods for supporting cycle-collection
+  void Traverse(nsCycleCollectionTraversalCallback* aCallback,
+                const char* aName)
+  {
+    for (EventInfo& info : mPendingEvents) {
+      ImplCycleCollectionTraverse(*aCallback, info.mElement, aName);
+    }
+  }
+  void Unlink() { mPendingEvents.Clear(); }
+
+protected:
+  typedef nsTArray<EventInfo> EventArray;
+
+  EventArray mPendingEvents;
+};
+
+template <class EventInfo>
+inline void
+ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField)
+{
+  aField.Unlink();
+}
+
+template <class EventInfo>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            DelayedEventDispatcher<EventInfo>& aField,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  aField.Traverse(&aCallback, aName);
+}
+
 } // namespace mozilla
 
 #endif /* !defined(mozilla_css_AnimationCommon_h) */
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 #include "mozilla/dom/CSSAnimationBinding.h"
 
-#include "mozilla/EventDispatcher.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "mozilla/dom/KeyframeEffect.h"
 
 #include "nsPresContext.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
@@ -246,20 +245,19 @@ CSSAnimation::QueueEvents()
   } else if (wasActive && isActive && !isSameIteration) {
     message = NS_ANIMATION_ITERATION;
   } else if (skippedActivePhase) {
     // First notifying for start of 0th iteration by appending an
     // 'animationstart':
     StickyTimeDuration elapsedTime =
       std::min(StickyTimeDuration(mEffect->InitialAdvance()),
                computedTiming.mActiveDuration);
-    AnimationEventInfo ei(owningElement, mAnimationName, NS_ANIMATION_START,
-                          elapsedTime,
-                          PseudoTypeAsString(owningPseudoType));
-    manager->QueueEvent(ei);
+    manager->QueueEvent(
+      AnimationEventInfo(owningElement, mAnimationName, NS_ANIMATION_START,
+                         elapsedTime, PseudoTypeAsString(owningPseudoType)));
     // Then have the shared code below append an 'animationend':
     message = NS_ANIMATION_END;
   } else {
     return; // No events need to be sent
   }
 
   StickyTimeDuration elapsedTime;
 
@@ -269,19 +267,19 @@ CSSAnimation::QueueEvents()
                                     computedTiming.mCurrentIteration;
     elapsedTime = StickyTimeDuration(std::max(iterationStart,
                                               mEffect->InitialAdvance()));
   } else {
     MOZ_ASSERT(message == NS_ANIMATION_END);
     elapsedTime = computedTiming.mActiveDuration;
   }
 
-  AnimationEventInfo ei(owningElement, mAnimationName, message, elapsedTime,
-                        PseudoTypeAsString(owningPseudoType));
-  manager->QueueEvent(ei);
+  manager->QueueEvent(
+    AnimationEventInfo(owningElement, mAnimationName, message, elapsedTime,
+                       PseudoTypeAsString(owningPseudoType)));
 }
 
 CommonAnimationManager*
 CSSAnimation::GetAnimationManager() const
 {
   nsPresContext* context = GetPresContext();
   if (!context) {
     return nullptr;
@@ -300,25 +298,17 @@ CSSAnimation::PseudoTypeAsString(nsCSSPs
       return NS_LITERAL_STRING("::after");
     default:
       return EmptyString();
   }
 }
 
 ////////////////////////// nsAnimationManager ////////////////////////////
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsAnimationManager)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAnimationManager)
-  tmp->mPendingEvents.Clear();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnimationManager)
-  for (AnimationEventInfo& info : tmp->mPendingEvents) {
-    ImplCycleCollectionTraverse(cb, info.mElement, "mPendingEvents.mElement");
-  }
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnimationManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnimationManager)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAnimationManager)
   NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRuleProcessor)
 NS_INTERFACE_MAP_END
@@ -361,17 +351,17 @@ nsAnimationManager::MaybeUpdateCascadeRe
 
 /* virtual */ size_t
 nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
-  // - mPendingEvents
+  // - mEventDispatcher
 }
 
 /* virtual */ size_t
 nsAnimationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
@@ -532,33 +522,27 @@ nsAnimationManager::CheckAnimationRule(n
   for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) {
     newAnimations[newAnimIdx]->CancelFromStyle();
   }
 
   UpdateCascadeResults(aStyleContext, collection);
 
   TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
   collection->EnsureStyleRuleFor(refreshTime, EnsureStyleRule_IsNotThrottled);
-  // We don't actually dispatch the mPendingEvents now.  We'll either
+  // We don't actually dispatch the pending events now.  We'll either
   // dispatch them the next time we get a refresh driver notification
   // or the next time somebody calls
   // nsPresShell::FlushPendingNotifications.
-  if (!mPendingEvents.IsEmpty()) {
+  if (mEventDispatcher.HasQueuedEvents()) {
     mPresContext->Document()->SetNeedStyleFlush();
   }
 
   return GetAnimationRule(aElement, aStyleContext->GetPseudoType());
 }
 
-void
-nsAnimationManager::QueueEvent(AnimationEventInfo& aEventInfo)
-{
-  mPendingEvents.AppendElement(aEventInfo);
-}
-
 struct KeyframeData {
   float mKey;
   uint32_t mIndex; // store original order since sort algorithm is not stable
   nsCSSKeyframeRule *mRule;
 };
 
 struct KeyframeDataComparator {
   bool Equals(const KeyframeData& A, const KeyframeData& B) const {
@@ -1017,24 +1001,8 @@ nsAnimationManager::FlushAnimations(Flus
   }
 
   if (didThrottle) {
     mPresContext->Document()->SetNeedStyleFlush();
   }
 
   MaybeStartOrStopObservingRefreshDriver();
 }
-
-void
-nsAnimationManager::DoDispatchEvents()
-{
-  EventArray events;
-  mPendingEvents.SwapElements(events);
-  // FIXME: Sort events here in timeline order, then document order
-  for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) {
-    AnimationEventInfo &info = events[i];
-    EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
-
-    if (!mPresContext) {
-      break;
-    }
-  }
-}
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -45,18 +45,16 @@ struct AnimationEventInfo {
   // to ourselves in order to work with nsTArray
   AnimationEventInfo(const AnimationEventInfo &aOther)
     : mElement(aOther.mElement), mEvent(true, aOther.mEvent.message)
   {
     mEvent.AssignAnimationEventData(aOther.mEvent, false);
   }
 };
 
-typedef InfallibleTArray<AnimationEventInfo> EventArray;
-
 namespace dom {
 
 class CSSAnimation final : public Animation
 {
 public:
  explicit CSSAnimation(nsIGlobalObject* aGlobal,
                        const nsSubstring& aAnimationName)
     : dom::Animation(aGlobal)
@@ -276,33 +274,31 @@ public:
    * :before or :after pseudo-element.
    */
   nsIStyleRule* CheckAnimationRule(nsStyleContext* aStyleContext,
                                    mozilla::dom::Element* aElement);
 
   /**
    * Add a pending event.
    */
-  void QueueEvent(mozilla::AnimationEventInfo& aEventInfo);
+  void QueueEvent(mozilla::AnimationEventInfo&& aEventInfo)
+  {
+    mEventDispatcher.QueueEvent(
+      mozilla::Forward<mozilla::AnimationEventInfo>(aEventInfo));
+  }
 
   /**
    * Dispatch any pending events.  We accumulate animationend and
    * animationiteration events only during refresh driver notifications
    * (and dispatch them at the end of such notifications), but we
    * accumulate animationstart events at other points when style
    * contexts are created.
    */
-  void DispatchEvents() {
-    // Fast-path the common case: no events
-    if (!mPendingEvents.IsEmpty()) {
-      DoDispatchEvents();
-    }
-  }
-
-  void ClearEventQueue() { mPendingEvents.Clear(); }
+  void DispatchEvents()  { mEventDispatcher.DispatchEvents(mPresContext); }
+  void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
 
 protected:
   virtual ~nsAnimationManager() {}
 
   virtual nsIAtom* GetAnimationsAtom() override {
     return nsGkAtoms::animationsProperty;
   }
   virtual nsIAtom* GetAnimationsBeforeAtom() override {
@@ -310,32 +306,29 @@ protected:
   }
   virtual nsIAtom* GetAnimationsAfterAtom() override {
     return nsGkAtoms::animationsOfAfterProperty;
   }
   virtual bool IsAnimationManager() override {
     return true;
   }
 
+  mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher;
+
 private:
   void BuildAnimations(nsStyleContext* aStyleContext,
                        mozilla::dom::Element* aTarget,
                        mozilla::dom::AnimationTimeline* aTimeline,
                        mozilla::AnimationPtrArray& aAnimations);
   bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
                       aSegments,
                     nsCSSProperty aProperty,
                     const mozilla::StyleAnimation& aAnimation,
                     float aFromKey, nsStyleContext* aFromContext,
                     mozilla::css::Declaration* aFromDeclaration,
                     float aToKey, nsStyleContext* aToContext);
 
   static void UpdateCascadeResults(nsStyleContext* aStyleContext,
                                    mozilla::AnimationCollection*
                                      aElementAnimations);
-
-  // The guts of DispatchEvents
-  void DoDispatchEvents();
-
-  mozilla::EventArray mPendingEvents;
 };
 
 #endif /* !defined(nsAnimationManager_h_) */