nsAnimationManager.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #ifndef nsAnimationManager_h_
  5. #define nsAnimationManager_h_
  6. #include "mozilla/Attributes.h"
  7. #include "mozilla/ContentEvents.h"
  8. #include "mozilla/EventForwards.h"
  9. #include "AnimationCommon.h"
  10. #include "mozilla/dom/Animation.h"
  11. #include "mozilla/MemoryReporting.h"
  12. #include "mozilla/TimeStamp.h"
  13. class nsIGlobalObject;
  14. class nsStyleContext;
  15. namespace mozilla {
  16. namespace css {
  17. class Declaration;
  18. } /* namespace css */
  19. namespace dom {
  20. class KeyframeEffectReadOnly;
  21. class Promise;
  22. } /* namespace dom */
  23. enum class CSSPseudoElementType : uint8_t;
  24. struct AnimationEventInfo {
  25. RefPtr<dom::Element> mElement;
  26. RefPtr<dom::Animation> mAnimation;
  27. InternalAnimationEvent mEvent;
  28. TimeStamp mTimeStamp;
  29. AnimationEventInfo(dom::Element* aElement,
  30. CSSPseudoElementType aPseudoType,
  31. EventMessage aMessage,
  32. const nsSubstring& aAnimationName,
  33. const StickyTimeDuration& aElapsedTime,
  34. const TimeStamp& aTimeStamp,
  35. dom::Animation* aAnimation)
  36. : mElement(aElement)
  37. , mAnimation(aAnimation)
  38. , mEvent(true, aMessage)
  39. , mTimeStamp(aTimeStamp)
  40. {
  41. // XXX Looks like nobody initialize WidgetEvent::time
  42. mEvent.mAnimationName = aAnimationName;
  43. mEvent.mElapsedTime = aElapsedTime.ToSeconds();
  44. mEvent.mPseudoElement =
  45. AnimationCollection<dom::CSSAnimation>::PseudoTypeAsString(aPseudoType);
  46. }
  47. // InternalAnimationEvent doesn't support copy-construction, so we need
  48. // to ourselves in order to work with nsTArray
  49. AnimationEventInfo(const AnimationEventInfo& aOther)
  50. : mElement(aOther.mElement)
  51. , mAnimation(aOther.mAnimation)
  52. , mEvent(true, aOther.mEvent.mMessage)
  53. , mTimeStamp(aOther.mTimeStamp)
  54. {
  55. mEvent.AssignAnimationEventData(aOther.mEvent, false);
  56. }
  57. };
  58. namespace dom {
  59. class CSSAnimation final : public Animation
  60. {
  61. public:
  62. explicit CSSAnimation(nsIGlobalObject* aGlobal,
  63. const nsSubstring& aAnimationName)
  64. : dom::Animation(aGlobal)
  65. , mAnimationName(aAnimationName)
  66. , mIsStylePaused(false)
  67. , mPauseShouldStick(false)
  68. , mNeedsNewAnimationIndexWhenRun(false)
  69. , mPreviousPhase(ComputedTiming::AnimationPhase::Idle)
  70. , mPreviousIteration(0)
  71. {
  72. // We might need to drop this assertion once we add a script-accessible
  73. // constructor but for animations generated from CSS markup the
  74. // animation-name should never be empty.
  75. MOZ_ASSERT(!mAnimationName.IsEmpty(), "animation-name should not be empty");
  76. }
  77. JSObject* WrapObject(JSContext* aCx,
  78. JS::Handle<JSObject*> aGivenProto) override;
  79. CSSAnimation* AsCSSAnimation() override { return this; }
  80. const CSSAnimation* AsCSSAnimation() const override { return this; }
  81. // CSSAnimation interface
  82. void GetAnimationName(nsString& aRetVal) const { aRetVal = mAnimationName; }
  83. // Alternative to GetAnimationName that returns a reference to the member
  84. // for more efficient internal usage.
  85. const nsString& AnimationName() const { return mAnimationName; }
  86. // Animation interface overrides
  87. virtual Promise* GetReady(ErrorResult& aRv) override;
  88. virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior) override;
  89. virtual void Pause(ErrorResult& aRv) override;
  90. virtual AnimationPlayState PlayStateFromJS() const override;
  91. virtual void PlayFromJS(ErrorResult& aRv) override;
  92. void PlayFromStyle();
  93. void PauseFromStyle();
  94. void CancelFromStyle() override
  95. {
  96. // When an animation is disassociated with style it enters an odd state
  97. // where its composite order is undefined until it first transitions
  98. // out of the idle state.
  99. //
  100. // Even if the composite order isn't defined we don't want it to be random
  101. // in case we need to determine the order to dispatch events associated
  102. // with an animation in this state. To solve this we treat the animation as
  103. // if it had been added to the end of the global animation list so that
  104. // its sort order is defined. We'll update this index again once the
  105. // animation leaves the idle state.
  106. mAnimationIndex = sNextAnimationIndex++;
  107. mNeedsNewAnimationIndexWhenRun = true;
  108. Animation::CancelFromStyle();
  109. // We need to do this *after* calling CancelFromStyle() since
  110. // CancelFromStyle might synchronously trigger a cancel event for which
  111. // we need an owning element to target the event at.
  112. mOwningElement = OwningElementRef();
  113. }
  114. void Tick() override;
  115. void QueueEvents(StickyTimeDuration aActiveTime = StickyTimeDuration());
  116. bool IsStylePaused() const { return mIsStylePaused; }
  117. bool HasLowerCompositeOrderThan(const CSSAnimation& aOther) const;
  118. void SetAnimationIndex(uint64_t aIndex)
  119. {
  120. MOZ_ASSERT(IsTiedToMarkup());
  121. if (IsRelevant() &&
  122. mAnimationIndex != aIndex) {
  123. nsNodeUtils::AnimationChanged(this);
  124. PostUpdate();
  125. }
  126. mAnimationIndex = aIndex;
  127. }
  128. // Sets the owning element which is used for determining the composite
  129. // order of CSSAnimation objects generated from CSS markup.
  130. //
  131. // @see mOwningElement
  132. void SetOwningElement(const OwningElementRef& aElement)
  133. {
  134. mOwningElement = aElement;
  135. }
  136. // True for animations that are generated from CSS markup and continue to
  137. // reflect changes to that markup.
  138. bool IsTiedToMarkup() const { return mOwningElement.IsSet(); }
  139. void MaybeQueueCancelEvent(StickyTimeDuration aActiveTime) override {
  140. QueueEvents(aActiveTime);
  141. }
  142. protected:
  143. virtual ~CSSAnimation()
  144. {
  145. MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
  146. "before a CSS animation is destroyed");
  147. }
  148. // Animation overrides
  149. void UpdateTiming(SeekFlag aSeekFlag,
  150. SyncNotifyFlag aSyncNotifyFlag) override;
  151. // Returns the duration from the start of the animation's source effect's
  152. // active interval to the point where the animation actually begins playback.
  153. // This is zero unless the animation's source effect has a negative delay in
  154. // which case it is the absolute value of that delay.
  155. // This is used for setting the elapsedTime member of CSS AnimationEvents.
  156. TimeDuration InitialAdvance() const {
  157. return mEffect ?
  158. std::max(TimeDuration(), mEffect->SpecifiedTiming().mDelay * -1) :
  159. TimeDuration();
  160. }
  161. nsString mAnimationName;
  162. // The (pseudo-)element whose computed animation-name refers to this
  163. // animation (if any).
  164. //
  165. // This is used for determining the relative composite order of animations
  166. // generated from CSS markup.
  167. //
  168. // Typically this will be the same as the target element of the keyframe
  169. // effect associated with this animation. However, it can differ in the
  170. // following circumstances:
  171. //
  172. // a) If script removes or replaces the effect of this animation,
  173. // b) If this animation is cancelled (e.g. by updating the
  174. // animation-name property or removing the owning element from the
  175. // document),
  176. // c) If this object is generated from script using the CSSAnimation
  177. // constructor.
  178. //
  179. // For (b) and (c) the owning element will return !IsSet().
  180. OwningElementRef mOwningElement;
  181. // When combining animation-play-state with play() / pause() the following
  182. // behavior applies:
  183. // 1. pause() is sticky and always overrides the underlying
  184. // animation-play-state
  185. // 2. If animation-play-state is 'paused', play() will temporarily override
  186. // it until animation-play-state next becomes 'running'.
  187. // 3. Calls to play() trigger finishing behavior but setting the
  188. // animation-play-state to 'running' does not.
  189. //
  190. // This leads to five distinct states:
  191. //
  192. // A. Running
  193. // B. Running and temporarily overriding animation-play-state: paused
  194. // C. Paused and sticky overriding animation-play-state: running
  195. // D. Paused and sticky overriding animation-play-state: paused
  196. // E. Paused by animation-play-state
  197. //
  198. // C and D may seem redundant but they differ in how to respond to the
  199. // sequence: call play(), set animation-play-state: paused.
  200. //
  201. // C will transition to A then E leaving the animation paused.
  202. // D will transition to B then B leaving the animation running.
  203. //
  204. // A state transition chart is as follows:
  205. //
  206. // A | B | C | D | E
  207. // ---------------------------
  208. // play() A | B | A | B | B
  209. // pause() C | D | C | D | D
  210. // 'running' A | A | C | C | A
  211. // 'paused' E | B | D | D | E
  212. //
  213. // The base class, Animation already provides a boolean value,
  214. // mIsPaused which gives us two states. To this we add a further two booleans
  215. // to represent the states as follows.
  216. //
  217. // A. Running
  218. // (!mIsPaused; !mIsStylePaused; !mPauseShouldStick)
  219. // B. Running and temporarily overriding animation-play-state: paused
  220. // (!mIsPaused; mIsStylePaused; !mPauseShouldStick)
  221. // C. Paused and sticky overriding animation-play-state: running
  222. // (mIsPaused; !mIsStylePaused; mPauseShouldStick)
  223. // D. Paused and sticky overriding animation-play-state: paused
  224. // (mIsPaused; mIsStylePaused; mPauseShouldStick)
  225. // E. Paused by animation-play-state
  226. // (mIsPaused; mIsStylePaused; !mPauseShouldStick)
  227. //
  228. // (That leaves 3 combinations of the boolean values that we never set because
  229. // they don't represent valid states.)
  230. bool mIsStylePaused;
  231. bool mPauseShouldStick;
  232. // When true, indicates that when this animation next leaves the idle state,
  233. // its animation index should be updated.
  234. bool mNeedsNewAnimationIndexWhenRun;
  235. // Phase and current iteration from the previous time we queued events.
  236. // This is used to determine what new events to dispatch.
  237. ComputedTiming::AnimationPhase mPreviousPhase;
  238. uint64_t mPreviousIteration;
  239. };
  240. } /* namespace dom */
  241. template <>
  242. struct AnimationTypeTraits<dom::CSSAnimation>
  243. {
  244. static nsIAtom* ElementPropertyAtom()
  245. {
  246. return nsGkAtoms::animationsProperty;
  247. }
  248. static nsIAtom* BeforePropertyAtom()
  249. {
  250. return nsGkAtoms::animationsOfBeforeProperty;
  251. }
  252. static nsIAtom* AfterPropertyAtom()
  253. {
  254. return nsGkAtoms::animationsOfAfterProperty;
  255. }
  256. };
  257. } /* namespace mozilla */
  258. class nsAnimationManager final
  259. : public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>
  260. {
  261. public:
  262. explicit nsAnimationManager(nsPresContext *aPresContext)
  263. : mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>(aPresContext)
  264. {
  265. }
  266. NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager)
  267. NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager)
  268. typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation>
  269. CSSAnimationCollection;
  270. typedef nsTArray<RefPtr<mozilla::dom::CSSAnimation>>
  271. OwningCSSAnimationPtrArray;
  272. /**
  273. * Update the set of animations on |aElement| based on |aStyleContext|.
  274. * If necessary, this will notify the corresponding EffectCompositor so
  275. * that it can update its animation rule.
  276. *
  277. * aStyleContext may be a style context for aElement or for its
  278. * :before or :after pseudo-element.
  279. */
  280. void UpdateAnimations(nsStyleContext* aStyleContext,
  281. mozilla::dom::Element* aElement);
  282. /**
  283. * Add a pending event.
  284. */
  285. void QueueEvent(mozilla::AnimationEventInfo&& aEventInfo)
  286. {
  287. mEventDispatcher.QueueEvent(
  288. mozilla::Forward<mozilla::AnimationEventInfo>(aEventInfo));
  289. }
  290. /**
  291. * Dispatch any pending events. We accumulate animationend and
  292. * animationiteration events only during refresh driver notifications
  293. * (and dispatch them at the end of such notifications), but we
  294. * accumulate animationstart events at other points when style
  295. * contexts are created.
  296. */
  297. void DispatchEvents()
  298. {
  299. RefPtr<nsAnimationManager> kungFuDeathGrip(this);
  300. mEventDispatcher.DispatchEvents(mPresContext);
  301. }
  302. void SortEvents() { mEventDispatcher.SortEvents(); }
  303. void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
  304. // Stop animations on the element. This method takes the real element
  305. // rather than the element for the generated content for animations on
  306. // ::before and ::after.
  307. void StopAnimationsForElement(mozilla::dom::Element* aElement,
  308. mozilla::CSSPseudoElementType aPseudoType);
  309. protected:
  310. ~nsAnimationManager() override = default;
  311. private:
  312. void BuildAnimations(nsStyleContext* aStyleContext,
  313. mozilla::dom::Element* aTarget,
  314. CSSAnimationCollection* aCollection,
  315. OwningCSSAnimationPtrArray& aAnimations);
  316. mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher;
  317. };
  318. #endif /* !defined(nsAnimationManager_h_) */