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