Bug 1078122 part 9 - Move queuing of CSS animation events to CSSAnimationPlayer; r=dholbert
authorBrian Birtles <birtles@gmail.com>
Mon, 20 Oct 2014 13:55:47 +0900
changeset 211183 b3b581cda9405581e89480d211c472693661a39d
parent 211182 1a66dbf7d8e10bb1393d6f6ab07b2120db563880
child 211184 92f19cf15b2dfe0683669b9f07dbeafc3e4c071c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdholbert
bugs1078122
milestone36.0a1
Bug 1078122 part 9 - Move queuing of CSS animation events to CSSAnimationPlayer; r=dholbert This patch moves the code for queuing CSS animation events from nsAnimationManager to CSSAnimationPlayer. In doing so, it also moves the mLastNotification member and associated enum values.
dom/animation/Animation.h
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -138,17 +138,16 @@ public:
             nsCSSPseudoElements::Type aPseudoType,
             const AnimationTiming &aTiming,
             const nsSubstring& aName)
     : mDocument(aDocument)
     , mTarget(aTarget)
     , mTiming(aTiming)
     , mName(aName)
     , mIsFinishedTransition(false)
-    , mLastNotification(LAST_NOTIFICATION_NONE)
     , mPseudoType(aPseudoType)
   {
     MOZ_ASSERT(aTarget, "null animation target is not yet supported");
   }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Animation)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Animation)
 
@@ -249,25 +248,16 @@ public:
     MOZ_ASSERT(AsTransition(),
                "Calling SetIsFinishedTransition but it's not a transition");
     mIsFinishedTransition = true;
   }
 
   bool IsCurrent() const;
   bool IsInEffect() const;
 
-  enum {
-    LAST_NOTIFICATION_NONE = uint64_t(-1),
-    LAST_NOTIFICATION_END = uint64_t(-2)
-  };
-  uint64_t LastNotification() const { return mLastNotification; }
-  void SetLastNotification(uint64_t aLastNotification) {
-    mLastNotification = aLastNotification;
-  }
-
   bool HasAnimationOfProperty(nsCSSProperty aProperty) const;
   const InfallibleTArray<AnimationProperty>& Properties() const {
     return mProperties;
   }
   InfallibleTArray<AnimationProperty>& Properties() {
     return mProperties;
   }
 
@@ -287,19 +277,16 @@ protected:
   nsCOMPtr<Element> mTarget;
   Nullable<TimeDuration> mParentTime;
 
   AnimationTiming mTiming;
   nsString mName;
   // A flag to mark transitions that have finished and are due to
   // be removed on the next throttle-able cycle.
   bool mIsFinishedTransition;
-  // One of the LAST_NOTIFICATION_* constants, or an integer for the iteration
-  // whose start we last notified on.
-  uint64_t mLastNotification;
   nsCSSPseudoElements::Type mPseudoType;
 
   InfallibleTArray<AnimationProperty> mProperties;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -58,99 +58,120 @@ CSSAnimationPlayer::PauseFromStyle()
     return;
   }
 
   mIsStylePaused = true;
   AnimationPlayer::Pause(eNoUpdate);
 }
 
 void
