LCOV - code coverage report
Current view: top level - layout/style - nsTransitionManager.h (source / functions) Hit Total Coverage
Test: output.info Lines: 69 95 72.6 %
Date: 2017-07-14 16:53:18 Functions: 29 43 67.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* Code to start and animate CSS transitions. */
       7             : 
       8             : #ifndef nsTransitionManager_h_
       9             : #define nsTransitionManager_h_
      10             : 
      11             : #include "mozilla/ComputedTiming.h"
      12             : #include "mozilla/ContentEvents.h"
      13             : #include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
      14             : #include "mozilla/MemoryReporting.h"
      15             : #include "mozilla/dom/Animation.h"
      16             : #include "mozilla/dom/KeyframeEffectReadOnly.h"
      17             : #include "AnimationCommon.h"
      18             : #include "nsCSSProps.h"
      19             : 
      20             : class nsIGlobalObject;
      21             : class nsStyleContext;
      22             : class nsPresContext;
      23             : class nsCSSPropertyIDSet;
      24             : 
      25             : namespace mozilla {
      26             : enum class CSSPseudoElementType : uint8_t;
      27             : struct Keyframe;
      28             : struct StyleTransition;
      29             : } // namespace mozilla
      30             : 
      31             : /*****************************************************************************
      32             :  * Per-Element data                                                          *
      33             :  *****************************************************************************/
      34             : 
      35             : namespace mozilla {
      36             : 
      37           0 : struct ElementPropertyTransition : public dom::KeyframeEffectReadOnly
      38             : {
      39           2 :   ElementPropertyTransition(nsIDocument* aDocument,
      40             :                             Maybe<OwningAnimationTarget>& aTarget,
      41             :                             const TimingParams &aTiming,
      42             :                             AnimationValue aStartForReversingTest,
      43             :                             double aReversePortion,
      44             :                             const KeyframeEffectParams& aEffectOptions)
      45           2 :     : dom::KeyframeEffectReadOnly(aDocument, aTarget, aTiming, aEffectOptions)
      46             :     , mStartForReversingTest(aStartForReversingTest)
      47           2 :     , mReversePortion(aReversePortion)
      48           2 :   { }
      49             : 
      50          10 :   ElementPropertyTransition* AsTransition() override { return this; }
      51           0 :   const ElementPropertyTransition* AsTransition() const override
      52             :   {
      53           0 :     return this;
      54             :   }
      55             : 
      56           2 :   nsCSSPropertyID TransitionProperty() const {
      57           2 :     MOZ_ASSERT(mKeyframes.Length() == 2,
      58             :                "Transitions should have exactly two animation keyframes. "
      59             :                "Perhaps we are using an un-initialized transition?");
      60           2 :     MOZ_ASSERT(mKeyframes[0].mPropertyValues.Length() == 1,
      61             :                "Transitions should have exactly one property in their first "
      62             :                "frame");
      63           2 :     return mKeyframes[0].mPropertyValues[0].mProperty;
      64             :   }
      65             : 
      66           2 :   AnimationValue ToValue() const {
      67             :     // If we failed to generate properties from the transition frames,
      68             :     // return a null value but also show a warning since we should be
      69             :     // detecting that kind of situation in advance and not generating a
      70             :     // transition in the first place.
      71           4 :     if (mProperties.Length() < 1 ||
      72           2 :         mProperties[0].mSegments.Length() < 1) {
      73           0 :       NS_WARNING("Failed to generate transition property values");
      74           0 :       return AnimationValue();
      75             :     }
      76           2 :     return mProperties[0].mSegments[0].mToValue;
      77             :   }
      78             : 
      79             :   // This is the start value to be used for a check for whether a
      80             :   // transition is being reversed.  Normally the same as
      81             :   // mProperties[0].mSegments[0].mFromValue, except when this transition
      82             :   // started as the reversal of another in-progress transition.
      83             :   // Needed so we can handle two reverses in a row.
      84             :   AnimationValue mStartForReversingTest;
      85             :   // Likewise, the portion (in value space) of the "full" reversed
      86             :   // transition that we're actually covering.  For example, if a :hover
      87             :   // effect has a transition that moves the element 10px to the right
      88             :   // (by changing 'left' from 0px to 10px), and the mouse moves in to
      89             :   // the element (starting the transition) but then moves out after the
      90             :   // transition has advanced 4px, the second transition (from 10px/4px
      91             :   // to 0px) will have mReversePortion of 0.4.  (If the mouse then moves
      92             :   // in again when the transition is back to 2px, the mReversePortion
      93             :   // for the third transition (from 0px/2px to 10px) will be 0.8.
      94             :   double mReversePortion;
      95             : 
      96             :   // Compute the portion of the *value* space that we should be through
      97             :   // at the current time.  (The input to the transition timing function
      98             :   // has time units, the output has value units.)
      99             :   double CurrentValuePortion() const;
     100             : 
     101             :   // For a new transition interrupting an existing transition on the
     102             :   // compositor, update the start value to match the value of the replaced
     103             :   // transitions at the current time.
     104             :   void UpdateStartValueFromReplacedTransition();
     105             : 
     106           0 :   struct ReplacedTransitionProperties {
     107             :     TimeDuration mStartTime;
     108             :     double mPlaybackRate;
     109             :     TimingParams mTiming;
     110             :     Maybe<ComputedTimingFunction> mTimingFunction;
     111             :     AnimationValue mFromValue, mToValue;
     112             :   };
     113             :   Maybe<ReplacedTransitionProperties> mReplacedTransition;
     114             : };
     115             : 
     116             : namespace dom {
     117             : 
     118             : class CSSTransition final : public Animation
     119             : {
     120             : public:
     121           2 :  explicit CSSTransition(nsIGlobalObject* aGlobal)
     122           2 :     : dom::Animation(aGlobal)
     123             :     , mPreviousTransitionPhase(TransitionPhase::Idle)
     124             :     , mNeedsNewAnimationIndexWhenRun(false)
     125           2 :     , mTransitionProperty(eCSSProperty_UNKNOWN)
     126             :   {
     127           2 :   }
     128             : 
     129             :   JSObject* WrapObject(JSContext* aCx,
     130             :                        JS::Handle<JSObject*> aGivenProto) override;
     131             : 
     132           0 :   CSSTransition* AsCSSTransition() override { return this; }
     133           6 :   const CSSTransition* AsCSSTransition() const override { return this; }
     134             : 
     135             :   // CSSTransition interface
     136             :   void GetTransitionProperty(nsString& aRetVal) const;
     137             : 
     138             :   // Animation interface overrides
     139             :   virtual AnimationPlayState PlayStateFromJS() const override;
     140             :   virtual void PlayFromJS(ErrorResult& aRv) override;
     141             : 
     142             :   // A variant of Play() that avoids posting style updates since this method
     143             :   // is expected to be called whilst already updating style.
     144           2 :   void PlayFromStyle()
     145             :   {
     146           4 :     ErrorResult rv;
     147           2 :     PlayNoUpdate(rv, Animation::LimitBehavior::Continue);
     148             :     // play() should not throw when LimitBehavior is Continue
     149           2 :     MOZ_ASSERT(!rv.Failed(), "Unexpected exception playing transition");
     150           2 :   }
     151             : 
     152           0 :   void CancelFromStyle() override
     153             :   {
     154             :     // The animation index to use for compositing will be established when
     155             :     // this transition next transitions out of the idle state but we still
     156             :     // update it now so that the sort order of this transition remains
     157             :     // defined until that moment.
     158             :     //
     159             :     // See longer explanation in CSSAnimation::CancelFromStyle.
     160           0 :     mAnimationIndex = sNextAnimationIndex++;
     161           0 :     mNeedsNewAnimationIndexWhenRun = true;
     162             : 
     163           0 :     Animation::CancelFromStyle();
     164             : 
     165             :     // It is important we do this *after* calling CancelFromStyle().
     166             :     // This is because CancelFromStyle() will end up posting a restyle and
     167             :     // that restyle should target the *transitions* level of the cascade.
     168             :     // However, once we clear the owning element, CascadeLevel() will begin
     169             :     // returning CascadeLevel::Animations.
     170           0 :     mOwningElement = OwningElementRef();
     171           0 :   }
     172             : 
     173             :   void SetEffectFromStyle(AnimationEffectReadOnly* aEffect);
     174             : 
     175             :   void Tick() override;
     176             : 
     177             :   nsCSSPropertyID TransitionProperty() const;
     178             :   AnimationValue ToValue() const;
     179             : 
     180             :   bool HasLowerCompositeOrderThan(const CSSTransition& aOther) const;
     181          16 :   EffectCompositor::CascadeLevel CascadeLevel() const override
     182             :   {
     183          16 :     return IsTiedToMarkup() ?
     184             :            EffectCompositor::CascadeLevel::Transitions :
     185          16 :            EffectCompositor::CascadeLevel::Animations;
     186             :   }
     187             : 
     188           2 :   void SetCreationSequence(uint64_t aIndex)
     189             :   {
     190           2 :     MOZ_ASSERT(IsTiedToMarkup());
     191           2 :     mAnimationIndex = aIndex;
     192           2 :   }
     193             : 
     194             :   // Sets the owning element which is used for determining the composite
     195             :   // oder of CSSTransition objects generated from CSS markup.
     196             :   //
     197             :   // @see mOwningElement
     198           2 :   void SetOwningElement(const OwningElementRef& aElement)
     199             :   {
     200           2 :     mOwningElement = aElement;
     201           2 :   }
     202             :   // True for transitions that are generated from CSS markup and continue to
     203             :   // reflect changes to that markup.
     204          30 :   bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
     205             : 
     206             :   // Return the animation current time based on a given TimeStamp, a given
     207             :   // start time and a given playbackRate on a given timeline.  This is useful
     208             :   // when we estimate the current animated value running on the compositor
     209             :   // because the animation on the compositor may be running ahead while
     210             :   // main-thread is busy.
     211             :   static Nullable<TimeDuration> GetCurrentTimeAt(
     212             :       const DocumentTimeline& aTimeline,
     213             :       const TimeStamp& aBaseTime,
     214             :       const TimeDuration& aStartTime,
     215             :       double aPlaybackRate);
     216             : 
     217           0 :   void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) override {
     218           0 :     QueueEvents(aActiveTime);
     219           0 :   }
     220             : 
     221             : protected:
     222           0 :   virtual ~CSSTransition()
     223           0 :   {
     224           0 :     MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
     225             :                                         "before a CSS transition is destroyed");
     226           0 :   }
     227             : 
     228             :   // Animation overrides
     229             :   void UpdateTiming(SeekFlag aSeekFlag,
     230             :                     SyncNotifyFlag aSyncNotifyFlag) override;
     231             : 
     232             :   void QueueEvents(StickyTimeDuration activeTime = StickyTimeDuration());
     233             : 
     234             : 
     235             :   enum class TransitionPhase;
     236             : 
     237             :   // The (pseudo-)element whose computed transition-property refers to this
     238             :   // transition (if any).
     239             :   //
     240             :   // This is used for determining the relative composite order of transitions
     241             :   // generated from CSS markup.
     242             :   //
     243             :   // Typically this will be the same as the target element of the keyframe
     244             :   // effect associated with this transition. However, it can differ in the
     245             :   // following circumstances:
     246             :   //
     247             :   // a) If script removes or replaces the effect of this transition,
     248             :   // b) If this transition is cancelled (e.g. by updating the
     249             :   //    transition-property or removing the owning element from the document),
     250             :   // c) If this object is generated from script using the CSSTransition
     251             :   //    constructor.
     252             :   //
     253             :   // For (b) and (c) the owning element will return !IsSet().
     254             :   OwningElementRef mOwningElement;
     255             : 
     256             :   // The 'transition phase' used to determine which transition events need
     257             :   // to be queued on this tick.
     258             :   // See: https://drafts.csswg.org/css-transitions-2/#transition-phase
     259             :   enum class TransitionPhase {
     260             :     Idle   = static_cast<int>(ComputedTiming::AnimationPhase::Idle),
     261             :     Before = static_cast<int>(ComputedTiming::AnimationPhase::Before),
     262             :     Active = static_cast<int>(ComputedTiming::AnimationPhase::Active),
     263             :     After  = static_cast<int>(ComputedTiming::AnimationPhase::After),
     264             :     Pending
     265             :   };
     266             :   TransitionPhase mPreviousTransitionPhase;
     267             : 
     268             :   // When true, indicates that when this transition next leaves the idle state,
     269             :   // its animation index should be updated.
     270             :   bool mNeedsNewAnimationIndexWhenRun;
     271             : 
     272             :   // Store the transition property and to-value here since we need that
     273             :   // information in order to determine if there is an existing transition
     274             :   // for a given style change. We can't store that information on the
     275             :   // ElementPropertyTransition (effect) however since it can be replaced
     276             :   // using the Web Animations API.
     277             :   nsCSSPropertyID mTransitionProperty;
     278             :   AnimationValue mTransitionToValue;
     279             : };
     280             : 
     281             : } // namespace dom
     282             : 
     283             : template <>
     284             : struct AnimationTypeTraits<dom::CSSTransition>
     285             : {
     286          18 :   static nsIAtom* ElementPropertyAtom()
     287             :   {
     288          18 :     return nsGkAtoms::transitionsProperty;
     289             :   }
     290           0 :   static nsIAtom* BeforePropertyAtom()
     291             :   {
     292           0 :     return nsGkAtoms::transitionsOfBeforeProperty;
     293             :   }
     294           0 :   static nsIAtom* AfterPropertyAtom()
     295             :   {
     296           0 :     return nsGkAtoms::transitionsOfAfterProperty;
     297             :   }
     298             : };
     299             : 
     300          27 : struct TransitionEventInfo {
     301             :   RefPtr<dom::Element> mElement;
     302             :   RefPtr<dom::Animation> mAnimation;
     303             :   InternalTransitionEvent mEvent;
     304             :   TimeStamp mTimeStamp;
     305             : 
     306           6 :   TransitionEventInfo(dom::Element* aElement,
     307             :                       CSSPseudoElementType aPseudoType,
     308             :                       EventMessage aMessage,
     309             :                       nsCSSPropertyID aProperty,
     310             :                       StickyTimeDuration aDuration,
     311             :                       const TimeStamp& aTimeStamp,
     312             :                       dom::Animation* aAnimation)
     313           6 :     : mElement(aElement)
     314             :     , mAnimation(aAnimation)
     315             :     , mEvent(true, aMessage)
     316           6 :     , mTimeStamp(aTimeStamp)
     317             :   {
     318             :     // XXX Looks like nobody initialize WidgetEvent::time
     319             :     mEvent.mPropertyName =
     320           6 :       NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty));
     321           6 :     mEvent.mElapsedTime = aDuration.ToSeconds();
     322             :     mEvent.mPseudoElement =
     323           6 :       AnimationCollection<dom::CSSTransition>::PseudoTypeAsString(aPseudoType);
     324           6 :   }
     325             : 
     326             :   // InternalTransitionEvent doesn't support copy-construction, so we need
     327             :   // to ourselves in order to work with nsTArray
     328          12 :   TransitionEventInfo(const TransitionEventInfo& aOther)
     329          12 :     : mElement(aOther.mElement)
     330             :     , mAnimation(aOther.mAnimation)
     331             :     , mEvent(aOther.mEvent)
     332          12 :     , mTimeStamp(aOther.mTimeStamp)
     333             :   {
     334          12 :     mEvent.AssignTransitionEventData(aOther.mEvent, false);
     335          12 :   }
     336             : };
     337             : 
     338             : } // namespace mozilla
     339             : 
     340             : class nsTransitionManager final
     341             :   : public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>
     342             : {
     343             : public:
     344          28 :   explicit nsTransitionManager(nsPresContext *aPresContext)
     345          28 :     : mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>(aPresContext)
     346          28 :     , mInAnimationOnlyStyleUpdate(false)
     347             :   {
     348          28 :   }
     349             : 
     350         116 :   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsTransitionManager)
     351         122 :   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTransitionManager)
     352             : 
     353             :   typedef mozilla::AnimationCollection<mozilla::dom::CSSTransition>
     354             :     CSSTransitionCollection;
     355             : 
     356             :   /**
     357             :    * StyleContextChanged
     358             :    *
     359             :    * To be called from RestyleManager::TryInitiatingTransition when the
     360             :    * style of an element has changed, to initiate transitions from
     361             :    * that style change.  For style contexts with :before and :after
     362             :    * pseudos, aElement is expected to be the generated before/after
     363             :    * element.
     364             :    *
     365             :    * It may modify the new style context (by replacing
     366             :    * *aNewStyleContext) to cover up some of the changes for the duration
     367             :    * of the restyling of descendants.  If it does, this function will
     368             :    * take care of causing the necessary restyle afterwards.
     369             :    */
     370             :   void StyleContextChanged(mozilla::dom::Element *aElement,
     371             :                            nsStyleContext *aOldStyleContext,
     372             :                            RefPtr<nsStyleContext>* aNewStyleContext /* inout */);
     373             : 
     374             :   /**
     375             :    * Update transitions for stylo.
     376             :    */
     377             :   bool UpdateTransitions(
     378             :     mozilla::dom::Element *aElement,
     379             :     mozilla::CSSPseudoElementType aPseudoType,
     380             :     const ServoComputedValues* aOldStyle,
     381             :     const ServoComputedValues* aNewStyle);
     382             : 
     383             :   /**
     384             :    * When we're resolving style for an element that previously didn't have
     385             :    * style, we might have some old finished transitions for it, if,
     386             :    * say, it was display:none for a while, but previously displayed.
     387             :    *
     388             :    * This method removes any finished transitions that don't match the
     389             :    * new style.
     390             :    */
     391             :   void PruneCompletedTransitions(mozilla::dom::Element* aElement,
     392             :                                  mozilla::CSSPseudoElementType aPseudoType,
     393             :                                  nsStyleContext* aNewStyleContext);
     394             : 
     395         488 :   void SetInAnimationOnlyStyleUpdate(bool aInAnimationOnlyUpdate) {
     396         488 :     mInAnimationOnlyStyleUpdate = aInAnimationOnlyUpdate;
     397         488 :   }
     398             : 
     399           0 :   bool InAnimationOnlyStyleUpdate() const {
     400           0 :     return mInAnimationOnlyStyleUpdate;
     401             :   }
     402             : 
     403           6 :   void QueueEvent(mozilla::TransitionEventInfo&& aEventInfo)
     404             :   {
     405           6 :     mEventDispatcher.QueueEvent(
     406           6 :       mozilla::Forward<mozilla::TransitionEventInfo>(aEventInfo));
     407           6 :   }
     408             : 
     409          42 :   void DispatchEvents()
     410             :   {
     411          84 :     RefPtr<nsTransitionManager> kungFuDeathGrip(this);
     412          42 :     mEventDispatcher.DispatchEvents(mPresContext);
     413          42 :   }
     414          42 :   void SortEvents()      { mEventDispatcher.SortEvents(); }
     415           4 :   void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
     416             : 
     417             : protected:
     418           9 :   virtual ~nsTransitionManager() {}
     419             : 
     420             :   typedef nsTArray<RefPtr<mozilla::dom::CSSTransition>>
     421             :     OwningCSSTransitionPtrArray;
     422             : 
     423             :   // Update transitions. This will start new transitions,
     424             :   // replace existing transitions, and stop existing transitions
     425             :   // as needed. aDisp and aElement must be non-null.
     426             :   // aElementTransitions is the collection of current transitions, and it
     427             :   // could be a nullptr if we don't have any transitions.
     428             :   template<typename StyleType> bool
     429             :   DoUpdateTransitions(const nsStyleDisplay* aDisp,
     430             :                       mozilla::dom::Element* aElement,
     431             :                       mozilla::CSSPseudoElementType aPseudoType,
     432             :                       CSSTransitionCollection*& aElementTransitions,
     433             :                       StyleType aOldStyle,
     434             :                       StyleType aNewStyle);
     435             : 
     436             :   template<typename StyleType> void
     437             :   ConsiderInitiatingTransition(nsCSSPropertyID aProperty,
     438             :                                const mozilla::StyleTransition& aTransition,
     439             :                                mozilla::dom::Element* aElement,
     440             :                                mozilla::CSSPseudoElementType aPseudoType,
     441             :                                CSSTransitionCollection*& aElementTransitions,
     442             :                                StyleType aOldStyle,
     443             :                                StyleType aNewStyle,
     444             :                                bool* aStartedAny,
     445             :                                nsCSSPropertyIDSet* aWhichStarted);
     446             : 
     447             :   bool mInAnimationOnlyStyleUpdate;
     448             : 
     449             :   mozilla::DelayedEventDispatcher<mozilla::TransitionEventInfo>
     450             :       mEventDispatcher;
     451             : };
     452             : 
     453             : #endif /* !defined(nsTransitionManager_h_) */

Generated by: LCOV version 1.13