/work/obj-fuzz/dist/include/mozilla/dom/KeyframeEffect.h
Line | Count | Source (jump to first uncovered line) |
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_KeyframeEffect_h |
8 | | #define mozilla_dom_KeyframeEffect_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/AnimationEffect.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 ComputedStyle; |
49 | | |
50 | | namespace dom { |
51 | | class ElementOrCSSPseudoElement; |
52 | | class GlobalObject; |
53 | | class OwningElementOrCSSPseudoElement; |
54 | | class UnrestrictedDoubleOrKeyframeAnimationOptions; |
55 | | class UnrestrictedDoubleOrKeyframeEffectOptions; |
56 | | enum class IterationCompositeOperation : uint8_t; |
57 | | enum class CompositeOperation : uint8_t; |
58 | | struct AnimationPropertyDetails; |
59 | | } |
60 | | |
61 | | struct AnimationProperty |
62 | | { |
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 | 0 | AnimationProperty() = default; |
83 | | AnimationProperty(const AnimationProperty& aOther) |
84 | 0 | : mProperty(aOther.mProperty), mSegments(aOther.mSegments) { } |
85 | | AnimationProperty& operator=(const AnimationProperty& aOther) |
86 | 0 | { |
87 | 0 | mProperty = aOther.mProperty; |
88 | 0 | mSegments = aOther.mSegments; |
89 | 0 | return *this; |
90 | 0 | } |
91 | | |
92 | | // NOTE: This operator does *not* compare the mIsRunningOnCompositor member. |
93 | | // This is because AnimationProperty objects are compared when recreating |
94 | | // CSS animations to determine if mutation observer change records need to |
95 | | // be created or not. However, at the point when these objects are compared |
96 | | // the mIsRunningOnCompositor will not have been set on the new objects so |
97 | | // we ignore this member to avoid generating spurious change records. |
98 | | bool operator==(const AnimationProperty& aOther) const |
99 | 0 | { |
100 | 0 | return mProperty == aOther.mProperty && |
101 | 0 | mSegments == aOther.mSegments; |
102 | 0 | } |
103 | | bool operator!=(const AnimationProperty& aOther) const |
104 | 0 | { |
105 | 0 | return !(*this == aOther); |
106 | 0 | } |
107 | | }; |
108 | | |
109 | | struct ElementPropertyTransition; |
110 | | |
111 | | namespace dom { |
112 | | |
113 | | class Animation; |
114 | | |
115 | | class KeyframeEffect : public AnimationEffect |
116 | | { |
117 | | public: |
118 | | KeyframeEffect(nsIDocument* aDocument, |
119 | | const Maybe<OwningAnimationTarget>& aTarget, |
120 | | const TimingParams& aTiming, |
121 | | const KeyframeEffectParams& aOptions); |
122 | | |
123 | | NS_DECL_ISUPPORTS_INHERITED |
124 | | NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(KeyframeEffect, |
125 | | AnimationEffect) |
126 | | |
127 | | virtual JSObject* WrapObject(JSContext* aCx, |
128 | | JS::Handle<JSObject*> aGivenProto) override; |
129 | | |
130 | 0 | KeyframeEffect* AsKeyframeEffect() override { return this; } |
131 | | |
132 | | // KeyframeEffect interface |
133 | | static already_AddRefed<KeyframeEffect> |
134 | | Constructor(const GlobalObject& aGlobal, |
135 | | const Nullable<ElementOrCSSPseudoElement>& aTarget, |
136 | | JS::Handle<JSObject*> aKeyframes, |
137 | | const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, |
138 | | ErrorResult& aRv); |
139 | | |
140 | | static already_AddRefed<KeyframeEffect> |
141 | | Constructor(const GlobalObject& aGlobal, |
142 | | KeyframeEffect& aSource, |
143 | | ErrorResult& aRv); |
144 | | |
145 | | // Variant of Constructor that accepts a KeyframeAnimationOptions object |
146 | | // for use with for Animatable.animate. |
147 | | // Not exposed to content. |
148 | | static already_AddRefed<KeyframeEffect> |
149 | | Constructor(const GlobalObject& aGlobal, |
150 | | const Nullable<ElementOrCSSPseudoElement>& aTarget, |
151 | | JS::Handle<JSObject*> aKeyframes, |
152 | | const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, |
153 | | ErrorResult& aRv); |
154 | | |
155 | | void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const; |
156 | | Maybe<NonOwningAnimationTarget> GetTarget() const |
157 | 0 | { |
158 | 0 | Maybe<NonOwningAnimationTarget> result; |
159 | 0 | if (mTarget) { |
160 | 0 | result.emplace(*mTarget); |
161 | 0 | } |
162 | 0 | return result; |
163 | 0 | } |
164 | | // This method calls GetTargetComputedStyle which is not safe to use when |
165 | | // we are in the middle of updating style. If we need to use this when |
166 | | // updating style, we should pass the ComputedStyle into this method and use |
167 | | // that to update the properties rather than calling |
168 | | // GetComputedStyle. |
169 | | void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget); |
170 | | |
171 | | void GetKeyframes(JSContext*& aCx, |
172 | | nsTArray<JSObject*>& aResult, |
173 | | ErrorResult& aRv); |
174 | | void GetProperties(nsTArray<AnimationPropertyDetails>& aProperties, |
175 | | ErrorResult& aRv) const; |
176 | | |
177 | | IterationCompositeOperation IterationComposite() const; |
178 | | void SetIterationComposite( |
179 | | const IterationCompositeOperation& aIterationComposite); |
180 | | |
181 | | CompositeOperation Composite() const; |
182 | | void SetComposite(const CompositeOperation& aComposite); |
183 | | |
184 | | void NotifySpecifiedTimingUpdated(); |
185 | | void NotifyAnimationTimingUpdated(); |
186 | | void RequestRestyle(EffectCompositor::RestyleType aRestyleType); |
187 | | void SetAnimation(Animation* aAnimation) override; |
188 | | void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes, |
189 | | ErrorResult& aRv); |
190 | | void SetKeyframes(nsTArray<Keyframe>&& aKeyframes, |
191 | | const ComputedStyle* aStyle); |
192 | | |
193 | | // Returns true if the effect includes |aProperty| regardless of whether the |
194 | | // property is overridden by !important rule. |
195 | | bool HasAnimationOfProperty(nsCSSPropertyID aProperty) const; |
196 | | |
197 | | // GetEffectiveAnimationOfProperty returns AnimationProperty corresponding |
198 | | // to a given CSS property if the effect includes the property and the |
199 | | // property is not overridden by !important rules. |
200 | | // Also EffectiveAnimationOfProperty returns true under the same condition. |
201 | | // |
202 | | // NOTE: We don't currently check for !important rules for properties that |
203 | | // can't run on the compositor. |
204 | | bool HasEffectiveAnimationOfProperty(nsCSSPropertyID aProperty) const |
205 | 0 | { |
206 | 0 | return GetEffectiveAnimationOfProperty(aProperty) != nullptr; |
207 | 0 | } |
208 | | const AnimationProperty* GetEffectiveAnimationOfProperty( |
209 | | nsCSSPropertyID aProperty) const; |
210 | | |
211 | | const InfallibleTArray<AnimationProperty>& Properties() const |
212 | 0 | { |
213 | 0 | return mProperties; |
214 | 0 | } |
215 | | |
216 | | // Update |mProperties| by recalculating from |mKeyframes| using |
217 | | // |aComputedStyle| to resolve specified values. |
218 | | void UpdateProperties(const ComputedStyle* aComputedValues); |
219 | | |
220 | | // Update various bits of state related to running ComposeStyle(). |
221 | | // We need to update this outside ComposeStyle() because we should avoid |
222 | | // mutating any state in ComposeStyle() since it might be called during |
223 | | // parallel traversal. |
224 | | void WillComposeStyle(); |
225 | | |
226 | | // Updates |aComposeResult| with the animation values produced by this |
227 | | // AnimationEffect for the current time except any properties contained |
228 | | // in |aPropertiesToSkip|. |
229 | | void ComposeStyle(RawServoAnimationValueMap& aComposeResult, |
230 | | const nsCSSPropertyIDSet& aPropertiesToSkip); |
231 | | |
232 | | |
233 | | // Returns true if at least one property is being animated on compositor. |
234 | | bool IsRunningOnCompositor() const; |
235 | | void SetIsRunningOnCompositor(nsCSSPropertyID aProperty, bool aIsRunning); |
236 | | void ResetIsRunningOnCompositor(); |
237 | | |
238 | | // Returns true if this effect, applied to |aFrame|, contains properties |
239 | | // that mean we shouldn't run transform compositor animations on this element. |
240 | | // |
241 | | // For example, if we have an animation of geometric properties like 'left' |
242 | | // and 'top' on an element, we force all 'transform' animations running at |
243 | | // the same time on the same element to run on the main thread. |
244 | | // |
245 | | // When returning true, |aPerformanceWarning| stores the reason why |
246 | | // we shouldn't run the transform animations. |
247 | | bool ShouldBlockAsyncTransformAnimations( |
248 | | const nsIFrame* aFrame, AnimationPerformanceWarning::Type& aPerformanceWarning) const; |
249 | | bool HasGeometricProperties() const; |
250 | | bool AffectsGeometry() const override |
251 | 0 | { |
252 | 0 | return GetTarget() && HasGeometricProperties(); |
253 | 0 | } |
254 | | |
255 | | nsIDocument* GetRenderedDocument() const; |
256 | | nsIPresShell* GetPresShell() const; |
257 | | |
258 | | // Associates a warning with the animated property on the specified frame |
259 | | // indicating why, for example, the property could not be animated on the |
260 | | // compositor. |aParams| and |aParamsLength| are optional parameters which |
261 | | // will be used to generate a localized message for devtools. |
262 | | void SetPerformanceWarning( |
263 | | nsCSSPropertyID aProperty, |
264 | | const AnimationPerformanceWarning& aWarning); |
265 | | |
266 | | // Cumulative change hint on each segment for each property. |
267 | | // This is used for deciding the animation is paint-only. |
268 | | void CalculateCumulativeChangeHint(const ComputedStyle* aStyle); |
269 | | |
270 | | // Returns true if all of animation properties' change hints |
271 | | // can ignore painting if the animation is not visible. |
272 | | // See nsChangeHint_Hints_CanIgnoreIfNotVisible in nsChangeHint.h |
273 | | // in detail which change hint can be ignored. |
274 | | bool CanIgnoreIfNotVisible() const; |
275 | | |
276 | | // Returns true if the effect is current state and has scale animation. |
277 | | // |aFrame| is used for calculation of scale values. |
278 | | bool ContainsAnimatedScale(const nsIFrame* aFrame) const; |
279 | | |
280 | | AnimationValue BaseStyle(nsCSSPropertyID aProperty) const |
281 | 0 | { |
282 | 0 | AnimationValue result; |
283 | 0 | bool hasProperty = false; |
284 | 0 | // We cannot use getters_AddRefs on RawServoAnimationValue because it is |
285 | 0 | // an incomplete type, so Get() doesn't work. Instead, use GetWeak, and |
286 | 0 | // then assign the raw pointer to a RefPtr. |
287 | 0 | result.mServo = mBaseStyleValuesForServo.GetWeak(aProperty, &hasProperty); |
288 | 0 | MOZ_ASSERT(hasProperty || result.IsNull()); |
289 | 0 | return result; |
290 | 0 | } |
291 | | |
292 | | static bool HasComputedTimingChanged( |
293 | | const ComputedTiming& aComputedTiming, |
294 | | IterationCompositeOperation aIterationComposite, |
295 | | const Nullable<double>& aProgressOnLastCompose, |
296 | | uint64_t aCurrentIterationOnLastCompose); |
297 | | |
298 | | protected: |
299 | 0 | ~KeyframeEffect() override = default; |
300 | | |
301 | | static Maybe<OwningAnimationTarget> |
302 | | ConvertTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget); |
303 | | |
304 | | template<class OptionsType> |
305 | | static already_AddRefed<KeyframeEffect> |
306 | | ConstructKeyframeEffect(const GlobalObject& aGlobal, |
307 | | const Nullable<ElementOrCSSPseudoElement>& aTarget, |
308 | | JS::Handle<JSObject*> aKeyframes, |
309 | | const OptionsType& aOptions, |
310 | | ErrorResult& aRv); |
311 | | |
312 | | // Build properties by recalculating from |mKeyframes| using |aComputedStyle| |
313 | | // to resolve specified values. This function also applies paced spacing if |
314 | | // needed. |
315 | | nsTArray<AnimationProperty> BuildProperties(const ComputedStyle* aStyle); |
316 | | |
317 | | // This effect is registered with its target element so long as: |
318 | | // |
319 | | // (a) It has a target element, and |
320 | | // (b) It is "relevant" (i.e. yet to finish but not idle, or finished but |
321 | | // filling forwards) |
322 | | // |
323 | | // As a result, we need to make sure this gets called whenever anything |
324 | | // changes with regards to this effects's timing including changes to the |
325 | | // owning Animation's timing. |
326 | | void UpdateTargetRegistration(); |
327 | | |
328 | | // Remove the current effect target from its EffectSet. |
329 | | void UnregisterTarget(); |
330 | | |
331 | | // Update the associated frame state bits so that, if necessary, a stacking |
332 | | // context will be created and the effect sent to the compositor. We |
333 | | // typically need to do this when the properties referenced by the keyframe |
334 | | // have changed, or when the target frame might have changed. |
335 | | void MaybeUpdateFrameForCompositor(); |
336 | | |
337 | | // Looks up the ComputedStyle associated with the target element, if any. |
338 | | // We need to be careful to *not* call this when we are updating the style |
339 | | // context. That's because calling GetComputedStyle when we are in the process |
340 | | // of building a ComputedStyle may trigger various forms of infinite |
341 | | // recursion. |
342 | | already_AddRefed<ComputedStyle> GetTargetComputedStyle(); |
343 | | |
344 | | // A wrapper for marking cascade update according to the current |
345 | | // target and its effectSet. |
346 | | void MarkCascadeNeedsUpdate(); |
347 | | |
348 | | void EnsureBaseStyles(const ComputedStyle* aComputedValues, |
349 | | const nsTArray<AnimationProperty>& aProperties); |
350 | | |
351 | | // Stylo version of the above function that also first checks for an additive |
352 | | // value in |aProperty|'s list of segments. |
353 | | void EnsureBaseStyle(const AnimationProperty& aProperty, |
354 | | nsPresContext* aPresContext, |
355 | | const ComputedStyle* aComputedValues, |
356 | | RefPtr<ComputedStyle>& aBaseComputedValues); |
357 | | |
358 | | Maybe<OwningAnimationTarget> mTarget; |
359 | | |
360 | | KeyframeEffectParams mEffectOptions; |
361 | | |
362 | | // The specified keyframes. |
363 | | nsTArray<Keyframe> mKeyframes; |
364 | | |
365 | | // A set of per-property value arrays, derived from |mKeyframes|. |
366 | | nsTArray<AnimationProperty> mProperties; |
367 | | |
368 | | // The computed progress last time we composed the style rule. This is |
369 | | // used to detect when the progress is not changing (e.g. due to a step |
370 | | // timing function) so we can avoid unnecessary style updates. |
371 | | Nullable<double> mProgressOnLastCompose; |
372 | | |
373 | | // The purpose of this value is the same as mProgressOnLastCompose but |
374 | | // this is used to detect when the current iteration is not changing |
375 | | // in the case when iterationComposite is accumulate. |
376 | | uint64_t mCurrentIterationOnLastCompose = 0; |
377 | | |
378 | | // We need to track when we go to or from being "in effect" since |
379 | | // we need to re-evaluate the cascade of animations when that changes. |
380 | | bool mInEffectOnLastAnimationTimingUpdate; |
381 | | |
382 | | // The non-animated values for properties in this effect that contain at |
383 | | // least one animation value that is composited with the underlying value |
384 | | // (i.e. it uses the additive or accumulate composite mode). |
385 | | nsRefPtrHashtable<nsUint32HashKey, RawServoAnimationValue> |
386 | | mBaseStyleValuesForServo; |
387 | | |
388 | | // True if this effect is in the EffectSet for its target element. This is |
389 | | // used as an optimization to avoid unnecessary hashmap lookups on the |
390 | | // EffectSet. |
391 | | bool mInEffectSet = false; |
392 | | |
393 | | private: |
394 | | nsChangeHint mCumulativeChangeHint; |
395 | | |
396 | | void ComposeStyleRule(RawServoAnimationValueMap& aAnimationValues, |
397 | | const AnimationProperty& aProperty, |
398 | | const AnimationPropertySegment& aSegment, |
399 | | const ComputedTiming& aComputedTiming); |
400 | | |
401 | | |
402 | | already_AddRefed<ComputedStyle> CreateComputedStyleForAnimationValue( |
403 | | nsCSSPropertyID aProperty, |
404 | | const AnimationValue& aValue, |
405 | | nsPresContext* aPresContext, |
406 | | const ComputedStyle* aBaseComputedStyle); |
407 | | |
408 | | // Return the primary frame for the target (pseudo-)element. |
409 | | nsIFrame* GetPrimaryFrame() const; |
410 | | // Returns the frame which is used for styling. |
411 | | nsIFrame* GetStyleFrame() const; |
412 | | |
413 | | bool CanThrottle() const; |
414 | | bool CanThrottleOverflowChanges(const nsIFrame& aFrame) const; |
415 | | bool CanThrottleOverflowChangesInScrollable(nsIFrame& aFrame) const; |
416 | | bool CanThrottleIfNotVisible(nsIFrame& aFrame) const; |
417 | | |
418 | | // Returns true if the computedTiming has changed since the last |
419 | | // composition. |
420 | | bool HasComputedTimingChanged() const; |
421 | | |
422 | | // Returns true unless Gecko limitations prevent performing transform |
423 | | // animations for |aFrame|. When returning true, the reason for the |
424 | | // limitation is stored in |aOutPerformanceWarning|. |
425 | | static bool CanAnimateTransformOnCompositor( |
426 | | const nsIFrame* aFrame, |
427 | | AnimationPerformanceWarning::Type& aPerformanceWarning); |
428 | | static bool IsGeometricProperty(const nsCSSPropertyID aProperty); |
429 | | |
430 | | static const TimeDuration OverflowRegionRefreshInterval(); |
431 | | |
432 | | void UpdateEffectSet(mozilla::EffectSet* aEffectSet = nullptr) const; |
433 | | |
434 | | // Returns true if this effect has properties that might affect the overflow |
435 | | // region. |
436 | | // This function is used for updating scroll bars or notifying intersection |
437 | | // observers reflected by the transform. |
438 | | bool HasPropertiesThatMightAffectOverflow() const |
439 | 0 | { |
440 | 0 | return mCumulativeChangeHint & (nsChangeHint_AddOrRemoveTransform | |
441 | 0 | nsChangeHint_UpdateOverflow | |
442 | 0 | nsChangeHint_UpdatePostTransformOverflow | |
443 | 0 | nsChangeHint_UpdateTransformLayer); |
444 | 0 | } |
445 | | |
446 | | // Returns true if this effect causes visibility change. |
447 | | // (i.e. 'visibility: hidden' -> 'visibility: visible' and vice versa.) |
448 | | bool HasVisibilityChange() const |
449 | 0 | { |
450 | 0 | return mCumulativeChangeHint & nsChangeHint_VisibilityChange; |
451 | 0 | } |
452 | | }; |
453 | | |
454 | | } // namespace dom |
455 | | } // namespace mozilla |
456 | | |
457 | | #endif // mozilla_dom_KeyframeEffect_h |