--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -552,29 +552,105 @@ ResolvedStyleCache::Get(nsPresContext *a
RefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()->
ResolveStyleByAddingRules(aParentStyleContext, rules);
mCache.Put(aKeyframeDeclaration, resultStrong);
result = resultStrong;
}
return result;
}
+class MOZ_STACK_CLASS CSSAnimationBuilder final {
+public:
+ CSSAnimationBuilder(nsStyleContext* aStyleContext,
+ dom::Element* aTarget)
+ : mStyleContext(aStyleContext)
+ , mTarget(aTarget)
+ {
+ MOZ_ASSERT(aStyleContext);
+ MOZ_ASSERT(aTarget);
+ }
+
+ already_AddRefed<CSSAnimation>
+ Build(nsPresContext* aPresContext,
+ const StyleAnimation& aSrc,
+ const nsCSSKeyframesRule* aRule);
+
+private:
+ void BuildAnimationProperties(nsPresContext* aPresContext,
+ const StyleAnimation& aSrc,
+ const nsCSSKeyframesRule* aRule,
+ InfallibleTArray<AnimationProperty>& aResult);
+ bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
+ aSegments,
+ nsCSSProperty aProperty,
+ const mozilla::StyleAnimation& aAnimation,
+ float aFromKey, nsStyleContext* aFromContext,
+ mozilla::css::Declaration* aFromDeclaration,
+ float aToKey, nsStyleContext* aToContext);
+
+ static TimingParams TimingParamsFrom(
+ const StyleAnimation& aStyleAnimation)
+ {
+ TimingParams timing;
+
+ timing.mDuration.SetAsUnrestrictedDouble() = aStyleAnimation.GetDuration();
+ timing.mDelay = TimeDuration::FromMilliseconds(aStyleAnimation.GetDelay());
+ timing.mIterations = aStyleAnimation.GetIterationCount();
+ timing.mDirection = aStyleAnimation.GetDirection();
+ timing.mFill = aStyleAnimation.GetFillMode();
+
+ return timing;
+ }
+
+ RefPtr<nsStyleContext> mStyleContext;
+ RefPtr<dom::Element> mTarget;
+
+ ResolvedStyleCache mResolvedStyles;
+ RefPtr<nsStyleContext> mStyleWithoutAnimation;
+};
+
+already_AddRefed<CSSAnimation>
+CSSAnimationBuilder::Build(nsPresContext* aPresContext,
+ const StyleAnimation& aSrc,
+ const nsCSSKeyframesRule* aRule)
+{
+ MOZ_ASSERT(aPresContext);
+ MOZ_ASSERT(aRule);
+
+ TimingParams timing = TimingParamsFrom(aSrc);
+ RefPtr<KeyframeEffectReadOnly> effect =
+ new KeyframeEffectReadOnly(aPresContext->Document(), mTarget,
+ mStyleContext->GetPseudoType(), timing);
+
+ InfallibleTArray<AnimationProperty> animationProperties;
+ BuildAnimationProperties(aPresContext, aSrc, aRule, animationProperties);
+ effect->Properties() = Move(animationProperties);
+
+ RefPtr<CSSAnimation> animation =
+ new CSSAnimation(aPresContext->Document()->GetScopeObject(),
+ aSrc.GetName());
+ animation->SetOwningElement(
+ OwningElementRef(*mTarget, mStyleContext->GetPseudoType()));
+
+ animation->SetEffect(effect);
+
+ return animation.forget();
+}
+
void
nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
dom::Element* aTarget,
dom::AnimationTimeline* aTimeline,
AnimationPtrArray& aAnimations)
{
MOZ_ASSERT(aAnimations.IsEmpty(), "expect empty array");
- ResolvedStyleCache resolvedStyles;
-
const nsStyleDisplay *disp = aStyleContext->StyleDisplay();
- RefPtr<nsStyleContext> styleWithoutAnimation;
+ CSSAnimationBuilder builder(aStyleContext, aTarget);
for (size_t animIdx = 0, animEnd = disp->mAnimationNameCount;
animIdx != animEnd; ++animIdx) {
const StyleAnimation& src = disp->mAnimations[animIdx];
// CSS Animations whose animation-name does not match a @keyframes rule do
// not generate animation events. This includes when the animation-name is
// "none" which is represented by an empty name in the StyleAnimation.
@@ -583,210 +659,205 @@ nsAnimationManager::BuildAnimations(nsSt
nsCSSKeyframesRule* rule =
src.GetName().IsEmpty()
? nullptr
: mPresContext->StyleSet()->KeyframesRuleForName(src.GetName());
if (!rule) {
continue;
}
- RefPtr<CSSAnimation> dest =
- new CSSAnimation(mPresContext->Document()->GetScopeObject(),
- src.GetName());
- dest->SetOwningElement(
- OwningElementRef(*aTarget, aStyleContext->GetPseudoType()));
+ RefPtr<CSSAnimation> dest = builder.Build(mPresContext, src, rule);
dest->SetTimeline(aTimeline);
dest->SetAnimationIndex(static_cast<uint64_t>(animIdx));
aAnimations.AppendElement(dest);
- TimingParams timing;
- timing.mDuration.SetAsUnrestrictedDouble() = src.GetDuration();
- timing.mDelay = TimeDuration::FromMilliseconds(src.GetDelay());
- timing.mIterations = src.GetIterationCount();
- timing.mDirection = src.GetDirection();
- timing.mFill = src.GetFillMode();
-
- RefPtr<KeyframeEffectReadOnly> destEffect =
- new KeyframeEffectReadOnly(mPresContext->Document(), aTarget,
- aStyleContext->GetPseudoType(), timing);
- dest->SetEffect(destEffect);
-
if (src.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED) {
dest->PauseFromStyle();
} else {
dest->PlayFromStyle();
}
+ }
+}
- // While current drafts of css3-animations say that later keyframes
- // with the same key entirely replace earlier ones (no cascading),
- // this is a bad idea and contradictory to the rest of CSS. So
- // we're going to keep all the keyframes for each key and then do
- // the replacement on a per-property basis rather than a per-rule
- // basis, just like everything else in CSS.
+void
+CSSAnimationBuilder::BuildAnimationProperties(
+ nsPresContext* aPresContext,
+ const StyleAnimation& aSrc,
+ const nsCSSKeyframesRule* aRule,
+ InfallibleTArray<AnimationProperty>& aResult)
+{
+ // While current drafts of css3-animations say that later keyframes
+ // with the same key entirely replace earlier ones (no cascading),
+ // this is a bad idea and contradictory to the rest of CSS. So
+ // we're going to keep all the keyframes for each key and then do
+ // the replacement on a per-property basis rather than a per-rule
+ // basis, just like everything else in CSS.
- AutoTArray<KeyframeData, 16> sortedKeyframes;
+ AutoTArray<KeyframeData, 16> sortedKeyframes;
- for (uint32_t ruleIdx = 0, ruleEnd = rule->StyleRuleCount();
- ruleIdx != ruleEnd; ++ruleIdx) {
- css::Rule* cssRule = rule->GetStyleRuleAt(ruleIdx);
- MOZ_ASSERT(cssRule, "must have rule");
- MOZ_ASSERT(cssRule->GetType() == css::Rule::KEYFRAME_RULE,
- "must be keyframe rule");
- nsCSSKeyframeRule *kfRule = static_cast<nsCSSKeyframeRule*>(cssRule);
+ for (uint32_t ruleIdx = 0, ruleEnd = aRule->StyleRuleCount();
+ ruleIdx != ruleEnd; ++ruleIdx) {
+ css::Rule* cssRule = aRule->GetStyleRuleAt(ruleIdx);
+ MOZ_ASSERT(cssRule, "must have rule");
+ MOZ_ASSERT(cssRule->GetType() == css::Rule::KEYFRAME_RULE,
+ "must be keyframe rule");
+ nsCSSKeyframeRule *kfRule = static_cast<nsCSSKeyframeRule*>(cssRule);
- const nsTArray<float> &keys = kfRule->GetKeys();
- for (uint32_t keyIdx = 0, keyEnd = keys.Length();
- keyIdx != keyEnd; ++keyIdx) {
- float key = keys[keyIdx];
- // FIXME (spec): The spec doesn't say what to do with
- // out-of-range keyframes. We'll ignore them.
- if (0.0f <= key && key <= 1.0f) {
- KeyframeData *data = sortedKeyframes.AppendElement();
- data->mKey = key;
- data->mIndex = ruleIdx;
- data->mRule = kfRule;
- }
+ const nsTArray<float> &keys = kfRule->GetKeys();
+ for (uint32_t keyIdx = 0, keyEnd = keys.Length();
+ keyIdx != keyEnd; ++keyIdx) {
+ float key = keys[keyIdx];
+ // FIXME (spec): The spec doesn't say what to do with
+ // out-of-range keyframes. We'll ignore them.
+ if (0.0f <= key && key <= 1.0f) {
+ KeyframeData *data = sortedKeyframes.AppendElement();
+ data->mKey = key;
+ data->mIndex = ruleIdx;
+ data->mRule = kfRule;
}
}
+ }
- sortedKeyframes.Sort(KeyframeDataComparator());
+ sortedKeyframes.Sort(KeyframeDataComparator());
+
+ if (sortedKeyframes.Length() == 0) {
+ // no segments
+ return;
+ }
+
+ // Record the properties that are present in any keyframe rules we
+ // are using.
+ nsCSSPropertySet properties;
- if (sortedKeyframes.Length() == 0) {
- // no segments
+ for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length();
+ kfIdx != kfEnd; ++kfIdx) {
+ css::Declaration *decl = sortedKeyframes[kfIdx].mRule->Declaration();
+ for (uint32_t propIdx = 0, propEnd = decl->Count();
+ propIdx != propEnd; ++propIdx) {
+ nsCSSProperty prop = decl->GetPropertyAt(propIdx);
+ if (prop != eCSSPropertyExtra_variable) {
+ // CSS Variables are not animatable
+ properties.AddProperty(prop);
+ }
+ }
+ }
+
+ for (nsCSSProperty prop = nsCSSProperty(0);
+ prop < eCSSProperty_COUNT_no_shorthands;
+ prop = nsCSSProperty(prop + 1)) {
+ if (!properties.HasProperty(prop) ||
+ nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None) {
continue;
}
- // Record the properties that are present in any keyframe rules we
- // are using.
- nsCSSPropertySet properties;
-
+ // Build a list of the keyframes to use for this property. This
+ // means we need every keyframe with the property in it, except
+ // for those keyframes where a later keyframe with the *same key*
+ // also has the property.
+ AutoTArray<uint32_t, 16> keyframesWithProperty;
+ float lastKey = 100.0f; // an invalid key
for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length();
kfIdx != kfEnd; ++kfIdx) {
- css::Declaration *decl = sortedKeyframes[kfIdx].mRule->Declaration();
- for (uint32_t propIdx = 0, propEnd = decl->Count();
- propIdx != propEnd; ++propIdx) {
- nsCSSProperty prop = decl->GetPropertyAt(propIdx);
- if (prop != eCSSPropertyExtra_variable) {
- // CSS Variables are not animatable
- properties.AddProperty(prop);
- }
+ KeyframeData &kf = sortedKeyframes[kfIdx];
+ if (!kf.mRule->Declaration()->HasProperty(prop)) {
+ continue;
}
+ if (kf.mKey == lastKey) {
+ // Replace previous occurrence of same key.
+ keyframesWithProperty[keyframesWithProperty.Length() - 1] = kfIdx;
+ } else {
+ keyframesWithProperty.AppendElement(kfIdx);
+ }
+ lastKey = kf.mKey;
}
- for (nsCSSProperty prop = nsCSSProperty(0);
- prop < eCSSProperty_COUNT_no_shorthands;
- prop = nsCSSProperty(prop + 1)) {
- if (!properties.HasProperty(prop) ||
- nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None) {
- continue;
- }
+ AnimationProperty &propData = *aResult.AppendElement();
+ propData.mProperty = prop;
+
+ KeyframeData *fromKeyframe = nullptr;
+ RefPtr<nsStyleContext> fromContext;
+ bool interpolated = true;
+ for (uint32_t wpIdx = 0, wpEnd = keyframesWithProperty.Length();
+ wpIdx != wpEnd; ++wpIdx) {
+ uint32_t kfIdx = keyframesWithProperty[wpIdx];
+ KeyframeData &toKeyframe = sortedKeyframes[kfIdx];
+
+ RefPtr<nsStyleContext> toContext =
+ mResolvedStyles.Get(aPresContext, mStyleContext,
+ toKeyframe.mRule->Declaration());
- // Build a list of the keyframes to use for this property. This
- // means we need every keyframe with the property in it, except
- // for those keyframes where a later keyframe with the *same key*
- // also has the property.
- AutoTArray<uint32_t, 16> keyframesWithProperty;
- float lastKey = 100.0f; // an invalid key
- for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length();
- kfIdx != kfEnd; ++kfIdx) {
- KeyframeData &kf = sortedKeyframes[kfIdx];
- if (!kf.mRule->Declaration()->HasProperty(prop)) {
- continue;
+ if (fromKeyframe) {
+ interpolated = interpolated &&
+ BuildSegment(propData.mSegments, prop, aSrc,
+ fromKeyframe->mKey, fromContext,
+ fromKeyframe->mRule->Declaration(),
+ toKeyframe.mKey, toContext);
+ } else {
+ if (toKeyframe.mKey != 0.0f) {
+ // There's no data for this property at 0%, so use the
+ // cascaded value above us.
+ if (!mStyleWithoutAnimation) {
+ mStyleWithoutAnimation = aPresContext->StyleSet()->
+ ResolveStyleWithoutAnimation(mTarget, mStyleContext,
+ eRestyle_AllHintsWithAnimations);
+ }
+ interpolated = interpolated &&
+ BuildSegment(propData.mSegments, prop, aSrc,
+ 0.0f, mStyleWithoutAnimation, nullptr,
+ toKeyframe.mKey, toContext);
}
- if (kf.mKey == lastKey) {
- // Replace previous occurrence of same key.
- keyframesWithProperty[keyframesWithProperty.Length() - 1] = kfIdx;
- } else {
- keyframesWithProperty.AppendElement(kfIdx);
- }
- lastKey = kf.mKey;
}
- AnimationProperty &propData = *destEffect->Properties().AppendElement();
- propData.mProperty = prop;
-
- KeyframeData *fromKeyframe = nullptr;
- RefPtr<nsStyleContext> fromContext;
- bool interpolated = true;
- for (uint32_t wpIdx = 0, wpEnd = keyframesWithProperty.Length();
- wpIdx != wpEnd; ++wpIdx) {
- uint32_t kfIdx = keyframesWithProperty[wpIdx];
- KeyframeData &toKeyframe = sortedKeyframes[kfIdx];
-
- RefPtr<nsStyleContext> toContext =
- resolvedStyles.Get(mPresContext, aStyleContext,
- toKeyframe.mRule->Declaration());
+ fromContext = toContext;
+ fromKeyframe = &toKeyframe;
+ }
- if (fromKeyframe) {
- interpolated = interpolated &&
- BuildSegment(propData.mSegments, prop, src,
- fromKeyframe->mKey, fromContext,
- fromKeyframe->mRule->Declaration(),
- toKeyframe.mKey, toContext);
- } else {
- if (toKeyframe.mKey != 0.0f) {
- // There's no data for this property at 0%, so use the
- // cascaded value above us.
- if (!styleWithoutAnimation) {
- styleWithoutAnimation = mPresContext->StyleSet()->
- ResolveStyleWithoutAnimation(aTarget, aStyleContext,
- eRestyle_AllHintsWithAnimations);
- }
- interpolated = interpolated &&
- BuildSegment(propData.mSegments, prop, src,
- 0.0f, styleWithoutAnimation, nullptr,
- toKeyframe.mKey, toContext);
- }
- }
-
- fromContext = toContext;
- fromKeyframe = &toKeyframe;
+ if (fromKeyframe->mKey != 1.0f) {
+ // There's no data for this property at 100%, so use the
+ // cascaded value above us.
+ if (!mStyleWithoutAnimation) {
+ mStyleWithoutAnimation = aPresContext->StyleSet()->
+ ResolveStyleWithoutAnimation(mTarget, mStyleContext,
+ eRestyle_AllHintsWithAnimations);
}
+ interpolated = interpolated &&
+ BuildSegment(propData.mSegments, prop, aSrc,
+ fromKeyframe->mKey, fromContext,
+ fromKeyframe->mRule->Declaration(),
+ 1.0f, mStyleWithoutAnimation);
+ }
- if (fromKeyframe->mKey != 1.0f) {
- // There's no data for this property at 100%, so use the
- // cascaded value above us.
- if (!styleWithoutAnimation) {
- styleWithoutAnimation = mPresContext->StyleSet()->
- ResolveStyleWithoutAnimation(aTarget, aStyleContext,
- eRestyle_AllHintsWithAnimations);
- }
- interpolated = interpolated &&
- BuildSegment(propData.mSegments, prop, src,
- fromKeyframe->mKey, fromContext,
- fromKeyframe->mRule->Declaration(),
- 1.0f, styleWithoutAnimation);
- }
-
- // If we failed to build any segments due to inability to
- // interpolate, remove the property from the animation. (It's not
- // clear if this is the right thing to do -- we could run some of
- // the segments, but it's really not clear whether we should skip
- // values (which?) or skip segments, so best to skip the whole
- // thing for now.)
- if (!interpolated) {
- destEffect->Properties().RemoveElementAt(
- destEffect->Properties().Length() - 1);
- }
+ // If we failed to build any segments due to inability to
+ // interpolate, remove the property from the animation. (It's not
+ // clear if this is the right thing to do -- we could run some of
+ // the segments, but it's really not clear whether we should skip
+ // values (which?) or skip segments, so best to skip the whole
+ // thing for now.)
+ if (!interpolated) {
+ aResult.RemoveElementAt(aResult.Length() - 1);
}
}
}
bool
-nsAnimationManager::BuildSegment(InfallibleTArray<AnimationPropertySegment>&
+CSSAnimationBuilder::BuildSegment(InfallibleTArray<AnimationPropertySegment>&
aSegments,
- nsCSSProperty aProperty,
- const StyleAnimation& aAnimation,
- float aFromKey, nsStyleContext* aFromContext,
- mozilla::css::Declaration* aFromDeclaration,
- float aToKey, nsStyleContext* aToContext)
+ nsCSSProperty aProperty,
+ const StyleAnimation& aAnimation,
+ float aFromKey, nsStyleContext* aFromContext,
+ mozilla::css::Declaration* aFromDeclaration,
+ float aToKey, nsStyleContext* aToContext)
{
StyleAnimationValue fromValue, toValue, dummyValue;
- if (!ExtractComputedValueForTransition(aProperty, aFromContext, fromValue) ||
- !ExtractComputedValueForTransition(aProperty, aToContext, toValue) ||
+ if (!CommonAnimationManager::ExtractComputedValueForTransition(aProperty,
+ aFromContext,
+ fromValue) ||
+ !CommonAnimationManager::ExtractComputedValueForTransition(aProperty,
+ aToContext,
+ toValue) ||
// Check that we can interpolate between these values
// (If this is ever a performance problem, we could add a
// CanInterpolate method, but it seems fine for now.)
!StyleAnimationValue::Interpolate(aProperty, fromValue, toValue,
0.5, dummyValue)) {
return false;
}