CSSAnimation.h (9548B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_CSSAnimation_h 8 #define mozilla_dom_CSSAnimation_h 9 10 #include "AnimationCommon.h" 11 #include "mozilla/StyleAnimationValue.h" 12 #include "mozilla/dom/Animation.h" 13 #include "mozilla/dom/KeyframeEffect.h" 14 #include "mozilla/dom/MutationObservers.h" 15 16 namespace mozilla { 17 // Properties of CSS Animations that can be overridden by the Web Animations API 18 // in a manner that means we should ignore subsequent changes to markup for that 19 // property. 20 enum class CSSAnimationProperties { 21 None = 0, 22 Keyframes = 1 << 0, 23 Duration = 1 << 1, 24 IterationCount = 1 << 2, 25 Direction = 1 << 3, 26 Delay = 1 << 4, 27 FillMode = 1 << 5, 28 Composition = 1 << 6, 29 Effect = Keyframes | Duration | IterationCount | Direction | Delay | 30 FillMode | Composition, 31 PlayState = 1 << 7, 32 }; 33 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSAnimationProperties) 34 35 namespace dom { 36 37 class CSSAnimation final : public Animation { 38 public: 39 explicit CSSAnimation(nsIGlobalObject* aGlobal, nsAtom* aAnimationName) 40 : dom::Animation(aGlobal), 41 mAnimationName(aAnimationName), 42 mNeedsNewAnimationIndexWhenRun(false), 43 mPreviousPhase(ComputedTiming::AnimationPhase::Idle), 44 mPreviousIteration(0) { 45 // We might need to drop this assertion once we add a script-accessible 46 // constructor but for animations generated from CSS markup the 47 // animation-name should never be empty. 48 MOZ_ASSERT(mAnimationName != nsGkAtoms::_empty, 49 "animation-name should not be 'none'"); 50 } 51 52 JSObject* WrapObject(JSContext* aCx, 53 JS::Handle<JSObject*> aGivenProto) override; 54 55 CSSAnimation* AsCSSAnimation() override { return this; } 56 const CSSAnimation* AsCSSAnimation() const override { return this; } 57 58 // CSSAnimation interface 59 void GetAnimationName(nsString& aRetVal) const { 60 mAnimationName->ToString(aRetVal); 61 } 62 63 nsAtom* AnimationName() const { return mAnimationName; } 64 65 // Animation interface overrides 66 void SetEffect(AnimationEffect* aEffect) override; 67 void SetStartTimeAsDouble(const Nullable<double>& aStartTime) override; 68 Promise* GetReady(ErrorResult& aRv) override; 69 void Reverse(ErrorResult& aRv) override; 70 71 // NOTE: tabbrowser.xml currently relies on the fact that reading the 72 // currentTime of a CSSAnimation does *not* flush style (whereas reading the 73 // playState does). If CSS Animations 2 specifies that reading currentTime 74 // also flushes style we will need to find another way to detect canceled 75 // animations in tabbrowser.xml. On the other hand, if CSS Animations 2 76 // specifies that reading playState does *not* flush style (and we drop the 77 // following override), then we should update tabbrowser.xml to check 78 // the playState instead. 79 AnimationPlayState PlayStateFromJS() const override; 80 bool PendingFromJS() const override; 81 void PlayFromJS(ErrorResult& aRv) override; 82 void PauseFromJS(ErrorResult& aRv) override; 83 84 void PlayFromStyle(); 85 void PauseFromStyle(); 86 void CancelFromStyle(PostRestyleMode aPostRestyle) { 87 Animation::Cancel(aPostRestyle); 88 89 // When an animation is disassociated with style it enters an odd state 90 // where its composite order is undefined until it first transitions 91 // out of the idle state. 92 // 93 // Even if the composite order isn't defined we don't want it to be random 94 // in case we need to determine the order to dispatch events associated 95 // with an animation in this state. To solve this we treat the animation as 96 // if it had been added to the end of the global animation list so that 97 // its sort order is defined. We'll update this index again once the 98 // animation leaves the idle state. 99 // 100 // Note: We have to update |mAnimationIndex| after calling 101 // Animation::Cancel(), which enqueues animationcancel event, to make sure 102 // we have the correct |mAnimationIndex| in AnimationEventInfo. 103 mAnimationIndex = sNextAnimationIndex++; 104 mNeedsNewAnimationIndexWhenRun = true; 105 106 // We need to do this *after* calling Cancel() since 107 // Cancel() might synchronously trigger a cancel event for which 108 // we need an owning element to target the event at. 109 mOwningElement = OwningElementRef(); 110 } 111 112 void Tick(TickState&) override; 113 void QueueEvents( 114 const StickyTimeDuration& aActiveTime = StickyTimeDuration()); 115 116 int32_t CompareCompositeOrder(const CSSAnimation& aOther, 117 nsContentUtils::NodeIndexCache&) const; 118 119 void SetAnimationIndex(uint64_t aIndex) { 120 MOZ_ASSERT(IsTiedToMarkup()); 121 if (IsRelevant() && mAnimationIndex != aIndex) { 122 MutationObservers::NotifyAnimationChanged(this); 123 PostUpdate(); 124 } 125 mAnimationIndex = aIndex; 126 } 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 mOwningElement = aElement; 134 } 135 // True for animations that are generated from CSS markup and continue to 136 // reflect changes to that markup. 137 bool IsTiedToMarkup() const { return mOwningElement.IsSet(); } 138 139 void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime) override { 140 QueueEvents(aActiveTime); 141 } 142 143 CSSAnimationProperties GetOverriddenProperties() const { 144 return mOverriddenProperties; 145 } 146 void AddOverriddenProperties(CSSAnimationProperties aProperties) { 147 mOverriddenProperties |= aProperties; 148 } 149 150 protected: 151 virtual ~CSSAnimation() { 152 MOZ_ASSERT(!mOwningElement.IsSet(), 153 "Owning element should be cleared " 154 "before a CSS animation is destroyed"); 155 } 156 157 // Animation overrides 158 void UpdateTiming(SeekFlag aSeekFlag, 159 SyncNotifyFlag aSyncNotifyFlag) override; 160 161 // Returns the duration from the start of the animation's source effect's 162 // active interval to the point where the animation actually begins playback. 163 // This is zero unless the animation's source effect has a negative delay in 164 // which case it is the absolute value of that delay. 165 // This is used for setting the elapsedTime member of CSS AnimationEvents. 166 TimeDuration InitialAdvance() const { 167 return mEffect ? std::max(TimeDuration(), 168 mEffect->NormalizedTiming().Delay() * -1) 169 : TimeDuration(); 170 } 171 172 RefPtr<nsAtom> mAnimationName; 173 174 // The (pseudo-)element whose computed animation-name refers to this 175 // animation (if any). 176 // 177 // This is used for determining the relative composite order of animations 178 // generated from CSS markup. 179 // 180 // Typically this will be the same as the target element of the keyframe 181 // effect associated with this animation. However, it can differ in the 182 // following circumstances: 183 // 184 // a) If script removes or replaces the effect of this animation, 185 // b) If this animation is cancelled (e.g. by updating the 186 // animation-name property or removing the owning element from the 187 // document), 188 // c) If this object is generated from script using the CSSAnimation 189 // constructor. 190 // 191 // For (b) and (c) the owning element will return !IsSet(). 192 OwningElementRef mOwningElement; 193 194 // When true, indicates that when this animation next leaves the idle state, 195 // its animation index should be updated. 196 bool mNeedsNewAnimationIndexWhenRun; 197 198 // Phase and current iteration from the previous time we queued events. 199 // This is used to determine what new events to dispatch. 200 ComputedTiming::AnimationPhase mPreviousPhase; 201 uint64_t mPreviousIteration; 202 203 // Properties that would normally be defined by the cascade but which have 204 // since been explicitly set via the Web Animations API. 205 CSSAnimationProperties mOverriddenProperties = CSSAnimationProperties::None; 206 }; 207 208 // A subclass of KeyframeEffect that reports when specific properties have been 209 // overridden via the Web Animations API. 210 class CSSAnimationKeyframeEffect : public KeyframeEffect { 211 public: 212 CSSAnimationKeyframeEffect(Document* aDocument, 213 OwningAnimationTarget&& aTarget, 214 TimingParams&& aTiming, 215 const KeyframeEffectParams& aOptions) 216 : KeyframeEffect(aDocument, std::move(aTarget), std::move(aTiming), 217 aOptions) {} 218 219 void GetTiming(EffectTiming& aRetVal) const override; 220 void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const override; 221 void UpdateTiming(const OptionalEffectTiming& aTiming, 222 ErrorResult& aRv) override; 223 void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes, 224 ErrorResult& aRv) override; 225 void SetComposite(const CompositeOperation& aComposite) override; 226 227 private: 228 CSSAnimation* GetOwningCSSAnimation() { 229 return mAnimation ? mAnimation->AsCSSAnimation() : nullptr; 230 } 231 const CSSAnimation* GetOwningCSSAnimation() const { 232 return mAnimation ? mAnimation->AsCSSAnimation() : nullptr; 233 } 234 235 // Flushes styles if our owning animation is a CSSAnimation 236 void MaybeFlushUnanimatedStyle() const; 237 }; 238 239 } // namespace dom 240 241 } // namespace mozilla 242 243 #endif // mozilla_dom_CSSAnimation_h