forked from mirrors/gecko-dev
		
	MozReview-Commit-ID: GBJtFdobipj --HG-- extra : rebase_source : 528abb3d04e52547e6c4e13ea396e080406008b1
		
			
				
	
	
		
			360 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 | 
						|
/* 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/. */
 | 
						|
#ifndef nsAnimationManager_h_
 | 
						|
#define nsAnimationManager_h_
 | 
						|
 | 
						|
#include "mozilla/Attributes.h"
 | 
						|
#include "mozilla/ContentEvents.h"
 | 
						|
#include "mozilla/EventForwards.h"
 | 
						|
#include "AnimationCommon.h"
 | 
						|
#include "mozilla/dom/Animation.h"
 | 
						|
#include "mozilla/MemoryReporting.h"
 | 
						|
#include "mozilla/TimeStamp.h"
 | 
						|
 | 
						|
class nsIGlobalObject;
 | 
						|
class nsStyleContext;
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace css {
 | 
						|
class Declaration;
 | 
						|
} /* namespace css */
 | 
						|
namespace dom {
 | 
						|
class KeyframeEffectReadOnly;
 | 
						|
class Promise;
 | 
						|
} /* namespace dom */
 | 
						|
 | 
						|
enum class CSSPseudoElementType : uint8_t;
 | 
						|
 | 
						|
