LCOV - code coverage report
Current view: top level - layout/style - nsAnimationManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 26 432 6.0 %
Date: 2017-07-14 16:53:18 Functions: 6 43 14.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsAnimationManager.h"
       7             : #include "nsTransitionManager.h"
       8             : #include "mozilla/dom/CSSAnimationBinding.h"
       9             : 
      10             : #include "mozilla/AnimationTarget.h"
      11             : #include "mozilla/EffectCompositor.h"
      12             : #include "mozilla/EffectSet.h"
      13             : #include "mozilla/MemoryReporting.h"
      14             : #include "mozilla/ServoStyleSet.h"
      15             : #include "mozilla/StyleAnimationValue.h"
      16             : #include "mozilla/dom/DocumentTimeline.h"
      17             : #include "mozilla/dom/KeyframeEffectReadOnly.h"
      18             : 
      19             : #include "nsPresContext.h"
      20             : #include "nsStyleSet.h"
      21             : #include "nsStyleChangeList.h"
      22             : #include "nsContentUtils.h"
      23             : #include "nsCSSRules.h"
      24             : #include "mozilla/GeckoRestyleManager.h"
      25             : #include "nsLayoutUtils.h"
      26             : #include "nsIFrame.h"
      27             : #include "nsIDocument.h"
      28             : #include "nsDOMMutationObserver.h"
      29             : #include "nsIPresShell.h"
      30             : #include "nsIPresShellInlines.h"
      31             : #include <algorithm> // std::stable_sort
      32             : #include <math.h>
      33             : 
      34             : using namespace mozilla;
      35             : using namespace mozilla::css;
      36             : using mozilla::dom::Animation;
      37             : using mozilla::dom::AnimationPlayState;
      38             : using mozilla::dom::KeyframeEffectReadOnly;
      39             : using mozilla::dom::CSSAnimation;
      40             : 
      41             : typedef mozilla::ComputedTiming::AnimationPhase AnimationPhase;
      42             : 
      43             : namespace {
      44             : 
      45             : struct AnimationEventParams {
      46             :   EventMessage mMessage;
      47             :   StickyTimeDuration mElapsedTime;
      48             :   TimeStamp mTimeStamp;
      49             : };
      50             : 
      51             : } // anonymous namespace
      52             : 
      53             : ////////////////////////// CSSAnimation ////////////////////////////
      54             : 
      55             : JSObject*
      56           0 : CSSAnimation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
      57             : {
      58           0 :   return dom::CSSAnimationBinding::Wrap(aCx, this, aGivenProto);
      59             : }
      60             : 
      61             : mozilla::dom::Promise*
      62           0 : CSSAnimation::GetReady(ErrorResult& aRv)
      63             : {
      64           0 :   FlushStyle();
      65           0 :   return Animation::GetReady(aRv);
      66             : }
      67             : 
      68             : void
      69           0 : CSSAnimation::Play(ErrorResult &aRv, LimitBehavior aLimitBehavior)
      70             : {
      71           0 :   mPauseShouldStick = false;
      72           0 :   Animation::Play(aRv, aLimitBehavior);
      73           0 : }
      74             : 
      75             : void
      76           0 : CSSAnimation::Pause(ErrorResult& aRv)
      77             : {
      78           0 :   mPauseShouldStick = true;
      79           0 :   Animation::Pause(aRv);
      80           0 : }
      81             : 
      82             : AnimationPlayState
      83           0 : CSSAnimation::PlayStateFromJS() const
      84             : {
      85             :   // Flush style to ensure that any properties controlling animation state
      86             :   // (e.g. animation-play-state) are fully updated.
      87           0 :   FlushStyle();
      88           0 :   return Animation::PlayStateFromJS();
      89             : }
      90             : 
      91             : void
      92           0 : CSSAnimation::PlayFromJS(ErrorResult& aRv)
      93             : {
      94             :   // Note that flushing style below might trigger calls to
      95             :   // PlayFromStyle()/PauseFromStyle() on this object.
      96           0 :   FlushStyle();
      97           0 :   Animation::PlayFromJS(aRv);
      98           0 : }
      99             : 
     100             : void
     101           0 : CSSAnimation::PlayFromStyle()
     102             : {
     103           0 :   mIsStylePaused = false;
     104           0 :   if (!mPauseShouldStick) {
     105           0 :     ErrorResult rv;
     106           0 :     PlayNoUpdate(rv, Animation::LimitBehavior::Continue);
     107             :     // play() should not throw when LimitBehavior is Continue
     108           0 :     MOZ_ASSERT(!rv.Failed(), "Unexpected exception playing animation");
     109             :   }
     110           0 : }
     111             : 
     112             : void
     113           0 : CSSAnimation::PauseFromStyle()
     114             : {
     115             :   // Check if the pause state is being overridden
     116           0 :   if (mIsStylePaused) {
     117           0 :     return;
     118             :   }
     119             : 
     120           0 :   mIsStylePaused = true;
     121           0 :   ErrorResult rv;
     122           0 :   PauseNoUpdate(rv);
     123             :   // pause() should only throw when *all* of the following conditions are true:
     124             :   // - we are in the idle state, and
     125             :   // - we have a negative playback rate, and
     126             :   // - we have an infinitely repeating animation
     127             :   // The first two conditions will never happen under regular style processing
     128             :   // but could happen if an author made modifications to the Animation object
     129             :   // and then updated animation-play-state. It's an unusual case and there's
     130             :   // no obvious way to pass on the exception information so we just silently
     131             :   // fail for now.
     132           0 :   if (rv.Failed()) {
     133           0 :     NS_WARNING("Unexpected exception pausing animation - silently failing");
     134             :   }
     135             : }
     136             : 
     137             : void
     138           0 : CSSAnimation::Tick()
     139             : {
     140           0 :   Animation::Tick();
     141           0 :   QueueEvents();
     142           0 : }
     143             : 
     144             : bool
     145           0 : CSSAnimation::HasLowerCompositeOrderThan(const CSSAnimation& aOther) const
     146             : {
     147           0 :   MOZ_ASSERT(IsTiedToMarkup() && aOther.IsTiedToMarkup(),
     148             :              "Should only be called for CSS animations that are sorted "
     149             :              "as CSS animations (i.e. tied to CSS markup)");
     150             : 
     151             :   // 0. Object-equality case
     152           0 :   if (&aOther == this) {
     153           0 :     return false;
     154             :   }
     155             : 
     156             :   // 1. Sort by document order
     157           0 :   if (!mOwningElement.Equals(aOther.mOwningElement)) {
     158           0 :     return mOwningElement.LessThan(aOther.mOwningElement);
     159             :   }
     160             : 
     161             :   // 2. (Same element and pseudo): Sort by position in animation-name
     162           0 :   return mAnimationIndex < aOther.mAnimationIndex;
     163             : }
     164             : 
     165             : void
     166           0 : CSSAnimation::QueueEvents(StickyTimeDuration aActiveTime)
     167             : {
     168             :   // If the animation is pending, we ignore animation events until we finish
     169             :   // pending.
     170           0 :   if (mPendingState != PendingState::NotPending) {
     171           0 :     return;
     172             :   }
     173             : 
     174             :   // CSS animations dispatch events at their owning element. This allows
     175             :   // script to repurpose a CSS animation to target a different element,
     176             :   // to use a group effect (which has no obvious "target element"), or
     177             :   // to remove the animation effect altogether whilst still getting
     178             :   // animation events.
     179             :   //
     180             :   // It does mean, however, that for a CSS animation that has no owning
     181             :   // element (e.g. it was created using the CSSAnimation constructor or
     182             :   // disassociated from CSS) no events are fired. If it becomes desirable
     183             :   // for these animations to still fire events we should spec the concept
     184             :   // of the "original owning element" or "event target" and allow script
     185             :   // to set it when creating a CSSAnimation object.
     186           0 :   if (!mOwningElement.IsSet()) {
     187           0 :     return;
     188             :   }
     189             : 
     190             :   dom::Element* owningElement;
     191             :   CSSPseudoElementType owningPseudoType;
     192           0 :   mOwningElement.GetElement(owningElement, owningPseudoType);
     193           0 :   MOZ_ASSERT(owningElement, "Owning element should be set");
     194             : 
     195             :   // Get the nsAnimationManager so we can queue events on it
     196             :   nsPresContext* presContext =
     197           0 :     nsContentUtils::GetContextForContent(owningElement);
     198           0 :   if (!presContext) {
     199           0 :     return;
     200             :   }
     201           0 :   nsAnimationManager* manager = presContext->AnimationManager();
     202             : 
     203           0 :   const StickyTimeDuration zeroDuration;
     204           0 :   uint64_t currentIteration = 0;
     205             :   ComputedTiming::AnimationPhase currentPhase;
     206           0 :   StickyTimeDuration intervalStartTime;
     207           0 :   StickyTimeDuration intervalEndTime;
     208           0 :   StickyTimeDuration iterationStartTime;
     209             : 
     210           0 :   if (!mEffect) {
     211             :     currentPhase = GetAnimationPhaseWithoutEffect
     212           0 :       <ComputedTiming::AnimationPhase>(*this);
     213             :   } else {
     214           0 :     ComputedTiming computedTiming = mEffect->GetComputedTiming();
     215           0 :     currentPhase = computedTiming.mPhase;
     216           0 :     currentIteration = computedTiming.mCurrentIteration;
     217           0 :     if (currentPhase == mPreviousPhase &&
     218           0 :         currentIteration == mPreviousIteration) {
     219           0 :       return;
     220             :     }
     221           0 :     intervalStartTime =
     222           0 :       std::max(std::min(StickyTimeDuration(-mEffect->SpecifiedTiming().Delay()),
     223             :                         computedTiming.mActiveDuration),
     224           0 :                zeroDuration);
     225           0 :     intervalEndTime =
     226           0 :       std::max(std::min((EffectEnd() - mEffect->SpecifiedTiming().Delay()),
     227             :                         computedTiming.mActiveDuration),
     228           0 :                zeroDuration);
     229             : 
     230           0 :     uint64_t iterationBoundary = mPreviousIteration > currentIteration
     231           0 :                                  ? currentIteration + 1
     232           0 :                                  : currentIteration;
     233             :     iterationStartTime  =
     234             :       computedTiming.mDuration.MultDouble(
     235           0 :         (iterationBoundary - computedTiming.mIterationStart));
     236             :   }
     237             : 
     238           0 :   TimeStamp startTimeStamp     = ElapsedTimeToTimeStamp(intervalStartTime);
     239           0 :   TimeStamp endTimeStamp       = ElapsedTimeToTimeStamp(intervalEndTime);
     240           0 :   TimeStamp iterationTimeStamp = ElapsedTimeToTimeStamp(iterationStartTime);
     241             : 
     242           0 :   AutoTArray<AnimationEventParams, 2> events;
     243             : 
     244             :   // Handle cancel event first
     245           0 :   if ((mPreviousPhase != AnimationPhase::Idle &&
     246           0 :        mPreviousPhase != AnimationPhase::After) &&
     247             :       currentPhase == AnimationPhase::Idle) {
     248           0 :     TimeStamp activeTimeStamp = ElapsedTimeToTimeStamp(aActiveTime);
     249           0 :     events.AppendElement(AnimationEventParams{ eAnimationCancel,
     250             :                                                aActiveTime,
     251           0 :                                                activeTimeStamp });
     252             :   }
     253             : 
     254           0 :   switch (mPreviousPhase) {
     255             :     case AnimationPhase::Idle:
     256             :     case AnimationPhase::Before:
     257           0 :       if (currentPhase == AnimationPhase::Active) {
     258           0 :         events.AppendElement(AnimationEventParams{ eAnimationStart,
     259             :                                                    intervalStartTime,
     260           0 :                                                    startTimeStamp });
     261           0 :       } else if (currentPhase == AnimationPhase::After) {
     262           0 :         events.AppendElement(AnimationEventParams{ eAnimationStart,
     263             :                                                    intervalStartTime,
     264           0 :                                                    startTimeStamp });
     265           0 :         events.AppendElement(AnimationEventParams{ eAnimationEnd,
     266             :                                                    intervalEndTime,
     267           0 :                                                    endTimeStamp });
     268             :       }
     269           0 :       break;
     270             :     case AnimationPhase::Active:
     271           0 :       if (currentPhase == AnimationPhase::Before) {
     272           0 :         events.AppendElement(AnimationEventParams{ eAnimationEnd,
     273             :                                                    intervalStartTime,
     274           0 :                                                    startTimeStamp });
     275           0 :       } else if (currentPhase == AnimationPhase::Active) {
     276             :         // The currentIteration must have changed or element we would have
     277             :         // returned early above.
     278           0 :         MOZ_ASSERT(currentIteration != mPreviousIteration);
     279           0 :         events.AppendElement(AnimationEventParams{ eAnimationIteration,
     280             :                                                    iterationStartTime,
     281           0 :                                                    iterationTimeStamp });
     282           0 :       } else if (currentPhase == AnimationPhase::After) {
     283           0 :         events.AppendElement(AnimationEventParams{ eAnimationEnd,
     284             :                                                    intervalEndTime,
     285           0 :                                                    endTimeStamp });
     286             :       }
     287           0 :       break;
     288             :     case AnimationPhase::After:
     289           0 :       if (currentPhase == AnimationPhase::Before) {
     290           0 :         events.AppendElement(AnimationEventParams{ eAnimationStart,
     291             :                                                    intervalEndTime,
     292           0 :                                                    startTimeStamp});
     293           0 :         events.AppendElement(AnimationEventParams{ eAnimationEnd,
     294             :                                                    intervalStartTime,
     295           0 :                                                    endTimeStamp });
     296           0 :       } else if (currentPhase == AnimationPhase::Active) {
     297           0 :         events.AppendElement(AnimationEventParams{ eAnimationStart,
     298             :                                                    intervalEndTime,
     299           0 :                                                    endTimeStamp });
     300             :       }
     301           0 :       break;
     302             :   }
     303           0 :   mPreviousPhase = currentPhase;
     304           0 :   mPreviousIteration = currentIteration;
     305             : 
     306           0 :   for (const AnimationEventParams& event : events){
     307             :     manager->QueueEvent(
     308           0 :                AnimationEventInfo(owningElement, owningPseudoType,
     309           0 :                                   event.mMessage, mAnimationName,
     310             :                                   event.mElapsedTime, event.mTimeStamp,
     311           0 :                                   this));
     312             :   }
     313             : }
     314             : 
     315             : void
     316           0 : CSSAnimation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag)
     317             : {
     318           0 :   if (mNeedsNewAnimationIndexWhenRun &&
     319           0 :       PlayState() != AnimationPlayState::Idle) {
     320           0 :     mAnimationIndex = sNextAnimationIndex++;
     321           0 :     mNeedsNewAnimationIndexWhenRun = false;
     322             :   }
     323             : 
     324           0 :   Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag);
     325           0 : }
     326             : 
     327             : ////////////////////////// nsAnimationManager ////////////////////////////
     328             : 
     329           0 : NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher)
     330             : 
     331           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsAnimationManager, AddRef)
     332           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsAnimationManager, Release)
     333             : 
     334             : // Find the matching animation by |aName| in the old list
     335             : // of animations and remove the matched animation from the list.
     336             : static already_AddRefed<CSSAnimation>
     337           0 : PopExistingAnimation(const nsAString& aName,
     338             :                      nsAnimationManager::CSSAnimationCollection* aCollection)
     339             : {
     340           0 :   if (!aCollection) {
     341           0 :     return nullptr;
     342             :   }
     343             : 
     344             :   // Animations are stored in reverse order to how they appear in the
     345             :   // animation-name property. However, we want to match animations beginning
     346             :   // from the end of the animation-name list, so we iterate *forwards*
     347             :   // through the collection.
     348           0 :   for (size_t idx = 0, length = aCollection->mAnimations.Length();
     349           0 :        idx != length; ++ idx) {
     350           0 :     CSSAnimation* cssAnim = aCollection->mAnimations[idx];
     351           0 :     if (cssAnim->AnimationName() == aName) {
     352           0 :       RefPtr<CSSAnimation> match = cssAnim;
     353           0 :       aCollection->mAnimations.RemoveElementAt(idx);
     354           0 :       return match.forget();
     355             :     }
     356             :   }
     357             : 
     358           0 :   return nullptr;
     359             : }
     360             : 
     361        1629 : class ResolvedStyleCache {
     362             : public:
     363        1629 :   ResolvedStyleCache() : mCache() {}
     364             :   nsStyleContext* Get(nsPresContext *aPresContext,
     365             :                       nsStyleContext *aParentStyleContext,
     366             :                       Declaration* aKeyframeDeclaration);
     367             : 
     368             : private:
     369             :   nsRefPtrHashtable<nsPtrHashKey<Declaration>, nsStyleContext> mCache;
     370             : };
     371             : 
     372             : nsStyleContext*
     373           0 : ResolvedStyleCache::Get(nsPresContext *aPresContext,
     374             :                         nsStyleContext *aParentStyleContext,
     375             :                         Declaration* aKeyframeDeclaration)
     376             : {
     377             :   // FIXME (spec):  The css3-animations spec isn't very clear about how
     378             :   // properties are resolved when they have values that depend on other
     379             :   // properties (e.g., values in 'em').  I presume that they're resolved
     380             :   // relative to the other styles of the element.  The question is
     381             :   // whether they are resolved relative to other animations:  I assume
     382             :   // that they're not, since that would prevent us from caching a lot of
     383             :   // data that we'd really like to cache (in particular, the
     384             :   // StyleAnimationValue values in AnimationPropertySegment).
     385           0 :   nsStyleContext *result = mCache.GetWeak(aKeyframeDeclaration);
     386           0 :   if (!result) {
     387           0 :     aKeyframeDeclaration->SetImmutable();
     388             :     // The spec says that !important declarations should just be ignored
     389           0 :     MOZ_ASSERT(!aKeyframeDeclaration->HasImportantData(),
     390             :                "Keyframe rule has !important data");
     391             : 
     392           0 :     nsCOMArray<nsIStyleRule> rules;
     393           0 :     rules.AppendObject(aKeyframeDeclaration);
     394           0 :     MOZ_ASSERT(aPresContext->StyleSet()->IsGecko(),
     395             :                "ServoStyleSet should not use nsAnimationManager for "
     396             :                "animations");
     397           0 :     RefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()->AsGecko()->
     398           0 :       ResolveStyleByAddingRules(aParentStyleContext, rules);
     399           0 :     mCache.Put(aKeyframeDeclaration, resultStrong);
     400           0 :     result = resultStrong;
     401             :   }
     402           0 :   return result;
     403             : }
     404             : 
     405             : class MOZ_STACK_CLASS ServoCSSAnimationBuilder final {
     406             : public:
     407           0 :   explicit ServoCSSAnimationBuilder(const ServoComputedValues* aComputedValues)
     408           0 :     : mComputedValues(aComputedValues)
     409             :   {
     410           0 :     MOZ_ASSERT(aComputedValues);
     411           0 :   }
     412             : 
     413           0 :   bool BuildKeyframes(nsPresContext* aPresContext,
     414             :                       const StyleAnimation& aSrc,
     415             :                       nsTArray<Keyframe>& aKeyframes)
     416             :   {
     417           0 :     ServoStyleSet* styleSet = aPresContext->StyleSet()->AsServo();
     418           0 :     MOZ_ASSERT(styleSet);
     419           0 :     const nsTimingFunction& timingFunction = aSrc.GetTimingFunction();
     420           0 :     return styleSet->GetKeyframesForName(aSrc.GetName(),
     421             :                                          timingFunction,
     422           0 :                                          aKeyframes);
     423             :   }
     424           0 :   void SetKeyframes(KeyframeEffectReadOnly& aEffect,
     425             :                     nsTArray<Keyframe>&& aKeyframes)
     426             :   {
     427           0 :     aEffect.SetKeyframes(Move(aKeyframes), mComputedValues);
     428           0 :   }
     429             : 
     430             : private:
     431             :   const ServoComputedValues* mComputedValues;
     432             : };
     433             : 
     434        1629 : class MOZ_STACK_CLASS GeckoCSSAnimationBuilder final {
     435             : public:
     436        1629 :   GeckoCSSAnimationBuilder(nsStyleContext* aStyleContext,
     437             :                            const NonOwningAnimationTarget& aTarget)
     438        1629 :     : mStyleContext(aStyleContext)
     439        1629 :     , mTarget(aTarget)
     440             :   {
     441        1629 :     MOZ_ASSERT(aStyleContext);
     442        1629 :     MOZ_ASSERT(aTarget.mElement);
     443        1629 :   }
     444             : 
     445             :   bool BuildKeyframes(nsPresContext* aPresContext,
     446             :                       const StyleAnimation& aSrc,
     447             :                       nsTArray<Keyframe>& aKeyframs);
     448           0 :   void SetKeyframes(KeyframeEffectReadOnly& aEffect,
     449             :                     nsTArray<Keyframe>&& aKeyframes)
     450             :   {
     451           0 :     aEffect.SetKeyframes(Move(aKeyframes), mStyleContext);
     452           0 :   }
     453             : 
     454             : private:
     455             :   nsTArray<Keyframe> BuildAnimationFrames(nsPresContext* aPresContext,
     456             :                                           const StyleAnimation& aSrc,
     457             :                                           const nsCSSKeyframesRule* aRule);
     458             :   Maybe<ComputedTimingFunction> GetKeyframeTimingFunction(
     459             :     nsPresContext* aPresContext,
     460             :     nsCSSKeyframeRule* aKeyframeRule,
     461             :     const Maybe<ComputedTimingFunction>& aInheritedTimingFunction);
     462             :   nsTArray<PropertyValuePair> GetKeyframePropertyValues(
     463             :     nsPresContext* aPresContext,
     464             :     nsCSSKeyframeRule* aKeyframeRule,
     465             :     nsCSSPropertyIDSet& aAnimatedProperties);
     466             :   void FillInMissingKeyframeValues(
     467             :     nsCSSPropertyIDSet aAnimatedProperties,
     468             :     nsCSSPropertyIDSet aPropertiesSetAtStart,
     469             :     nsCSSPropertyIDSet aPropertiesSetAtEnd,
     470             :     const Maybe<ComputedTimingFunction>& aInheritedTimingFunction,
     471             :     nsTArray<Keyframe>& aKeyframes);
     472             : 
     473             :   RefPtr<nsStyleContext> mStyleContext;
     474             :   NonOwningAnimationTarget mTarget;
     475             : 
     476             :   ResolvedStyleCache mResolvedStyles;
     477             : };
     478             : 
     479             : static Maybe<ComputedTimingFunction>
     480             : ConvertTimingFunction(const nsTimingFunction& aTimingFunction);
     481             : 
     482             : template<class BuilderType>
     483             : static void
     484           0 : UpdateOldAnimationPropertiesWithNew(
     485             :     CSSAnimation& aOld,
     486             :     TimingParams& aNewTiming,
     487             :     nsTArray<Keyframe>&& aNewKeyframes,
     488             :     bool aNewIsStylePaused,
     489             :     BuilderType& aBuilder)
     490             : {
     491           0 :   bool animationChanged = false;
     492             : 
     493             :   // Update the old from the new so we can keep the original object
     494             :   // identity (and any expando properties attached to it).
     495           0 :   if (aOld.GetEffect()) {
     496           0 :     dom::AnimationEffectReadOnly* oldEffect = aOld.GetEffect();
     497           0 :     animationChanged = oldEffect->SpecifiedTiming() != aNewTiming;
     498           0 :     oldEffect->SetSpecifiedTiming(aNewTiming);
     499             : 
     500           0 :     KeyframeEffectReadOnly* oldKeyframeEffect = oldEffect->AsKeyframeEffect();
     501           0 :     if (oldKeyframeEffect) {
     502           0 :       aBuilder.SetKeyframes(*oldKeyframeEffect, Move(aNewKeyframes));
     503             :     }
     504             :   }
     505             : 
     506             :   // Handle changes in play state. If the animation is idle, however,
     507             :   // changes to animation-play-state should *not* restart it.
     508           0 :   if (aOld.PlayState() != AnimationPlayState::Idle) {
     509             :     // CSSAnimation takes care of override behavior so that,
     510             :     // for example, if the author has called pause(), that will
     511             :     // override the animation-play-state.
     512             :     // (We should check aNew->IsStylePaused() but that requires
     513             :     //  downcasting to CSSAnimation and we happen to know that
     514             :     //  aNew will only ever be paused by calling PauseFromStyle
     515             :     //  making IsPausedOrPausing synonymous in this case.)
     516           0 :     if (!aOld.IsStylePaused() && aNewIsStylePaused) {
     517           0 :       aOld.PauseFromStyle();
     518           0 :       animationChanged = true;
     519           0 :     } else if (aOld.IsStylePaused() && !aNewIsStylePaused) {
     520           0 :       aOld.PlayFromStyle();
     521           0 :       animationChanged = true;
     522             :     }
     523             :   }
     524             : 
     525             :   // Updating the effect timing above might already have caused the
     526             :   // animation to become irrelevant so only add a changed record if
     527             :   // the animation is still relevant.
     528           0 :   if (animationChanged && aOld.IsRelevant()) {
     529           0 :     nsNodeUtils::AnimationChanged(&aOld);
     530             :   }
     531           0 : }
     532             : 
     533             : // Returns a new animation set up with given StyleAnimation.
     534             : // Or returns an existing animation matching StyleAnimation's name updated
     535             : // with the new StyleAnimation.
     536             : template<class BuilderType>
     537             : static already_AddRefed<CSSAnimation>
     538           0 : BuildAnimation(nsPresContext* aPresContext,
     539             :                const NonOwningAnimationTarget& aTarget,
     540             :                const StyleAnimation& aSrc,
     541             :                BuilderType& aBuilder,
     542             :                nsAnimationManager::CSSAnimationCollection* aCollection)
     543             : {
     544           0 :   MOZ_ASSERT(aPresContext);
     545             : 
     546           0 :   nsTArray<Keyframe> keyframes;
     547           0 :   if (!aBuilder.BuildKeyframes(aPresContext, aSrc, keyframes)) {
     548           0 :     return nullptr;
     549             :   }
     550             : 
     551             :   TimingParams timing = TimingParamsFromCSSParams(aSrc.GetDuration(),
     552             :                                                   aSrc.GetDelay(),
     553             :                                                   aSrc.GetIterationCount(),
     554             :                                                   aSrc.GetDirection(),
     555           0 :                                                   aSrc.GetFillMode());
     556             : 
     557             :   bool isStylePaused =
     558           0 :     aSrc.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED;
     559             : 
     560             :   // Find the matching animation with animation name in the old list
     561             :   // of animations and remove the matched animation from the list.
     562             :   RefPtr<CSSAnimation> oldAnim =
     563           0 :     PopExistingAnimation(aSrc.GetName(), aCollection);
     564             : 
     565           0 :   if (oldAnim) {
     566             :     // Copy over the start times and (if still paused) pause starts
     567             :     // for each animation (matching on name only) that was also in the
     568             :     // old list of animations.
     569             :     // This means that we honor dynamic changes, which isn't what the
     570             :     // spec says to do, but WebKit seems to honor at least some of
     571             :     // them.  See
     572             :     // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html
     573             :     // In order to honor what the spec said, we'd copy more data over.
     574           0 :     UpdateOldAnimationPropertiesWithNew(*oldAnim,
     575             :                                         timing,
     576           0 :                                         Move(keyframes),
     577             :                                         isStylePaused,
     578             :                                         aBuilder);
     579           0 :     return oldAnim.forget();
     580             :   }
     581             : 
     582             :   // mTarget is non-null here, so we emplace it directly.
     583           0 :   Maybe<OwningAnimationTarget> target;
     584           0 :   target.emplace(aTarget.mElement, aTarget.mPseudoType);
     585           0 :   KeyframeEffectParams effectOptions;
     586             :   RefPtr<KeyframeEffectReadOnly> effect =
     587           0 :     new KeyframeEffectReadOnly(aPresContext->Document(), target, timing,
     588           0 :                                effectOptions);
     589             : 
     590           0 :   aBuilder.SetKeyframes(*effect, Move(keyframes));
     591             : 
     592             :   RefPtr<CSSAnimation> animation =
     593           0 :     new CSSAnimation(aPresContext->Document()->GetScopeObject(),
     594           0 :                      aSrc.GetName());
     595           0 :   animation->SetOwningElement(
     596           0 :     OwningElementRef(*aTarget.mElement, aTarget.mPseudoType));
     597             : 
     598           0 :   animation->SetTimelineNoUpdate(aTarget.mElement->OwnerDoc()->Timeline());
     599           0 :   animation->SetEffectNoUpdate(effect);
     600             : 
     601           0 :   if (isStylePaused) {
     602           0 :     animation->PauseFromStyle();
     603             :   } else {
     604           0 :     animation->PlayFromStyle();
     605             :   }
     606             : 
     607           0 :   return animation.forget();
     608             : }
     609             : 
     610             : bool
     611           0 : GeckoCSSAnimationBuilder::BuildKeyframes(nsPresContext* aPresContext,
     612             :                                          const StyleAnimation& aSrc,
     613             :                                          nsTArray<Keyframe>& aKeyframes)
     614             : {
     615           0 :   MOZ_ASSERT(aPresContext);
     616           0 :   MOZ_ASSERT(aPresContext->StyleSet()->IsGecko());
     617             : 
     618             :   nsCSSKeyframesRule* rule =
     619           0 :     aPresContext->StyleSet()->AsGecko()->KeyframesRuleForName(aSrc.GetName());
     620           0 :   if (!rule) {
     621           0 :     return false;
     622             :   }
     623             : 
     624           0 :   aKeyframes = BuildAnimationFrames(aPresContext, aSrc, rule);
     625             : 
     626           0 :   return true;
     627             : }
     628             : 
     629             : nsTArray<Keyframe>
     630           0 : GeckoCSSAnimationBuilder::BuildAnimationFrames(nsPresContext* aPresContext,
     631             :                                                const StyleAnimation& aSrc,
     632             :                                                const nsCSSKeyframesRule* aRule)
     633             : {
     634             :   // Ideally we'd like to build up a set of Keyframe objects that more-or-less
     635             :   // reflect the keyframes as-specified in the @keyframes rule(s) so that
     636             :   // authors get something intuitive when they call anim.effect.getKeyframes().
     637             :   //
     638             :   // That, however, proves to be difficult because the way CSS declarations are
     639             :   // processed differs from how we are able to represent keyframes as
     640             :   // JavaScript objects in the Web Animations API.
     641             :   //
     642             :   // For example,
     643             :   //
     644             :   //   { margin: 10px; margin-left: 20px }
     645             :   //
     646             :   // could be represented as:
     647             :   //
     648             :   //   { margin: '10px', marginLeft: '20px' }
     649             :   //
     650             :   // BUT:
     651             :   //
     652             :   //   { margin-left: 20px; margin: 10px }
     653             :   //
     654             :   // would be represented as:
     655             :   //
     656             :   //   { margin: '10px' }
     657             :   //
     658             :   // Likewise,
     659             :   //
     660             :   //   { margin-left: 20px; margin-left: 30px }
     661             :   //
     662             :   // would be represented as:
     663             :   //
     664             :   //   { marginLeft: '30px' }
     665             :   //
     666             :   // As such, the mapping between source @keyframes and the Keyframe objects
     667             :   // becomes obscured. The deviation is even more significant when we consider
     668             :   // cascading between @keyframes rules and variable references in shorthand
     669             :   // properties.
     670             :   //
     671             :   // We could, perhaps, produce a mapping that makes sense most of the time
     672             :   // but it would be complex and need to be specified and implemented
     673             :   // interoperably. Instead, for now, for CSS Animations (and CSS Transitions,
     674             :   // for that matter) we resolve values on @keyframes down to computed values
     675             :   // (thereby expanding shorthands and variable references) and then pick up the
     676             :   // last value for each longhand property at each offset.
     677             : 
     678             :   // FIXME: There is a pending spec change to make multiple @keyframes
     679             :   // rules with the same name cascade but we don't support that yet.
     680             : 
     681             :   Maybe<ComputedTimingFunction> inheritedTimingFunction =
     682           0 :     ConvertTimingFunction(aSrc.GetTimingFunction());
     683             : 
     684             :   // First, make up Keyframe objects for each rule
     685           0 :   nsTArray<Keyframe> keyframes;
     686           0 :   nsCSSPropertyIDSet animatedProperties;
     687             : 
     688           0 :   for (auto ruleIdx = 0, ruleEnd = aRule->StyleRuleCount();
     689           0 :        ruleIdx != ruleEnd; ++ruleIdx) {
     690           0 :     css::Rule* cssRule = aRule->GetStyleRuleAt(ruleIdx);
     691           0 :     MOZ_ASSERT(cssRule, "must have rule");
     692           0 :     MOZ_ASSERT(cssRule->GetType() == css::Rule::KEYFRAME_RULE,
     693             :                "must be keyframe rule");
     694           0 :     nsCSSKeyframeRule* keyframeRule = static_cast<nsCSSKeyframeRule*>(cssRule);
     695             : 
     696           0 :     const nsTArray<float>& keys = keyframeRule->GetKeys();
     697           0 :     for (float key : keys) {
     698           0 :       if (key < 0.0f || key > 1.0f) {
     699           0 :         continue;
     700             :       }
     701             : 
     702           0 :       Keyframe keyframe;
     703           0 :       keyframe.mOffset.emplace(key);
     704             :       keyframe.mTimingFunction =
     705           0 :         GetKeyframeTimingFunction(aPresContext, keyframeRule,
     706           0 :                                   inheritedTimingFunction);
     707             :       keyframe.mPropertyValues =
     708           0 :         GetKeyframePropertyValues(aPresContext, keyframeRule,
     709           0 :                                   animatedProperties);
     710             : 
     711           0 :       keyframes.AppendElement(Move(keyframe));
     712             :     }
     713             :   }
     714             : 
     715             :   // Next, stable sort by offset
     716           0 :   std::stable_sort(keyframes.begin(), keyframes.end(),
     717           0 :                    [](const Keyframe& a, const Keyframe& b)
     718             :                    {
     719           0 :                      return a.mOffset < b.mOffset;
     720           0 :                    });
     721             : 
     722             :   // Then walk backwards through the keyframes and drop overridden properties.
     723           0 :   nsCSSPropertyIDSet propertiesSetAtCurrentOffset;
     724           0 :   nsCSSPropertyIDSet propertiesSetAtStart;
     725           0 :   nsCSSPropertyIDSet propertiesSetAtEnd;
     726           0 :   double currentOffset = -1.0;
     727           0 :   for (size_t keyframeIdx = keyframes.Length();
     728           0 :        keyframeIdx > 0;
     729             :        --keyframeIdx) {
     730           0 :     Keyframe& keyframe = keyframes[keyframeIdx - 1];
     731           0 :     MOZ_ASSERT(keyframe.mOffset, "Should have filled in the offset");
     732             : 
     733           0 :     if (keyframe.mOffset.value() != currentOffset) {
     734           0 :       propertiesSetAtCurrentOffset.Empty();
     735           0 :       currentOffset = keyframe.mOffset.value();
     736             :     }
     737             : 
     738             :     // Get the set of properties from this keyframe that have not
     739             :     // already been set at this offset.
     740           0 :     nsTArray<PropertyValuePair> uniquePropertyValues;
     741           0 :     uniquePropertyValues.SetCapacity(keyframe.mPropertyValues.Length());
     742           0 :     for (const PropertyValuePair& pair : keyframe.mPropertyValues) {
     743           0 :       if (!propertiesSetAtCurrentOffset.HasProperty(pair.mProperty)) {
     744           0 :         uniquePropertyValues.AppendElement(pair);
     745           0 :         propertiesSetAtCurrentOffset.AddProperty(pair.mProperty);
     746             : 
     747           0 :         if (currentOffset == 0.0) {
     748           0 :           propertiesSetAtStart.AddProperty(pair.mProperty);
     749           0 :         } else if (currentOffset == 1.0) {
     750           0 :           propertiesSetAtEnd.AddProperty(pair.mProperty);
     751             :         }
     752             :       }
     753             :     }
     754             : 
     755             :     // If we have a keyframe at the same offset with the same timing
     756             :     // function we should merge our (unique) values into it.
     757             :     // Otherwise, we should update the existing keyframe with only the
     758             :     // unique properties.
     759             :     //
     760             :     // Bug 1293490: We should also match composite modes here.
     761           0 :     Keyframe* existingKeyframe = nullptr;
     762             :     // Don't bother searching for an existing keyframe if we don't
     763             :     // have anything to contribute to it.
     764           0 :     if (!uniquePropertyValues.IsEmpty()) {
     765           0 :       for (size_t i = keyframeIdx; i < keyframes.Length(); i++) {
     766           0 :         Keyframe& kf = keyframes[i];
     767           0 :         if (kf.mOffset.value() != currentOffset) {
     768           0 :           break;
     769             :         }
     770           0 :         if (kf.mTimingFunction == keyframe.mTimingFunction) {
     771           0 :           existingKeyframe = &kf;
     772           0 :           break;
     773             :         }
     774             :       }
     775             :     }
     776             : 
     777           0 :     if (existingKeyframe) {
     778             :       existingKeyframe->
     779           0 :         mPropertyValues.AppendElements(Move(uniquePropertyValues));
     780           0 :       keyframe.mPropertyValues.Clear();
     781             :     } else {
     782           0 :       keyframe.mPropertyValues.SwapElements(uniquePropertyValues);
     783             :     }
     784             : 
     785             :     // Check for a now-empty keyframe
     786           0 :     if (keyframe.mPropertyValues.IsEmpty()) {
     787           0 :       keyframes.RemoveElementAt(keyframeIdx - 1);
     788             :       // existingKeyframe might dangle now
     789             :     }
     790             :   }
     791             : 
     792             :   // Finally, we need to look for any animated properties that have an
     793             :   // implicit 'to' or 'from' value and fill in the appropriate keyframe
     794             :   // with the current computed style.
     795             :   FillInMissingKeyframeValues(animatedProperties, propertiesSetAtStart,
     796             :                               propertiesSetAtEnd, inheritedTimingFunction,
     797           0 :                               keyframes);
     798             : 
     799           0 :   return keyframes;
     800             : }
     801             : 
     802             : Maybe<ComputedTimingFunction>
     803           0 : GeckoCSSAnimationBuilder::GetKeyframeTimingFunction(
     804             :     nsPresContext* aPresContext,
     805             :     nsCSSKeyframeRule* aKeyframeRule,
     806             :     const Maybe<ComputedTimingFunction>& aInheritedTimingFunction)
     807             : {
     808           0 :   Maybe<ComputedTimingFunction> result;
     809             : 
     810           0 :   if (aKeyframeRule->Declaration() &&
     811           0 :       aKeyframeRule->Declaration()->HasProperty(
     812             :         eCSSProperty_animation_timing_function)) {
     813             :     RefPtr<nsStyleContext> keyframeRuleContext =
     814             :       mResolvedStyles.Get(aPresContext, mStyleContext,
     815           0 :                           aKeyframeRule->Declaration());
     816           0 :     const nsTimingFunction& tf = keyframeRuleContext->StyleDisplay()->
     817           0 :       mAnimations[0].GetTimingFunction();
     818           0 :     result = ConvertTimingFunction(tf);
     819             :   } else {
     820           0 :     result = aInheritedTimingFunction;
     821             :   }
     822             : 
     823           0 :   return result;
     824             : }
     825             : 
     826             : static Maybe<ComputedTimingFunction>
     827           0 : ConvertTimingFunction(const nsTimingFunction& aTimingFunction)
     828             : {
     829           0 :   Maybe<ComputedTimingFunction> result;
     830             : 
     831           0 :   if (aTimingFunction.mType != nsTimingFunction::Type::Linear) {
     832           0 :     result.emplace();
     833           0 :     result->Init(aTimingFunction);
     834             :   }
     835             : 
     836           0 :   return result;
     837             : }
     838             : 
     839             : nsTArray<PropertyValuePair>
     840           0 : GeckoCSSAnimationBuilder::GetKeyframePropertyValues(
     841             :     nsPresContext* aPresContext,
     842             :     nsCSSKeyframeRule* aKeyframeRule,
     843             :     nsCSSPropertyIDSet& aAnimatedProperties)
     844             : {
     845           0 :   nsTArray<PropertyValuePair> result;
     846             :   RefPtr<nsStyleContext> styleContext =
     847             :     mResolvedStyles.Get(aPresContext, mStyleContext,
     848           0 :                         aKeyframeRule->Declaration());
     849             : 
     850           0 :   for (nsCSSPropertyID prop = nsCSSPropertyID(0);
     851           0 :        prop < eCSSProperty_COUNT_no_shorthands;
     852           0 :        prop = nsCSSPropertyID(prop + 1)) {
     853           0 :     if (nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None ||
     854           0 :         !aKeyframeRule->Declaration()->HasNonImportantValueFor(prop)) {
     855           0 :       continue;
     856             :     }
     857             : 
     858           0 :     StyleAnimationValue computedValue;
     859           0 :     if (!StyleAnimationValue::ExtractComputedValue(prop, styleContext,
     860             :                                                    computedValue)) {
     861           0 :       continue;
     862             :     }
     863             : 
     864           0 :     nsCSSValue propertyValue;
     865             :     DebugOnly<bool> uncomputeResult =
     866           0 :       StyleAnimationValue::UncomputeValue(prop, Move(computedValue),
     867           0 :                                           propertyValue);
     868           0 :     MOZ_ASSERT(uncomputeResult,
     869             :                "Unable to get specified value from computed value");
     870           0 :     MOZ_ASSERT(propertyValue.GetUnit() != eCSSUnit_Null,
     871             :                "Not expecting to read invalid properties");
     872             : 
     873           0 :     result.AppendElement(Move(PropertyValuePair(prop, Move(propertyValue))));
     874           0 :     aAnimatedProperties.AddProperty(prop);
     875             :   }
     876             : 
     877           0 :   return result;
     878             : }
     879             : 
     880             : void
     881           0 : GeckoCSSAnimationBuilder::FillInMissingKeyframeValues(
     882             :     nsCSSPropertyIDSet aAnimatedProperties,
     883             :     nsCSSPropertyIDSet aPropertiesSetAtStart,
     884             :     nsCSSPropertyIDSet aPropertiesSetAtEnd,
     885             :     const Maybe<ComputedTimingFunction>& aInheritedTimingFunction,
     886             :     nsTArray<Keyframe>& aKeyframes)
     887             : {
     888             :   static const size_t kNotSet = static_cast<size_t>(-1);
     889             : 
     890             :   // Find/create the keyframe to add start values to
     891           0 :   size_t startKeyframeIndex = kNotSet;
     892           0 :   if (!aAnimatedProperties.Equals(aPropertiesSetAtStart) &&
     893           0 :       !nsAnimationManager::FindMatchingKeyframe(aKeyframes,
     894             :                                                 0.0,
     895             :                                                 aInheritedTimingFunction,
     896             :                                                 startKeyframeIndex)) {
     897           0 :     Keyframe newKeyframe;
     898           0 :     newKeyframe.mOffset.emplace(0.0);
     899           0 :     newKeyframe.mTimingFunction = aInheritedTimingFunction;
     900           0 :     aKeyframes.InsertElementAt(startKeyframeIndex, Move(newKeyframe));
     901             :   }
     902             : 
     903             :   // Find/create the keyframe to add end values to
     904           0 :   size_t endKeyframeIndex = kNotSet;
     905           0 :   if (!aAnimatedProperties.Equals(aPropertiesSetAtEnd)) {
     906           0 :     if (!nsAnimationManager::FindMatchingKeyframe(Reversed(aKeyframes),
     907             :                                                   1.0,
     908             :                                                   aInheritedTimingFunction,
     909             :                                                   endKeyframeIndex)) {
     910           0 :       Keyframe newKeyframe;
     911           0 :       newKeyframe.mOffset.emplace(1.0);
     912           0 :       newKeyframe.mTimingFunction = aInheritedTimingFunction;
     913           0 :       aKeyframes.AppendElement(Move(newKeyframe));
     914           0 :       endKeyframeIndex = aKeyframes.Length() - 1;
     915             :     } else {
     916             :       // endKeyframeIndex is currently a count from the end of the array
     917             :       // so we need to reverse it.
     918           0 :       endKeyframeIndex = aKeyframes.Length() - 1 - endKeyframeIndex;
     919             :     }
     920             :   }
     921             : 
     922           0 :   if (startKeyframeIndex == kNotSet && endKeyframeIndex == kNotSet) {
     923           0 :     return;
     924             :   }
     925             : 
     926             :   // Now that we have finished manipulating aKeyframes, it is safe to
     927             :   // take pointers to its elements.
     928           0 :   Keyframe* startKeyframe = startKeyframeIndex == kNotSet
     929           0 :                             ? nullptr : &aKeyframes[startKeyframeIndex];
     930           0 :   Keyframe* endKeyframe   = endKeyframeIndex == kNotSet
     931           0 :                             ? nullptr : &aKeyframes[endKeyframeIndex];
     932             : 
     933             :   // Iterate through all properties and fill-in missing values
     934           0 :   for (nsCSSPropertyID prop = nsCSSPropertyID(0);
     935           0 :        prop < eCSSProperty_COUNT_no_shorthands;
     936           0 :        prop = nsCSSPropertyID(prop + 1)) {
     937           0 :     if (!aAnimatedProperties.HasProperty(prop)) {
     938           0 :       continue;
     939             :     }
     940             : 
     941           0 :     if (startKeyframe && !aPropertiesSetAtStart.HasProperty(prop)) {
     942             :       // An uninitialized nsCSSValue represents the underlying value.
     943           0 :       PropertyValuePair propertyValue(prop, Move(nsCSSValue()));
     944           0 :       startKeyframe->mPropertyValues.AppendElement(Move(propertyValue));
     945             :     }
     946           0 :     if (endKeyframe && !aPropertiesSetAtEnd.HasProperty(prop)) {
     947             :       // An uninitialized nsCSSValue represents the underlying value.
     948           0 :       PropertyValuePair propertyValue(prop, Move(nsCSSValue()));
     949           0 :       endKeyframe->mPropertyValues.AppendElement(Move(propertyValue));
     950             :     }
     951             :   }
     952             : }
     953             : 
     954             : template<class BuilderType>
     955             : static nsAnimationManager::OwningCSSAnimationPtrArray
     956           0 : BuildAnimations(nsPresContext* aPresContext,
     957             :                 const NonOwningAnimationTarget& aTarget,
     958             :                 const nsStyleAutoArray<StyleAnimation>& aStyleAnimations,
     959             :                 uint32_t aStyleAnimationNameCount,
     960             :                 BuilderType& aBuilder,
     961             :                 nsAnimationManager::CSSAnimationCollection* aCollection)
     962             : {
     963           0 :   nsAnimationManager::OwningCSSAnimationPtrArray result;
     964             : 
     965           0 :   for (size_t animIdx = aStyleAnimationNameCount; animIdx-- != 0;) {
     966           0 :     const StyleAnimation& src = aStyleAnimations[animIdx];
     967             : 
     968             :     // CSS Animations whose animation-name does not match a @keyframes rule do
     969             :     // not generate animation events. This includes when the animation-name is
     970             :     // "none" which is represented by an empty name in the StyleAnimation.
     971             :     // Since such animations neither affect style nor dispatch events, we do
     972             :     // not generate a corresponding CSSAnimation for them.
     973           0 :     if (src.GetName().IsEmpty()) {
     974           0 :       continue;
     975             :     }
     976             : 
     977             :     RefPtr<CSSAnimation> dest = BuildAnimation(aPresContext,
     978             :                                                aTarget,
     979             :                                                src,
     980             :                                                aBuilder,
     981           0 :                                                aCollection);
     982           0 :     if (!dest) {
     983           0 :       continue;
     984             :     }
     985             : 
     986           0 :     dest->SetAnimationIndex(static_cast<uint64_t>(animIdx));
     987           0 :     result.AppendElement(dest);
     988             :   }
     989           0 :   return result;
     990             : }
     991             : 
     992             : void
     993        2834 : nsAnimationManager::UpdateAnimations(nsStyleContext* aStyleContext,
     994             :                                      mozilla::dom::Element* aElement)
     995             : {
     996        2834 :   MOZ_ASSERT(mPresContext->IsDynamic(),
     997             :              "Should not update animations for print or print preview");
     998        2834 :   MOZ_ASSERT(aElement->IsInComposedDoc(),
     999             :              "Should not update animations that are not attached to the "
    1000             :              "document tree");
    1001             : 
    1002        2834 :   if (aStyleContext->IsInDisplayNoneSubtree()) {
    1003        1205 :     StopAnimationsForElement(aElement, aStyleContext->GetPseudoType());
    1004        1205 :     return;
    1005             :   }
    1006             : 
    1007        1629 :   NonOwningAnimationTarget target(aElement, aStyleContext->GetPseudoType());
    1008        3258 :   GeckoCSSAnimationBuilder builder(aStyleContext, target);
    1009             : 
    1010        1629 :   const nsStyleDisplay* disp = aStyleContext->StyleDisplay();
    1011        1629 :   DoUpdateAnimations(target, *disp, builder);
    1012             : }
    1013             : 
    1014             : void
    1015           0 : nsAnimationManager::UpdateAnimations(
    1016             :   dom::Element* aElement,
    1017             :   CSSPseudoElementType aPseudoType,
    1018             :   const ServoComputedValues* aComputedValues)
    1019             : {
    1020           0 :   MOZ_ASSERT(mPresContext->IsDynamic(),
    1021             :              "Should not update animations for print or print preview");
    1022           0 :   MOZ_ASSERT(aElement->IsInComposedDoc(),
    1023             :              "Should not update animations that are not attached to the "
    1024             :              "document tree");
    1025             : 
    1026           0 :   if (!aComputedValues) {
    1027             :     // If we are in a display:none subtree we will have no computed values.
    1028             :     // Since CSS animations should not run in display:none subtrees we should
    1029             :     // stop (actually, destroy) any animations on this element here.
    1030           0 :     StopAnimationsForElement(aElement, aPseudoType);
    1031           0 :     return;
    1032             :   }
    1033             : 
    1034           0 :   NonOwningAnimationTarget target(aElement, aPseudoType);
    1035           0 :   ServoCSSAnimationBuilder builder(aComputedValues);
    1036             : 
    1037             :   const nsStyleDisplay *disp =
    1038           0 :     Servo_GetStyleDisplay(aComputedValues);
    1039           0 :   DoUpdateAnimations(target, *disp, builder);
    1040             : }
    1041             : 
    1042             : template<class BuilderType>
    1043             : void
    1044        1629 : nsAnimationManager::DoUpdateAnimations(
    1045             :   const NonOwningAnimationTarget& aTarget,
    1046             :   const nsStyleDisplay& aStyleDisplay,
    1047             :   BuilderType& aBuilder)
    1048             : {
    1049             :   // Everything that causes our animation data to change triggers a
    1050             :   // style change, which in turn triggers a non-animation restyle.
    1051             :   // Likewise, when we initially construct frames, we're not in a
    1052             :   // style change, but also not in an animation restyle.
    1053             : 
    1054             :   CSSAnimationCollection* collection =
    1055        1629 :     CSSAnimationCollection::GetAnimationCollection(aTarget.mElement,
    1056        3258 :                                                    aTarget.mPseudoType);
    1057        3258 :   if (!collection &&
    1058        3258 :       aStyleDisplay.mAnimationNameCount == 1 &&
    1059        1629 :       aStyleDisplay.mAnimations[0].GetName().IsEmpty()) {
    1060        3258 :     return;
    1061             :   }
    1062             : 
    1063           0 :   nsAutoAnimationMutationBatch mb(aTarget.mElement->OwnerDoc());
    1064             : 
    1065             :   // Build the updated animations list, extracting matching animations from
    1066             :   // the existing collection as we go.
    1067           0 :   OwningCSSAnimationPtrArray newAnimations;
    1068           0 :   newAnimations = BuildAnimations(mPresContext,
    1069             :                                   aTarget,
    1070             :                                   aStyleDisplay.mAnimations,
    1071           0 :                                   aStyleDisplay.mAnimationNameCount,
    1072             :                                   aBuilder,
    1073             :                                   collection);
    1074             : 
    1075           0 :   if (newAnimations.IsEmpty()) {
    1076           0 :     if (collection) {
    1077           0 :       collection->Destroy();
    1078             :     }
    1079           0 :     return;
    1080             :   }
    1081             : 
    1082           0 :   if (!collection) {
    1083           0 :     bool createdCollection = false;
    1084           0 :     collection =
    1085             :       CSSAnimationCollection::GetOrCreateAnimationCollection(
    1086           0 :         aTarget.mElement, aTarget.mPseudoType, &createdCollection);
    1087           0 :     if (!collection) {
    1088           0 :       MOZ_ASSERT(!createdCollection, "outparam should agree with return value");
    1089           0 :       NS_WARNING("allocating collection failed");
    1090           0 :       return;
    1091             :     }
    1092             : 
    1093           0 :     if (createdCollection) {
    1094           0 :       AddElementCollection(collection);
    1095             :     }
    1096             :   }
    1097           0 :   collection->mAnimations.SwapElements(newAnimations);
    1098             : 
    1099             :   // Cancel removed animations
    1100           0 :   for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) {
    1101           0 :     newAnimations[newAnimIdx]->CancelFromStyle();
    1102             :   }
    1103             : }

Generated by: LCOV version 1.13