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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_KeyframeEffectReadOnly_h 8 #define mozilla_dom_KeyframeEffectReadOnly_h 9 10 #include "nsChangeHint.h" 11 #include "nsCSSPropertyID.h" 12 #include "nsCSSPropertyIDSet.h" 13 #include "nsCSSValue.h" 14 #include "nsCycleCollectionParticipant.h" 15 #include "nsRefPtrHashtable.h" 16 #include "nsTArray.h" 17 #include "nsWrapperCache.h" 18 #include "mozilla/AnimationPerformanceWarning.h" 19 #include "mozilla/AnimationPropertySegment.h" 20 #include "mozilla/AnimationTarget.h" 21 #include "mozilla/Attributes.h" 22 #include "mozilla/ComputedTimingFunction.h" 23 #include "mozilla/EffectCompositor.h" 24 #include "mozilla/Keyframe.h" 25 #include "mozilla/KeyframeEffectParams.h" 26 // RawServoDeclarationBlock and associated RefPtrTraits 27 #include "mozilla/ServoBindingTypes.h" 28 #include "mozilla/StyleAnimationValue.h" 29 #include "mozilla/dom/AnimationEffectReadOnly.h" 30 #include "mozilla/dom/BindingDeclarations.h" 31 #include "mozilla/dom/Element.h" 32 33 struct JSContext; 34 class JSObject; 35 class nsIContent; 36 class nsIDocument; 37 class nsIFrame; 38 class nsIPresShell; 39 40 namespace mozilla { 41 42 class AnimValuesStyleRule; 43 enum class CSSPseudoElementType : uint8_t; 44 class ErrorResult; 45 struct AnimationRule; 46 struct TimingParams; 47 class EffectSet; 48 class ServoStyleContext; 49 class GeckoStyleContext; 50 51 namespace dom { 52 class ElementOrCSSPseudoElement; 53 class GlobalObject; 54 class OwningElementOrCSSPseudoElement; 55 class UnrestrictedDoubleOrKeyframeAnimationOptions; 56 class UnrestrictedDoubleOrKeyframeEffectOptions; 57 enum class IterationCompositeOperation : uint8_t; 58 enum class CompositeOperation : uint8_t; 59 struct AnimationPropertyDetails; 60 } // namespace dom 61 62 struct AnimationProperty { 63 nsCSSPropertyID mProperty = eCSSProperty_UNKNOWN; 64 65 // If true, the propery is currently being animated on the compositor. 66 // 67 // Note that when the owning Animation requests a non-throttled restyle, in 68 // between calling RequestRestyle on its EffectCompositor and when the 69 // restyle is performed, this member may temporarily become false even if 70 // the animation remains on the layer after the restyle. 71 // 72 // **NOTE**: This member is not included when comparing AnimationProperty 73 // objects for equality. 74 bool mIsRunningOnCompositor = false; 75 76 Maybe<AnimationPerformanceWarning> mPerformanceWarning; 77 78 InfallibleTArray<AnimationPropertySegment> mSegments; 79 80 // The copy constructor/assignment doesn't copy mIsRunningOnCompositor and 81 // mPerformanceWarning. 82 AnimationProperty() = default; AnimationPropertyAnimationProperty83 AnimationProperty(const AnimationProperty& aOther) 84 : mProperty(aOther.mProperty), mSegments(aOther.mSegments) {} 85 AnimationProperty& operator=(const AnimationProperty& aOther) { 86 mProperty = aOther.mProperty; 87 mSegments = aOther.mSegments; 88 return *this; 89 } 90 91 // NOTE: This operator does *not* compare the mIsRunningOnCompositor member. 92 // This is because AnimationProperty objects are compared when recreating 93 // CSS animations to determine if mutation observer change records need to 94 // be created or not. However, at the point when these objects are compared 95 // the mIsRunningOnCompositor will not have been set on the new objects so 96 // we ignore this member to avoid generating spurious change records. 97 bool operator==(const AnimationProperty& aOther) const { 98 return mProperty == aOther.mProperty && mSegments == aOther.mSegments; 99 } 100 bool operator!=(const AnimationProperty& aOther) const { 101 return !(*this == aOther); 102 } 103 }; 104 105 struct ElementPropertyTransition; 106 107 namespace dom { 108 109 class Animation; 110 111 class KeyframeEffectReadOnly : public AnimationEffectReadOnly { 112 public: 113 KeyframeEffectReadOnly(nsIDocument* aDocument, 114 const Maybe<OwningAnimationTarget>& aTarget, 115 const TimingParams& aTiming, 116 const KeyframeEffectParams& aOptions); 117 118 NS_DECL_ISUPPORTS_INHERITED 119 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( 120 KeyframeEffectReadOnly, AnimationEffectReadOnly) 121 122 virtual JSObject* WrapObject(JSContext* aCx, 123 JS::Handle<JSObject*> aGivenProto) override; 124 AsKeyframeEffect()125 KeyframeEffectReadOnly* AsKeyframeEffect() override { return this; } 126 127 // KeyframeEffectReadOnly interface 128 static already_AddRefed<KeyframeEffectReadOnly> Constructor( 129 const GlobalObject& aGlobal, 130 const Nullable<ElementOrCSSPseudoElement>& aTarget, 131 JS::Handle<JSObject*> aKeyframes, 132 const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, 133 ErrorResult& aRv); 134 135 static already_AddRefed<KeyframeEffectReadOnly> Constructor( 136 const GlobalObject& aGlobal, KeyframeEffectReadOnly& aSource, 137 ErrorResult& aRv); 138 139 void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const; GetTarget()140 Maybe<NonOwningAnimationTarget> GetTarget() const { 141 Maybe<NonOwningAnimationTarget> result; 142 if (mTarget) { 143 result.emplace(*mTarget); 144 } 145 return result; 146 } 147 void GetKeyframes(JSContext*& aCx, nsTArray<JSObject*>& aResult, 148 ErrorResult& aRv); 149 void GetProperties(nsTArray<AnimationPropertyDetails>& aProperties, 150 ErrorResult& aRv) const; 151 152 IterationCompositeOperation IterationComposite() const; 153 CompositeOperation Composite() const; 154 void NotifyAnimationTimingUpdated(); 155 void RequestRestyle(EffectCompositor::RestyleType aRestyleType); 156 void SetAnimation(Animation* aAnimation) override; 157 void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes, 158 ErrorResult& aRv); 159 #ifdef MOZ_OLD_STYLE 160 void SetKeyframes(nsTArray<Keyframe>&& aKeyframes, 161 GeckoStyleContext* aStyleContext); 162 #endif 163 void SetKeyframes(nsTArray<Keyframe>&& aKeyframes, 164 const ServoStyleContext* aComputedValues); 165 166 // Returns true if the effect includes |aProperty| regardless of whether the 167 // property is overridden by !important rule. 168 bool HasAnimationOfProperty(nsCSSPropertyID aProperty) const; 169 170 // GetEffectiveAnimationOfProperty returns AnimationProperty corresponding 171 // to a given CSS property if the effect includes the property and the 172 // property is not overridden by !important rules. 173 // Also EffectiveAnimationOfProperty returns true under the same condition. 174 // 175 // NOTE: We don't currently check for !important rules for properties that 176 // can't run on the compositor. HasEffectiveAnimationOfProperty(nsCSSPropertyID aProperty)177 bool HasEffectiveAnimationOfProperty(nsCSSPropertyID aProperty) const { 178 return GetEffectiveAnimationOfProperty(aProperty) != nullptr; 179 } 180 const AnimationProperty* GetEffectiveAnimationOfProperty( 181 nsCSSPropertyID aProperty) const; 182 Properties()183 const InfallibleTArray<AnimationProperty>& Properties() const { 184 return mProperties; 185 } 186 187 // Update |mProperties| by recalculating from |mKeyframes| using 188 // |aStyleContext| to resolve specified values. 189 void UpdateProperties(nsStyleContext* aStyleContext); 190 // Servo version of the above function. 191 void UpdateProperties(const ServoStyleContext* aComputedValues); 192 193 // Update various bits of state related to running ComposeStyle(). 194 // We need to update this outside ComposeStyle() because we should avoid 195 // mutating any state in ComposeStyle() since it might be called during 196 // parallel traversal. 197 void WillComposeStyle(); 198 199 // Updates |aComposeResult| with the animation values produced by this 200 // AnimationEffect for the current time except any properties contained 201 // in |aPropertiesToSkip|. 202 template <typename ComposeAnimationResult> 203 void ComposeStyle(ComposeAnimationResult&& aRestultContainer, 204 const nsCSSPropertyIDSet& aPropertiesToSkip); 205 206 #ifdef MOZ_OLD_STYLE 207 // Composite |aValueToComposite| on |aUnderlyingValue| with 208 // |aCompositeOperation|. 209 // Returns |aValueToComposite| if |aCompositeOperation| is Replace. 210 static StyleAnimationValue CompositeValue( 211 nsCSSPropertyID aProperty, const StyleAnimationValue& aValueToComposite, 212 const StyleAnimationValue& aUnderlyingValue, 213 CompositeOperation aCompositeOperation); 214 #endif 215 216 // Returns true if at least one property is being animated on compositor. 217 bool IsRunningOnCompositor() const; 218 void SetIsRunningOnCompositor(nsCSSPropertyID aProperty, bool aIsRunning); 219 void ResetIsRunningOnCompositor(); 220 221 // Returns true if this effect, applied to |aFrame|, contains properties 222 // that mean we shouldn't run transform compositor animations on this element. 223 // 224 // For example, if we have an animation of geometric properties like 'left' 225 // and 'top' on an element, we force all 'transform' animations running at 226 // the same time on the same element to run on the main thread. 227 // 228 // When returning true, |aPerformanceWarning| stores the reason why 229 // we shouldn't run the transform animations. 230 bool ShouldBlockAsyncTransformAnimations( 231 const nsIFrame* aFrame, 232 AnimationPerformanceWarning::Type& aPerformanceWarning) const; 233 bool HasGeometricProperties() const; AffectsGeometry()234 bool AffectsGeometry() const override { 235 return GetTarget() && HasGeometricProperties(); 236 } 237 238 nsIDocument* GetRenderedDocument() const; 239 nsIPresShell* GetPresShell() const; 240 241 // Associates a warning with the animated property on the specified frame 242 // indicating why, for example, the property could not be animated on the 243 // compositor. |aParams| and |aParamsLength| are optional parameters which 244 // will be used to generate a localized message for devtools. 245 void SetPerformanceWarning(nsCSSPropertyID aProperty, 246 const AnimationPerformanceWarning& aWarning); 247 248 // Cumulative change hint on each segment for each property. 249 // This is used for deciding the animation is paint-only. 250 template <typename StyleType> 251 void CalculateCumulativeChangeHint(StyleType* aStyleContext); 252 253 // Returns true if all of animation properties' change hints 254 // can ignore painting if the animation is not visible. 255 // See nsChangeHint_Hints_CanIgnoreIfNotVisible in nsChangeHint.h 256 // in detail which change hint can be ignored. 257 bool CanIgnoreIfNotVisible() const; 258 259 // Returns true if the effect is current state and has scale animation. 260 // |aFrame| is used for calculation of scale values. 261 bool ContainsAnimatedScale(const nsIFrame* aFrame) const; 262 BaseStyle(nsCSSPropertyID aProperty)263 AnimationValue BaseStyle(nsCSSPropertyID aProperty) const { 264 AnimationValue result; 265 bool hasProperty = false; 266 if (mDocument->IsStyledByServo()) { 267 // We cannot use getters_AddRefs on RawServoAnimationValue because it is 268 // an incomplete type, so Get() doesn't work. Instead, use GetWeak, and 269 // then assign the raw pointer to a RefPtr. 270 result.mServo = mBaseStyleValuesForServo.GetWeak(aProperty, &hasProperty); 271 } else { 272 #ifdef MOZ_OLD_STYLE 273 hasProperty = mBaseStyleValues.Get(aProperty, &result.mGecko); 274 #else 275 MOZ_CRASH("old style system disabled"); 276 #endif 277 } 278 MOZ_ASSERT(hasProperty || result.IsNull()); 279 return result; 280 } 281 282 protected: 283 KeyframeEffectReadOnly(nsIDocument* aDocument, 284 const Maybe<OwningAnimationTarget>& aTarget, 285 AnimationEffectTimingReadOnly* aTiming, 286 const KeyframeEffectParams& aOptions); 287 288 ~KeyframeEffectReadOnly() override = default; 289 290 static Maybe<OwningAnimationTarget> ConvertTarget( 291 const Nullable<ElementOrCSSPseudoElement>& aTarget); 292 293 template <class KeyframeEffectType, class OptionsType> 294 static already_AddRefed<KeyframeEffectType> ConstructKeyframeEffect( 295 const GlobalObject& aGlobal, 296 const Nullable<ElementOrCSSPseudoElement>& aTarget, 297 JS::Handle<JSObject*> aKeyframes, const OptionsType& aOptions, 298 ErrorResult& aRv); 299 300 template <class KeyframeEffectType> 301 static already_AddRefed<KeyframeEffectType> ConstructKeyframeEffect( 302 const GlobalObject& aGlobal, KeyframeEffectReadOnly& aSource, 303 ErrorResult& aRv); 304 305 // Build properties by recalculating from |mKeyframes| using |aStyleContext| 306 // to resolve specified values. This function also applies paced spacing if 307 // needed. 308 template <typename StyleType> 309 nsTArray<AnimationProperty> BuildProperties(StyleType* aStyle); 310 311 // This effect is registered with its target element so long as: 312 // 313 // (a) It has a target element, and 314 // (b) It is "relevant" (i.e. yet to finish but not idle, or finished but 315 // filling forwards) 316 // 317 // As a result, we need to make sure this gets called whenever anything 318 // changes with regards to this effects's timing including changes to the 319 // owning Animation's timing. 320 void UpdateTargetRegistration(); 321 322 // Remove the current effect target from its EffectSet. 323 void UnregisterTarget(); 324 325 // Update the associated frame state bits so that, if necessary, a stacking 326 // context will be created and the effect sent to the compositor. We 327 // typically need to do this when the properties referenced by the keyframe 328 // have changed, or when the target frame might have changed. 329 void MaybeUpdateFrameForCompositor(); 330 331 // Looks up the style context associated with the target element, if any. 332 // We need to be careful to *not* call this when we are updating the style 333 // context. That's because calling GetStyleContext when we are in the process 334 // of building a style context may trigger various forms of infinite 335 // recursion. 336 already_AddRefed<nsStyleContext> GetTargetStyleContext(); 337 338 // A wrapper for marking cascade update according to the current 339 // target and its effectSet. 340 void MarkCascadeNeedsUpdate(); 341 342 #ifdef MOZ_OLD_STYLE 343 // Composites |aValueToComposite| using |aCompositeOperation| onto the value 344 // for |aProperty| in |aAnimationRule|, or, if there is no suitable value in 345 // |aAnimationRule|, uses the base value for the property recorded on the 346 // target element's EffectSet. 347 StyleAnimationValue CompositeValue( 348 nsCSSPropertyID aProperty, 349 const RefPtr<AnimValuesStyleRule>& aAnimationRule, 350 const StyleAnimationValue& aValueToComposite, 351 CompositeOperation aCompositeOperation); 352 353 // Returns underlying style animation value for |aProperty|. 354 StyleAnimationValue GetUnderlyingStyle( 355 nsCSSPropertyID aProperty, 356 const RefPtr<AnimValuesStyleRule>& aAnimationRule); 357 358 // Ensure the base styles is available for any properties in |aProperties|. 359 void EnsureBaseStyles(GeckoStyleContext* aStyleContext, 360 const nsTArray<AnimationProperty>& aProperties); 361 #endif 362 void EnsureBaseStyles(const ServoStyleContext* aComputedValues, 363 const nsTArray<AnimationProperty>& aProperties); 364 365 #ifdef MOZ_OLD_STYLE 366 // If no base style is already stored for |aProperty|, resolves the base style 367 // for |aProperty| using |aStyleContext| and stores it in mBaseStyleValues. 368 // If |aCachedBaseStyleContext| is non-null, it will be used, otherwise the 369 // base style context will be resolved and stored in 370 // |aCachedBaseStyleContext|. 371 void EnsureBaseStyle(nsCSSPropertyID aProperty, 372 GeckoStyleContext* aStyleContext, 373 RefPtr<GeckoStyleContext>& aCachedBaseStyleContext); 374 #endif 375 // Stylo version of the above function that also first checks for an additive 376 // value in |aProperty|'s list of segments. 377 void EnsureBaseStyle(const AnimationProperty& aProperty, 378 nsPresContext* aPresContext, 379 const ServoStyleContext* aComputedValues, 380 RefPtr<mozilla::ServoStyleContext>& aBaseComputedValues); 381 382 Maybe<OwningAnimationTarget> mTarget; 383 384 KeyframeEffectParams mEffectOptions; 385 386 // The specified keyframes. 387 nsTArray<Keyframe> mKeyframes; 388 389 // A set of per-property value arrays, derived from |mKeyframes|. 390 nsTArray<AnimationProperty> mProperties; 391 392 // The computed progress last time we composed the style rule. This is 393 // used to detect when the progress is not changing (e.g. due to a step 394 // timing function) so we can avoid unnecessary style updates. 395 Nullable<double> mProgressOnLastCompose; 396 397 // The purpose of this value is the same as mProgressOnLastCompose but 398 // this is used to detect when the current iteration is not changing 399 // in the case when iterationComposite is accumulate. 400 uint64_t mCurrentIterationOnLastCompose = 0; 401 402 // We need to track when we go to or from being "in effect" since 403 // we need to re-evaluate the cascade of animations when that changes. 404 bool mInEffectOnLastAnimationTimingUpdate; 405 406 // The non-animated values for properties in this effect that contain at 407 // least one animation value that is composited with the underlying value 408 // (i.e. it uses the additive or accumulate composite mode). 409 #ifdef MOZ_OLD_STYLE 410 nsDataHashtable<nsUint32HashKey, StyleAnimationValue> mBaseStyleValues; 411 #endif 412 nsRefPtrHashtable<nsUint32HashKey, RawServoAnimationValue> 413 mBaseStyleValuesForServo; 414 415 // True if this effect is in the EffectSet for its target element. This is 416 // used as an optimization to avoid unnecessary hashmap lookups on the 417 // EffectSet. 418 bool mInEffectSet = false; 419 420 private: 421 nsChangeHint mCumulativeChangeHint; 422 423 template <typename StyleType> 424 void DoSetKeyframes(nsTArray<Keyframe>&& aKeyframes, StyleType* aStyle); 425 426 template <typename StyleType> 427 void DoUpdateProperties(StyleType* aStyle); 428 429 #ifdef MOZ_OLD_STYLE 430 void ComposeStyleRule(RefPtr<AnimValuesStyleRule>& aStyleRule, 431 const AnimationProperty& aProperty, 432 const AnimationPropertySegment& aSegment, 433 const ComputedTiming& aComputedTiming); 434 #endif 435 436 void ComposeStyleRule(RawServoAnimationValueMap& aAnimationValues, 437 const AnimationProperty& aProperty, 438 const AnimationPropertySegment& aSegment, 439 const ComputedTiming& aComputedTiming); 440 441 #ifdef MOZ_OLD_STYLE 442 already_AddRefed<nsStyleContext> CreateStyleContextForAnimationValue( 443 nsCSSPropertyID aProperty, const AnimationValue& aValue, 444 GeckoStyleContext* aBaseStyleContext); 445 #endif 446 447 already_AddRefed<nsStyleContext> CreateStyleContextForAnimationValue( 448 nsCSSPropertyID aProperty, const AnimationValue& aValue, 449 const ServoStyleContext* aBaseStyleContext); 450 451 nsIFrame* GetAnimationFrame() const; 452 453 bool CanThrottle() const; 454 bool CanThrottleTransformChanges(const nsIFrame& aFrame) const; 455 bool CanThrottleTransformChangesInScrollable(nsIFrame& aFrame) const; 456 457 // Returns true if the computedTiming has changed since the last 458 // composition. 459 bool HasComputedTimingChanged() const; 460 461 // Returns true unless Gecko limitations prevent performing transform 462 // animations for |aFrame|. When returning true, the reason for the 463 // limitation is stored in |aOutPerformanceWarning|. 464 static bool CanAnimateTransformOnCompositor( 465 const nsIFrame* aFrame, 466 AnimationPerformanceWarning::Type& aPerformanceWarning); 467 static bool IsGeometricProperty(const nsCSSPropertyID aProperty); 468 469 static const TimeDuration OverflowRegionRefreshInterval(); 470 471 void UpdateEffectSet(mozilla::EffectSet* aEffectSet = nullptr) const; 472 473 // Returns true if this effect has transform and the transform might affect 474 // the overflow region. 475 // This function is used for updating scroll bars or notifying intersection 476 // observers reflected by the transform. HasTransformThatMightAffectOverflow()477 bool HasTransformThatMightAffectOverflow() const { 478 return mCumulativeChangeHint & (nsChangeHint_UpdatePostTransformOverflow | 479 nsChangeHint_AddOrRemoveTransform | 480 nsChangeHint_UpdateTransformLayer); 481 } 482 483 // FIXME: This flag will be removed in bug 1324966. 484 bool mIsComposingStyle = false; 485 486 // Returns true if this effect causes visibility change. 487 // (i.e. 'visibility: hidden' -> 'visibility: visible' and vice versa.) HasVisibilityChange()488 bool HasVisibilityChange() const { 489 return mCumulativeChangeHint & nsChangeHint_VisibilityChange; 490 } 491 }; 492 493 } // namespace dom 494 } // namespace mozilla 495 496 #endif // mozilla_dom_KeyframeEffectReadOnly_h 497