LCOV - code coverage report
Current view: top level - dom/animation - KeyframeEffectReadOnly.h (source / functions) Hit Total Coverage
Test: output.info Lines: 23 31 74.2 %
Date: 2017-07-14 16:53:18 Functions: 9 17 52.9 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13