Bug 1358966 - Split mCSSValue into a RawServoAnimationValue and a StyleAnimationValue. r=birtles
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Fri, 09 Jun 2017 06:19:37 +0900
changeset 411213 4d87f2bf4b10369af0dd83a2ef962a23299ee8d9
parent 411212 c54f6b4989cc20ef8958622f5f5ece501a062851
child 411214 f234d46a9b40f3674f3b1741ebe0cd38a79dce02
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles
bugs1358966
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1358966 - Split mCSSValue into a RawServoAnimationValue and a StyleAnimationValue. r=birtles In case of gecko, we use only the StyleAnimationValue. Whereas in case of servo, we use RawServoAnimationValue and make it an array to store multiple values for shorthand sub properties. MozReview-Commit-ID: 4PnWBk9WP2Q
dom/smil/nsSMILCSSValueType.cpp
--- a/dom/smil/nsSMILCSSValueType.cpp
+++ b/dom/smil/nsSMILCSSValueType.cpp
@@ -28,40 +28,67 @@
 
 using namespace mozilla::dom;
 using mozilla::StyleAnimationValue;
 
 /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
 
 struct ValueWrapper {
   ValueWrapper(nsCSSPropertyID aPropID, const AnimationValue& aValue)
-    : mPropID(aPropID), mCSSValue(aValue) {}
+    : mPropID(aPropID)
+  {
+    if (aValue.mServo) {
+      mServoValue = aValue.mServo;
+      return;
+    }
+    mGeckoValue = aValue.mGecko;
+  }
   ValueWrapper(nsCSSPropertyID aPropID, const StyleAnimationValue& aValue)
-    : mPropID(aPropID), mCSSValue(aValue) {}
+    : mPropID(aPropID), mGeckoValue(aValue) {}
   ValueWrapper(nsCSSPropertyID aPropID,
                const RefPtr<RawServoAnimationValue>& aValue)
-    : mPropID(aPropID), mCSSValue(aValue) {}
+    : mPropID(aPropID), mServoValue(aValue) {}
+
+  bool operator==(const ValueWrapper& aOther) const
+  {
+    if (mPropID != aOther.mPropID) {
+      return false;
+    }
+
+    if (mServoValue && aOther.mServoValue) {
+      return Servo_AnimationValue_DeepEqual(mServoValue, aOther.mServoValue);
+    }
+    return !mServoValue && !aOther.mServoValue &&
+           mGeckoValue == aOther.mGeckoValue;
+  }
+
+  bool operator!=(const ValueWrapper& aOther) const
+  {
+    return !(*this == aOther);
+  }
 
   nsCSSPropertyID mPropID;
-  AnimationValue mCSSValue;
+  RefPtr<RawServoAnimationValue> mServoValue;
+  StyleAnimationValue mGeckoValue;
+
 };
 
 // Helper Methods
 // --------------
