Coverage Report

Created: 2018-09-25 14:53

/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