From b986ff9440915f572dd1a66da5a1339236f15757 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Mon, 20 Oct 2014 13:55:46 +0900 Subject: [PATCH] Bug 1078122 part 3 - Move animation value building down to the Animation objects; r=dholbert This patch extracts the logic for calculating animation styles from AnimationPlayerCollection and puts the bulk of it into the Animation objects. Some of the initial logic surrounding the animation player state (e.g. is it paused or not, etc.) is put into AnimationPlayer. In future we may shift this logic even further down to the AnimationEffect objects but currently we don't create such objects unless necessary. --- dom/animation/Animation.cpp | 85 ++++++++++++++++++++++++ dom/animation/Animation.h | 11 ++++ dom/animation/AnimationPlayer.cpp | 16 +++++ dom/animation/AnimationPlayer.h | 15 +++++ layout/style/AnimationCommon.cpp | 105 ++---------------------------- layout/style/moz.build | 1 + 6 files changed, 134 insertions(+), 99 deletions(-) diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index eb43ea06fd11..55a6af26e52f 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -7,6 +7,8 @@ #include "mozilla/dom/AnimationBinding.h" #include "mozilla/dom/AnimationEffect.h" #include "mozilla/FloatingPoint.h" +#include "AnimationCommon.h" +#include "nsCSSPropertySet.h" namespace mozilla { @@ -262,5 +264,88 @@ Animation::HasAnimationOfProperty(nsCSSProperty aProperty) const return false; } +void +Animation::ComposeStyle(nsRefPtr& aStyleRule, + nsCSSPropertySet& aSetProperties) +{ + ComputedTiming computedTiming = GetComputedTiming(); + + // If the time fraction is null, we don't have fill data for the current + // time so we shouldn't animate. + if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) { + return; + } + + MOZ_ASSERT(0.0 <= computedTiming.mTimeFraction && + computedTiming.mTimeFraction <= 1.0, + "timing fraction should be in [0-1]"); + + for (size_t propIdx = 0, propEnd = mProperties.Length(); + propIdx != propEnd; ++propIdx) + { + const AnimationProperty& prop = mProperties[propIdx]; + + MOZ_ASSERT(prop.mSegments[0].mFromKey == 0.0, "incorrect first from key"); + MOZ_ASSERT(prop.mSegments[prop.mSegments.Length() - 1].mToKey == 1.0, + "incorrect last to key"); + + if (aSetProperties.HasProperty(prop.mProperty)) { + // Animations are composed by AnimationPlayerCollection by iterating + // from the last animation to first. For animations targetting the + // same property, the later one wins. So if this property is already set, + // we should not override it. + return; + } + + aSetProperties.AddProperty(prop.mProperty); + + MOZ_ASSERT(prop.mSegments.Length() > 0, + "property should not be in animations if it has no segments"); + + // FIXME: Maybe cache the current segment? + const AnimationPropertySegment *segment = prop.mSegments.Elements(), + *segmentEnd = segment + prop.mSegments.Length(); + while (segment->mToKey < computedTiming.mTimeFraction) { + MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys"); + ++segment; + if (segment == segmentEnd) { + MOZ_ASSERT_UNREACHABLE("incorrect time fraction"); + break; // in order to continue in outer loop (just below) + } + MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys"); + } + if (segment == segmentEnd) { + continue; + } + MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys"); + MOZ_ASSERT(segment >= prop.mSegments.Elements() && + size_t(segment - prop.mSegments.Elements()) < + prop.mSegments.Length(), + "out of array bounds"); + + if (!aStyleRule) { + // Allocate the style rule now that we know we have animation data. + aStyleRule = new css::AnimValuesStyleRule(); + } + + double positionInSegment = + (computedTiming.mTimeFraction - segment->mFromKey) / + (segment->mToKey - segment->mFromKey); + double valuePosition = + segment->mTimingFunction.GetValue(positionInSegment); + + StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty); + +#ifdef DEBUG + bool result = +#endif + StyleAnimationValue::Interpolate(prop.mProperty, + segment->mFromValue, + segment->mToValue, + valuePosition, *val); + MOZ_ASSERT(result, "interpolate must succeed now"); + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/animation/Animation.h b/dom/animation/Animation.h index 4075faf2edd3..f10dbaeb62af 100644 --- a/dom/animation/Animation.h +++ b/dom/animation/Animation.h @@ -21,8 +21,12 @@ #include "nsStyleStruct.h" // for nsTimingFunction struct JSContext; +class nsCSSPropertySet; namespace mozilla { +namespace css { +class AnimValuesStyleRule; +} // namespace css /** * Input timing parameters. @@ -259,6 +263,13 @@ public: return mProperties; } + // Updates |aStyleRule| with the animation values produced by this + // Animation for the current time except any properties already contained + // in |aSetProperties|. + // Any updated properties are added to |aSetProperties|. + void ComposeStyle(nsRefPtr& aStyleRule, + nsCSSPropertySet& aSetProperties); + protected: virtual ~Animation() { } diff --git a/dom/animation/AnimationPlayer.cpp b/dom/animation/AnimationPlayer.cpp index 5f2778879c63..d15a7f22dc66 100644 --- a/dom/animation/AnimationPlayer.cpp +++ b/dom/animation/AnimationPlayer.cpp @@ -210,6 +210,22 @@ AnimationPlayer::CanThrottle() const return mSource->LastNotification() == Animation::LAST_NOTIFICATION_END; } +void +AnimationPlayer::ComposeStyle(nsRefPtr& aStyleRule, + nsCSSPropertySet& aSetProperties, + bool& aNeedsRefreshes) +{ + if (!mSource || mSource->IsFinishedTransition()) { + return; + } + + if (PlayState() == AnimationPlayState::Running) { + aNeedsRefreshes = true; + } + + mSource->ComposeStyle(aStyleRule, aSetProperties); +} + void AnimationPlayer::FlushStyle() const { diff --git a/dom/animation/AnimationPlayer.h b/dom/animation/AnimationPlayer.h index c4f3ac884e87..f523b58f6925 100644 --- a/dom/animation/AnimationPlayer.h +++ b/dom/animation/AnimationPlayer.h @@ -21,8 +21,12 @@ #endif struct JSContext; +class nsCSSPropertySet; namespace mozilla { +namespace css { +class AnimValuesStyleRule; +} // namespace css class CSSAnimationPlayer; @@ -100,6 +104,17 @@ public: // running on the compositor). bool CanThrottle() const; + // Updates |aStyleRule| with the animation values of this player's source + // content, if any. + // Any properties already contained in |aSetProperties| are not changed. Any + // properties that are changed are added to |aSetProperties|. + // |aNeedsRefreshes| will be set to true if this player expects to update + // the style rule on the next refresh driver tick as well (because it + // is running and has source content to sample). + void ComposeStyle(nsRefPtr& aStyleRule, + nsCSSPropertySet& aSetProperties, + bool& aNeedsRefreshes); + // The beginning of the delay period. Nullable mStartTime; // Timeline timescale diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp index 534d4ac94102..cdce8fcee7a5 100644 --- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -512,108 +512,15 @@ AnimationPlayerCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime, // We'll set mNeedsRefreshes to true below in all cases where we need them. mNeedsRefreshes = false; - // FIXME(spec): assume that properties in higher animations override - // those in lower ones. - // Therefore, we iterate from last animation to first. + // If multiple animations specify behavior for the same property the + // animation which occurs last in the value of animation-name wins. + // As a result, we iterate from last animation to first and, if a + // property has already been set, we don't leave it. nsCSSPropertySet properties; for (size_t playerIdx = mPlayers.Length(); playerIdx-- != 0; ) { - AnimationPlayer* player = mPlayers[playerIdx]; - - if (!player->GetSource() || player->GetSource()->IsFinishedTransition()) { - continue; - } - - // The GetComputedTiming() call here handles pausing. But: - // FIXME: avoid recalculating every time when paused. - ComputedTiming computedTiming = player->GetSource()->GetComputedTiming(); - - if ((computedTiming.mPhase == ComputedTiming::AnimationPhase_Before || - computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) && - !player->IsPaused()) { - mNeedsRefreshes = true; - } - - // If the time fraction is null, we don't have fill data for the current - // time so we shouldn't animate. - // Likewise, if the player has no source content. - if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) { - continue; - } - - NS_ABORT_IF_FALSE(0.0 <= computedTiming.mTimeFraction && - computedTiming.mTimeFraction <= 1.0, - "timing fraction should be in [0-1]"); - - const Animation* anim = player->GetSource(); - for (size_t propIdx = 0, propEnd = anim->Properties().Length(); - propIdx != propEnd; ++propIdx) - { - const AnimationProperty& prop = anim->Properties()[propIdx]; - - NS_ABORT_IF_FALSE(prop.mSegments[0].mFromKey == 0.0, - "incorrect first from key"); - NS_ABORT_IF_FALSE(prop.mSegments[prop.mSegments.Length() - 1].mToKey - == 1.0, - "incorrect last to key"); - - if (properties.HasProperty(prop.mProperty)) { - // A later animation already set this property. - continue; - } - properties.AddProperty(prop.mProperty); - - NS_ABORT_IF_FALSE(prop.mSegments.Length() > 0, - "property should not be in animations if it " - "has no segments"); - - // FIXME: Maybe cache the current segment? - const AnimationPropertySegment *segment = prop.mSegments.Elements(), - *segmentEnd = segment + prop.mSegments.Length(); - while (segment->mToKey < computedTiming.mTimeFraction) { - NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey, - "incorrect keys"); - ++segment; - if (segment == segmentEnd) { - NS_ABORT_IF_FALSE(false, "incorrect time fraction"); - break; // in order to continue in outer loop (just below) - } - NS_ABORT_IF_FALSE(segment->mFromKey == (segment-1)->mToKey, - "incorrect keys"); - } - if (segment == segmentEnd) { - continue; - } - NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey, - "incorrect keys"); - NS_ABORT_IF_FALSE(segment >= prop.mSegments.Elements() && - size_t(segment - prop.mSegments.Elements()) < - prop.mSegments.Length(), - "out of array bounds"); - - if (!mStyleRule) { - // Allocate the style rule now that we know we have animation data. - mStyleRule = new css::AnimValuesStyleRule(); - } - - double positionInSegment = - (computedTiming.mTimeFraction - segment->mFromKey) / - (segment->mToKey - segment->mFromKey); - double valuePosition = - segment->mTimingFunction.GetValue(positionInSegment); - - StyleAnimationValue *val = - mStyleRule->AddEmptyValue(prop.mProperty); - -#ifdef DEBUG - bool result = -#endif - StyleAnimationValue::Interpolate(prop.mProperty, - segment->mFromValue, - segment->mToValue, - valuePosition, *val); - NS_ABORT_IF_FALSE(result, "interpolate must succeed now"); - } + mPlayers[playerIdx]->ComposeStyle(mStyleRule, properties, + mNeedsRefreshes); } } } diff --git a/layout/style/moz.build b/layout/style/moz.build index 170128d376c3..81b3b9a47ec7 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -21,6 +21,7 @@ EXPORTS += [ 'nsCSSParser.h', 'nsCSSPropAliasList.h', 'nsCSSProperty.h', + 'nsCSSPropertySet.h', 'nsCSSPropList.h', 'nsCSSProps.h', 'nsCSSPseudoClasses.h',