+CSSAnimationPlayer::QueueEvents(EventArray& aEventsToDispatch)
+{
+  if (!mSource) {
+    return;
+  }
+
+  ComputedTiming computedTiming = mSource->GetComputedTiming();
+
+  dom::Element* target;
+  nsCSSPseudoElements::Type targetPseudoType;
+  mSource->GetTarget(target, targetPseudoType);
+
+  switch (computedTiming.mPhase) {
+    case ComputedTiming::AnimationPhase_Null:
+    case ComputedTiming::AnimationPhase_Before:
+      // Do nothing
+      break;
+
+    case ComputedTiming::AnimationPhase_Active:
+      // Dispatch 'animationstart' or 'animationiteration' when needed.
+      if (computedTiming.mCurrentIteration != mLastNotification) {
+        // Notify 'animationstart' even if a negative delay puts us
+        // past the first iteration.
+        // Note that when somebody changes the animation-duration
+        // dynamically, this will fire an extra iteration event
+        // immediately in many cases.  It's not clear to me if that's the
+        // right thing to do.
+        uint32_t message = mLastNotification == LAST_NOTIFICATION_NONE
+                           ? NS_ANIMATION_START
+                           : NS_ANIMATION_ITERATION;
+        mLastNotification = computedTiming.mCurrentIteration;
+        TimeDuration iterationStart =
+          mSource->Timing().mIterationDuration *
+          computedTiming.mCurrentIteration;
+        TimeDuration elapsedTime =
+          std::max(iterationStart, mSource->InitialAdvance());
+        AnimationEventInfo ei(target, Name(), message,
+                              StickyTimeDuration(elapsedTime),
+                              PseudoTypeAsString(targetPseudoType));
+        aEventsToDispatch.AppendElement(ei);
+      }
+      break;
+
+    case ComputedTiming::AnimationPhase_After:
+      // If we skipped the animation interval entirely, dispatch
+      // 'animationstart' first
+      if (mLastNotification == LAST_NOTIFICATION_NONE) {
+        // Notifying for start of 0th iteration.
+        // (This is overwritten below but we set it here to maintain
+        // internal consistency.)
+        mLastNotification = 0;
+        StickyTimeDuration elapsedTime =
+          std::min(StickyTimeDuration(mSource->InitialAdvance()),
+                   computedTiming.mActiveDuration);
+        AnimationEventInfo ei(target, Name(), NS_ANIMATION_START,
+                              elapsedTime,
+                              PseudoTypeAsString(targetPseudoType));
+        aEventsToDispatch.AppendElement(ei);
+      }
+      // Dispatch 'animationend' when needed.
+      if (mLastNotification != LAST_NOTIFICATION_END) {
+        mLastNotification = LAST_NOTIFICATION_END;
+        AnimationEventInfo ei(target, Name(), NS_ANIMATION_END,
+                              computedTiming.mActiveDuration,
+                              PseudoTypeAsString(targetPseudoType));
+        aEventsToDispatch.AppendElement(ei);
+      }
+      break;
+  }
+}
+
+/* static */ nsString
+CSSAnimationPlayer::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType)
+{
+  switch (aPseudoType) {
+    case nsCSSPseudoElements::ePseudo_before:
+      return NS_LITERAL_STRING("::before");
+    case nsCSSPseudoElements::ePseudo_after:
+      return NS_LITERAL_STRING("::after");
+    default:
+      return EmptyString();
+  }
+}
+
+void
 nsAnimationManager::UpdateStyleAndEvents(AnimationPlayerCollection*
                                            aCollection,
                                          TimeStamp aRefreshTime,
                                          EnsureStyleRuleFlags aFlags)
 {
   aCollection->EnsureStyleRuleFor(aRefreshTime, aFlags);
-  GetEventsForCurrentTime(aCollection, mPendingEvents);
+  QueueEvents(aCollection, mPendingEvents);
   CheckNeedsRefresh();
 }
 
 void
