Bug 1180125 part 7 - Queue transition events from CSSTransition::Tick; r=dbaron
authorBrian Birtles <birtles@gmail.com>
Wed, 29 Jul 2015 10:57:40 +0900
changeset 288381 c9a55501a48fa4d022403a912e0770f1a2461ac1
parent 288380 0162e758928d832be2fab18a113d41e8e2d9ad6f
child 288382 a7054bcee797e83e8ce2359a0e572abf9fde886e
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, 1181392, 1178665
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 7 - Queue transition events from CSSTransition::Tick; r=dbaron This patch moves the logic for queueing events out of the logic for flushing transitions making it a separate step. It still doesn't delay the dispatch of those events into a separate step, however. That is done in a subsequent patch. This patch also makes sure to clear any queued events when the nsPresShell that owns the transition manager is destroyed. We don't expect CSSTransition::Tick to be called anywhere except nsTransitionManger::FlushTransitions so there shouldn't be any orphaned events but for completeness it seems best to add this now. (Later, when we tick transitions from their timeline we will need this.) This patch introduces a separate flag to CSSTransition for tracking if a transition is newly-finished so we can correctly dispatch the transitionend event. Although, this may seem to be redundant with the "IsFinishedTransition" we also track, that state will soon be removed in bug 1181392 and hence this flag will be needed then. Note that Animation already has flags mIsPreviousStateFinished and mFinishedAtLastComposeStyle which would appear to be similar however, - mIsPreviousStateFinished will be removed in bug 1178665 and is updated more often than we queue events so it is not useful here. - mFinishedAtLastComposeStyle is used to determine if we can throttle a style update and is also updated more frequently than we queue events and hence can't be used here. Once we guarantee one call to Tick() per frame we may be able to simplify this by tracking "state on last tick" but for now we need this additional flag on CSSTransition. CSSAnimation has a similar flag for this (mPreviousPhaseOrIteration) which we may be able to unify at the same point.
layout/base/nsPresShell.cpp
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1247,16 +1247,17 @@ PresShell::Destroy()
 
     if (mDocument->HasAnimationController()) {
       mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
     }
   }
 
   if (mPresContext) {
     mPresContext->AnimationManager()->ClearEventQueue();
+    mPresContext->TransitionManager()->ClearEventQueue();
   }
 
   // Revoke any pending events.  We need to do this and cancel pending reflows
   // before we destroy the frame manager, since apparently frame destruction
   // sometimes spins the event queue when plug-ins are involved(!).
   rd->RemoveLayoutFlushObserver(this);
   if (mHiddenInvalidationObserverRefreshDriver) {
     mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this);
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -112,16 +112,52 @@ CSSTransition::GetAnimationManager() con
   nsPresContext* context = GetPresContext();
   if (!context) {
     return nullptr;
   }
 
   return context->TransitionManager();
 }
 