struct AnimationEventInfo {
 | 
						|
  RefPtr<dom::Element> mElement;
 | 
						|
  RefPtr<dom::Animation> mAnimation;
 | 
						|
  InternalAnimationEvent mEvent;
 | 
						|
  TimeStamp mTimeStamp;
 | 
						|
 | 
						|
  AnimationEventInfo(dom::Element* aElement,
 | 
						|
                     CSSPseudoElementType aPseudoType,
 | 
						|
                     EventMessage aMessage,
 | 
						|
                     const nsSubstring& aAnimationName,
 | 
						|
                     const StickyTimeDuration& aElapsedTime,
 | 
						|
                     const TimeStamp& aTimeStamp,
 | 
						|
                     dom::Animation* aAnimation)
 | 
						|
    : mElement(aElement)
 | 
						|
    , mAnimation(aAnimation)
 | 
						|
    , mEvent(true, aMessage)
 | 
						|
    , mTimeStamp(aTimeStamp)
 | 
						|
  {
 | 
						|
    // XXX Looks like nobody initialize WidgetEvent::time
 | 
						|
    mEvent.mAnimationName = aAnimationName;
 | 
						|
    mEvent.mElapsedTime = aElapsedTime.ToSeconds();
 | 
						|
    mEvent.mPseudoElement =
 | 
						|
      AnimationCollection<dom::CSSAnimation>::PseudoTypeAsString(aPseudoType);
 | 
						|
  }
 | 
						|
 | 
						|
  // InternalAnimationEvent doesn't support copy-construction, so we need
 | 
						|
  // to ourselves in order to work with nsTArray
 | 
						|
  AnimationEventInfo(const AnimationEventInfo& aOther)
 | 
						|
    : mElement(aOther.mElement)
 | 
						|
    , mAnimation(aOther.mAnimation)
 | 
						|
    , mEvent(true, aOther.mEvent.mMessage)
 | 
						|
    , mTimeStamp(aOther.mTimeStamp)
 | 
						|
  {
 | 
						|
    mEvent.AssignAnimationEventData(aOther.mEvent, false);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
namespace dom {
 | 
						|
 | 
						|
class CSSAnimation final : public Animation
 | 
						|
{
 | 
						|
public:
 | 
						|
 explicit CSSAnimation(nsIGlobalObject* aGlobal,
 | 
						|
                       const nsSubstring& aAnimationName)
 | 
						|
    : dom::Animation(aGlobal)
 | 
						|
    , mAnimationName(aAnimationName)
 | 
						|
    , mIsStylePaused(false)
 | 
						|
    , mPauseShouldStick(false)
 | 
						|
    , mNeedsNewAnimationIndexWhenRun(false)
 | 
						|
    , mPreviousPhaseOrIteration(PREVIOUS_PHASE_BEFORE)
 | 
						|
  {
 | 
						|
    // We might need to drop this assertion once we add a script-accessible
 | 
						|
    // constructor but for animations generated from CSS markup the
 | 
						|
    // animation-name should never be empty.
 | 
						|
    MOZ_ASSERT(!mAnimationName.IsEmpty(), "animation-name should not be empty");
 | 
						|
  }
 | 
						|
 | 
						|
  JSObject* WrapObject(JSContext* aCx,
 | 
						|
                       JS::Handle<JSObject*> aGivenProto) override;
 | 
						|
 | 
						|
  CSSAnimation* AsCSSAnimation() override { return this; }
 | 
						|
  const CSSAnimation* AsCSSAnimation() const override { return this; }
 | 
						|
 | 
						|
  // CSSAnimation interface
 | 
						|
  void GetAnimationName(nsString& aRetVal) const { aRetVal = mAnimationName; }
 | 
						|
 | 
						|
  // Alternative to GetAnimationName that returns a reference to the member
 | 
						|
  // for more efficient internal usage.
 | 
						|
  const nsString& AnimationName() const { return mAnimationName; }
 | 
						|
 | 
						|
  // Animation interface overrides
 | 
						|
  virtual Promise* GetReady(ErrorResult& aRv) override;
 | 
						|
  virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior) override;
 | 
						|
  virtual void Pause(ErrorResult& aRv) override;
 | 
						|
 | 
						|
  virtual AnimationPlayState PlayStateFromJS() const override;
 | 
						|
  virtual void PlayFromJS(ErrorResult& aRv) override;
 | 
						|
 | 
						|
  void PlayFromStyle();
 | 
						|
  void PauseFromStyle();
 | 
						|
  void CancelFromStyle() override
 | 
						|
  {
 | 
						|
    mOwningElement = OwningElementRef();
 | 
						|
 | 
						|
    // When an animation is disassociated with style it enters an odd state
 | 
						|
    // where its composite order is undefined until it first transitions
 | 
						|
    // out of the idle state.
 | 
						|
    //
 | 
						|
    // Even if the composite order isn't defined we don't want it to be random
 | 
						|
    // in case we need to determine the order to dispatch events associated
 | 
						|
    // with an animation in this state. To solve this we treat the animation as
 | 
						|
    // if it had been added to the end of the global animation list so that
 | 
						|
    // its sort order is defined. We'll update this index again once the
 | 
						|
    // animation leaves the idle state.
 | 
						|
    mAnimationIndex = sNextAnimationIndex++;
 | 
						|
    mNeedsNewAnimationIndexWhenRun = true;
 | 
						|
 | 
						|
    Animation::CancelFromStyle();
 | 
						|
  }
 | 
						|
 | 
						|
  void Tick() override;
 | 
						|
  void QueueEvents();
 | 
						|
 | 
						|
  bool IsStylePaused() const { return mIsStylePaused; }
 | 
						|
 | 
						|
  bool HasLowerCompositeOrderThan(const CSSAnimation& aOther) const;
 | 
						|
 | 
						|
  void SetAnimationIndex(uint64_t aIndex)
 | 
						|
  {
 | 
						|
    MOZ_ASSERT(IsTiedToMarkup());
 | 
						|
    mAnimationIndex = aIndex;
 | 
						|
  }
 | 
						|
 | 
						|
  // Sets the owning element which is used for determining the composite
 | 
						|
  // order of CSSAnimation objects generated from CSS markup.
 | 
						|
  //
 | 
						|
  // @see mOwningElement
 | 
						|
  void SetOwningElement(const OwningElementRef& aElement)
 | 
						|
  {
 | 
						|
    mOwningElement = aElement;
 | 
						|
  }
 | 
						|
  // True for animations that are generated from CSS markup and continue to
 | 
						|
  // reflect changes to that markup.
 | 
						|
  bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
 | 
						|
 | 
						|
protected:
 | 
						|
  virtual ~CSSAnimation()
 | 
						|
  {
 | 
						|
    MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
 | 
						|
                                        "before a CSS animation is destroyed");
 | 
						|
  }
 | 
						|
 | 
						|
  // Animation overrides
 | 
						|
  void UpdateTiming(SeekFlag aSeekFlag,
 | 
						|
                    SyncNotifyFlag aSyncNotifyFlag) override;
 | 
						|
 | 
						|
  // Returns the duration from the start of the animation's source effect's
 | 
						|
  // active interval to the point where the animation actually begins playback.
 | 
						|
  // This is zero unless the animation's source effect has a negative delay in
 | 
						|
  // which case it is the absolute value of that delay.
 | 
						|
  // This is used for setting the elapsedTime member of CSS AnimationEvents.
 | 
						|
  TimeDuration InitialAdvance() const {
 | 
						|
    return mEffect ?
 | 
						|
           std::max(TimeDuration(), mEffect->SpecifiedTiming().mDelay * -1) :
 | 
						|
           TimeDuration();
 | 
						|
  }
 | 
						|
  // Converts an AnimationEvent's elapsedTime value to an equivalent TimeStamp
 | 
						|
  // that can be used to sort events by when they occurred.
 | 
						|
  TimeStamp ElapsedTimeToTimeStamp(const StickyTimeDuration& aElapsedTime)
 | 
						|
    const;
 | 
						|
 | 
						|
  nsString mAnimationName;
 | 
						|
 | 
						|
  // The (pseudo-)element whose computed animation-name refers to this
 | 
						|
  // animation (if any).
 | 
						|
  //
 | 
						|
  // This is used for determining the relative composite order of animations
 | 
						|
  // generated from CSS markup.
 | 
						|
  //
 | 
						|
  // Typically this will be the same as the target element of the keyframe
 | 
						|
  // effect associated with this animation. However, it can differ in the
 | 
						|
  // following circumstances:
 | 
						|
  //
 | 
						|
  // a) If script removes or replaces the effect of this animation,
 | 
						|
  // b) If this animation is cancelled (e.g. by updating the
 | 
						|
  //    animation-name property or removing the owning element from the
 | 
						|
  //    document),
 | 
						|
  // c) If this object is generated from script using the CSSAnimation
 | 
						|
  //    constructor.
 | 
						|
  //
 | 
						|
  // For (b) and (c) the owning element will return !IsSet().
 | 
						|
  OwningElementRef mOwningElement;
 | 
						|
 | 
						|
  // 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.
 | 
						|
  //
 | 
						|
  // This leads to five distinct states:
 | 
						|
  //
 | 
						|
  // A. Running
 | 
						|
  // B. Running and temporarily overriding animation-play-state: paused
 | 
						|
  // C. Paused and sticky overriding animation-play-state: running
 | 
						|
  // D. Paused and sticky overriding animation-play-state: paused
 | 
						|
  // E. Paused by animation-play-state
 | 
						|
  //
 | 
						|
  // C and D may seem redundant but they differ in how to respond to the
 | 
						|
  // sequence: call play(), set animation-play-state: paused.
 | 
						|
  //
 | 
						|
  // C will transition to A then E leaving the animation paused.
 | 
						|
  // D will transition to B then B leaving the animation running.
 | 
						|
  //
 | 
						|
  // A state transition chart is as follows:
 | 
						|
  //
 | 
						|
  //             A | B | C | D | E
 | 
						|
  //   ---------------------------
 | 
						|
  //   play()    A | B | A | B | B
 | 
						|
  //   pause()   C | D | C | D | D
 | 
						|
  //   'running' A | A | C | C | A
 | 
						|
  //   'paused'  E | B | D | D | E
 | 
						|
  //
 | 
						|
  // The base class, Animation already provides a boolean value,
 | 
						|
  // mIsPaused which gives us two states. To this we add a further two booleans
 | 
						|
  // to represent the states as follows.
 | 
						|
  //
 | 
						|
  // A. Running
 | 
						|
  //    (!mIsPaused; !mIsStylePaused; !mPauseShouldStick)
 | 
						|
  // B. Running and temporarily overriding animation-play-state: paused
 | 
						|
  //    (!mIsPaused; mIsStylePaused; !mPauseShouldStick)
 | 
						|
  // C. Paused and sticky overriding animation-play-state: running
 | 
						|
  //    (mIsPaused; !mIsStylePaused; mPauseShouldStick)
 | 
						|
  // D. Paused and sticky overriding animation-play-state: paused
 | 
						|
  //    (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;
 | 
						|
 | 
						|
  // When true, indicates that when this animation next leaves the idle state,
 | 
						|
  // its animation index should be updated.
 | 
						|
  bool mNeedsNewAnimationIndexWhenRun;
 | 
						|
 | 
						|
  enum {
 | 
						|
    PREVIOUS_PHASE_BEFORE = uint64_t(-1),
 | 
						|
    PREVIOUS_PHASE_AFTER = uint64_t(-2)
 | 
						|
  };
 | 
						|
  // One of the PREVIOUS_PHASE_* constants, or an integer for the iteration
 | 
						|
  // whose start we last notified on.
 | 
						|
  uint64_t mPreviousPhaseOrIteration;
 | 
						|
};
 | 
						|
 | 
						|
} /* namespace dom */
 | 
						|
 | 
						|
