Bug 1343753 - Part 3: Use AnimationValue in ElementPropertyTransition and CSSTransition. draft
authorBoris Chiou <boris.chiou@gmail.com>
Thu, 09 Mar 2017 12:33:15 +0800
changeset 561125 e8fd2bcb0b849735014835603f832964a8edfc8a
parent 561124 c94ed972ac65869b4024487df7bec785df447067
child 561126 5372efd7341b75961ded0ac56e81015d1d7fa9b8
push id53643
push userbmo:boris.chiou@gmail.com
push dateWed, 12 Apr 2017 08:51:47 +0000
bugs1343753
milestone55.0a1
Bug 1343753 - Part 3: Use AnimationValue in ElementPropertyTransition and CSSTransition. We also need to update the interpolation code based on ServoAnimationValue in ElementPropertyTransition::UpdateStartValueFromReplacedTransition(). Therefore, ElementPropertyTransition can be used by both Gecko and Servo. MozReview-Commit-ID: BrIpvRR3te8
layout/style/StyleAnimationValue.cpp
layout/style/StyleAnimationValue.h
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -5204,16 +5204,22 @@ AnimationValue::operator==(const Animati
   MOZ_ASSERT(!mServo || mGecko.IsNull());
   if (mServo && aOther.mServo) {
     return Servo_AnimationValue_DeepEqual(mServo, aOther.mServo);
   }
   return !mServo && !aOther.mServo &&
          mGecko == aOther.mGecko;
 }
 
+bool
+AnimationValue::operator!=(const AnimationValue& aOther) const
+{
+  return !operator==(aOther);
+}
+
 float
 AnimationValue::GetOpacity() const
 {
   MOZ_ASSERT(!mServo != mGecko.IsNull());
   return mServo ? Servo_AnimationValue_GetOpacity(mServo)
                 : mGecko.GetFloatValue();
 }
 
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -574,16 +574,17 @@ struct AnimationValue
   // ever be set.
   // FIXME: After obsoleting StyleAnimationValue, we should remove mGecko, and
   // make AnimationValue a wrapper of RawServoAnimationValue to hide these
   // FFIs.
   StyleAnimationValue mGecko;
   RefPtr<RawServoAnimationValue> mServo;
 
   bool operator==(const AnimationValue& aOther) const;
+  bool operator!=(const AnimationValue& aOther) const;
 
   bool IsNull() const { return mGecko.IsNull() && !mServo; }
 
   float GetOpacity() const;
 
   // Returns the scale for mGecko or mServo, which are calculated with
   // reference to aFrame.
   gfxSize GetScaleValue(const nsIFrame* aFrame) const;
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -103,37 +103,53 @@ ElementPropertyTransition::UpdateStartVa
     mReplacedTransition->mTiming,
     mReplacedTransition->mPlaybackRate);
 
   if (!computedTiming.mProgress.IsNull()) {
     double valuePosition =
       ComputedTimingFunction::GetPortion(mReplacedTransition->mTimingFunction,
                                          computedTiming.mProgress.Value(),
                                          computedTiming.mBeforeFlag);
-    StyleAnimationValue startValue;
-    if (StyleAnimationValue::Interpolate(mProperties[0].mProperty,
-                                         mReplacedTransition->mFromValue,
-                                         mReplacedTransition->mToValue,
-                                         valuePosition, startValue)) {
-      MOZ_ASSERT(mProperties.Length() == 1 &&
-                 mProperties[0].mSegments.Length() == 1,
-                 "The transition should have one property and one segment");
+
+    MOZ_ASSERT(mProperties.Length() == 1 &&
+               mProperties[0].mSegments.Length() == 1,
+               "The transition should have one property and one segment");
+    MOZ_ASSERT(mKeyframes.Length() == 2,
+               "Transitions should have exactly two animation keyframes");
+    MOZ_ASSERT(mKeyframes[0].mPropertyValues.Length() == 1,
+               "Transitions should have exactly one property in their first "
+               "frame");
+
+    const AnimationValue& replacedFrom = mReplacedTransition->mFromValue;
+    const AnimationValue& replacedTo = mReplacedTransition->mToValue;
+    AnimationValue startValue;
+    if (mDocument->IsStyledByServo()) {
+      startValue.mServo =
+        Servo_AnimationValues_Interpolate(replacedFrom.mServo,
+                                          replacedTo.mServo,
+                                          valuePosition).Consume();
+      if (startValue.mServo) {
+        mKeyframes[0].mPropertyValues[0].mServoDeclarationBlock =
+          Servo_AnimationValue_Uncompute(startValue.mServo).Consume();
+        mProperties[0].mSegments[0].mFromValue = Move(startValue);
+      }
+    } else if (StyleAnimationValue::Interpolate(mProperties[0].mProperty,
+                                                replacedFrom.mGecko,
+                                                replacedTo.mGecko,
+                                                valuePosition,
+                                                startValue.mGecko)) {
       nsCSSValue cssValue;
       DebugOnly<bool> uncomputeResult =
         StyleAnimationValue::UncomputeValue(mProperties[0].mProperty,
-                                            startValue,
+                                            startValue.mGecko,
                                             cssValue);
-      mProperties[0].mSegments[0].mFromValue.mGecko = Move(startValue);
       MOZ_ASSERT(uncomputeResult, "UncomputeValue should not fail");
-      MOZ_ASSERT(mKeyframes.Length() == 2,
-          "Transitions should have exactly two animation keyframes");
-      MOZ_ASSERT(mKeyframes[0].mPropertyValues.Length() == 1,
-          "Transitions should have exactly one property in their first "
-          "frame");
       mKeyframes[0].mPropertyValues[0].mValue = cssValue;
+
+      mProperties[0].mSegments[0].mFromValue = Move(startValue);
     }
   }
 
   mReplacedTransition.reset();
 }
 
 ////////////////////////// CSSTransition ////////////////////////////
 
