LCOV - code coverage report
Current view: top level - layout/style - nsAnimationManager.h (source / functions) Hit Total Coverage
Test: output.info Lines: 14 81 17.3 %
Date: 2017-07-14 16:53:18 Functions: 12 37 32.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             : #ifndef nsAnimationManager_h_
       6             : #define nsAnimationManager_h_
       7             : 
       8             : #include "mozilla/Attributes.h"
       9             : #include "mozilla/ContentEvents.h"
      10             : #include "mozilla/EventForwards.h"
      11             : #include "AnimationCommon.h"
      12             : #include "mozilla/dom/Animation.h"
      13             : #include "mozilla/Keyframe.h"
      14             : #include "mozilla/MemoryReporting.h"
      15             : #include "mozilla/TimeStamp.h"
      16             : #include "nsRFPService.h"
      17             : 
      18             : class nsIGlobalObject;
      19             : class nsStyleContext;
      20             : struct nsStyleDisplay;
      21             : struct ServoComputedValues;
      22             : 
      23             : namespace mozilla {
      24             : namespace css {
      25             : class Declaration;
      26             : } /* namespace css */
      27             : namespace dom {
      28             : class KeyframeEffectReadOnly;
      29             : class Promise;
      30             : } /* namespace dom */
      31             : 
      32             : enum class CSSPseudoElementType : uint8_t;
      33             : struct NonOwningAnimationTarget;
      34             : 
      35           0 : struct AnimationEventInfo {
      36             :   RefPtr<dom::Element> mElement;
      37             :   RefPtr<dom::Animation> mAnimation;
      38             :   InternalAnimationEvent mEvent;
      39             :   TimeStamp mTimeStamp;
      40             : 
      41           0 :   AnimationEventInfo(dom::Element* aElement,
      42             :                      CSSPseudoElementType aPseudoType,
      43             :                      EventMessage aMessage,
      44             :                      const nsAString& aAnimationName,
      45             :                      const StickyTimeDuration& aElapsedTime,
      46             :                      const TimeStamp& aTimeStamp,
      47             :                      dom::Animation* aAnimation)
      48           0 :     : mElement(aElement)
      49             :     , mAnimation(aAnimation)
      50             :     , mEvent(true, aMessage)
      51           0 :     , mTimeStamp(aTimeStamp)
      52             :   {
      53             :     // XXX Looks like nobody initialize WidgetEvent::time
      54           0 :     mEvent.mAnimationName = aAnimationName;
      55           0 :     mEvent.mElapsedTime =
      56           0 :       nsRFPService::ReduceTimePrecisionAsSecs(aElapsedTime.ToSeconds());
      57             :     mEvent.mPseudoElement =
      58           0 :       AnimationCollection<dom::CSSAnimation>::PseudoTypeAsString(aPseudoType);
      59           0 :   }
      60             : 
      61             :   // InternalAnimationEvent doesn't support copy-construction, so we need
      62             :   // to ourselves in order to work with nsTArray
      63           0 :   AnimationEventInfo(const AnimationEventInfo& aOther)
      64           0 :     : mElement(aOther.mElement)
      65             :     , mAnimation(aOther.mAnimation)
      66           0 :     , mEvent(true, aOther.mEvent.mMessage)
      67           0 :     , mTimeStamp(aOther.mTimeStamp)
      68             :   {
      69           0 :     mEvent.AssignAnimationEventData(aOther.mEvent, false);
      70           0 :   }
      71             : };
      72             : 
      73             : namespace dom {
      74             : 
      75             : class CSSAnimation final : public Animation
      76             : {
      77             : public:
      78           0 :  explicit CSSAnimation(nsIGlobalObject* aGlobal,
      79             :                        const nsAString& aAnimationName)
      80           0 :     : dom::Animation(aGlobal)
      81             :     , mAnimationName(aAnimationName)
      82             :     , mIsStylePaused(false)
      83             :     , mPauseShouldStick(false)
      84             :     , mNeedsNewAnimationIndexWhenRun(false)
      85             :     , mPreviousPhase(ComputedTiming::AnimationPhase::Idle)
      86           0 :     , mPreviousIteration(0)
      87             :   {
      88             :     // We might need to drop this assertion once we add a script-accessible
      89             :     // constructor but for animations generated from CSS markup the
      90             :     // animation-name should never be empty.
      91           0 :     MOZ_ASSERT(!mAnimationName.IsEmpty(), "animation-name should not be empty");
      92           0 :   }
      93             : 
      94             :   JSObject* WrapObject(JSContext* aCx,
      95             :                        JS::Handle<JSObject*> aGivenProto) override;
      96             : 
      97           0 :   CSSAnimation* AsCSSAnimation() override { return this; }
      98           0 :   const CSSAnimation* AsCSSAnimation() const override { return this; }
      99             : 
     100             :   // CSSAnimation interface
     101           0 :   void GetAnimationName(nsString& aRetVal) const { aRetVal = mAnimationName; }
     102             : 
     103             :   // Alternative to GetAnimationName that returns a reference to the member
     104             :   // for more efficient internal usage.
     105           0 :   const nsString& AnimationName() const { return mAnimationName; }
     106             : 
     107             :   // Animation interface overrides
     108             :   virtual Promise* GetReady(ErrorResult& aRv) override;
     109             :   virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior) override;
     110             :   virtual void Pause(ErrorResult& aRv) override;
     111             : 
     112             :   virtual AnimationPlayState PlayStateFromJS() const override;
     113             :   virtual void PlayFromJS(ErrorResult& aRv) override;
     114             : 
     115             :   void PlayFromStyle();
     116             :   void PauseFromStyle();
     117           0 :   void CancelFromStyle() override
     118             :   {
     119             :     // When an animation is disassociated with style it enters an odd state
     120             :     // where its composite order is undefined until it first transitions
     121             :     // out of the idle state.
     122             :     //
     123             :     // Even if the composite order isn't defined we don't want it to be random
     124             :     // in case we need to determine the order to dispatch events associated
     125             :     // with an animation in this state. To solve this we treat the animation as
     126             :     // if it had been added to the end of the global animation list so that
     127             :     // its sort order is defined. We'll update this index again once the
     128             :     // animation leaves the idle state.
     129           0 :     mAnimationIndex = sNextAnimationIndex++;
     130           0 :     mNeedsNewAnimationIndexWhenRun = true;
     131             : 
     132           0 :     Animation::CancelFromStyle();
     133             : 
     134             :     // We need to do this *after* calling CancelFromStyle() since
     135             :     // CancelFromStyle might synchronously trigger a cancel event for which
     136             :     // we need an owning element to target the event at.
     137           0 :     mOwningElement = OwningElementRef();
     138           0 :   }
     139             : 
     140             :   void Tick() override;
     141             :   void QueueEvents(StickyTimeDuration aActiveTime = StickyTimeDuration());
     142             : 
     143           0 :   bool IsStylePaused() const { return mIsStylePaused; }
     144             : 
     145             :   bool HasLowerCompositeOrderThan(const CSSAnimation& aOther) const;
     146             : 
     147           0 :   void SetAnimationIndex(uint64_t aIndex)
     148             :   {
     149           0 :     MOZ_ASSERT(IsTiedToMarkup());
     150           0 :     if (IsRelevant() &&
     151           0 :         mAnimationIndex != aIndex) {
     152           0 :       nsNodeUtils::AnimationChanged(this);
     153           0 :       PostUpdate();
     154             :     }
     155           0 :     mAnimationIndex = aIndex;
     156           0 :   }
     157             : 
     158             :   // Sets the owning element which is used for determining the composite
     159             :   // order of CSSAnimation objects generated from CSS markup.
     160             :   //
     161             :   // @see mOwningElement
     162           0 :   void SetOwningElement(const OwningElementRef& aElement)
     163             :   {
     164           0 :     mOwningElement = aElement;
     165           0 :   }
     166             :   // True for animations that are generated from CSS markup and continue to
     167             :   // reflect changes to that markup.
     168           0 :   bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
     169             : 
     170           0 :   void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) override {
     171           0 :     QueueEvents(aActiveTime);
     172           0 :   }
     173             : 
     174             : protected:
     175           0 :   virtual ~CSSAnimation()
     176           0 :   {
     177           0 :     MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
     178             :                                         "before a CSS animation is destroyed");
     179           0 :   }
     180             : 
     181             :   // Animation overrides
     182             :   void UpdateTiming(SeekFlag aSeekFlag,
     183             :                     SyncNotifyFlag aSyncNotifyFlag) override;
     184             : 
     185             :   // Returns the duration from the start of the animation's source effect's
     186             :   // active interval to the point where the animation actually begins playback.
     187             :   // This is zero unless the animation's source effect has a negative delay in
     188             :   // which case it is the absolute value of that delay.
     189             :   // This is used for setting the elapsedTime member of CSS AnimationEvents.
     190             :   TimeDuration InitialAdvance() const {
     191             :     return mEffect ?
     192             :            std::max(TimeDuration(), mEffect->SpecifiedTiming().Delay() * -1) :
     193             :            TimeDuration();
     194             :   }
     195             : 
     196             :   nsString mAnimationName;
     197             : 
     198             :   // The (pseudo-)element whose computed animation-name refers to this
     199             :   // animation (if any).
     200             :   //
     201             :   // This is used for determining the relative composite order of animations
     202             :   // generated from CSS markup.
     203             :   //
     204             :   // Typically this will be the same as the target element of the keyframe
     205             :   // effect associated with this animation. However, it can differ in the
     206             :   // following circumstances:
     207             :   //
     208             :   // a) If script removes or replaces the effect of this animation,
     209             :   // b) If this animation is cancelled (e.g. by updating the
     210             :   //    animation-name property or removing the owning element from the
     211             :   //    document),
     212             :   // c) If this object is generated from script using the CSSAnimation
     213             :   //    constructor.
     214             :   //
     215             :   // For (b) and (c) the owning element will return !IsSet().
     216             :   OwningElementRef mOwningElement;
     217             : 
     218             :   // When combining animation-play-state with play() / pause() the following
     219             :   // behavior applies:
     220             :   // 1. pause() is sticky and always overrides the underlying
     221             :   //    animation-play-state
     222             :   // 2. If animation-play-state is 'paused', play() will temporarily override
     223             :   //    it until animation-play-state next becomes 'running'.
     224             :   // 3. Calls to play() trigger finishing behavior but setting the
     225             :   //    animation-play-state to 'running' does not.
     226             :   //
     227             :   // This leads to five distinct states:
     228             :   //
     229             :   // A. Running
     230             :   // B. Running and temporarily overriding animation-play-state: paused
     231             :   // C. Paused and sticky overriding animation-play-state: running
     232             :   // D. Paused and sticky overriding animation-play-state: paused
     233             :   // E. Paused by animation-play-state
     234             :   //
     235             :   // C and D may seem redundant but they differ in how to respond to the
     236             :   // sequence: call play(), set animation-play-state: paused.
     237             :   //
     238             :   // C will transition to A then E leaving the animation paused.
     239             :   // D will transition to B then B leaving the animation running.
     240             :   //
     241             :   // A state transition chart is as follows:
     242             :   //
     243             :   //             A | B | C | D | E
     244             :   //   ---------------------------
     245             :   //   play()    A | B | A | B | B
     246             :   //   pause()   C | D | C | D | D
     247             :   //   'running' A | A | C | C | A
     248             :   //   'paused'  E | B | D | D | E
     249             :   //
     250             :   // The base class, Animation already provides a boolean value,
     251             :   // mIsPaused which gives us two states. To this we add a further two booleans
     252             :   // to represent the states as follows.
     253             :   //
     254             :   // A. Running
     255             :   //    (!mIsPaused; !mIsStylePaused; !mPauseShouldStick)
     256             :   // B. Running and temporarily overriding animation-play-state: paused
     257             :   //    (!mIsPaused; mIsStylePaused; !mPauseShouldStick)
     258             :   // C. Paused and sticky overriding animation-play-state: running
     259             :   //    (mIsPaused; !mIsStylePaused; mPauseShouldStick)
     260             :   // D. Paused and sticky overriding animation-play-state: paused
     261             :   //    (mIsPaused; mIsStylePaused; mPauseShouldStick)
     262             :   // E. Paused by animation-play-state
     263             :   //    (mIsPaused; mIsStylePaused; !mPauseShouldStick)
     264             :   //
     265             :   // (That leaves 3 combinations of the boolean values that we never set because
     266             :   // they don't represent valid states.)
     267             :   bool mIsStylePaused;
     268             :   bool mPauseShouldStick;
     269             : 
     270             :   // When true, indicates that when this animation next leaves the idle state,
     271             :   // its animation index should be updated.
     272             :   bool mNeedsNewAnimationIndexWhenRun;
     273             : 
     274             :   // Phase and current iteration from the previous time we queued events.
     275             :   // This is used to determine what new events to dispatch.
     276             :   ComputedTiming::AnimationPhase mPreviousPhase;
     277             :   uint64_t mPreviousIteration;
     278             : };
     279             : 
     280             : } /* namespace dom */
     281             : 
     282             : template <>
     283             : struct AnimationTypeTraits<dom::CSSAnimation>
     284             : {
     285          12 :   static nsIAtom* ElementPropertyAtom()
     286             :   {
     287          12 :     return nsGkAtoms::animationsProperty;
     288             :   }
     289           0 :   static nsIAtom* BeforePropertyAtom()
     290             :   {
     291           0 :     return nsGkAtoms::animationsOfBeforeProperty;
     292             :   }
     293           0 :   static nsIAtom* AfterPropertyAtom()
     294             :   {
     295           0 :     return nsGkAtoms::animationsOfAfterProperty;
     296             :   }
     297             : };
     298             : 
     299             : } /* namespace mozilla */
     300             : 
     301             : class nsAnimationManager final
     302             :   : public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>
     303             : {
     304             : public:
     305          28 :   explicit nsAnimationManager(nsPresContext *aPresContext)
     306          28 :     : mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>(aPresContext)
     307             :   {
     308          28 :   }
     309             : 
     310         116 :   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager)
     311         122 :   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager)
     312             : 
     313             :   typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation>
     314             :     CSSAnimationCollection;
     315             :   typedef nsTArray<RefPtr<mozilla::dom::CSSAnimation>>
     316             :     OwningCSSAnimationPtrArray;
     317             : 
     318             :   /**
     319             :    * Update the set of animations on |aElement| based on |aStyleContext|.
     320             :    * If necessary, this will notify the corresponding EffectCompositor so
     321             :    * that it can update its animation rule.
     322             :    *
     323             :    * aStyleContext may be a style context for aElement or for its
     324             :    * :before or :after pseudo-element.
     325             :    */
     326             :   void UpdateAnimations(nsStyleContext* aStyleContext,
     327             :                         mozilla::dom::Element* aElement);
     328             : 
     329             :   /**
     330             :    * This function does the same thing as the above UpdateAnimations()
     331             :    * but with servo's computed values.
     332             :    */
     333             :   void UpdateAnimations(
     334             :     mozilla::dom::Element* aElement,
     335             :     mozilla::CSSPseudoElementType aPseudoType,
     336             :     const ServoComputedValues* aComputedValues);
     337             : 
     338             :   /**
     339             :    * Add a pending event.
     340             :    */
     341           0 :   void QueueEvent(mozilla::AnimationEventInfo&& aEventInfo)
     342             :   {
     343           0 :     mEventDispatcher.QueueEvent(
     344           0 :       mozilla::Forward<mozilla::AnimationEventInfo>(aEventInfo));
     345           0 :   }
     346             : 
     347             :   /**
     348             :    * Dispatch any pending events.  We accumulate animationend and
     349             :    * animationiteration events only during refresh driver notifications
     350             :    * (and dispatch them at the end of such notifications), but we
     351             :    * accumulate animationstart events at other points when style
     352             :    * contexts are created.
     353             :    */
     354          42 :   void DispatchEvents()
     355             :   {
     356          84 :     RefPtr<nsAnimationManager> kungFuDeathGrip(this);
     357          42 :     mEventDispatcher.DispatchEvents(mPresContext);
     358          42 :   }
     359          42 :   void SortEvents()      { mEventDispatcher.SortEvents(); }
     360           4 :   void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
     361             : 
     362             :   // Utility function to walk through |aIter| to find the Keyframe with
     363             :   // matching offset and timing function but stopping as soon as the offset
     364             :   // differs from |aOffset| (i.e. it assumes a sorted iterator).
     365             :   //
     366             :   // If a matching Keyframe is found,
     367             :   //   Returns true and sets |aIndex| to the index of the matching Keyframe
     368             :   //   within |aIter|.
     369             :   //
     370             :   // If no matching Keyframe is found,
     371             :   //   Returns false and sets |aIndex| to the index in the iterator of the
     372             :   //   first Keyframe with an offset differing to |aOffset| or, if the end
     373             :   //   of the iterator is reached, sets |aIndex| to the index after the last
     374             :   //   Keyframe.
     375             :   template <class IterType, class TimingFunctionType>
     376           0 :   static bool FindMatchingKeyframe(
     377             :     IterType&& aIter,
     378             :     double aOffset,
     379             :     const TimingFunctionType& aTimingFunctionToMatch,
     380             :     size_t& aIndex)
     381             :   {
     382           0 :     aIndex = 0;
     383           0 :     for (mozilla::Keyframe& keyframe : aIter) {
     384           0 :       if (keyframe.mOffset.value() != aOffset) {
     385           0 :         break;
     386             :       }
     387           0 :       if (keyframe.mTimingFunction == aTimingFunctionToMatch) {
     388           0 :         return true;
     389             :       }
     390           0 :       ++aIndex;
     391             :     }
     392           0 :     return false;
     393             :   }
     394             : 
     395             : protected:
     396           9 :   ~nsAnimationManager() override = default;
     397             : 
     398             : private:
     399             :   template<class BuilderType>
     400             :   void DoUpdateAnimations(
     401             :     const mozilla::NonOwningAnimationTarget& aTarget,
     402             :     const nsStyleDisplay& aStyleDisplay,
     403             :     BuilderType& aBuilder);
     404             : 
     405             :   mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher;
     406             : };
     407             : 
     408             : #endif /* !defined(nsAnimationManager_h_) */

Generated by: LCOV version 1.13