LCOV - code coverage report
Current view: top level - dom/animation - KeyframeEffectReadOnly.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 301 782 38.5 %
Date: 2017-07-14 16:53:18 Functions: 39 85 45.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             : #include "mozilla/dom/KeyframeEffectReadOnly.h"
       8             : 
       9             : #include "gfxPrefs.h"
      10             : #include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
      11             :   // For UnrestrictedDoubleOrKeyframeAnimationOptions;
      12             : #include "mozilla/dom/CSSPseudoElement.h"
      13             : #include "mozilla/dom/KeyframeEffectBinding.h"
      14             : #include "mozilla/AnimValuesStyleRule.h"
      15             : #include "mozilla/AnimationUtils.h"
      16             : #include "mozilla/AutoRestore.h"
      17             : #include "mozilla/EffectSet.h"
      18             : #include "mozilla/GeckoStyleContext.h"
      19             : #include "mozilla/FloatingPoint.h" // For IsFinite
      20             : #include "mozilla/LookAndFeel.h" // For LookAndFeel::GetInt
      21             : #include "mozilla/KeyframeUtils.h"
      22             : #include "mozilla/ServoBindings.h"
      23             : #include "mozilla/Telemetry.h"
      24             : #include "mozilla/TypeTraits.h"
      25             : #include "Layers.h" // For Layer
      26             : #include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetStyleContext
      27             : #include "nsCSSPropertyIDSet.h"
      28             : #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
      29             : #include "nsCSSPseudoElements.h" // For CSSPseudoElementType
      30             : #include "nsIPresShell.h"
      31             : #include "nsIScriptError.h"
      32             : #include "nsStyleContextInlines.h"
      33             : 
      34             : namespace mozilla {
      35             : 
      36             : bool
      37          16 : PropertyValuePair::operator==(const PropertyValuePair& aOther) const
      38             : {
      39          16 :   if (mProperty != aOther.mProperty || mValue != aOther.mValue) {
      40           0 :     return false;
      41             :   }
      42          16 :   if (mServoDeclarationBlock == aOther.mServoDeclarationBlock) {
      43          16 :     return true;
      44             :   }
      45           0 :   if (!mServoDeclarationBlock || !aOther.mServoDeclarationBlock) {
      46           0 :     return false;
      47             :   }
      48             :   return Servo_DeclarationBlock_Equals(mServoDeclarationBlock,
      49           0 :                                        aOther.mServoDeclarationBlock);
      50             : }
      51             : 
      52             : namespace dom {
      53             : 
      54           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
      55             :                                    AnimationEffectReadOnly,
      56             :                                    mTarget)
      57             : 
      58           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
      59             :                                                AnimationEffectReadOnly)
      60           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
      61             : 
      62           2 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly)
      63           0 : NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadOnly)
      64             : 
      65           8 : NS_IMPL_ADDREF_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
      66           6 : NS_IMPL_RELEASE_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
      67             : 
      68           2 : KeyframeEffectReadOnly::KeyframeEffectReadOnly(
      69             :   nsIDocument* aDocument,
      70             :   const Maybe<OwningAnimationTarget>& aTarget,
      71             :   const TimingParams& aTiming,
      72           2 :   const KeyframeEffectParams& aOptions)
      73             :   : KeyframeEffectReadOnly(aDocument, aTarget,
      74             :                            new AnimationEffectTimingReadOnly(aDocument,
      75           2 :                                                              aTiming),
      76           2 :                            aOptions)
      77             : {
      78           2 : }
      79             : 
      80           2 : KeyframeEffectReadOnly::KeyframeEffectReadOnly(
      81             :   nsIDocument* aDocument,
      82             :   const Maybe<OwningAnimationTarget>& aTarget,
      83             :   AnimationEffectTimingReadOnly* aTiming,
      84           2 :   const KeyframeEffectParams& aOptions)
      85             :   : AnimationEffectReadOnly(aDocument, aTiming)
      86             :   , mTarget(aTarget)
      87             :   , mEffectOptions(aOptions)
      88             :   , mInEffectOnLastAnimationTimingUpdate(false)
      89           2 :   , mCumulativeChangeHint(nsChangeHint(0))
      90             : {
      91           2 : }
      92             : 
      93             : JSObject*
      94           0 : KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
      95             :                                    JS::Handle<JSObject*> aGivenProto)
      96             : {
      97           0 :   return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
      98             : }
      99             : 
     100             : IterationCompositeOperation
     101           0 : KeyframeEffectReadOnly::IterationComposite() const
     102             : {
     103           0 :   return mEffectOptions.mIterationComposite;
     104             : }
     105             : 
     106             : CompositeOperation
     107           0 : KeyframeEffectReadOnly::Composite() const
     108             : {
     109           0 :   return mEffectOptions.mComposite;
     110             : }
     111             : 
     112             : void
     113          32 : KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
     114             : {
     115          32 :   UpdateTargetRegistration();
     116             : 
     117             :   // If the effect is not relevant it will be removed from the target
     118             :   // element's effect set. However, effects not in the effect set
     119             :   // will not be included in the set of candidate effects for running on
     120             :   // the compositor and hence they won't have their compositor status
     121             :   // updated. As a result, we need to make sure we clear their compositor
     122             :   // status here.
     123          32 :   bool isRelevant = mAnimation && mAnimation->IsRelevant();
     124          32 :   if (!isRelevant) {
     125           6 :     ResetIsRunningOnCompositor();
     126             :   }
     127             : 
     128             :   // Detect changes to "in effect" status since we need to recalculate the
     129             :   // animation cascade for this element whenever that changes.
     130          32 :   bool inEffect = IsInEffect();
     131          32 :   if (inEffect != mInEffectOnLastAnimationTimingUpdate) {
     132           4 :     MarkCascadeNeedsUpdate();
     133           4 :     mInEffectOnLastAnimationTimingUpdate = inEffect;
     134             :   }
     135             : 
     136             :   // Request restyle if necessary.
     137          32 :   if (mAnimation && !mProperties.IsEmpty() && HasComputedTimingChanged()) {
     138             :     EffectCompositor::RestyleType restyleType =
     139           6 :       CanThrottle() ?
     140             :       EffectCompositor::RestyleType::Throttled :
     141           6 :       EffectCompositor::RestyleType::Standard;
     142           6 :     RequestRestyle(restyleType);
     143             :   }
     144             : 
     145             :   // If we're no longer "in effect", our ComposeStyle method will never be
     146             :   // called and we will never have a chance to update mProgressOnLastCompose
     147             :   // and mCurrentIterationOnLastCompose.
     148             :   // We clear them here to ensure that if we later become "in effect" we will
     149             :   // request a restyle (above).
     150          32 :   if (!inEffect) {
     151           6 :      mProgressOnLastCompose.SetNull();
     152           6 :      mCurrentIterationOnLastCompose = 0;
     153             :   }
     154          32 : }
     155             : 
     156             : static bool
     157           2 : KeyframesEqualIgnoringComputedOffsets(const nsTArray<Keyframe>& aLhs,
     158             :                                       const nsTArray<Keyframe>& aRhs)
     159             : {
     160           2 :   if (aLhs.Length() != aRhs.Length()) {
     161           2 :     return false;
     162             :   }
     163             : 
     164           0 :   for (size_t i = 0, len = aLhs.Length(); i < len; ++i) {
     165           0 :     const Keyframe& a = aLhs[i];
     166           0 :     const Keyframe& b = aRhs[i];
     167           0 :     if (a.mOffset != b.mOffset ||
     168           0 :         a.mTimingFunction != b.mTimingFunction ||
     169           0 :         a.mPropertyValues != b.mPropertyValues) {
     170           0 :       return false;
     171             :     }
     172             :   }
     173           0 :   return true;
     174             : }
     175             : 
     176             : // https://w3c.github.io/web-animations/#dom-keyframeeffect-setkeyframes
     177             : void
     178           0 : KeyframeEffectReadOnly::SetKeyframes(JSContext* aContext,
     179             :                                      JS::Handle<JSObject*> aKeyframes,
     180             :                                      ErrorResult& aRv)
     181             : {
     182             :   nsTArray<Keyframe> keyframes =
     183           0 :     KeyframeUtils::GetKeyframesFromObject(aContext, mDocument, aKeyframes, aRv);
     184           0 :   if (aRv.Failed()) {
     185           0 :     return;
     186             :   }
     187             : 
     188           0 :   RefPtr<nsStyleContext> styleContext = GetTargetStyleContext();
     189           0 :   SetKeyframes(Move(keyframes), styleContext);
     190             : }
     191             : 
     192             : void
     193           2 : KeyframeEffectReadOnly::SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
     194             :                                      nsStyleContext* aStyleContext)
     195             : {
     196           2 :   DoSetKeyframes(Move(aKeyframes), Move(aStyleContext));
     197           2 : }
     198             : 
     199             : void
     200           0 : KeyframeEffectReadOnly::SetKeyframes(
     201             :   nsTArray<Keyframe>&& aKeyframes,
     202             :   const ServoComputedValues* aComputedValues)
     203             : {
     204           0 :   DoSetKeyframes(Move(aKeyframes), aComputedValues);
     205           0 : }
     206             : 
     207             : template<typename StyleType>
     208             : void
     209           2 : KeyframeEffectReadOnly::DoSetKeyframes(nsTArray<Keyframe>&& aKeyframes,
     210             :                                        StyleType* aStyle)
     211             : {
     212             :   static_assert(IsSame<StyleType, nsStyleContext>::value ||
     213             :                 IsSame<StyleType, const ServoComputedValues>::value,
     214             :                 "StyleType should be nsStyleContext* or "
     215             :                 "const ServoComputedValues*");
     216             : 
     217           2 :   if (KeyframesEqualIgnoringComputedOffsets(aKeyframes, mKeyframes)) {
     218           0 :     return;
     219             :   }
     220             : 
     221           2 :   mKeyframes = Move(aKeyframes);
     222           2 :   KeyframeUtils::DistributeKeyframes(mKeyframes);
     223             : 
     224           2 :   if (mAnimation && mAnimation->IsRelevant()) {
     225           0 :     nsNodeUtils::AnimationChanged(mAnimation);
     226             :   }
     227             : 
     228             :   // We need to call UpdateProperties() if the StyleType is not nullptr.
     229           2 :   if (aStyle) {
     230           2 :     UpdateProperties(aStyle);
     231           2 :     MaybeUpdateFrameForCompositor();
     232             :   }
     233             : }
     234             : 
     235             : const AnimationProperty*
     236          22 : KeyframeEffectReadOnly::GetEffectiveAnimationOfProperty(
     237             :   nsCSSPropertyID aProperty) const
     238             : {
     239             :   EffectSet* effectSet =
     240          22 :     EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
     241          26 :   for (size_t propIdx = 0, propEnd = mProperties.Length();
     242          26 :        propIdx != propEnd; ++propIdx) {
     243          22 :     if (aProperty == mProperties[propIdx].mProperty) {
     244          18 :       const AnimationProperty* result = &mProperties[propIdx];
     245             :       // Skip if there is a property of animation level that is overridden
     246             :       // by !important rules.
     247          36 :       if (effectSet &&
     248          18 :           effectSet->PropertiesWithImportantRules()
     249          36 :             .HasProperty(result->mProperty) &&
     250           0 :           effectSet->PropertiesForAnimationsLevel()
     251           0 :             .HasProperty(result->mProperty)) {
     252           0 :         result = nullptr;
     253             :       }
     254          18 :       return result;
     255             :     }
     256             :   }
     257           4 :   return nullptr;
     258             : }
     259             : 
     260             : bool
     261           4 : KeyframeEffectReadOnly::HasAnimationOfProperty(nsCSSPropertyID aProperty) const
     262             : {
     263           6 :   for (const AnimationProperty& property : mProperties) {
     264           4 :     if (property.mProperty == aProperty) {
     265           2 :       return true;
     266             :     }
     267             :   }
     268           2 :   return false;
     269             : }
     270             : 
     271             : #ifdef DEBUG
     272             : bool
     273           8 : SpecifiedKeyframeArraysAreEqual(const nsTArray<Keyframe>& aA,
     274             :                                 const nsTArray<Keyframe>& aB)
     275             : {
     276           8 :   if (aA.Length() != aB.Length()) {
     277           0 :     return false;
     278             :   }
     279             : 
     280          24 :   for (size_t i = 0; i < aA.Length(); i++) {
     281          16 :     const Keyframe& a = aA[i];
     282          16 :     const Keyframe& b = aB[i];
     283          48 :     if (a.mOffset         != b.mOffset ||
     284          32 :         a.mTimingFunction != b.mTimingFunction ||
     285          16 :         a.mPropertyValues != b.mPropertyValues) {
     286           0 :       return false;
     287             :     }
     288             :   }
     289             : 
     290           8 :   return true;
     291             : }
     292             : #endif
     293             : 
     294             : void
     295           8 : KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
     296             : {
     297           8 :   MOZ_ASSERT(aStyleContext);
     298             : 
     299           8 :   if (!mDocument->IsStyledByServo()) {
     300           8 :     DoUpdateProperties(Move(aStyleContext));
     301           8 :     return;
     302             :   }
     303             : 
     304           0 :   const ServoComputedValues* currentStyle = aStyleContext->ComputedValues();
     305             : 
     306           0 :   DoUpdateProperties(currentStyle);
     307             : }
     308             : 
     309             : void
     310           0 : KeyframeEffectReadOnly::UpdateProperties(
     311             :   const ServoComputedValues* aComputedValues)
     312             : {
     313           0 :   DoUpdateProperties(aComputedValues);
     314           0 : }
     315             : 
     316             : template<typename StyleType>
     317             : void
     318           8 : KeyframeEffectReadOnly::DoUpdateProperties(StyleType* aStyle)
     319             : {
     320           8 :   MOZ_ASSERT(aStyle);
     321             : 
     322             :   // Skip updating properties when we are composing style.
     323             :   // FIXME: Bug 1324966. Drop this check once we have a function to get
     324             :   // nsStyleContext without resolving animating style.
     325           8 :   MOZ_DIAGNOSTIC_ASSERT(!mIsComposingStyle,
     326             :                         "Should not be called while processing ComposeStyle()");
     327           8 :   if (mIsComposingStyle) {
     328           6 :     return;
     329             :   }
     330             : 
     331          10 :   nsTArray<AnimationProperty> properties = BuildProperties(aStyle);
     332             : 
     333             :   // We need to update base styles even if any properties are not changed at all
     334             :   // since base styles might have been changed due to parent style changes, etc.
     335           8 :   EnsureBaseStyles(aStyle, properties);
     336             : 
     337           8 :   if (mProperties == properties) {
     338           6 :     return;
     339             :   }
     340             : 
     341             :   // Preserve the state of the mIsRunningOnCompositor flag.
     342           2 :   nsCSSPropertyIDSet runningOnCompositorProperties;
     343             : 
     344           2 :   for (const AnimationProperty& property : mProperties) {
     345           0 :     if (property.mIsRunningOnCompositor) {
     346           0 :       runningOnCompositorProperties.AddProperty(property.mProperty);
     347             :     }
     348             :   }
     349             : 
     350           2 :   mProperties = Move(properties);
     351           2 :   UpdateEffectSet();
     352             : 
     353           4 :   for (AnimationProperty& property : mProperties) {
     354           2 :     property.mIsRunningOnCompositor =
     355           2 :       runningOnCompositorProperties.HasProperty(property.mProperty);
     356             :   }
     357             : 
     358           2 :   CalculateCumulativeChangeHint(aStyle);
     359             : 
     360           2 :   MarkCascadeNeedsUpdate();
     361             : 
     362           2 :   RequestRestyle(EffectCompositor::RestyleType::Layer);
     363             : }
     364             : 
     365             : /* static */ StyleAnimationValue
     366           8 : KeyframeEffectReadOnly::CompositeValue(
     367             :   nsCSSPropertyID aProperty,
     368             :   const StyleAnimationValue& aValueToComposite,
     369             :   const StyleAnimationValue& aUnderlyingValue,
     370             :   CompositeOperation aCompositeOperation)
     371             : {
     372             :   // Just return the underlying value if |aValueToComposite| is null
     373             :   // (i.e. missing keyframe).
     374           8 :   if (aValueToComposite.IsNull()) {
     375           0 :     return aUnderlyingValue;
     376             :   }
     377             : 
     378           8 :   switch (aCompositeOperation) {
     379             :     case dom::CompositeOperation::Replace:
     380           8 :       return aValueToComposite;
     381             :     case dom::CompositeOperation::Add: {
     382           0 :       StyleAnimationValue result(aValueToComposite);
     383             :       return StyleAnimationValue::Add(aProperty,
     384             :                                       aUnderlyingValue,
     385           0 :                                       Move(result));
     386             :     }
     387             :     case dom::CompositeOperation::Accumulate: {
     388           0 :       StyleAnimationValue result(aValueToComposite);
     389             :       return StyleAnimationValue::Accumulate(aProperty,
     390             :                                              aUnderlyingValue,
     391           0 :                                              Move(result));
     392             :     }
     393             :     default:
     394           0 :       MOZ_ASSERT_UNREACHABLE("Unknown compisite operation type");
     395             :       break;
     396             :   }
     397             :   return StyleAnimationValue();
     398             : }
     399             : 
     400             : StyleAnimationValue
     401           8 : KeyframeEffectReadOnly::GetUnderlyingStyle(
     402             :   nsCSSPropertyID aProperty,
     403             :   const RefPtr<AnimValuesStyleRule>& aAnimationRule)
     404             : {
     405           8 :   StyleAnimationValue result;
     406             : 
     407           8 :   if (aAnimationRule && aAnimationRule->HasValue(aProperty)) {
     408             :     // If we have already composed style for the property, we use the style
     409             :     // as the underlying style.
     410           0 :     DebugOnly<bool> success = aAnimationRule->GetValue(aProperty, result);
     411           0 :     MOZ_ASSERT(success, "AnimValuesStyleRule::GetValue should not fail");
     412             :   } else {
     413             :     // If we are composing with composite operation that is not 'replace'
     414             :     // and we have not composed style for the property yet, we have to get
     415             :     // the base style for the property.
     416           8 :     result = BaseStyle(aProperty).mGecko;
     417             :   }
     418             : 
     419           8 :   return result;
     420             : }
     421             : 
     422             : StyleAnimationValue
     423           8 : KeyframeEffectReadOnly::CompositeValue(
     424             :   nsCSSPropertyID aProperty,
     425             :   const RefPtr<AnimValuesStyleRule>& aAnimationRule,
     426             :   const StyleAnimationValue& aValueToComposite,
     427             :   CompositeOperation aCompositeOperation)
     428             : {
     429           8 :   MOZ_ASSERT(mTarget, "CompositeValue should be called with target element");
     430           8 :   MOZ_ASSERT(!mDocument->IsStyledByServo());
     431             : 
     432             :   StyleAnimationValue underlyingValue =
     433          16 :     GetUnderlyingStyle(aProperty, aAnimationRule);
     434             : 
     435             :   return CompositeValue(aProperty,
     436             :                         aValueToComposite,
     437             :                         underlyingValue,
     438          16 :                         aCompositeOperation);
     439             : }
     440             : 
     441             : void
     442           8 : KeyframeEffectReadOnly::EnsureBaseStyles(
     443             :   nsStyleContext* aStyleContext,
     444             :   const nsTArray<AnimationProperty>& aProperties)
     445             : {
     446           8 :   if (!mTarget) {
     447           0 :     return;
     448             :   }
     449             : 
     450           8 :   mBaseStyleValues.Clear();
     451             : 
     452          16 :   RefPtr<nsStyleContext> cachedBaseStyleContext;
     453             : 
     454          16 :   for (const AnimationProperty& property : aProperties) {
     455          16 :     for (const AnimationPropertySegment& segment : property.mSegments) {
     456           8 :       if (segment.HasReplaceableValues()) {
     457           8 :         continue;
     458             :       }
     459             : 
     460           0 :       EnsureBaseStyle(property.mProperty,
     461             :                       aStyleContext,
     462           0 :                       cachedBaseStyleContext);
     463           0 :       break;
     464             :     }
     465             :   }
     466             : }
     467             : 
     468             : void
     469           0 : KeyframeEffectReadOnly::EnsureBaseStyle(
     470             :   nsCSSPropertyID aProperty,
     471             :   nsStyleContext* aStyleContext,
     472             :   RefPtr<nsStyleContext>& aCachedBaseStyleContext)
     473             : {
     474           0 :   if (mBaseStyleValues.Contains(aProperty)) {
     475           0 :     return;
     476             :   }
     477             : 
     478           0 :   if (!aCachedBaseStyleContext) {
     479             :     aCachedBaseStyleContext =
     480           0 :       aStyleContext->PresContext()->StyleSet()->AsGecko()->
     481           0 :         ResolveStyleByRemovingAnimation(mTarget->mElement,
     482             :                                         aStyleContext,
     483           0 :                                         eRestyle_AllHintsWithAnimations);
     484             :   }
     485             : 
     486           0 :   StyleAnimationValue result;
     487             :   DebugOnly<bool> success =
     488           0 :     StyleAnimationValue::ExtractComputedValue(aProperty,
     489             :                                               aCachedBaseStyleContext,
     490           0 :                                               result);
     491             : 
     492           0 :   MOZ_ASSERT(success, "Should be able to extract computed animation value");
     493           0 :   MOZ_ASSERT(!result.IsNull(), "Should have a valid StyleAnimationValue");
     494             : 
     495           0 :   mBaseStyleValues.Put(aProperty, result);
     496             : }
     497             : 
     498             : void
     499           0 : KeyframeEffectReadOnly::EnsureBaseStyles(
     500             :   const ServoComputedValues* aComputedValues,
     501             :   const nsTArray<AnimationProperty>& aProperties)
     502             : {
     503           0 :   if (!mTarget) {
     504           0 :     return;
     505             :   }
     506             : 
     507           0 :   mBaseStyleValuesForServo.Clear();
     508             : 
     509             :   nsPresContext* presContext =
     510           0 :     nsContentUtils::GetContextForContent(mTarget->mElement);
     511           0 :   MOZ_ASSERT(presContext,
     512             :              "nsPresContext should not be nullptr since this EnsureBaseStyles "
     513             :              "supposed to be called right after getting computed values with "
     514             :              "a valid nsPresContext");
     515             : 
     516           0 :   RefPtr<const ServoComputedValues> baseComputedValues;
     517           0 :   for (const AnimationProperty& property : aProperties) {
     518           0 :     EnsureBaseStyle(property,
     519           0 :                     mTarget->mPseudoType,
     520             :                     presContext,
     521             :                     aComputedValues,
     522           0 :                     baseComputedValues);
     523             :   }
     524             : }
     525             : 
     526             : void
     527           0 : KeyframeEffectReadOnly::EnsureBaseStyle(
     528             :   const AnimationProperty& aProperty,
     529             :   CSSPseudoElementType aPseudoType,
     530             :   nsPresContext* aPresContext,
     531             :   const ServoComputedValues* aComputedStyle,
     532             :   RefPtr<const ServoComputedValues>& aBaseComputedValues)
     533             : {
     534           0 :   bool hasAdditiveValues = false;
     535             : 
     536           0 :   for (const AnimationPropertySegment& segment : aProperty.mSegments) {
     537           0 :     if (!segment.HasReplaceableValues()) {
     538           0 :       hasAdditiveValues = true;
     539           0 :       break;
     540             :     }
     541             :   }
     542             : 
     543           0 :   if (!hasAdditiveValues) {
     544           0 :     return;
     545             :   }
     546             : 
     547           0 :   if (!aBaseComputedValues) {
     548             :     aBaseComputedValues =
     549           0 :       aPresContext->StyleSet()->AsServo()->GetBaseComputedValuesForElement(
     550           0 :           mTarget->mElement,
     551             :           aPseudoType,
     552           0 :           aComputedStyle);
     553             :   }
     554             :   RefPtr<RawServoAnimationValue> baseValue =
     555           0 :     Servo_ComputedValues_ExtractAnimationValue(aBaseComputedValues,
     556           0 :                                                aProperty.mProperty).Consume();
     557           0 :   mBaseStyleValuesForServo.Put(aProperty.mProperty, baseValue);
     558             : }
     559             : 
     560             : void
     561           4 : KeyframeEffectReadOnly::WillComposeStyle()
     562             : {
     563           8 :   ComputedTiming computedTiming = GetComputedTiming();
     564           4 :   mProgressOnLastCompose = computedTiming.mProgress;
     565           4 :   mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
     566           4 : }
     567             : 
     568             : void
     569           4 : KeyframeEffectReadOnly::ComposeStyleRule(
     570             :   RefPtr<AnimValuesStyleRule>& aStyleRule,
     571             :   const AnimationProperty& aProperty,
     572             :   const AnimationPropertySegment& aSegment,
     573             :   const ComputedTiming& aComputedTiming)
     574             : {
     575             :   StyleAnimationValue fromValue =
     576           4 :     CompositeValue(aProperty.mProperty, aStyleRule,
     577             :                    aSegment.mFromValue.mGecko,
     578          12 :                    aSegment.mFromComposite);
     579             :   StyleAnimationValue toValue =
     580           4 :     CompositeValue(aProperty.mProperty, aStyleRule,
     581             :                    aSegment.mToValue.mGecko,
     582          12 :                    aSegment.mToComposite);
     583           4 :   if (fromValue.IsNull() || toValue.IsNull()) {
     584           0 :     return;
     585             :   }
     586             : 
     587           4 :   if (!aStyleRule) {
     588             :     // Allocate the style rule now that we know we have animation data.
     589           4 :     aStyleRule = new AnimValuesStyleRule();
     590             :   }
     591             : 
     592             :   // Iteration composition for accumulate
     593           4 :   if (mEffectOptions.mIterationComposite ==
     594           0 :       IterationCompositeOperation::Accumulate &&
     595           0 :       aComputedTiming.mCurrentIteration > 0) {
     596             :     const AnimationPropertySegment& lastSegment =
     597           0 :       aProperty.mSegments.LastElement();
     598             :     // FIXME: Bug 1293492: Add a utility function to calculate both of
     599             :     // below StyleAnimationValues.
     600           0 :     StyleAnimationValue lastValue = lastSegment.mToValue.mGecko.IsNull()
     601           0 :       ? GetUnderlyingStyle(aProperty.mProperty, aStyleRule)
     602           0 :       : lastSegment.mToValue.mGecko;
     603             :     fromValue =
     604           0 :       StyleAnimationValue::Accumulate(aProperty.mProperty,
     605             :                                       lastValue,
     606           0 :                                       Move(fromValue),
     607           0 :                                       aComputedTiming.mCurrentIteration);
     608             :     toValue =
     609           0 :       StyleAnimationValue::Accumulate(aProperty.mProperty,
     610             :                                       lastValue,
     611           0 :                                       Move(toValue),
     612           0 :                                       aComputedTiming.mCurrentIteration);
     613             :   }
     614             : 
     615             :   // Special handling for zero-length segments
     616           4 :   if (aSegment.mToKey == aSegment.mFromKey) {
     617           0 :     if (aComputedTiming.mProgress.Value() < 0) {
     618           0 :       aStyleRule->AddValue(aProperty.mProperty, Move(fromValue));
     619             :     } else {
     620           0 :       aStyleRule->AddValue(aProperty.mProperty, Move(toValue));
     621             :     }
     622           0 :     return;
     623             :   }
     624             : 
     625             :   double positionInSegment =
     626           4 :     (aComputedTiming.mProgress.Value() - aSegment.mFromKey) /
     627           4 :     (aSegment.mToKey - aSegment.mFromKey);
     628             :   double valuePosition =
     629           4 :     ComputedTimingFunction::GetPortion(aSegment.mTimingFunction,
     630             :                                        positionInSegment,
     631           8 :                                        aComputedTiming.mBeforeFlag);
     632             : 
     633           4 :   MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
     634             : 
     635           8 :   StyleAnimationValue val;
     636           4 :   if (StyleAnimationValue::Interpolate(aProperty.mProperty,
     637             :                                        fromValue,
     638             :                                        toValue,
     639             :                                        valuePosition, val)) {
     640           4 :     aStyleRule->AddValue(aProperty.mProperty, Move(val));
     641           0 :   } else if (valuePosition < 0.5) {
     642           0 :     aStyleRule->AddValue(aProperty.mProperty, Move(fromValue));
     643             :   } else {
     644           0 :     aStyleRule->AddValue(aProperty.mProperty, Move(toValue));
     645             :   }
     646             : }
     647             : 
     648             : void
     649           0 : KeyframeEffectReadOnly::ComposeStyleRule(
     650             :   RawServoAnimationValueMap& aAnimationValues,
     651             :   const AnimationProperty& aProperty,
     652             :   const AnimationPropertySegment& aSegment,
     653             :   const ComputedTiming& aComputedTiming)
     654             : {
     655           0 :   Servo_AnimationCompose(&aAnimationValues,
     656           0 :                          &mBaseStyleValuesForServo,
     657           0 :                          aProperty.mProperty,
     658             :                          &aSegment,
     659           0 :                          &aProperty.mSegments.LastElement(),
     660             :                          &aComputedTiming,
     661           0 :                          mEffectOptions.mIterationComposite);
     662           0 : }
     663             : 
     664             : template<typename ComposeAnimationResult>
     665             : void
     666           4 : KeyframeEffectReadOnly::ComposeStyle(
     667             :   ComposeAnimationResult&& aComposeResult,
     668             :   const nsCSSPropertyIDSet& aPropertiesToSkip)
     669             : {
     670           4 :   MOZ_DIAGNOSTIC_ASSERT(!mIsComposingStyle,
     671             :                         "Should not be called recursively");
     672           4 :   if (mIsComposingStyle) {
     673           0 :     return;
     674             :   }
     675             : 
     676           8 :   AutoRestore<bool> isComposingStyle(mIsComposingStyle);
     677           4 :   mIsComposingStyle = true;
     678             : 
     679           8 :   ComputedTiming computedTiming = GetComputedTiming();
     680             : 
     681             :   // If the progress is null, we don't have fill data for the current
     682             :   // time so we shouldn't animate.
     683           4 :   if (computedTiming.mProgress.IsNull()) {
     684           0 :     return;
     685             :   }
     686             : 
     687           8 :   for (size_t propIdx = 0, propEnd = mProperties.Length();
     688           8 :        propIdx != propEnd; ++propIdx)
     689             :   {
     690           4 :     const AnimationProperty& prop = mProperties[propIdx];
     691             : 
     692           4 :     MOZ_ASSERT(prop.mSegments[0].mFromKey == 0.0, "incorrect first from key");
     693           4 :     MOZ_ASSERT(prop.mSegments[prop.mSegments.Length() - 1].mToKey == 1.0,
     694             :                "incorrect last to key");
     695             : 
     696           4 :     if (aPropertiesToSkip.HasProperty(prop.mProperty)) {
     697           0 :       continue;
     698             :     }
     699             : 
     700           4 :     MOZ_ASSERT(prop.mSegments.Length() > 0,
     701             :                "property should not be in animations if it has no segments");
     702             : 
     703             :     // FIXME: Maybe cache the current segment?
     704           4 :     const AnimationPropertySegment *segment = prop.mSegments.Elements(),
     705           4 :                                 *segmentEnd = segment + prop.mSegments.Length();
     706           4 :     while (segment->mToKey <= computedTiming.mProgress.Value()) {
     707           0 :       MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys");
     708           0 :       if ((segment+1) == segmentEnd) {
     709           0 :         break;
     710             :       }
     711           0 :       ++segment;
     712           0 :       MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys");
     713             :     }
     714           4 :     MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys");
     715           4 :     MOZ_ASSERT(segment >= prop.mSegments.Elements() &&
     716             :                size_t(segment - prop.mSegments.Elements()) <
     717             :                  prop.mSegments.Length(),
     718             :                "out of array bounds");
     719             : 
     720           4 :     ComposeStyleRule(Forward<ComposeAnimationResult>(aComposeResult),
     721             :                      prop,
     722             :                      *segment,
     723             :                      computedTiming);
     724             :   }
     725             : }
     726             : 
     727             : bool
     728           0 : KeyframeEffectReadOnly::IsRunningOnCompositor() const
     729             : {
     730             :   // We consider animation is running on compositor if there is at least
     731             :   // one property running on compositor.
     732             :   // Animation.IsRunningOnCompotitor will return more fine grained
     733             :   // information in bug 1196114.
     734           0 :   for (const AnimationProperty& property : mProperties) {
     735           0 :     if (property.mIsRunningOnCompositor) {
     736           0 :       return true;
     737             :     }
     738             :   }
     739           0 :   return false;
     740             : }
     741             : 
     742             : void
     743           0 : KeyframeEffectReadOnly::SetIsRunningOnCompositor(nsCSSPropertyID aProperty,
     744             :                                                  bool aIsRunning)
     745             : {
     746           0 :   MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
     747             :                                       CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
     748             :              "Property being animated on compositor is a recognized "
     749             :              "compositor-animatable property");
     750           0 :   for (AnimationProperty& property : mProperties) {
     751           0 :     if (property.mProperty == aProperty) {
     752           0 :       property.mIsRunningOnCompositor = aIsRunning;
     753             :       // We currently only set a performance warning message when animations
     754             :       // cannot be run on the compositor, so if this animation is running
     755             :       // on the compositor we don't need a message.
     756           0 :       if (aIsRunning) {
     757           0 :         property.mPerformanceWarning.reset();
     758             :       }
     759           0 :       return;
     760             :     }
     761             :   }
     762             : }
     763             : 
     764             : void
     765           6 : KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
     766             : {
     767          12 :   for (AnimationProperty& property : mProperties) {
     768           6 :     property.mIsRunningOnCompositor = false;
     769             :   }
     770           6 : }
     771             : 
     772             : static const KeyframeEffectOptions&
     773           0 : KeyframeEffectOptionsFromUnion(
     774             :   const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
     775             : {
     776           0 :   MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
     777           0 :   return aOptions.GetAsKeyframeEffectOptions();
     778             : }
     779             : 
     780             : static const KeyframeEffectOptions&
     781           0 : KeyframeEffectOptionsFromUnion(
     782             :   const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions)
     783             : {
     784           0 :   MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
     785           0 :   return aOptions.GetAsKeyframeAnimationOptions();
     786             : }
     787             : 
     788             : template <class OptionsType>
     789             : static KeyframeEffectParams
     790           0 : KeyframeEffectParamsFromUnion(const OptionsType& aOptions,
     791             :                               CallerType aCallerType)
     792             : {
     793           0 :   KeyframeEffectParams result;
     794           0 :   if (aOptions.IsUnrestrictedDouble() ||
     795             :       // Ignore iterationComposite if the Web Animations API is not enabled,
     796             :       // then the default value 'Replace' will be used.
     797           0 :       !AnimationUtils::IsCoreAPIEnabledForCaller(aCallerType)) {
     798           0 :     return result;
     799             :   }
     800             : 
     801             :   const KeyframeEffectOptions& options =
     802           0 :     KeyframeEffectOptionsFromUnion(aOptions);
     803           0 :   result.mIterationComposite = options.mIterationComposite;
     804           0 :   result.mComposite = options.mComposite;
     805           0 :   return result;
     806             : }
     807             : 
     808             : /* static */ Maybe<OwningAnimationTarget>
     809           0 : KeyframeEffectReadOnly::ConvertTarget(
     810             :   const Nullable<ElementOrCSSPseudoElement>& aTarget)
     811             : {
     812             :   // Return value optimization.
     813           0 :   Maybe<OwningAnimationTarget> result;
     814             : 
     815           0 :   if (aTarget.IsNull()) {
     816           0 :     return result;
     817             :   }
     818             : 
     819           0 :   const ElementOrCSSPseudoElement& target = aTarget.Value();
     820           0 :   MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
     821             :              "Uninitialized target");
     822             : 
     823           0 :   if (target.IsElement()) {
     824           0 :     result.emplace(&target.GetAsElement());
     825             :   } else {
     826           0 :     RefPtr<Element> elem = target.GetAsCSSPseudoElement().ParentElement();
     827           0 :     result.emplace(elem, target.GetAsCSSPseudoElement().GetType());
     828             :   }
     829           0 :   return result;
     830             : }
     831             : 
     832             : template <class KeyframeEffectType, class OptionsType>
     833             : /* static */ already_AddRefed<KeyframeEffectType>
     834           0 : KeyframeEffectReadOnly::ConstructKeyframeEffect(
     835             :     const GlobalObject& aGlobal,
     836             :     const Nullable<ElementOrCSSPseudoElement>& aTarget,
     837             :     JS::Handle<JSObject*> aKeyframes,
     838             :     const OptionsType& aOptions,
     839             :     ErrorResult& aRv)
     840             : {
     841           0 :   nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
     842           0 :   if (!doc) {
     843           0 :     aRv.Throw(NS_ERROR_FAILURE);
     844           0 :     return nullptr;
     845             :   }
     846             : 
     847             :   TimingParams timingParams =
     848           0 :     TimingParams::FromOptionsUnion(aOptions, doc, aRv);
     849           0 :   if (aRv.Failed()) {
     850           0 :     return nullptr;
     851             :   }
     852             : 
     853             :   KeyframeEffectParams effectOptions =
     854           0 :     KeyframeEffectParamsFromUnion(aOptions, aGlobal.CallerType());
     855             : 
     856           0 :   Maybe<OwningAnimationTarget> target = ConvertTarget(aTarget);
     857             :   RefPtr<KeyframeEffectType> effect =
     858           0 :     new KeyframeEffectType(doc, target, timingParams, effectOptions);
     859             : 
     860           0 :   effect->SetKeyframes(aGlobal.Context(), aKeyframes, aRv);
     861           0 :   if (aRv.Failed()) {
     862           0 :     return nullptr;
     863             :   }
     864             : 
     865           0 :   return effect.forget();
     866             : }
     867             : 
     868             : template<class KeyframeEffectType>
     869             : /* static */ already_AddRefed<KeyframeEffectType>
     870           0 : KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal,
     871             :                                                 KeyframeEffectReadOnly& aSource,
     872             :                                                 ErrorResult& aRv)
     873             : {
     874           0 :   nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
     875           0 :   if (!doc) {
     876           0 :     aRv.Throw(NS_ERROR_FAILURE);
     877           0 :     return nullptr;
     878             :   }
     879             : 
     880             :   // Create a new KeyframeEffectReadOnly object with aSource's target,
     881             :   // iteration composite operation, composite operation, and spacing mode.
     882             :   // The constructor creates a new AnimationEffect(ReadOnly) object by
     883             :   // aSource's TimingParams.
     884             :   // Note: we don't need to re-throw exceptions since the value specified on
     885             :   //       aSource's timing object can be assumed valid.
     886             :   RefPtr<KeyframeEffectType> effect =
     887           0 :     new KeyframeEffectType(doc,
     888             :                            aSource.mTarget,
     889           0 :                            aSource.SpecifiedTiming(),
     890           0 :                            aSource.mEffectOptions);
     891             :   // Copy cumulative change hint. mCumulativeChangeHint should be the same as
     892             :   // the source one because both of targets are the same.
     893           0 :   effect->mCumulativeChangeHint = aSource.mCumulativeChangeHint;
     894             : 
     895             :   // Copy aSource's keyframes and animation properties.
     896             :   // Note: We don't call SetKeyframes directly, which might revise the
     897             :   //       computed offsets and rebuild the animation properties.
     898             :   // FIXME: Bug 1314537: We have to make sure SharedKeyframeList is handled
     899             :   //        properly.
     900           0 :   effect->mKeyframes = aSource.mKeyframes;
     901           0 :   effect->mProperties = aSource.mProperties;
     902           0 :   return effect.forget();
     903             : }
     904             : 
     905             : template<typename StyleType>
     906             : nsTArray<AnimationProperty>
     907           8 : KeyframeEffectReadOnly::BuildProperties(StyleType* aStyle)
     908             : {
     909             :   static_assert(IsSame<StyleType, nsStyleContext>::value ||
     910             :                 IsSame<StyleType, const ServoComputedValues>::value,
     911             :                 "StyleType should be nsStyleContext* or "
     912             :                 "const ServoComputedValues*");
     913             : 
     914           8 :   MOZ_ASSERT(aStyle);
     915             : 
     916           8 :   nsTArray<AnimationProperty> result;
     917             :   // If mTarget is null, return an empty property array.
     918           8 :   if (!mTarget) {
     919           0 :     return result;
     920             :   }
     921             : 
     922             :   // When GetComputedKeyframeValues or GetAnimationPropertiesFromKeyframes
     923             :   // calculate computed values from |mKeyframes|, they could possibly
     924             :   // trigger a subsequent restyle in which we rebuild animations. If that
     925             :   // happens we could find that |mKeyframes| is overwritten while it is
     926             :   // being iterated over. Normally that shouldn't happen but just in case we
     927             :   // make a copy of |mKeyframes| first and iterate over that instead.
     928          16 :   auto keyframesCopy(mKeyframes);
     929             : 
     930          16 :   result =
     931             :     KeyframeUtils::GetAnimationPropertiesFromKeyframes(
     932             :       keyframesCopy,
     933           8 :       mTarget->mElement,
     934             :       aStyle,
     935             :       mEffectOptions.mComposite);
     936             : 
     937             : #ifdef DEBUG
     938           8 :   MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
     939             :              "Apart from the computed offset members, the keyframes array"
     940             :              " should not be modified");
     941             : #endif
     942             : 
     943           8 :   mKeyframes.SwapElements(keyframesCopy);
     944           8 :   return result;
     945             : }
     946             : 
     947             : void
     948          32 : KeyframeEffectReadOnly::UpdateTargetRegistration()
     949             : {
     950          32 :   if (!mTarget) {
     951           0 :     return;
     952             :   }
     953             : 
     954          32 :   bool isRelevant = mAnimation && mAnimation->IsRelevant();
     955             : 
     956             :   // Animation::IsRelevant() returns a cached value. It only updates when
     957             :   // something calls Animation::UpdateRelevance. Whenever our timing changes,
     958             :   // we should be notifying our Animation before calling this, so
     959             :   // Animation::IsRelevant() should be up-to-date by the time we get here.
     960          32 :   MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(),
     961             :              "Out of date Animation::IsRelevant value");
     962             : 
     963          32 :   if (isRelevant && !mInEffectSet) {
     964             :     EffectSet* effectSet =
     965           2 :       EffectSet::GetOrCreateEffectSet(mTarget->mElement, mTarget->mPseudoType);
     966           2 :     effectSet->AddEffect(*this);
     967           2 :     mInEffectSet = true;
     968           2 :     UpdateEffectSet(effectSet);
     969          30 :   } else if (!isRelevant && mInEffectSet) {
     970           2 :     UnregisterTarget();
     971             :   }
     972             : }
     973             : 
     974             : void
     975           2 : KeyframeEffectReadOnly::UnregisterTarget()
     976             : {
     977           2 :   if (!mInEffectSet) {
     978           0 :     return;
     979             :   }
     980             : 
     981             :   EffectSet* effectSet =
     982           2 :     EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
     983           2 :   MOZ_ASSERT(effectSet, "If mInEffectSet is true, there must be an EffectSet"
     984             :                         " on the target element");
     985           2 :   mInEffectSet = false;
     986           2 :   if (effectSet) {
     987           2 :     effectSet->RemoveEffect(*this);
     988             : 
     989           2 :     if (effectSet->IsEmpty()) {
     990           2 :       EffectSet::DestroyEffectSet(mTarget->mElement, mTarget->mPseudoType);
     991             :     }
     992             :   }
     993             : }
     994             : 
     995             : void
     996          12 : KeyframeEffectReadOnly::RequestRestyle(
     997             :   EffectCompositor::RestyleType aRestyleType)
     998             : {
     999          12 :    if (!mTarget) {
    1000           0 :     return;
    1001             :   }
    1002          12 :   nsPresContext* presContext = nsContentUtils::GetContextForContent(mTarget->mElement);
    1003          12 :   if (presContext && mAnimation) {
    1004             :     presContext->EffectCompositor()->
    1005           8 :       RequestRestyle(mTarget->mElement, mTarget->mPseudoType,
    1006          16 :                      aRestyleType, mAnimation->CascadeLevel());
    1007             :   }
    1008             : }
    1009             : 
    1010             : already_AddRefed<nsStyleContext>
    1011           0 : KeyframeEffectReadOnly::GetTargetStyleContext()
    1012             : {
    1013           0 :   nsIPresShell* shell = GetPresShell();
    1014           0 :   if (!shell) {
    1015           0 :     return nullptr;
    1016             :   }
    1017             : 
    1018           0 :   MOZ_ASSERT(mTarget,
    1019             :              "Should only have a presshell when we have a target element");
    1020             : 
    1021           0 :   nsIAtom* pseudo = mTarget->mPseudoType < CSSPseudoElementType::Count
    1022           0 :                     ? nsCSSPseudoElements::GetPseudoAtom(mTarget->mPseudoType)
    1023           0 :                     : nullptr;
    1024             : 
    1025           0 :   return nsComputedDOMStyle::GetStyleContext(mTarget->mElement, pseudo, shell);
    1026             : }
    1027             : 
    1028             : #ifdef DEBUG
    1029             : void
    1030           0 : DumpAnimationProperties(nsTArray<AnimationProperty>& aAnimationProperties)
    1031             : {
    1032           0 :   for (auto& p : aAnimationProperties) {
    1033           0 :     printf("%s\n", nsCSSProps::GetStringValue(p.mProperty).get());
    1034           0 :     for (auto& s : p.mSegments) {
    1035           0 :       nsString fromValue, toValue;
    1036           0 :       s.mFromValue.SerializeSpecifiedValue(p.mProperty, fromValue);
    1037           0 :       s.mToValue.SerializeSpecifiedValue(p.mProperty, toValue);
    1038           0 :       printf("  %f..%f: %s..%s\n", s.mFromKey, s.mToKey,
    1039           0 :              NS_ConvertUTF16toUTF8(fromValue).get(),
    1040           0 :              NS_ConvertUTF16toUTF8(toValue).get());
    1041             :     }
    1042             :   }
    1043           0 : }
    1044             : #endif
    1045             : 
    1046             : /* static */ already_AddRefed<KeyframeEffectReadOnly>
    1047           0 : KeyframeEffectReadOnly::Constructor(
    1048             :     const GlobalObject& aGlobal,
    1049             :     const Nullable<ElementOrCSSPseudoElement>& aTarget,
    1050             :     JS::Handle<JSObject*> aKeyframes,
    1051             :     const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
    1052             :     ErrorResult& aRv)
    1053             : {
    1054             :   return ConstructKeyframeEffect<KeyframeEffectReadOnly>(aGlobal, aTarget,
    1055             :                                                          aKeyframes, aOptions,
    1056           0 :                                                          aRv);
    1057             : }
    1058             : 
    1059             : /* static */ already_AddRefed<KeyframeEffectReadOnly>
    1060           0 : KeyframeEffectReadOnly::Constructor(const GlobalObject& aGlobal,
    1061             :                                     KeyframeEffectReadOnly& aSource,
    1062             :                                     ErrorResult& aRv)
    1063             : {
    1064           0 :   return ConstructKeyframeEffect<KeyframeEffectReadOnly>(aGlobal, aSource, aRv);
    1065             : }
    1066             : 
    1067             : void
    1068           0 : KeyframeEffectReadOnly::GetTarget(
    1069             :     Nullable<OwningElementOrCSSPseudoElement>& aRv) const
    1070             : {
    1071           0 :   if (!mTarget) {
    1072           0 :     aRv.SetNull();
    1073           0 :     return;
    1074             :   }
    1075             : 
    1076           0 :   switch (mTarget->mPseudoType) {
    1077             :     case CSSPseudoElementType::before:
    1078             :     case CSSPseudoElementType::after:
    1079           0 :       aRv.SetValue().SetAsCSSPseudoElement() =
    1080           0 :         CSSPseudoElement::GetCSSPseudoElement(mTarget->mElement,
    1081           0 :                                               mTarget->mPseudoType);
    1082           0 :       break;
    1083             : 
    1084             :     case CSSPseudoElementType::NotPseudo:
    1085           0 :       aRv.SetValue().SetAsElement() = mTarget->mElement;
    1086           0 :       break;
    1087             : 
    1088             :     default:
    1089           0 :       NS_NOTREACHED("Animation of unsupported pseudo-type");
    1090           0 :       aRv.SetNull();
    1091             :   }
    1092             : }
    1093             : 
    1094             : static void
    1095           0 : CreatePropertyValue(nsCSSPropertyID aProperty,
    1096             :                     float aOffset,
    1097             :                     const Maybe<ComputedTimingFunction>& aTimingFunction,
    1098             :                     const AnimationValue& aValue,
    1099             :                     dom::CompositeOperation aComposite,
    1100             :                     AnimationPropertyValueDetails& aResult)
    1101             : {
    1102           0 :   aResult.mOffset = aOffset;
    1103             : 
    1104           0 :   if (!aValue.IsNull()) {
    1105           0 :     nsString stringValue;
    1106           0 :     aValue.SerializeSpecifiedValue(aProperty, stringValue);
    1107           0 :     aResult.mValue.Construct(stringValue);
    1108             :   }
    1109             : 
    1110           0 :   if (aTimingFunction) {
    1111           0 :     aResult.mEasing.Construct();
    1112           0 :     aTimingFunction->AppendToString(aResult.mEasing.Value());
    1113             :   } else {
    1114           0 :     aResult.mEasing.Construct(NS_LITERAL_STRING("linear"));
    1115             :   }
    1116             : 
    1117           0 :   aResult.mComposite = aComposite;
    1118           0 : }
    1119             : 
    1120             : void
    1121           0 : KeyframeEffectReadOnly::GetProperties(
    1122             :     nsTArray<AnimationPropertyDetails>& aProperties,
    1123             :     ErrorResult& aRv) const
    1124             : {
    1125           0 :   for (const AnimationProperty& property : mProperties) {
    1126           0 :     AnimationPropertyDetails propertyDetails;
    1127             :     propertyDetails.mProperty =
    1128           0 :       NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(property.mProperty));
    1129           0 :     propertyDetails.mRunningOnCompositor = property.mIsRunningOnCompositor;
    1130             : 
    1131           0 :     nsXPIDLString localizedString;
    1132           0 :     if (property.mPerformanceWarning &&
    1133           0 :         property.mPerformanceWarning->ToLocalizedString(localizedString)) {
    1134           0 :       propertyDetails.mWarning.Construct(localizedString);
    1135             :     }
    1136             : 
    1137           0 :     if (!propertyDetails.mValues.SetCapacity(property.mSegments.Length(),
    1138             :                                              mozilla::fallible)) {
    1139           0 :       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    1140           0 :       return;
    1141             :     }
    1142             : 
    1143           0 :     for (size_t segmentIdx = 0, segmentLen = property.mSegments.Length();
    1144           0 :          segmentIdx < segmentLen;
    1145             :          segmentIdx++)
    1146             :     {
    1147           0 :       const AnimationPropertySegment& segment = property.mSegments[segmentIdx];
    1148             : 
    1149           0 :       binding_detail::FastAnimationPropertyValueDetails fromValue;
    1150           0 :       CreatePropertyValue(property.mProperty, segment.mFromKey,
    1151             :                           segment.mTimingFunction, segment.mFromValue,
    1152           0 :                           segment.mFromComposite, fromValue);
    1153             :       // We don't apply timing functions for zero-length segments, so
    1154             :       // don't return one here.
    1155           0 :       if (segment.mFromKey == segment.mToKey) {
    1156           0 :         fromValue.mEasing.Reset();
    1157             :       }
    1158             :       // The following won't fail since we have already allocated the capacity
    1159             :       // above.
    1160           0 :       propertyDetails.mValues.AppendElement(fromValue, mozilla::fallible);
    1161             : 
    1162             :       // Normally we can ignore the to-value for this segment since it is
    1163             :       // identical to the from-value from the next segment. However, we need
    1164             :       // to add it if either:
    1165             :       // a) this is the last segment, or
    1166             :       // b) the next segment's from-value differs.
    1167           0 :       if (segmentIdx == segmentLen - 1 ||
    1168           0 :           property.mSegments[segmentIdx + 1].mFromValue.mGecko !=
    1169             :             segment.mToValue.mGecko) {
    1170           0 :         binding_detail::FastAnimationPropertyValueDetails toValue;
    1171           0 :         CreatePropertyValue(property.mProperty, segment.mToKey,
    1172           0 :                             Nothing(), segment.mToValue,
    1173           0 :                             segment.mToComposite, toValue);
    1174             :         // It doesn't really make sense to have a timing function on the
    1175             :         // last property value or before a sudden jump so we just drop the
    1176             :         // easing property altogether.
    1177           0 :         toValue.mEasing.Reset();
    1178           0 :         propertyDetails.mValues.AppendElement(toValue, mozilla::fallible);
    1179             :       }
    1180             :     }
    1181             : 
    1182           0 :     aProperties.AppendElement(propertyDetails);
    1183             :   }
    1184             : }
    1185             : 
    1186             : void
    1187           0 : KeyframeEffectReadOnly::GetKeyframes(JSContext*& aCx,
    1188             :                                      nsTArray<JSObject*>& aResult,
    1189             :                                      ErrorResult& aRv)
    1190             : {
    1191           0 :   MOZ_ASSERT(aResult.IsEmpty());
    1192           0 :   MOZ_ASSERT(!aRv.Failed());
    1193             : 
    1194           0 :   if (!aResult.SetCapacity(mKeyframes.Length(), mozilla::fallible)) {
    1195           0 :     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    1196           0 :     return;
    1197             :   }
    1198             : 
    1199           0 :   bool isServo = mDocument->IsStyledByServo();
    1200             : 
    1201           0 :   for (const Keyframe& keyframe : mKeyframes) {
    1202             :     // Set up a dictionary object for the explicit members
    1203           0 :     BaseComputedKeyframe keyframeDict;
    1204           0 :     if (keyframe.mOffset) {
    1205           0 :       keyframeDict.mOffset.SetValue(keyframe.mOffset.value());
    1206             :     }
    1207           0 :     MOZ_ASSERT(keyframe.mComputedOffset != Keyframe::kComputedOffsetNotSet,
    1208             :                "Invalid computed offset");
    1209           0 :     keyframeDict.mComputedOffset.Construct(keyframe.mComputedOffset);
    1210           0 :     if (keyframe.mTimingFunction) {
    1211           0 :       keyframeDict.mEasing.Truncate();
    1212           0 :       keyframe.mTimingFunction.ref().AppendToString(keyframeDict.mEasing);
    1213             :     } // else if null, leave easing as its default "linear".
    1214             : 
    1215           0 :     if (keyframe.mComposite) {
    1216           0 :       keyframeDict.mComposite.Construct(keyframe.mComposite.value());
    1217             :     }
    1218             : 
    1219           0 :     JS::Rooted<JS::Value> keyframeJSValue(aCx);
    1220           0 :     if (!ToJSValue(aCx, keyframeDict, &keyframeJSValue)) {
    1221           0 :       aRv.Throw(NS_ERROR_FAILURE);
    1222           0 :       return;
    1223             :     }
    1224             : 
    1225           0 :     JS::Rooted<JSObject*> keyframeObject(aCx, &keyframeJSValue.toObject());
    1226           0 :     for (const PropertyValuePair& propertyValue : keyframe.mPropertyValues) {
    1227           0 :       nsAutoString stringValue;
    1228           0 :       if (isServo) {
    1229           0 :         if (propertyValue.mServoDeclarationBlock) {
    1230             :           Servo_DeclarationBlock_SerializeOneValue(
    1231             :             propertyValue.mServoDeclarationBlock,
    1232           0 :             propertyValue.mProperty, &stringValue);
    1233             :         } else {
    1234             :           RawServoAnimationValue* value =
    1235           0 :             mBaseStyleValuesForServo.GetWeak(propertyValue.mProperty);
    1236             : 
    1237           0 :           if (value) {
    1238             :             Servo_AnimationValue_Serialize(value,
    1239           0 :                                            propertyValue.mProperty,
    1240           0 :                                            &stringValue);
    1241             :           }
    1242             :         }
    1243           0 :       } else if (nsCSSProps::IsShorthand(propertyValue.mProperty)) {
    1244             :          // nsCSSValue::AppendToString does not accept shorthands properties but
    1245             :          // works with token stream values if we pass eCSSProperty_UNKNOWN as
    1246             :          // the property.
    1247           0 :          propertyValue.mValue.AppendToString(
    1248           0 :            eCSSProperty_UNKNOWN, stringValue, nsCSSValue::eNormalized);
    1249             :       } else {
    1250           0 :         nsCSSValue cssValue = propertyValue.mValue;
    1251           0 :         if (cssValue.GetUnit() == eCSSUnit_Null) {
    1252             :           // We use an uninitialized nsCSSValue to represent the
    1253             :           // "neutral value". We currently only do this for keyframes generated
    1254             :           // from CSS animations with missing 0%/100% keyframes. Furthermore,
    1255             :           // currently (at least until bug 1339334) keyframes generated from
    1256             :           // CSS animations only contain longhand properties so we only need to
    1257             :           // handle null nsCSSValues for longhand properties.
    1258             :           DebugOnly<bool> uncomputeResult =
    1259           0 :             StyleAnimationValue::UncomputeValue(
    1260           0 :               propertyValue.mProperty,
    1261           0 :               Move(BaseStyle(propertyValue.mProperty).mGecko),
    1262           0 :               cssValue);
    1263             : 
    1264           0 :           MOZ_ASSERT(uncomputeResult,
    1265             :                      "Unable to get specified value from computed value");
    1266           0 :           MOZ_ASSERT(cssValue.GetUnit() != eCSSUnit_Null,
    1267             :                      "Got null computed value");
    1268             :         }
    1269           0 :         cssValue.AppendToString(propertyValue.mProperty,
    1270           0 :                                 stringValue, nsCSSValue::eNormalized);
    1271             :       }
    1272             : 
    1273           0 :       const char* name = nsCSSProps::PropertyIDLName(propertyValue.mProperty);
    1274           0 :       JS::Rooted<JS::Value> value(aCx);
    1275           0 :       if (!ToJSValue(aCx, stringValue, &value) ||
    1276           0 :           !JS_DefineProperty(aCx, keyframeObject, name, value,
    1277             :                              JSPROP_ENUMERATE)) {
    1278           0 :         aRv.Throw(NS_ERROR_FAILURE);
    1279           0 :         return;
    1280             :       }
    1281             :     }
    1282             : 
    1283           0 :     aResult.AppendElement(keyframeObject);
    1284             :   }
    1285             : }
    1286             : 
    1287             : /* static */ const TimeDuration
    1288           0 : KeyframeEffectReadOnly::OverflowRegionRefreshInterval()
    1289             : {
    1290             :   // The amount of time we can wait between updating throttled animations
    1291             :   // on the main thread that influence the overflow region.
    1292             :   static const TimeDuration kOverflowRegionRefreshInterval =
    1293           0 :     TimeDuration::FromMilliseconds(200);
    1294             : 
    1295           0 :   return kOverflowRegionRefreshInterval;
    1296             : }
    1297             : 
    1298             : bool
    1299           6 : KeyframeEffectReadOnly::CanThrottle() const
    1300             : {
    1301             :   // Unthrottle if we are not in effect or current. This will be the case when
    1302             :   // our owning animation has finished, is idle, or when we are in the delay
    1303             :   // phase (but without a backwards fill). In each case the computed progress
    1304             :   // value produced on each tick will be the same so we will skip requesting
    1305             :   // unnecessary restyles in NotifyAnimationTimingUpdated. Any calls we *do* get
    1306             :   // here will be because of a change in state (e.g. we are newly finished or
    1307             :   // newly no longer in effect) in which case we shouldn't throttle the sample.
    1308           6 :   if (!IsInEffect() || !IsCurrent()) {
    1309           2 :     return false;
    1310             :   }
    1311             : 
    1312           4 :   nsIFrame* frame = GetAnimationFrame();
    1313           4 :   if (!frame) {
    1314             :     // There are two possible cases here.
    1315             :     // a) No target element
    1316             :     // b) The target element has no frame, e.g. because it is in a display:none
    1317             :     //    subtree.
    1318             :     // In either case we can throttle the animation because there is no
    1319             :     // need to update on the main thread.
    1320           0 :     return true;
    1321             :   }
    1322             : 
    1323             :   // We can throttle the animation if the animation is paint only and
    1324             :   // the target frame is out of view or the document is in background tabs.
    1325           4 :   if (CanIgnoreIfNotVisible()) {
    1326           4 :     nsIPresShell* presShell = GetPresShell();
    1327           8 :     if ((presShell && !presShell->IsActive()) ||
    1328           4 :         frame->IsScrolledOutOfView()) {
    1329           0 :       return true;
    1330             :     }
    1331             :   }
    1332             : 
    1333             :   // First we need to check layer generation and transform overflow
    1334             :   // prior to the property.mIsRunningOnCompositor check because we should
    1335             :   // occasionally unthrottle these animations even if the animations are
    1336             :   // already running on compositor.
    1337           4 :   for (const LayerAnimationInfo::Record& record :
    1338           4 :         LayerAnimationInfo::sRecords) {
    1339             :     // Skip properties that are overridden by !important rules.
    1340             :     // (GetEffectiveAnimationOfProperty, as called by
    1341             :     // HasEffectiveAnimationOfProperty, only returns a property which is
    1342             :     // neither overridden by !important rules nor overridden by other
    1343             :     // animation.)
    1344           8 :     if (!HasEffectiveAnimationOfProperty(record.mProperty)) {
    1345           4 :       continue;
    1346             :     }
    1347             : 
    1348           4 :     EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
    1349           8 :                                                    mTarget->mPseudoType);
    1350           4 :     MOZ_ASSERT(effectSet, "CanThrottle should be called on an effect "
    1351             :                           "associated with a target element");
    1352             :     layers::Layer* layer =
    1353           4 :       FrameLayerBuilder::GetDedicatedLayer(frame, record.mLayerType);
    1354             :     // Unthrottle if the layer needs to be brought up to date
    1355           4 :     if (!layer ||
    1356           0 :         effectSet->GetAnimationGeneration() !=
    1357           0 :           layer->GetAnimationGeneration()) {
    1358           4 :       return false;
    1359             :     }
    1360             : 
    1361             :     // If this is a transform animation that affects the overflow region,
    1362             :     // we should unthrottle the animation periodically.
    1363           0 :     if (record.mProperty == eCSSProperty_transform &&
    1364           0 :         !CanThrottleTransformChanges(*frame)) {
    1365           0 :       return false;
    1366             :     }
    1367             :   }
    1368             : 
    1369           0 :   for (const AnimationProperty& property : mProperties) {
    1370           0 :     if (!property.mIsRunningOnCompositor) {
    1371           0 :       return false;
    1372             :     }
    1373             :   }
    1374             : 
    1375           0 :   return true;
    1376             : }
    1377             : 
    1378             : bool
    1379           0 : KeyframeEffectReadOnly::CanThrottleTransformChanges(nsIFrame& aFrame) const
    1380             : {
    1381             :   // If we know that the animation cannot cause overflow,
    1382             :   // we can just disable flushes for this animation.
    1383             : 
    1384             :   // If we don't show scrollbars, we don't care about overflow.
    1385           0 :   if (LookAndFeel::GetInt(LookAndFeel::eIntID_ShowHideScrollbars) == 0) {
    1386           0 :     return true;
    1387             :   }
    1388             : 
    1389           0 :   TimeStamp now = aFrame.PresContext()->RefreshDriver()->MostRecentRefresh();
    1390             : 
    1391           0 :   EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
    1392           0 :                                                  mTarget->mPseudoType);
    1393           0 :   MOZ_ASSERT(effectSet, "CanThrottleTransformChanges is expected to be called"
    1394             :                         " on an effect in an effect set");
    1395           0 :   MOZ_ASSERT(mAnimation, "CanThrottleTransformChanges is expected to be called"
    1396             :                          " on an effect with a parent animation");
    1397           0 :   TimeStamp lastSyncTime = effectSet->LastTransformSyncTime();
    1398             :   // If this animation can cause overflow, we can throttle some of the ticks.
    1399           0 :   if (!lastSyncTime.IsNull() &&
    1400           0 :       (now - lastSyncTime) < OverflowRegionRefreshInterval()) {
    1401           0 :     return true;
    1402             :   }
    1403             : 
    1404             :   // If the nearest scrollable ancestor has overflow:hidden,
    1405             :   // we don't care about overflow.
    1406             :   nsIScrollableFrame* scrollable =
    1407           0 :     nsLayoutUtils::GetNearestScrollableFrame(&aFrame);
    1408           0 :   if (!scrollable) {
    1409           0 :     return true;
    1410             :   }
    1411             : 
    1412           0 :   ScrollbarStyles ss = scrollable->GetScrollbarStyles();
    1413           0 :   if (ss.mVertical == NS_STYLE_OVERFLOW_HIDDEN &&
    1414           0 :       ss.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN &&
    1415           0 :       scrollable->GetLogicalScrollPosition() == nsPoint(0, 0)) {
    1416           0 :     return true;
    1417             :   }
    1418             : 
    1419           0 :   return false;
    1420             : }
    1421             : 
    1422             : nsIFrame*
    1423           6 : KeyframeEffectReadOnly::GetAnimationFrame() const
    1424             : {
    1425           6 :   if (!mTarget) {
    1426           0 :     return nullptr;
    1427             :   }
    1428             : 
    1429             :   nsIFrame* frame;
    1430           6 :   if (mTarget->mPseudoType == CSSPseudoElementType::before) {
    1431           0 :     frame = nsLayoutUtils::GetBeforeFrame(mTarget->mElement);
    1432           6 :   } else if (mTarget->mPseudoType == CSSPseudoElementType::after) {
    1433           0 :     frame = nsLayoutUtils::GetAfterFrame(mTarget->mElement);
    1434             :   } else {
    1435           6 :     frame = mTarget->mElement->GetPrimaryFrame();
    1436           6 :     MOZ_ASSERT(mTarget->mPseudoType == CSSPseudoElementType::NotPseudo,
    1437             :                "unknown mTarget->mPseudoType");
    1438             :   }
    1439             : 
    1440           6 :   if (!frame) {
    1441           0 :     return nullptr;
    1442             :   }
    1443             : 
    1444           6 :   return nsLayoutUtils::GetStyleFrame(frame);
    1445             : }
    1446             : 
    1447             : nsIDocument*
    1448          24 : KeyframeEffectReadOnly::GetRenderedDocument() const
    1449             : {
    1450          24 :   if (!mTarget) {
    1451           0 :     return nullptr;
    1452             :   }
    1453          24 :   return mTarget->mElement->GetComposedDoc();
    1454             : }
    1455             : 
    1456             : nsIPresShell*
    1457           4 : KeyframeEffectReadOnly::GetPresShell() const
    1458             : {
    1459           4 :   nsIDocument* doc = GetRenderedDocument();
    1460           4 :   if (!doc) {
    1461           0 :     return nullptr;
    1462             :   }
    1463           4 :   return doc->GetShell();
    1464             : }
    1465             : 
    1466             : /* static */ bool
    1467           0 : KeyframeEffectReadOnly::IsGeometricProperty(
    1468             :   const nsCSSPropertyID aProperty)
    1469             : {
    1470           0 :   MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
    1471             :              "Property should be a longhand property");
    1472             : 
    1473           0 :   switch (aProperty) {
    1474             :     case eCSSProperty_bottom:
    1475             :     case eCSSProperty_height:
    1476             :     case eCSSProperty_left:
    1477             :     case eCSSProperty_margin_bottom:
    1478             :     case eCSSProperty_margin_left:
    1479             :     case eCSSProperty_margin_right:
    1480             :     case eCSSProperty_margin_top:
    1481             :     case eCSSProperty_padding_bottom:
    1482             :     case eCSSProperty_padding_left:
    1483             :     case eCSSProperty_padding_right:
    1484             :     case eCSSProperty_padding_top:
    1485             :     case eCSSProperty_right:
    1486             :     case eCSSProperty_top:
    1487             :     case eCSSProperty_width:
    1488           0 :       return true;
    1489             :     default:
    1490           0 :       return false;
    1491             :   }
    1492             : }
    1493             : 
    1494             : /* static */ bool
    1495           0 : KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
    1496             :   const nsIFrame* aFrame,
    1497             :   AnimationPerformanceWarning::Type& aPerformanceWarning)
    1498             : {
    1499             :   // Disallow OMTA for preserve-3d transform. Note that we check the style property
    1500             :   // rather than Extend3DContext() since that can recurse back into this function
    1501             :   // via HasOpacity(). See bug 779598.
    1502           0 :   if (aFrame->Combines3DTransformWithAncestors() ||
    1503           0 :       aFrame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
    1504           0 :     aPerformanceWarning = AnimationPerformanceWarning::Type::TransformPreserve3D;
    1505           0 :     return false;
    1506             :   }
    1507             :   // Note that testing BackfaceIsHidden() is not a sufficient test for
    1508             :   // what we need for animating backface-visibility correctly if we
    1509             :   // remove the above test for Extend3DContext(); that would require
    1510             :   // looking at backface-visibility on descendants as well. See bug 1186204.
    1511           0 :   if (aFrame->BackfaceIsHidden()) {
    1512           0 :     aPerformanceWarning =
    1513             :       AnimationPerformanceWarning::Type::TransformBackfaceVisibilityHidden;
    1514           0 :     return false;
    1515             :   }
    1516             :   // Async 'transform' animations of aFrames with SVG transforms is not
    1517             :   // supported.  See bug 779599.
    1518           0 :   if (aFrame->IsSVGTransformed()) {
    1519           0 :     aPerformanceWarning = AnimationPerformanceWarning::Type::TransformSVG;
    1520           0 :     return false;
    1521             :   }
    1522             : 
    1523           0 :   return true;
    1524             : }
    1525             : 
    1526             : bool
    1527           0 : KeyframeEffectReadOnly::ShouldBlockAsyncTransformAnimations(
    1528             :   const nsIFrame* aFrame,
    1529             :   AnimationPerformanceWarning::Type& aPerformanceWarning) const
    1530             : {
    1531             :   EffectSet* effectSet =
    1532           0 :     EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
    1533           0 :   for (const AnimationProperty& property : mProperties) {
    1534             :     // If there is a property for animations level that is overridden by
    1535             :     // !important rules, it should not block other animations from running
    1536             :     // on the compositor.
    1537             :     // NOTE: We don't currently check for !important rules for properties that
    1538             :     // don't run on the compositor. As result such properties (e.g. margin-left)
    1539             :     // can still block async animations even if they are overridden by
    1540             :     // !important rules.
    1541           0 :     if (effectSet &&
    1542           0 :         effectSet->PropertiesWithImportantRules()
    1543           0 :           .HasProperty(property.mProperty) &&
    1544           0 :         effectSet->PropertiesForAnimationsLevel()
    1545           0 :           .HasProperty(property.mProperty)) {
    1546           0 :       continue;
    1547             :     }
    1548             :     // Check for geometric properties
    1549           0 :     if (IsGeometricProperty(property.mProperty)) {
    1550           0 :       aPerformanceWarning =
    1551             :         AnimationPerformanceWarning::Type::TransformWithGeometricProperties;
    1552           0 :       return true;
    1553             :     }
    1554             : 
    1555             :     // Check for unsupported transform animations
    1556           0 :     if (property.mProperty == eCSSProperty_transform) {
    1557           0 :       if (!CanAnimateTransformOnCompositor(aFrame,
    1558             :                                            aPerformanceWarning)) {
    1559           0 :         return true;
    1560             :       }
    1561             :     }
    1562             :   }
    1563             : 
    1564           0 :   return false;
    1565             : }
    1566             : 
    1567             : bool
    1568           0 : KeyframeEffectReadOnly::HasGeometricProperties() const
    1569             : {
    1570           0 :   for (const AnimationProperty& property : mProperties) {
    1571           0 :     if (IsGeometricProperty(property.mProperty)) {
    1572           0 :       return true;
    1573             :     }
    1574             :   }
    1575             : 
    1576           0 :   return false;
    1577             : }
    1578             : 
    1579             : void
    1580           0 : KeyframeEffectReadOnly::SetPerformanceWarning(
    1581             :   nsCSSPropertyID aProperty,
    1582             :   const AnimationPerformanceWarning& aWarning)
    1583             : {
    1584           0 :   if (aWarning.mType == AnimationPerformanceWarning::Type::ContentTooLarge &&
    1585           0 :       !mRecordedContentTooLarge) {
    1586             :     // ContentTooLarge stores: frameSize (w x h),
    1587             :     //                         relativeLimit (w x h), i.e. =~ viewport size *
    1588             :     //                                                          ratioLimit
    1589             :     //                         absoluteLimit (w x h)
    1590           0 :     MOZ_ASSERT(aWarning.mParams && aWarning.mParams->Length() >= 4,
    1591             :                "ContentTooLarge warning should have at least 4 parameters");
    1592           0 :     const nsTArray<int32_t>& params = aWarning.mParams.ref();
    1593           0 :     uint32_t frameSize = uint32_t(params[0]) * params[1];
    1594           0 :     float viewportRatioX = gfxPrefs::AnimationPrerenderViewportRatioLimitX();
    1595           0 :     float viewportRatioY = gfxPrefs::AnimationPrerenderViewportRatioLimitY();
    1596           0 :     double viewportWidth = viewportRatioX ? params[2] / viewportRatioX
    1597           0 :                                           : params[2];
    1598           0 :     double viewportHeight = viewportRatioY ? params[3] / viewportRatioY
    1599           0 :                                            : params[3];
    1600           0 :     double viewportSize = viewportWidth * viewportHeight;
    1601           0 :     uint32_t frameToViewport = frameSize / viewportSize * 100.0;
    1602             :     Telemetry::Accumulate(
    1603           0 :       Telemetry::ASYNC_ANIMATION_CONTENT_TOO_LARGE_FRAME_SIZE, frameSize);
    1604             :     Telemetry::Accumulate(
    1605           0 :       Telemetry::ASYNC_ANIMATION_CONTENT_TOO_LARGE_PERCENTAGE, frameToViewport);
    1606           0 :     mRecordedContentTooLarge = true;
    1607             :   }
    1608             : 
    1609           0 :   for (AnimationProperty& property : mProperties) {
    1610           0 :     if (property.mProperty == aProperty &&
    1611           0 :         (!property.mPerformanceWarning ||
    1612           0 :          *property.mPerformanceWarning != aWarning)) {
    1613           0 :       property.mPerformanceWarning = Some(aWarning);
    1614             : 
    1615           0 :       nsXPIDLString localizedString;
    1616           0 :       if (nsLayoutUtils::IsAnimationLoggingEnabled() &&
    1617           0 :           property.mPerformanceWarning->ToLocalizedString(localizedString)) {
    1618           0 :         nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
    1619           0 :         AnimationUtils::LogAsyncAnimationFailure(logMessage, mTarget->mElement);
    1620             :       }
    1621           0 :       return;
    1622             :     }
    1623             :   }
    1624             : }
    1625             : 
    1626             : void
    1627           0 : KeyframeEffectReadOnly::RecordFrameSizeTelemetry(uint32_t aPixelArea) {
    1628           0 :   if (!mRecordedFrameSize) {
    1629           0 :     Telemetry::Accumulate(Telemetry::ASYNC_ANIMATION_FRAME_SIZE, aPixelArea);
    1630           0 :     mRecordedFrameSize = true;
    1631             :   }
    1632           0 : }
    1633             : 
    1634             : static already_AddRefed<nsStyleContext>
    1635           4 : CreateStyleContextForAnimationValue(nsCSSPropertyID aProperty,
    1636             :                                     const StyleAnimationValue& aValue,
    1637             :                                     nsStyleContext* aBaseStyleContext)
    1638             : {
    1639           4 :   MOZ_ASSERT(aBaseStyleContext,
    1640             :              "CreateStyleContextForAnimationValue needs to be called "
    1641             :              "with a valid nsStyleContext");
    1642             : 
    1643           8 :   RefPtr<AnimValuesStyleRule> styleRule = new AnimValuesStyleRule();
    1644           4 :   styleRule->AddValue(aProperty, aValue);
    1645             : 
    1646           8 :   nsCOMArray<nsIStyleRule> rules;
    1647           4 :   rules.AppendObject(styleRule);
    1648             : 
    1649           4 :   MOZ_ASSERT(aBaseStyleContext->PresContext()->StyleSet()->IsGecko(),
    1650             :              "ServoStyleSet should not use StyleAnimationValue for animations");
    1651             :   nsStyleSet* styleSet =
    1652           4 :     aBaseStyleContext->PresContext()->StyleSet()->AsGecko();
    1653             : 
    1654             :   RefPtr<nsStyleContext> styleContext =
    1655           8 :     styleSet->ResolveStyleByAddingRules(aBaseStyleContext, rules);
    1656             : 
    1657             :   // We need to call StyleData to generate cached data for the style context.
    1658             :   // Otherwise CalcStyleDifference returns no meaningful result.
    1659           4 :   styleContext->AsGecko()->StyleData(nsCSSProps::kSIDTable[aProperty]);
    1660             : 
    1661           8 :   return styleContext.forget();
    1662             : }
    1663             : 
    1664             : void
    1665           2 : KeyframeEffectReadOnly::CalculateCumulativeChangeHint(
    1666             :   nsStyleContext *aStyleContext)
    1667             : {
    1668           2 :   if (mDocument->IsStyledByServo()) {
    1669             :     // FIXME (bug 1303235): Do this for Servo too
    1670           0 :     return;
    1671             :   }
    1672           2 :   mCumulativeChangeHint = nsChangeHint(0);
    1673             : 
    1674           4 :   for (const AnimationProperty& property : mProperties) {
    1675           4 :     for (const AnimationPropertySegment& segment : property.mSegments) {
    1676             :       // In case composite operation is not 'replace' or value is null,
    1677             :       // we can't throttle animations which will not cause any layout changes
    1678             :       // on invisible elements because we can't calculate the change hint for
    1679             :       // such properties until we compose it.
    1680           2 :       if (!segment.HasReplaceableValues()) {
    1681           0 :         mCumulativeChangeHint = ~nsChangeHint_Hints_CanIgnoreIfNotVisible;
    1682           0 :         return;
    1683             :       }
    1684             :       RefPtr<nsStyleContext> fromContext =
    1685           4 :         CreateStyleContextForAnimationValue(property.mProperty,
    1686             :                                             segment.mFromValue.mGecko,
    1687           4 :                                             aStyleContext);
    1688             : 
    1689             :       RefPtr<nsStyleContext> toContext =
    1690           4 :         CreateStyleContextForAnimationValue(property.mProperty,
    1691             :                                             segment.mToValue.mGecko,
    1692           4 :                                             aStyleContext);
    1693             : 
    1694           2 :       uint32_t equalStructs = 0;
    1695           2 :       uint32_t samePointerStructs = 0;
    1696             :       nsChangeHint changeHint =
    1697           2 :         fromContext->CalcStyleDifference(toContext,
    1698             :                                          &equalStructs,
    1699           2 :                                          &samePointerStructs);
    1700             : 
    1701           2 :       mCumulativeChangeHint |= changeHint;
    1702             :     }
    1703             :   }
    1704             : }
    1705             : 
    1706             : void
    1707           2 : KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
    1708             : {
    1709           2 :   if (mAnimation == aAnimation) {
    1710           0 :     return;
    1711             :   }
    1712             : 
    1713             :   // Restyle for the old animation.
    1714           2 :   RequestRestyle(EffectCompositor::RestyleType::Layer);
    1715             : 
    1716           2 :   mAnimation = aAnimation;
    1717             : 
    1718             :   // The order of these function calls is important:
    1719             :   // NotifyAnimationTimingUpdated() need the updated mIsRelevant flag to check
    1720             :   // if it should create the effectSet or not, and MarkCascadeNeedsUpdate()
    1721             :   // needs a valid effectSet, so we should call them in this order.
    1722           2 :   if (mAnimation) {
    1723           2 :     mAnimation->UpdateRelevance();
    1724             :   }
    1725           2 :   NotifyAnimationTimingUpdated();
    1726           2 :   if (mAnimation) {
    1727           2 :     MarkCascadeNeedsUpdate();
    1728             :   }
    1729             : }
    1730             : 
    1731             : bool
    1732           4 : KeyframeEffectReadOnly::CanIgnoreIfNotVisible() const
    1733             : {
    1734           4 :   if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
    1735           0 :     return false;
    1736             :   }
    1737             : 
    1738             :   // FIXME (bug 1303235): We don't calculate mCumulativeChangeHint for
    1739             :   // the Servo backend yet
    1740           4 :   if (mDocument->IsStyledByServo()) {
    1741           0 :     return false;
    1742             :   }
    1743             : 
    1744             :   // FIXME: For further sophisticated optimization we need to check
    1745             :   // change hint on the segment corresponding to computedTiming.progress.
    1746           4 :   return NS_IsHintSubset(
    1747           4 :     mCumulativeChangeHint, nsChangeHint_Hints_CanIgnoreIfNotVisible);
    1748             : }
    1749             : 
    1750             : void
    1751           2 : KeyframeEffectReadOnly::MaybeUpdateFrameForCompositor()
    1752             : {
    1753           2 :   nsIFrame* frame = GetAnimationFrame();
    1754           2 :   if (!frame) {
    1755           0 :     return;
    1756             :   }
    1757             : 
    1758             :   // FIXME: Bug 1272495: If this effect does not win in the cascade, the
    1759             :   // NS_FRAME_MAY_BE_TRANSFORMED flag should be removed when the animation
    1760             :   // will be removed from effect set or the transform keyframes are removed
    1761             :   // by setKeyframes. The latter case will be hard to solve though.
    1762           4 :   for (const AnimationProperty& property : mProperties) {
    1763           2 :     if (property.mProperty == eCSSProperty_transform) {
    1764           0 :       frame->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
    1765           0 :       return;
    1766             :     }
    1767             :   }
    1768             : }
    1769             : 
    1770             : void
    1771           8 : KeyframeEffectReadOnly::MarkCascadeNeedsUpdate()
    1772             : {
    1773           8 :   if (!mTarget) {
    1774           0 :     return;
    1775             :   }
    1776             : 
    1777           8 :   EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
    1778          16 :                                                  mTarget->mPseudoType);
    1779           8 :   if (!effectSet) {
    1780           6 :     return;
    1781             :   }
    1782           2 :   effectSet->MarkCascadeNeedsUpdate();
    1783             : }
    1784             : 
    1785             : bool
    1786          32 : KeyframeEffectReadOnly::HasComputedTimingChanged() const
    1787             : {
    1788             :   // Typically we don't need to request a restyle if the progress hasn't
    1789             :   // changed since the last call to ComposeStyle. The one exception is if the
    1790             :   // iteration composite mode is 'accumulate' and the current iteration has
    1791             :   // changed, since that will often produce a different result.
    1792          64 :   ComputedTiming computedTiming = GetComputedTiming();
    1793          64 :   return computedTiming.mProgress != mProgressOnLastCompose ||
    1794          26 :          (mEffectOptions.mIterationComposite ==
    1795           0 :             IterationCompositeOperation::Accumulate &&
    1796           0 :          computedTiming.mCurrentIteration !=
    1797          64 :           mCurrentIterationOnLastCompose);
    1798             : }
    1799             : 
    1800             : bool
    1801           0 : KeyframeEffectReadOnly::ContainsAnimatedScale(const nsIFrame* aFrame) const
    1802             : {
    1803           0 :   if (!IsCurrent()) {
    1804           0 :     return false;
    1805             :   }
    1806             : 
    1807           0 :   for (const AnimationProperty& prop : mProperties) {
    1808           0 :     if (prop.mProperty != eCSSProperty_transform) {
    1809           0 :       continue;
    1810             :     }
    1811             : 
    1812           0 :     AnimationValue baseStyle = BaseStyle(prop.mProperty);
    1813           0 :     if (baseStyle.IsNull()) {
    1814             :       // If we failed to get the base style, we consider it has scale value
    1815             :       // here just to be safe.
    1816           0 :       return true;
    1817             :     }
    1818           0 :     gfxSize size = baseStyle.GetScaleValue(aFrame);
    1819           0 :     if (size != gfxSize(1.0f, 1.0f)) {
    1820           0 :       return true;
    1821             :     }
    1822             : 
    1823             :     // This is actually overestimate because there are some cases that combining
    1824             :     // the base value and from/to value produces 1:1 scale. But it doesn't
    1825             :     // really matter.
    1826           0 :     for (const AnimationPropertySegment& segment : prop.mSegments) {
    1827           0 :       if (!segment.mFromValue.IsNull()) {
    1828           0 :         gfxSize from = segment.mFromValue.GetScaleValue(aFrame);
    1829           0 :         if (from != gfxSize(1.0f, 1.0f)) {
    1830           0 :           return true;
    1831             :         }
    1832             :       }
    1833           0 :       if (!segment.mToValue.IsNull()) {
    1834           0 :         gfxSize to = segment.mToValue.GetScaleValue(aFrame);
    1835           0 :         if (to != gfxSize(1.0f, 1.0f)) {
    1836           0 :           return true;
    1837             :         }
    1838             :       }
    1839             :     }
    1840             :   }
    1841             : 
    1842           0 :   return false;
    1843             : }
    1844             : 
    1845             : void
    1846           4 : KeyframeEffectReadOnly::UpdateEffectSet(EffectSet* aEffectSet) const
    1847             : {
    1848           4 :   if (!mInEffectSet) {
    1849           2 :     return;
    1850             :   }
    1851             : 
    1852             :   EffectSet* effectSet =
    1853           2 :     aEffectSet ? aEffectSet
    1854           0 :                : EffectSet::GetEffectSet(mTarget->mElement,
    1855           2 :                                          mTarget->mPseudoType);
    1856           2 :   if (!effectSet) {
    1857           0 :     return;
    1858             :   }
    1859             : 
    1860           2 :   if (HasAnimationOfProperty(eCSSProperty_opacity)) {
    1861           2 :     effectSet->SetMayHaveOpacityAnimation();
    1862             :   }
    1863           2 :   if (HasAnimationOfProperty(eCSSProperty_transform)) {
    1864           0 :     effectSet->SetMayHaveTransformAnimation();
    1865             :   }
    1866             : }
    1867             : 
    1868             : template
    1869             : void
    1870             : KeyframeEffectReadOnly::ComposeStyle<RefPtr<AnimValuesStyleRule>&>(
    1871             :   RefPtr<AnimValuesStyleRule>& aAnimationRule,
    1872             :   const nsCSSPropertyIDSet& aPropertiesToSkip);
    1873             : 
    1874             : template
    1875             : void
    1876             : KeyframeEffectReadOnly::ComposeStyle<RawServoAnimationValueMap&>(
    1877             :   RawServoAnimationValueMap& aAnimationValues,
    1878             :   const nsCSSPropertyIDSet& aPropertiesToSkip);
    1879             : 
    1880             : } // namespace dom
    1881             : } // namespace mozilla

Generated by: LCOV version 1.13