+void
+CSSTransition::QueueEvents()
+{
+  AnimationPlayState playState = PlayState();
+  bool newlyFinished = !mWasFinishedOnLastTick &&
+                       playState == AnimationPlayState::Finished;
+  mWasFinishedOnLastTick = playState == AnimationPlayState::Finished;
+
+  if (!newlyFinished || !mEffect || !mOwningElement.IsSet()) {
+    return;
+  }
+
+  dom::Element* owningElement;
+  nsCSSPseudoElements::Type owningPseudoType;
+  mOwningElement.GetElement(owningElement, owningPseudoType);
+  MOZ_ASSERT(owningElement, "Owning element should be set");
+
+  nsPresContext* presContext = mOwningElement.GetRenderedPresContext();
+  if (!presContext) {
+    return;
+  }
+  nsTransitionManager* manager = presContext->TransitionManager();
+
+  manager->QueueEvent(
+    TransitionEventInfo(owningElement, TransitionProperty(),
+                        mEffect->Timing().mIterationDuration,
+                        owningPseudoType));
+}
+
+void
+CSSTransition::Tick()
+{
+  Animation::Tick();
+  QueueEvents();
+}
+
 nsCSSProperty
 CSSTransition::TransitionProperty() const
 {
   // FIXME: Once we support replacing/removing the effect (bug 1049975)
   // we'll need to store the original transition property so we keep
   // returning the same value in that case.
   dom::KeyframeEffectReadOnly* effect = GetEffect();
   MOZ_ASSERT(effect && effect->AsTransition(),
@@ -912,24 +948,16 @@ nsTransitionManager::FlushTransitions(Fl
       do {
         --i;
         Animation* anim = collection->mAnimations[i];
         if (!anim->GetEffect()->IsFinishedTransition()) {
           MOZ_ASSERT(anim->GetEffect(), "Transitions should have an effect");
           ComputedTiming computedTiming =
             anim->GetEffect()->GetComputedTiming();
           if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) {
-            nsCSSProperty prop =
-              anim->GetEffect()->AsTransition()->TransitionProperty();
-            TimeDuration duration =
-              anim->GetEffect()->Timing().mIterationDuration;
-            mEventDispatcher.QueueEvent(
-              TransitionEventInfo(collection->mElement, prop,
-                                  duration, collection->PseudoElementType()));
-
             // Leave this transition in the list for one more refresh
             // cycle, since we haven't yet processed its style change, and
             // if we also have (already, or will have from processing
             // transitionend events or other refresh driver notifications)
             // a non-animation style change that would affect it, we need
             // to know not to start a new transition for the transition
             // from the almost-completed value to the final value.
             anim->GetEffect()->SetIsFinishedTransition(true);
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -79,16 +79,17 @@ struct ElementPropertyTransition : publi
 
 namespace dom {
 
 class CSSTransition final : public Animation
 {
 public:
  explicit CSSTransition(nsIGlobalObject* aGlobal)
     : dom::Animation(aGlobal)
+    , mWasFinishedOnLastTick(false)
   {
   }
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   CSSTransition* AsCSSTransition() override { return this; }
   const CSSTransition* AsCSSTransition() const override { return this; }
@@ -112,16 +113,18 @@ public:
 
   void CancelFromStyle() override
   {
     mOwningElement = OwningElementRef();
     Animation::CancelFromStyle();
     MOZ_ASSERT(mSequenceNum == kUnsequenced);
   }
 
+  void Tick() override;
+
   nsCSSProperty TransitionProperty() const;
 
   bool HasLowerCompositeOrderThan(const Animation& aOther) const override;
   bool IsUsingCustomCompositeOrder() const override
   {
     return mOwningElement.IsSet();
   }
 
@@ -161,19 +164,23 @@ protected:
   virtual ~CSSTransition()
   {
     MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
                                         "before a CSS transition is destroyed");
   }
 
   virtual CommonAnimationManager* GetAnimationManager() const override;
 
+  void QueueEvents();
+
   // The (pseudo-)element whose computed transition-property refers to this
   // transition (if any).
   OwningElementRef mOwningElement;
+
+  bool mWasFinishedOnLastTick;
 };
 
 } // namespace dom
 
 struct TransitionEventInfo {
   nsCOMPtr<nsIContent> mElement;
   InternalTransitionEvent mEvent;
 
@@ -267,16 +274,24 @@ public:
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     MOZ_MUST_OVERRIDE override;
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime) override;
 
   void FlushTransitions(FlushFlags aFlags);
 
+  void QueueEvent(mozilla::TransitionEventInfo&& aEventInfo)
+  {
+    mEventDispatcher.QueueEvent(
+      mozilla::Forward<mozilla::TransitionEventInfo>(aEventInfo));
+  }
+
+  void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
+
 protected:
   virtual ~nsTransitionManager() {}
 
   virtual nsIAtom* GetAnimationsAtom() override {
     return nsGkAtoms::transitionsProperty;
   }
   virtual nsIAtom* GetAnimationsBeforeAtom() override {
     return nsGkAtoms::transitionsOfBeforeProperty;