-static const AnimationValue*
+static const StyleAnimationValue*
 GetZeroValueForUnit(StyleAnimationValue::Unit aUnit)
 {
-  static const AnimationValue sZeroCoord(
-    StyleAnimationValue(0, StyleAnimationValue::CoordConstructor));
-  static const AnimationValue sZeroPercent(
-    StyleAnimationValue(0.0f, StyleAnimationValue::PercentConstructor));
-  static const AnimationValue sZeroFloat(
-    StyleAnimationValue(0.0f,  StyleAnimationValue::FloatConstructor));
-  static const AnimationValue sZeroColor(
-    StyleAnimationValue(NS_RGB(0,0,0), StyleAnimationValue::ColorConstructor));
+  static const StyleAnimationValue
+    sZeroCoord(0, StyleAnimationValue::CoordConstructor);
+  static const StyleAnimationValue
+    sZeroPercent(0.0f, StyleAnimationValue::PercentConstructor);
+  static const StyleAnimationValue
+    sZeroFloat(0.0f,  StyleAnimationValue::FloatConstructor);
+  static const StyleAnimationValue
+    sZeroColor(NS_RGB(0,0,0), StyleAnimationValue::ColorConstructor);
 
   MOZ_ASSERT(aUnit != StyleAnimationValue::eUnit_Null,
              "Need non-null unit for a zero value");
   switch (aUnit) {
     case StyleAnimationValue::eUnit_Coord:
       return &sZeroCoord;
     case StyleAnimationValue::eUnit_Percent:
       return &sZeroPercent;
@@ -74,76 +101,70 @@ GetZeroValueForUnit(StyleAnimationValue:
   }
 }
 
 // This method requires at least one of its arguments to be non-null.
 //
 // If one argument is null, this method updates it to point to "zero"
 // for the other argument's Unit (if applicable; otherwise, we return false).
 //
-// If neither argument is null, this method generally does nothing, though it
-// may apply a workaround for the special case where a 0 length-value is mixed
-// with a eUnit_Float value.  (See comment below.)
+// If neither argument is null, this method does nothing.
 //
-// |aZeroValueStorage| should be a null AnimationValue. This is used for the
-// Servo backend where we may need to allocate a new ServoAnimationValue to
+// |aZeroValueStorage| should be a reference to a RefPtr<RawServoAnimationValue>.
+// This is used where we may need to allocate a new ServoAnimationValue to
 // represent the appropriate zero value.
 //
-// Returns true on success, or false.
+// Returns true on success, or otherwise.
 static bool
-FinalizeStyleAnimationValues(const AnimationValue*& aValue1,
-                             const AnimationValue*& aValue2,
-                             AnimationValue& aZeroValueStorage)
+FinalizeServoAnimationValues(const RefPtr<RawServoAnimationValue>*& aValue1,
+                             const RefPtr<RawServoAnimationValue>*& aValue2,
+                             RefPtr<RawServoAnimationValue>& aZeroValueStorage)
+{
+  MOZ_ASSERT(aValue1 || aValue2, "expecting at least one non-null value");
+
+  // Are we missing either val? (If so, it's an implied 0 in other val's units)
+
+  if (!aValue1) {
+    aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue2).Consume();
+    aValue1 = &aZeroValueStorage;
+  } else if (!aValue2) {
+    aZeroValueStorage = Servo_AnimationValues_GetZeroValue(*aValue1).Consume();
+    aValue2 = &aZeroValueStorage;
+  }
+  return *aValue1 && *aValue2;
+}
+
+static bool
+FinalizeStyleAnimationValues(const StyleAnimationValue*& aValue1,
+                             const StyleAnimationValue*& aValue2)
 {
   MOZ_ASSERT(aValue1 || aValue2,
              "expecting at least one non-null value");
-  MOZ_ASSERT(!aValue1 || !aValue2 || !aValue1->mServo == !aValue2->mServo,
-             "If both values are specified, they should be for the same"
-             " style system");
-  MOZ_ASSERT(aZeroValueStorage.IsNull(),
-             "Zero storage should be empty");
-
-  bool isServo = aValue1 ? aValue1->mServo : aValue2->mServo;
-
-  // Are we missing either val? (If so, it's an implied 0 in other val's units)
-
-  if (isServo) {
-    if (!aValue1) {
-      aZeroValueStorage.mServo =
-        Servo_AnimationValues_GetZeroValue(aValue2->mServo).Consume();
-      aValue1 = &aZeroValueStorage;
-    } else if (!aValue2) {
-      aZeroValueStorage.mServo =
-        Servo_AnimationValues_GetZeroValue(aValue1->mServo).Consume();
-      aValue2 = &aZeroValueStorage;
-    }
-    return aValue1->mServo && aValue2->mServo;
-  }
 
   if (!aValue1) {
-    aValue1 = GetZeroValueForUnit(aValue2->mGecko.GetUnit());
+    aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
     return !!aValue1; // Fail if we have no zero value for this unit.
   }
   if (!aValue2) {
-    aValue2 = GetZeroValueForUnit(aValue1->mGecko.GetUnit());
+    aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
     return !!aValue2; // Fail if we have no zero value for this unit.
   }
 
   // Ok, both values were specified.
   // Need to handle a special-case, though: unitless nonzero length (parsed as
   // eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord).  These
   // won't interoperate in StyleAnimationValue, since their Units don't match.
   // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
-  const AnimationValue& zeroCoord =
+  const StyleAnimationValue& zeroCoord =
     *GetZeroValueForUnit(StyleAnimationValue::eUnit_Coord);
   if (*aValue1 == zeroCoord &&
-      aValue2->mGecko.GetUnit() == StyleAnimationValue::eUnit_Float) {
+      aValue2->GetUnit() == StyleAnimationValue::eUnit_Float) {
     aValue1 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
   } else if (*aValue2 == zeroCoord &&
-             aValue1->mGecko.GetUnit() == StyleAnimationValue::eUnit_Float) {
+             aValue1->GetUnit() == StyleAnimationValue::eUnit_Float) {
     aValue2 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
   }
 
   return true;
 }
 
 static void
 InvertSign(StyleAnimationValue& aValue)