-nsAnimationManager::GetEventsForCurrentTime(AnimationPlayerCollection*
-                                              aCollection,
-                                            EventArray& aEventsToDispatch)
+nsAnimationManager::QueueEvents(AnimationPlayerCollection* aCollection,
+                                EventArray& aEventsToDispatch)
 {
   for (size_t playerIdx = aCollection->mPlayers.Length(); playerIdx-- != 0; ) {
-    AnimationPlayer* player = aCollection->mPlayers[playerIdx];
-    Animation* anim = player->GetSource();
-    if (!anim) {
-      continue;
-    }
-
-    ComputedTiming computedTiming = anim->GetComputedTiming();
-
-    switch (computedTiming.mPhase) {
-      case ComputedTiming::AnimationPhase_Null:
-      case ComputedTiming::AnimationPhase_Before:
-        // Do nothing
-        break;
-
-      case ComputedTiming::AnimationPhase_Active:
-        // Dispatch 'animationstart' or 'animationiteration' when needed.
-        if (computedTiming.mCurrentIteration != anim->LastNotification()) {
-          // Notify 'animationstart' even if a negative delay puts us
-          // past the first iteration.
-          // Note that when somebody changes the animation-duration
-          // dynamically, this will fire an extra iteration event
-          // immediately in many cases.  It's not clear to me if that's the
-          // right thing to do.
-          uint32_t message =
-            anim->LastNotification() == Animation::LAST_NOTIFICATION_NONE
-                                        ? NS_ANIMATION_START
-                                        : NS_ANIMATION_ITERATION;
-          anim->SetLastNotification(computedTiming.mCurrentIteration);
-          TimeDuration iterationStart =
-            anim->Timing().mIterationDuration *
-            computedTiming.mCurrentIteration;
-          TimeDuration elapsedTime =
-            std::max(iterationStart, anim->InitialAdvance());
-          AnimationEventInfo ei(aCollection->mElement, player->Name(), message,
-                                StickyTimeDuration(elapsedTime),
-                                aCollection->PseudoElement());
-          aEventsToDispatch.AppendElement(ei);
-        }
-        break;
-
-      case ComputedTiming::AnimationPhase_After:
-        // If we skipped the animation interval entirely, dispatch
-        // 'animationstart' first
-        if (anim->LastNotification() == Animation::LAST_NOTIFICATION_NONE) {
-          // Notifying for start of 0th iteration.
-          // (This is overwritten below but we set it here to maintain
-          // internal consistency.)
-          anim->SetLastNotification(0);
-          StickyTimeDuration elapsedTime =
-            std::min(StickyTimeDuration(anim->InitialAdvance()),
-                     computedTiming.mActiveDuration);
-          AnimationEventInfo ei(aCollection->mElement,
-                                player->Name(), NS_ANIMATION_START,
-                                elapsedTime, aCollection->PseudoElement());
-          aEventsToDispatch.AppendElement(ei);
-        }
-        // Dispatch 'animationend' when needed.
-        if (anim->LastNotification() != Animation::LAST_NOTIFICATION_END) {
-          anim->SetLastNotification(Animation::LAST_NOTIFICATION_END);
-          AnimationEventInfo ei(aCollection->mElement,
-                                player->Name(), NS_ANIMATION_END,
-                                computedTiming.mActiveDuration,
-                                aCollection->PseudoElement());
-          aEventsToDispatch.AppendElement(ei);
-        }
-        break;
-    }
+    CSSAnimationPlayer* player =
+      aCollection->mPlayers[playerIdx]->AsCSSAnimationPlayer();
+    MOZ_ASSERT(player, "Expected a collection of CSS Animation players");
+    player->QueueEvents(aEventsToDispatch);
   }
 }
 
 AnimationPlayerCollection*
 nsAnimationManager::GetAnimationPlayers(dom::Element *aElement,
                                         nsCSSPseudoElements::Type aPseudoType,
                                         bool aCreateIfNeeded)
 {
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -51,33 +51,38 @@ typedef InfallibleTArray<AnimationEventI
 
 class CSSAnimationPlayer MOZ_FINAL : public dom::AnimationPlayer
 {
 public:
  explicit CSSAnimationPlayer(dom::AnimationTimeline* aTimeline)
     : dom::AnimationPlayer(aTimeline)
     , mIsStylePaused(false)
     , mPauseShouldStick(false)
+    , mLastNotification(LAST_NOTIFICATION_NONE)
   {
   }
 
   virtual CSSAnimationPlayer*
   AsCSSAnimationPlayer() MOZ_OVERRIDE { return this; }
 
   virtual void Play(UpdateFlags aUpdateFlags) MOZ_OVERRIDE;
   virtual void Pause(UpdateFlags aUpdateFlags) MOZ_OVERRIDE;
 
   void PlayFromStyle();
   void PauseFromStyle();
 
   bool IsStylePaused() const { return mIsStylePaused; }
 
+  void QueueEvents(EventArray& aEventsToDispatch);
+
 protected:
   virtual ~CSSAnimationPlayer() { }
 
+  static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType);
+
   // When combining animation-play-state with play() / pause() the following
   // behavior applies:
   // 1. pause() is sticky and always overrides the underlying
   //    animation-play-state
   // 2. If animation-play-state is 'paused', play() will temporarily override
   //    it until animation-play-state next becomes 'running'.
   // 3. Calls to play() trigger finishing behavior but setting the
   //    animation-play-state to 'running' does not.
@@ -119,16 +124,24 @@ protected:
   //    (mIsPaused; mIsStylePaused; mPauseShouldStick)
   // E. Paused by animation-play-state
   //    (mIsPaused; mIsStylePaused; !mPauseShouldStick)
   //
   // (That leaves 3 combinations of the boolean values that we never set because
   // they don't represent valid states.)
   bool mIsStylePaused;
   bool mPauseShouldStick;
+
+  enum {
+    LAST_NOTIFICATION_NONE = uint64_t(-1),
+    LAST_NOTIFICATION_END = uint64_t(-2)
+  };
+  // One of the LAST_NOTIFICATION_* constants, or an integer for the iteration
+  // whose start we last notified on.
+  uint64_t mLastNotification;
 };
 
 } /* namespace mozilla */
 
 class nsAnimationManager MOZ_FINAL
   : public mozilla::css::CommonAnimationManager
 {
 public:
@@ -154,18 +167,18 @@ public:
     } while ((aContent = aContent->GetParent()));
 
     return false;
   }
 
   void UpdateStyleAndEvents(mozilla::AnimationPlayerCollection* aEA,
                             mozilla::TimeStamp aRefreshTime,
                             mozilla::EnsureStyleRuleFlags aFlags);
-  void GetEventsForCurrentTime(mozilla::AnimationPlayerCollection* aEA,
-                               mozilla::EventArray &aEventsToDispatch);
+  void QueueEvents(mozilla::AnimationPlayerCollection* aEA,
+                   mozilla::EventArray &aEventsToDispatch);
 
   // nsIStyleRuleProcessor (parts)
   virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
 #ifdef MOZ_XUL
   virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
 #endif