Bug 1242872 - Part 1: Introduce CSSAnimationBuilder to factor a bunch of stuff in BuildAnimations and CheckAnimationRule out. r?dbaron draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Fri, 19 Feb 2016 09:16:15 +0900
changeset 332026 4a8162aed964a2acfd5233277ab5aa394d10b73e
parent 331857 fcd35e10fa17d9fd11d92be48ae9698c2a900f1c
child 332027 4f05039f0ae1dc3dc79f38af90381e11b4fadb13
push id11141
push userbmo:hiikezoe@mozilla-japan.org
push dateFri, 19 Feb 2016 02:33:47 +0000
reviewersdbaron
bugs1242872
milestone47.0a1
Bug 1242872 - Part 1: Introduce CSSAnimationBuilder to factor a bunch of stuff in BuildAnimations and CheckAnimationRule out. r?dbaron MozReview-Commit-ID: 7921De3IVA6
layout/style/nsAnimationManager.cpp
layout/style/nsAnimationManager.h
--- 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;
   }
 
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -343,18 +343,11 @@ protected:
 
   mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher;
 
 private:
   void BuildAnimations(nsStyleContext* aStyleContext,
                        mozilla::dom::Element* aTarget,
                        mozilla::dom::AnimationTimeline* aTimeline,
                        mozilla::AnimationPtrArray& aAnimations);
-  bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
-                      aSegments,
-                    nsCSSProperty aProperty,
-                    const mozilla::StyleAnimation& aAnimation,
-                    float aFromKey, nsStyleContext* aFromContext,
-                    mozilla::css::Declaration* aFromDeclaration,
-                    float aToKey, nsStyleContext* aToContext);
 };
 
 #endif /* !defined(nsAnimationManager_h_) */