template <>
 | 
						|
struct AnimationTypeTraits<dom::CSSAnimation>
 | 
						|
{
 | 
						|
  static nsIAtom* ElementPropertyAtom()
 | 
						|
  {
 | 
						|
    return nsGkAtoms::animationsProperty;
 | 
						|
  }
 | 
						|
  static nsIAtom* BeforePropertyAtom()
 | 
						|
  {
 | 
						|
    return nsGkAtoms::animationsOfBeforeProperty;
 | 
						|
  }
 | 
						|
  static nsIAtom* AfterPropertyAtom()
 | 
						|
  {
 | 
						|
    return nsGkAtoms::animationsOfAfterProperty;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
} /* namespace mozilla */
 | 
						|
 | 
						|
class nsAnimationManager final
 | 
						|
  : public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>
 | 
						|
{
 | 
						|
public:
 | 
						|
  explicit nsAnimationManager(nsPresContext *aPresContext)
 | 
						|
    : mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>(aPresContext)
 | 
						|
  {
 | 
						|
  }
 | 
						|
 | 
						|
  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager)
 | 
						|
  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager)
 | 
						|
 | 
						|
  typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation>
 | 
						|
    CSSAnimationCollection;
 | 
						|
  typedef nsTArray<RefPtr<mozilla::dom::CSSAnimation>>
 | 
						|
    OwningCSSAnimationPtrArray;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Update the set of animations on |aElement| based on |aStyleContext|.
 | 
						|
   * If necessary, this will notify the corresponding EffectCompositor so
 | 
						|
   * that it can update its animation rule.
 | 
						|
   *
 | 
						|
   * aStyleContext may be a style context for aElement or for its
 | 
						|
   * :before or :after pseudo-element.
 | 
						|
   */
 | 
						|
  void UpdateAnimations(nsStyleContext* aStyleContext,
 | 
						|
                        mozilla::dom::Element* aElement);
 | 
						|
 | 
						|
  /**
 | 
						|
   * Add a pending event.
 | 
						|
   */
 | 
						|
  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()
 | 
						|
  {
 | 
						|
    RefPtr<nsAnimationManager> kungFuDeathGrip(this);
 | 
						|
    mEventDispatcher.DispatchEvents(mPresContext);
 | 
						|
  }
 | 
						|
  void SortEvents()      { mEventDispatcher.SortEvents(); }
 | 
						|
  void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
 | 
						|
 | 
						|
  // Stop animations on the element. This method takes the real element
 | 
						|
  // rather than the element for the generated content for animations on
 | 
						|
  // ::before and ::after.
 | 
						|
  void StopAnimationsForElement(mozilla::dom::Element* aElement,
 | 
						|
                                mozilla::CSSPseudoElementType aPseudoType);
 | 
						|
 | 
						|
protected:
 | 
						|
  ~nsAnimationManager() override = default;
 | 
						|
 | 
						|
private:
 | 
						|
 | 
						|
  void BuildAnimations(nsStyleContext* aStyleContext,
 | 
						|
                       mozilla::dom::Element* aTarget,
 | 
						|
                       CSSAnimationCollection* aCollection,
 | 
						|
                       OwningCSSAnimationPtrArray& aAnimations);
 | 
						|
 | 
						|
  mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher;
 | 
						|
};
 | 
						|
 | 
						|
#endif /* !defined(nsAnimationManager_h_) */
 |