@@ -338,17 +354,17 @@ CSSTransition::Tick()
 nsCSSPropertyID
 CSSTransition::TransitionProperty() const
 {
   MOZ_ASSERT(eCSSProperty_UNKNOWN != mTransitionProperty,
              "Transition property should be initialized");
   return mTransitionProperty;
 }
 
-StyleAnimationValue
+AnimationValue
 CSSTransition::ToValue() const
 {
   MOZ_ASSERT(!mTransitionToValue.IsNull(),
              "Transition ToValue should be initialized");
   return mTransitionToValue;
 }
 
 bool
@@ -693,17 +709,17 @@ nsTransitionManager::UpdateTransitions(
            !allTransitionProperties.HasProperty(anim->TransitionProperty())) ||
           // properties whose computed values changed but for which we
           // did not start a new transition (because delay and
           // duration are both zero, or because the new value is not
           // interpolable); a new transition would have anim->ToValue()
           // matching currentValue
           !ExtractNonDiscreteComputedValue(anim->TransitionProperty(),
                                            aNewStyleContext, currentValue) ||
-          currentValue != anim->ToValue()) {
+          currentValue != anim->ToValue().mGecko) {
         // stop the transition
         if (anim->HasCurrentEffect()) {
           EffectSet* effectSet =
             EffectSet::GetEffectSet(aElement,
                                     aNewStyleContext->GetPseudoType());
           if (effectSet) {
             effectSet->UpdateAnimationGeneration(mPresContext);
           }
@@ -806,17 +822,17 @@ nsTransitionManager::ConsiderInitiatingT
 
   AnimationValue startValue, endValue, dummyValue;
   bool haveValues =
     ExtractNonDiscreteComputedValue(aProperty, aOldStyleContext,
                                     startValue.mGecko) &&
     ExtractNonDiscreteComputedValue(aProperty, aNewStyleContext,
                                     endValue.mGecko);
 
-  bool haveChange = startValue.mGecko != endValue.mGecko;
+  bool haveChange = startValue != endValue;
 
   bool shouldAnimate =
     haveValues &&
     haveChange &&
     // 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,
@@ -852,18 +868,17 @@ nsTransitionManager::ConsiderInitiatingT
   // there's no value change), but we need to return early here rather
   // than cancel the running transition because shouldAnimate is false!
   //
   // Likewise, if we got a style change that changed the value to the
   // endpoint of our finished transition, we also don't want to start
   // a new transition for the reasons described in
   // https://lists.w3.org/Archives/Public/www-style/2015Jan/0444.html .
   if (haveCurrentTransition && haveValues &&
-      aElementTransitions->mAnimations[currentIndex]->ToValue() ==
-        endValue.mGecko) {
+      aElementTransitions->mAnimations[currentIndex]->ToValue() == endValue) {
     // GetAnimationRule already called RestyleForAnimation.
     return;
   }
 
   if (!shouldAnimate) {
     if (haveCurrentTransition) {
       // We're in the middle of a transition, and just got a non-transition
       // style change to something that we can't animate.  This might happen
@@ -906,17 +921,17 @@ nsTransitionManager::ConsiderInitiatingT
   // If the new transition reverses an existing one, we'll need to
   // handle the timing differently.
   // FIXME: Move mStartForReversingTest, mReversePortion to CSSTransition,
   //        and set the timing function on transitions as an effect-level
   //        easing (rather than keyframe-level easing). (Bug 1292001)
   if (haveCurrentTransition &&
       aElementTransitions->mAnimations[currentIndex]->HasCurrentEffect() &&
       oldPT &&
-      oldPT->mStartForReversingTest == endValue.mGecko) {
+      oldPT->mStartForReversingTest == endValue) {
     // Compute the appropriate negative transition-delay such that right
     // now we'd end up at the current position.
     double valuePortion =
       oldPT->CurrentValuePortion() * oldPT->mReversePortion +
       (1.0 - oldPT->mReversePortion);
     // A timing function with negative y1 (or y2!) might make
     // valuePortion negative.  In this case, we still want to apply our
     // reversing logic based on relative distances, not make duration
@@ -937,33 +952,33 @@ nsTransitionManager::ConsiderInitiatingT
     // function, so reduce them along with the duration, but don't
     // reduce positive delays.
     if (delay < 0.0f) {
       delay *= valuePortion;
     }
 
     duration *= valuePortion;
 
-    startForReversingTest.mGecko = oldPT->ToValue();
+    startForReversingTest = oldPT->ToValue();
     reversePortion = valuePortion;
   }
 
   TimingParams timing =
     TimingParamsFromCSSParams(duration, delay,
                               1.0 /* iteration count */,
                               dom::PlaybackDirection::Normal,
                               dom::FillMode::Backwards);
 
   // aElement is non-null here, so we emplace it directly.
   Maybe<OwningAnimationTarget> target;
   target.emplace(aElement, aNewStyleContext->GetPseudoType());
   KeyframeEffectParams effectOptions;
   RefPtr<ElementPropertyTransition> pt =
     new ElementPropertyTransition(aElement->OwnerDoc(), target, timing,
-                                  startForReversingTest.mGecko, reversePortion,
+                                  startForReversingTest, reversePortion,
                                   effectOptions);
 
   pt->SetKeyframes(GetTransitionKeyframes(aProperty,
                                           Move(startValue), Move(endValue), tf),
                    aNewStyleContext);
 
   MOZ_ASSERT(mPresContext->RestyleManager()->IsGecko(),
              "ServoRestyleManager should not use nsTransitionManager "
@@ -1018,18 +1033,18 @@ nsTransitionManager::ConsiderInitiatingT
       const AnimationPropertySegment& segment =
         oldPT->Properties()[0].mSegments[0];
       pt->mReplacedTransition.emplace(
         ElementPropertyTransition::ReplacedTransitionProperties({
           oldPT->GetAnimation()->GetStartTime().Value(),
           oldPT->GetAnimation()->PlaybackRate(),
           oldPT->SpecifiedTiming(),
           segment.mTimingFunction,
-          segment.mFromValue.mGecko,
-          segment.mToValue.mGecko
+          segment.mFromValue,
+          segment.mToValue
         })
       );
     }
     animations[currentIndex]->CancelFromStyle();
     oldPT = nullptr; // Clear pointer so it doesn't dangle
     animations[currentIndex] = animation;
   } else {
     if (!animations.AppendElement(animation)) {
@@ -1073,19 +1088,20 @@ nsTransitionManager::PruneCompletedTrans
     CSSTransition* anim = animations[i];
 
     if (anim->HasCurrentEffect()) {
       continue;
     }
 
     // Since effect is a finished transition, we know it didn't
     // influence style.
-    StyleAnimationValue currentValue;
+    AnimationValue currentValue;
     if (!ExtractNonDiscreteComputedValue(anim->TransitionProperty(),
-                                         aNewStyleContext, currentValue) ||
+                                         aNewStyleContext,
+                                         currentValue.mGecko) ||
         currentValue != anim->ToValue()) {
       anim->CancelFromStyle();
       animations.RemoveElementAt(i);
     }
   } while (i != 0);
 
   if (collection->mAnimations.IsEmpty()) {
     collection->Destroy();
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -34,17 +34,17 @@ struct StyleTransition;
 
 namespace mozilla {
 
 struct ElementPropertyTransition : public dom::KeyframeEffectReadOnly
 {
   ElementPropertyTransition(nsIDocument* aDocument,
                             Maybe<OwningAnimationTarget>& aTarget,
                             const TimingParams &aTiming,
-                            StyleAnimationValue aStartForReversingTest,
+                            AnimationValue aStartForReversingTest,
                             double aReversePortion,
                             const KeyframeEffectParams& aEffectOptions)
     : dom::KeyframeEffectReadOnly(aDocument, aTarget, aTiming, aEffectOptions)
     , mStartForReversingTest(aStartForReversingTest)
     , mReversePortion(aReversePortion)
   { }
 
   ElementPropertyTransition* AsTransition() override { return this; }
@@ -58,35 +58,35 @@ struct ElementPropertyTransition : publi
                "Transitions should have exactly two animation keyframes. "
                "Perhaps we are using an un-initialized transition?");
     MOZ_ASSERT(mKeyframes[0].mPropertyValues.Length() == 1,
                "Transitions should have exactly one property in their first "
                "frame");
     return mKeyframes[0].mPropertyValues[0].mProperty;
   }
 
-  StyleAnimationValue ToValue() const {
+  AnimationValue ToValue() const {
     // If we failed to generate properties from the transition frames,
     // return a null value but also show a warning since we should be
     // detecting that kind of situation in advance and not generating a
     // transition in the first place.
     if (mProperties.Length() < 1 ||
         mProperties[0].mSegments.Length() < 1) {
       NS_WARNING("Failed to generate transition property values");
-      return StyleAnimationValue();
+      return AnimationValue();
     }
-    return mProperties[0].mSegments[0].mToValue.mGecko;
+    return mProperties[0].mSegments[0].mToValue;
   }
 
   // This is the start value to be used for a check for whether a
   // transition is being reversed.  Normally the same as
   // mProperties[0].mSegments[0].mFromValue, except when this transition
   // started as the reversal of another in-progress transition.
   // Needed so we can handle two reverses in a row.
-  StyleAnimationValue mStartForReversingTest;
+  AnimationValue mStartForReversingTest;
   // Likewise, the portion (in value space) of the "full" reversed
   // transition that we're actually covering.  For example, if a :hover
   // effect has a transition that moves the element 10px to the right
   // (by changing 'left' from 0px to 10px), and the mouse moves in to
   // the element (starting the transition) but then moves out after the
   // transition has advanced 4px, the second transition (from 10px/4px
   // to 0px) will have mReversePortion of 0.4.  (If the mouse then moves
   // in again when the transition is back to 2px, the mReversePortion
@@ -103,17 +103,17 @@ struct ElementPropertyTransition : publi
   // transitions at the current time.
   void UpdateStartValueFromReplacedTransition();
 
   struct ReplacedTransitionProperties {
     TimeDuration mStartTime;
     double mPlaybackRate;
     TimingParams mTiming;
     Maybe<ComputedTimingFunction> mTimingFunction;
-    StyleAnimationValue mFromValue, mToValue;
+    AnimationValue mFromValue, mToValue;
   };
   Maybe<ReplacedTransitionProperties> mReplacedTransition;
 };
 
 namespace dom {
 
 class CSSTransition final : public Animation
 {
@@ -170,17 +170,17 @@ public:
     mOwningElement = OwningElementRef();
   }
 
   void SetEffectFromStyle(AnimationEffectReadOnly* aEffect);
 
   void Tick() override;
 
   nsCSSPropertyID TransitionProperty() const;
-  StyleAnimationValue ToValue() const;
+  AnimationValue ToValue() const;
 
   bool HasLowerCompositeOrderThan(const CSSTransition& aOther) const;
   EffectCompositor::CascadeLevel CascadeLevel() const override
   {
     return IsTiedToMarkup() ?
            EffectCompositor::CascadeLevel::Transitions :
            EffectCompositor::CascadeLevel::Animations;
   }
@@ -270,17 +270,17 @@ protected:
   bool mNeedsNewAnimationIndexWhenRun;
 
   // Store the transition property and to-value here since we need that
   // information in order to determine if there is an existing transition
   // for a given style change. We can't store that information on the
   // ElementPropertyTransition (effect) however since it can be replaced
   // using the Web Animations API.
   nsCSSPropertyID mTransitionProperty;
-  StyleAnimationValue mTransitionToValue;
+  AnimationValue mTransitionToValue;
 };
 
 } // namespace dom
 
 template <>
 struct AnimationTypeTraits<dom::CSSTransition>
 {
   static nsIAtom* ElementPropertyAtom()