@@ -229,31 +250,77 @@ nsSMILCSSValueType::IsEqual(const nsSMIL
   const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft);
   const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight);
 
   if (leftWrapper) {
     if (rightWrapper) {
       // Both non-null
       NS_WARNING_ASSERTION(leftWrapper != rightWrapper,
                            "Two nsSMILValues with matching ValueWrapper ptr");
-      return (leftWrapper->mPropID == rightWrapper->mPropID &&
-              leftWrapper->mCSSValue == rightWrapper->mCSSValue);
+      return *leftWrapper == *rightWrapper;
     }
     // Left non-null, right null
     return false;
   }
   if (rightWrapper) {
     // Left null, right non-null
     return false;
   }
   // Both null
   return true;
 }
 
 static bool
+AddOrAccumulateForServo(nsSMILValue& aDest,
+                        const ValueWrapper* aValueToAddWrapper,
+                        ValueWrapper* aDestWrapper,
+                        CompositeOperation aCompositeOp,
+                        uint64_t aCount)
+{
+  nsCSSPropertyID property = aValueToAddWrapper
+                             ? aValueToAddWrapper->mPropID
+                             : aDestWrapper->mPropID;
+  const RefPtr<RawServoAnimationValue>* valueToAdd =
+    aValueToAddWrapper
+    ? &aValueToAddWrapper->mServoValue
+    : nullptr;
+  const RefPtr<RawServoAnimationValue>* destValue =
+    aDestWrapper
+    ? &aDestWrapper->mServoValue
+    : nullptr;
+  RefPtr<RawServoAnimationValue> zeroValueStorage;
+  if (!FinalizeServoAnimationValues(valueToAdd, destValue, zeroValueStorage)) {
+    return false;
+  }
+
+  // FinalizeServoAnimationValues may have updated destValue so we should make
+  // sure the aDest and aDestWrapper outparams are up-to-date.
+  if (aDestWrapper) {
+    aDestWrapper->mServoValue = *destValue;
+  } else {
+    // aDest may be a barely-initialized "zero" destination.
+    aDest.mU.mPtr = aDestWrapper = new ValueWrapper(property, *destValue);
+  }
+
+  RefPtr<RawServoAnimationValue> result;
+  if (aCompositeOp == CompositeOperation::Add) {
+    result = Servo_AnimationValues_Add(*destValue, *valueToAdd).Consume();
+  } else {
+    result = Servo_AnimationValues_Accumulate(*destValue,
+                                              *valueToAdd,
+                                              aCount).Consume();
+  }
+
+  if (result) {
+    aDestWrapper->mServoValue = result;
+  }
+  return result;
+}
+
+static bool
 AddOrAccumulate(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
                 CompositeOperation aCompositeOp, uint64_t aCount)
 {
   MOZ_ASSERT(aValueToAdd.mType == aDest.mType,
              "Trying to add mismatching types");
   MOZ_ASSERT(aValueToAdd.mType == &nsSMILCSSValueType::sSingleton,
              "Unexpected SMIL value type");
   MOZ_ASSERT(aCompositeOp == CompositeOperation::Add ||
@@ -272,62 +339,53 @@ AddOrAccumulate(nsSMILValue& aDest, cons
                              : destWrapper->mPropID;
   // Special case: font-size-adjust and stroke-dasharray are explicitly
   // non-additive (even though StyleAnimationValue *could* support adding them)
   if (property == eCSSProperty_font_size_adjust ||
       property == eCSSProperty_stroke_dasharray) {
     return false;
   }
 
-  const AnimationValue* valueToAdd = valueToAddWrapper
-                                     ? &valueToAddWrapper->mCSSValue
-                                     : nullptr;
-  const AnimationValue* destValue = destWrapper
-                                    ? &destWrapper->mCSSValue
-                                    : nullptr;
-  AnimationValue zeroValueStorage;
-  if (!FinalizeStyleAnimationValues(valueToAdd, destValue,
-                                    zeroValueStorage)) {
+  bool isServo = valueToAddWrapper
+                 ? valueToAddWrapper->mServoValue
+                 : destWrapper->mServoValue;
+  if (isServo) {
+    return AddOrAccumulateForServo(aDest,
+                                   valueToAddWrapper,
+                                   destWrapper,
+                                   aCompositeOp,
+                                   aCount);
+  }
+
+  const StyleAnimationValue* valueToAdd = valueToAddWrapper ?
+    &valueToAddWrapper->mGeckoValue : nullptr;
+  const StyleAnimationValue* destValue = destWrapper ?
+    &destWrapper->mGeckoValue : nullptr;
+  if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
     return false;
   }
   // Did FinalizeStyleAnimationValues change destValue?
   // If so, update outparam to use the new value.
-  if (destWrapper && &destWrapper->mCSSValue != destValue) {
-    destWrapper->mCSSValue = *destValue;
+  if (destWrapper && &destWrapper->mGeckoValue != destValue) {
+    destWrapper->mGeckoValue = *destValue;
   }
 
   // Handle barely-initialized "zero" destination.
   if (!destWrapper) {
     aDest.mU.mPtr = destWrapper = new ValueWrapper(property, *destValue);
   }
 
-  if (destWrapper->mCSSValue.mServo) {
-    RefPtr<RawServoAnimationValue> result;
-    if (aCompositeOp == CompositeOperation::Add) {
-      result = Servo_AnimationValues_Add(destWrapper->mCSSValue.mServo,
-                                         valueToAdd->mServo).Consume();
-    } else {
-      result = Servo_AnimationValues_Accumulate(destWrapper->mCSSValue.mServo,
-                                                valueToAdd->mServo,
-                                                aCount).Consume();
-    }
-
-    if (result) {
-      destWrapper->mCSSValue.mServo = result;
-    }
-    return result;
-  }
-
   // For Gecko, we currently call Add for either composite mode.
   //
   // This is not ideal, but it doesn't make any difference for the set of
   // properties we currently allow adding in SMIL and this code path will
   // hopefully become obsolete before we expand that set.
-  return StyleAnimationValue::Add(property, destWrapper->mCSSValue.mGecko,
-                                  valueToAdd->mGecko, aCount);
+  return StyleAnimationValue::Add(property,
+                                  destWrapper->mGeckoValue,
+                                  valueToAddWrapper->mGeckoValue, aCount);
 }
 
 nsresult
 nsSMILCSSValueType::SandwichAdd(nsSMILValue& aDest,
                                 const nsSMILValue& aValueToAdd) const
 {
   return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Add, 1)
          ? NS_OK
@@ -339,54 +397,120 @@ nsSMILCSSValueType::Add(nsSMILValue& aDe
                         uint32_t aCount) const
 {
   return AddOrAccumulate(aDest, aValueToAdd, CompositeOperation::Accumulate,
                          aCount)
          ? NS_OK
          : NS_ERROR_FAILURE;
 }
 
+static nsresult
+ComputeDistanceForServo(const ValueWrapper* aFromWrapper,
+                        const ValueWrapper& aToWrapper,
+                        double& aDistance)
+{
+  const RefPtr<RawServoAnimationValue>* fromValue =
+    aFromWrapper ? &aFromWrapper->mServoValue : nullptr;
+  const RefPtr<RawServoAnimationValue>* toValue = &aToWrapper.mServoValue;
+  RefPtr<RawServoAnimationValue> zeroValueStorage;
+  if (!FinalizeServoAnimationValues(fromValue, toValue, zeroValueStorage)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aDistance = Servo_AnimationValues_ComputeDistance(*fromValue, *toValue);
+
+  return NS_OK;
+}
+
 nsresult
 nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
                                     const nsSMILValue& aTo,
                                     double& aDistance) const
 {
   MOZ_ASSERT(aFrom.mType == aTo.mType,
              "Trying to compare different types");
   MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");
 
   const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
   const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
   MOZ_ASSERT(toWrapper, "expecting non-null endpoint");
 
-  const AnimationValue* fromCSSValue = fromWrapper
-                                       ? &fromWrapper->mCSSValue
-                                       : nullptr;
-  const AnimationValue* toCSSValue = &toWrapper->mCSSValue;
-  AnimationValue zeroValueStorage;
-  if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue,
-                                    zeroValueStorage)) {
+  if (toWrapper->mServoValue) {
+    return ComputeDistanceForServo(fromWrapper, *toWrapper, aDistance);
+  }
+
+  const StyleAnimationValue* fromCSSValue = fromWrapper ?
+    &fromWrapper->mGeckoValue : nullptr;
+  const StyleAnimationValue* toCSSValue = &toWrapper->mGeckoValue;
+  if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
     return NS_ERROR_FAILURE;
   }
 
-  if (toCSSValue->mServo) {
-    aDistance = Servo_AnimationValues_ComputeDistance(fromCSSValue->mServo,
-                                                      toCSSValue->mServo);
-    return NS_OK;
-  }
-
   return StyleAnimationValue::ComputeDistance(toWrapper->mPropID,
-                                              fromCSSValue->mGecko,
-                                              toCSSValue->mGecko,
+                                              fromWrapper->mGeckoValue,
+                                              toWrapper->mGeckoValue,
                                               nullptr,
                                               aDistance)
          ? NS_OK
          : NS_ERROR_FAILURE;
 }
 
+static nsresult
+InterpolateForGecko(const ValueWrapper* aStartWrapper,
+                    const ValueWrapper& aEndWrapper,
+                    double aUnitDistance,
+                    nsSMILValue& aResult)
+{
+  const StyleAnimationValue* startCSSValue = aStartWrapper
+                                             ? &aStartWrapper->mGeckoValue
+                                             : nullptr;
+  const StyleAnimationValue* endCSSValue = &aEndWrapper.mGeckoValue;
+  if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  StyleAnimationValue resultValue;
+  if (StyleAnimationValue::Interpolate(aEndWrapper.mPropID,
+                                       *startCSSValue,
+                                       *endCSSValue,
+                                       aUnitDistance, resultValue)) {
+    aResult.mU.mPtr = new ValueWrapper(aEndWrapper.mPropID, resultValue);
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
+}
+
+static nsresult
+InterpolateForServo(const ValueWrapper* aStartWrapper,
+                    const ValueWrapper& aEndWrapper,
+                    double aUnitDistance,
+                    nsSMILValue& aResult)
+{
+  const RefPtr<RawServoAnimationValue>*
+    startValue = aStartWrapper
+                 ? &aStartWrapper->mServoValue
+                 : nullptr;
+  const RefPtr<RawServoAnimationValue>* endValue = &aEndWrapper.mServoValue;
+  RefPtr<RawServoAnimationValue> zeroValueStorage;
+  if (!FinalizeServoAnimationValues(startValue, endValue, zeroValueStorage)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<RawServoAnimationValue> result =
+    Servo_AnimationValues_Interpolate(*startValue,
+                                      *endValue,
+                                      aUnitDistance).Consume();
+  if (!result) {
+    return NS_ERROR_FAILURE;
+  }
+  aResult.mU.mPtr = new ValueWrapper(aEndWrapper.mPropID, Move(result));
+
+  return NS_OK;
+}
+
 nsresult
 nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
                                 const nsSMILValue& aEndVal,
                                 double aUnitDistance,
                                 nsSMILValue& aResult) const
 {
   MOZ_ASSERT(aStartVal.mType == aEndVal.mType,
              "Trying to interpolate different types");
@@ -396,51 +520,27 @@ nsSMILCSSValueType::Interpolate(const ns
   MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
              "unit distance value out of bounds");
   MOZ_ASSERT(!aResult.mU.mPtr, "expecting barely-initialized outparam");
 
   const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
   const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
   MOZ_ASSERT(endWrapper, "expecting non-null endpoint");
 
-  const AnimationValue* startCSSValue = startWrapper
-                                        ? &startWrapper->mCSSValue
-                                        : nullptr;
-  const AnimationValue* endCSSValue = &endWrapper->mCSSValue;
-  AnimationValue zeroValueStorage;
-  if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue,
-                                    zeroValueStorage)) {
-    return NS_ERROR_FAILURE;
+  if (endWrapper->mServoValue) {
+    return InterpolateForServo(startWrapper,
+                               *endWrapper,
+                               aUnitDistance,
+                               aResult);
   }
 
-  MOZ_ASSERT(!startCSSValue ||
-             !startCSSValue->mServo == !endCSSValue->mServo,
-             "Start and end values should use the same style system");
-
-  if (endCSSValue->mServo) {
-    RefPtr<RawServoAnimationValue> resultValue =
-      Servo_AnimationValues_Interpolate(startCSSValue->mServo,
-                                        endCSSValue->mServo,
-                                        aUnitDistance).Consume();
-    if (!resultValue) {
-      return NS_ERROR_FAILURE;
-    }
-    aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue);
-    return NS_OK;
-  }
-
-  StyleAnimationValue resultValue;
-  if (StyleAnimationValue::Interpolate(endWrapper->mPropID,
-                                       startCSSValue->mGecko,
-                                       endCSSValue->mGecko,
-                                       aUnitDistance, resultValue)) {
-    aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue);
-    return NS_OK;
-  }
-  return NS_ERROR_FAILURE;
+  return InterpolateForGecko(startWrapper,
+                             *endWrapper,
+                             aUnitDistance,
+                             aResult);
 }
 
 // Helper function to extract presContext
 static nsPresContext*
 GetPresContextForElement(Element* aElem)
 {
   nsIDocument* doc = aElem->GetUncomposedDoc();
   if (!doc) {
@@ -676,17 +776,27 @@ nsSMILCSSValueType::ValueToString(const 
 {
   MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
              "Unexpected SMIL value type");
   const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
   if (!wrapper) {
     return;
   }
 
-  wrapper->mCSSValue.SerializeSpecifiedValue(wrapper->mPropID, aString);
+  if (wrapper->mServoValue) {
+    Servo_AnimationValue_Serialize(wrapper->mServoValue,
+                                   wrapper->mPropID,
+                                   &aString);
+    return;
+  }
+
+  DebugOnly<bool> uncomputeResult =
+    StyleAnimationValue::UncomputeValue(wrapper->mPropID,
+                                        wrapper->mGeckoValue,
+                                        aString);
 }
 
 // static
 nsCSSPropertyID
 nsSMILCSSValueType::PropertyFromValue(const nsSMILValue& aValue)
 {
   if (aValue.mType != &nsSMILCSSValueType::sSingleton) {
     return eCSSProperty_UNKNOWN;