Bug 1272549 - Part 9: Compute distance for mismatched transform lists. draft
authorBoris Chiou <boris.chiou@gmail.com>
Wed, 05 Oct 2016 15:36:16 +0800
changeset 428029 1e78d536634bdf6df7c581acbfdc911f17cf7e47
parent 428028 a13fd09b64ee3dd1a0a8bd8d368282c9c48205b0
child 428030 d55af3fe90bd12634f1e608e0a5f13a9af532533
child 428055 fcb3461a35085c1fc1e8c9c58da66fa62f4b6549
child 428492 997afdfc6987158ac99b63143d01a1b34ef58728
push id33214
push userbmo:boris.chiou@gmail.com
push dateFri, 21 Oct 2016 13:45:27 +0000
bugs1272549
milestone52.0a1
Bug 1272549 - Part 9: Compute distance for mismatched transform lists. MozReview-Commit-ID: JJWcMgj88GX
dom/animation/KeyframeEffectReadOnly.cpp
dom/animation/KeyframeUtils.cpp
dom/animation/KeyframeUtils.h
dom/base/nsDOMWindowUtils.cpp
dom/smil/nsSMILCSSValueType.cpp
layout/style/StyleAnimationValue.cpp
layout/style/StyleAnimationValue.h
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -260,17 +260,17 @@ KeyframeEffectReadOnly::UpdateProperties
     nsTArray<ComputedKeyframeValues> computedValues =
       KeyframeUtils::GetComputedKeyframeValues(keyframesCopy,
                                                mTarget->mElement,
                                                aStyleContext);
 
     if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
       KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
                                   mEffectOptions.mPacedProperty,
-                                  computedValues);
+                                  computedValues, aStyleContext);
     }
 
     properties =
       KeyframeUtils::GetAnimationPropertiesFromKeyframes(keyframesCopy,
                                                          computedValues,
                                                          aStyleContext);
 
 #ifdef DEBUG
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -411,17 +411,18 @@ static void
 DistributeRange(const Range<Keyframe>& aSpacingRange);
 
 static void
 PaceRange(const Range<Keyframe>& aKeyframes,
           const Range<double>& aCumulativeDistances);
 
 static nsTArray<double>
 GetCumulativeDistances(const nsTArray<ComputedKeyframeValues>& aValues,
-                       nsCSSPropertyID aProperty);
+                       nsCSSPropertyID aProperty,
+                       nsStyleContext* aStyleContext);
 
 // ------------------------------------------------------------------
 //
 // Public API
 //
 // ------------------------------------------------------------------
 
 /* static */ nsTArray<Keyframe>
@@ -475,28 +476,30 @@ KeyframeUtils::GetKeyframesFromObject(JS
 
   return keyframes;
 }
 
 /* static */ void
 KeyframeUtils::ApplySpacing(nsTArray<Keyframe>& aKeyframes,
                             SpacingMode aSpacingMode,
                             nsCSSPropertyID aProperty,
-                            nsTArray<ComputedKeyframeValues>& aComputedValues)
+                            nsTArray<ComputedKeyframeValues>& aComputedValues,
+                            nsStyleContext* aStyleContext)
 {
   if (aKeyframes.IsEmpty()) {
     return;
   }
 
   nsTArray<double> cumulativeDistances;
   if (aSpacingMode == SpacingMode::paced) {
     MOZ_ASSERT(IsAnimatableProperty(aProperty),
                "Paced property should be animatable");
 
-    cumulativeDistances = GetCumulativeDistances(aComputedValues, aProperty);
+    cumulativeDistances = GetCumulativeDistances(aComputedValues, aProperty,
+                                                 aStyleContext);
     // Reset the computed offsets if using paced spacing.
     for (Keyframe& keyframe : aKeyframes) {
       keyframe.mComputedOffset = Keyframe::kComputedOffsetNotSet;
     }
   }
 
   // If the first keyframe has an unspecified offset, fill it in with 0%.
   // If there is only a single keyframe, then it gets 100%.
@@ -577,17 +580,17 @@ KeyframeUtils::ApplySpacing(nsTArray<Key
   }
 }
 
 /* static */ void
 KeyframeUtils::ApplyDistributeSpacing(nsTArray<Keyframe>& aKeyframes)
 {
   nsTArray<ComputedKeyframeValues> emptyArray;
   ApplySpacing(aKeyframes, SpacingMode::distribute, eCSSProperty_UNKNOWN,
-               emptyArray);
+               emptyArray, nullptr);
 }
 
 /* static */ nsTArray<ComputedKeyframeValues>
 KeyframeUtils::GetComputedKeyframeValues(const nsTArray<Keyframe>& aKeyframes,
                                          dom::Element* aElement,
                                          nsStyleContext* aStyleContext)
 {
   MOZ_ASSERT(aStyleContext);
@@ -1558,22 +1561,24 @@ PaceRange(const Range<Keyframe>& aKeyfra
   }
 }
 
 /**
  * Get cumulative distances for the paced property.
  *
  * @param aValues The computed values returned by GetComputedKeyframeValues.
  * @param aPacedProperty The paced property.
+ * @param aStyleContext The style context for computing distance on transform.
  * @return The cumulative distances for the paced property. The length will be
  *   the same as aValues.
  */
 static nsTArray<double>
 GetCumulativeDistances(const nsTArray<ComputedKeyframeValues>& aValues,
-                       nsCSSPropertyID aPacedProperty)
+                       nsCSSPropertyID aPacedProperty,
+                       nsStyleContext* aStyleContext)
 {
   // a) If aPacedProperty is a shorthand property, get its components.
   //    Otherwise, just add the longhand property into the set.
   size_t pacedPropertyCount = 0;
   nsCSSPropertyIDSet pacedPropertySet;
   bool isShorthand = nsCSSProps::IsShorthand(aPacedProperty);
   if (isShorthand) {
     CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPacedProperty,
@@ -1631,29 +1636,31 @@ GetCumulativeDistances(const nsTArray<Co
           MOZ_ASSERT(pacedValues[propIdx].mProperty == prop,
                      "Property mismatch");
 
           double componentDistance = 0.0;
           if (StyleAnimationValue::ComputeDistance(
                 prop,
                 prevPacedValues[propIdx].mValue,
                 pacedValues[propIdx].mValue,
+                aStyleContext,
                 componentDistance)) {
             dist += componentDistance * componentDistance;
           }
         }
         dist = sqrt(dist);
       } else {
         // If the property is longhand, we just use the 1st value.
         // If ComputeDistance() fails, |dist| will remain zero so there will be
         // no distance between the previous paced value and this value.
         Unused <<
           StyleAnimationValue::ComputeDistance(aPacedProperty,
                                                prevPacedValues[0].mValue,
                                                pacedValues[0].mValue,
+                                               aStyleContext,
                                                dist);
       }
       cumulativeDistances[i] = cumulativeDistances[preIdx] + dist;
     }
     prevPacedValues.SwapElements(pacedValues);
     preIdx = i;
   }
   return cumulativeDistances;
--- a/dom/animation/KeyframeUtils.h
+++ b/dom/animation/KeyframeUtils.h
@@ -93,21 +93,24 @@ public:
    * @param aSpacingMode The spacing mode to apply.
    * @param aProperty The paced property. Only used when |aSpacingMode| is
    *   SpacingMode::paced. In all other cases it is ignored and hence may be
    *   any value, e.g. eCSSProperty_UNKNOWN.
    * @param aComputedValues The set of computed keyframe values as returned by
    *   GetComputedKeyframeValues. Only used when |aSpacingMode| is
    *   SpacingMode::paced. In all other cases this parameter is unused and may
    *   be any value including an empty array.
+   * @param aStyleContext The style context used for calculating paced spacing
+   *                      on transform.
    */
   static void ApplySpacing(nsTArray<Keyframe>& aKeyframes,
                            SpacingMode aSpacingMode,
                            nsCSSPropertyID aProperty,
-                           nsTArray<ComputedKeyframeValues>& aComputedValues);
+                           nsTArray<ComputedKeyframeValues>& aComputedValues,
+                           nsStyleContext* aStyleContext);
 
   /**
    * Wrapper for ApplySpacing to simplify using distribute spacing.
    *
    * @param aKeyframes The set of keyframes to adjust.
    */
   static void ApplyDistributeSpacing(nsTArray<Keyframe>& aKeyframes);
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2698,23 +2698,29 @@ nsDOMWindowUtils::ComputeAnimationDistan
     property = eCSSProperty_UNKNOWN;
   }
 
   MOZ_ASSERT(property == eCSSProperty_UNKNOWN ||
              !nsCSSProps::IsShorthand(property),
              "should not have shorthand");
 
   StyleAnimationValue v1, v2;
+  Element* element = content->AsElement();
   if (property == eCSSProperty_UNKNOWN ||
-      !ComputeAnimationValue(property, content->AsElement(), aValue1, v1) ||
-      !ComputeAnimationValue(property, content->AsElement(), aValue2, v2)) {
+      !ComputeAnimationValue(property, element, aValue1, v1) ||
+      !ComputeAnimationValue(property, element, aValue2, v2)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  if (!StyleAnimationValue::ComputeDistance(property, v1, v2, *aResult)) {
+  nsIPresShell* shell = element->GetUncomposedDoc()->GetShell();
+  RefPtr<nsStyleContext> styleContext = shell
+    ? nsComputedDOMStyle::GetStyleContextForElement(element, nullptr, shell)
+    : nullptr;
+  if (!StyleAnimationValue::ComputeDistance(property, v1, v2, styleContext,
+                                            *aResult)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsDOMWindowUtils::RenderDocument(const nsRect& aRect,
--- a/dom/smil/nsSMILCSSValueType.cpp
+++ b/dom/smil/nsSMILCSSValueType.cpp
@@ -273,16 +273,17 @@ nsSMILCSSValueType::ComputeDistance(cons
     &fromWrapper->mCSSValue : nullptr;
   const StyleAnimationValue* toCSSValue = &toWrapper->mCSSValue;
   if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
     return NS_ERROR_FAILURE;
   }
 
   return StyleAnimationValue::ComputeDistance(toWrapper->mPropID,
                                               *fromCSSValue, *toCSSValue,
+                                              nullptr,
                                               aDistance) ?
     NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
                                 const nsSMILValue& aEndVal,
                                 double aUnitDistance,
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -1010,20 +1010,55 @@ ComputeTransformListDistance(const nsCSS
     aList1 = aList1->mNext;
     aList2 = aList2->mNext;
     MOZ_ASSERT(!aList1 == !aList2,
                "aList1 and aList2 should have the same length.");
   } while (aList1);
   return sqrt(distance);
 }
 
+static double
+ComputeMismatchedTransfromListDistance(const nsCSSValueList* aList1,
+                                       const nsCSSValueList* aList2,
+                                       nsStyleContext* aStyleContext)
+{
+  // We need nsStyleContext and nsPresContext to compute calc() values while
+  // processing the translate part of transforms.
+  if (!aStyleContext) {
+    return 0.0;
+  }
+
+  RuleNodeCacheConditions dontCare;
+  bool dontCareBool;
+  nsStyleTransformMatrix::TransformReferenceBox emptyRefBox;
+
+  Matrix4x4 m1 = nsStyleTransformMatrix::ReadTransforms(
+                   aList1,
+                   aStyleContext,
+                   aStyleContext->PresContext(),
+                   dontCare,
+                   emptyRefBox,
+                   nsPresContext::AppUnitsPerCSSPixel(),
+                   &dontCareBool);
+  Matrix4x4 m2 = nsStyleTransformMatrix::ReadTransforms(
+                   aList2,
+                   aStyleContext,
+                   aStyleContext->PresContext(),
+                   dontCare,
+                   emptyRefBox,
+                   nsPresContext::AppUnitsPerCSSPixel(),
+                   &dontCareBool);
+  return sqrt(ComputeTransform3DMatrixDistance(m1, m2));
+}
+
 bool
 StyleAnimationValue::ComputeDistance(nsCSSPropertyID aProperty,
                                      const StyleAnimationValue& aStartValue,
                                      const StyleAnimationValue& aEndValue,
+                                     nsStyleContext* aStyleContext,
                                      double& aDistance)
 {
   Unit commonUnit =
     GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
 
   switch (commonUnit) {
     case eUnit_Null:
     case eUnit_Auto:
@@ -1427,23 +1462,21 @@ StyleAnimationValue::ComputeDistance(nsC
 
           item1 = item1->mNext;
           item2 = item2->mNext;
         } while (item1 && item2);
 
         if (item1 || item2) {
           // Either the transform function types don't match or
           // the lengths don't match.
-
-          // TODO: Implement this for mismatched transform function in the later
-          // patch.
-          aDistance = 0.0;
-          return false;
+          aDistance =
+            ComputeMismatchedTransfromListDistance(list1, list2, aStyleContext);
+        } else {
+          aDistance = ComputeTransformListDistance(list1, list2);
         }
-        aDistance = ComputeTransformListDistance(list1, list2);
       }
       return true;
     }
     case eUnit_BackgroundPositionCoord: {
       const nsCSSValueList *position1 = aStartValue.GetCSSValueListValue();
       const nsCSSValueList *position2 = aEndValue.GetCSSValueListValue();
 
       double squareDistance = 0.0;
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -82,23 +82,26 @@ public:
    *
    * If this method succeeds, the returned distance value is guaranteed to be
    * non-negative.
    *
    * @param aStartValue The start of the interval for which the distance
    *                    should be calculated.
    * @param aEndValue   The end of the interval for which the distance
    *                    should be calculated.
+   * @param aStyleContext The style context to use for processing the
+   *                      translate part of transforms.
    * @param aDistance   The result of the calculation.
    * @return true on success, false on failure.
    */
   static MOZ_MUST_USE bool
   ComputeDistance(nsCSSPropertyID aProperty,
                   const StyleAnimationValue& aStartValue,
                   const StyleAnimationValue& aEndValue,
+                  nsStyleContext* aStyleContext,
                   double& aDistance);
 
   /**
    * Calculates an interpolated value that is the specified |aPortion| between
    * the two given values.
    *
    * This really just does the following calculation:
    *   aResultValue = (1.0 - aPortion) * aStartValue + aPortion * aEndValue
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -172,17 +172,17 @@ ProcessTranslatePart(const nsCSSValue& a
     offset = nsRuleNode::CalcLength(aValue, aContext, aPresContext,
                                     aConditions);
   }
 
   float translation = NSAppUnitsToFloatPixels(offset,
                                               nsPresContext::AppUnitsPerCSSPixel());
   // We want to avoid calling aDimensionGetter if there's no percentage to be
   // resolved (for performance reasons - see TransformReferenceBox).
-  if (percent != 0.0f && aRefBox) {
+  if (percent != 0.0f && aRefBox && !aRefBox->IsEmpty()) {
     translation += percent *
                      NSAppUnitsToFloatPixels((aRefBox->*aDimensionGetter)(),
                                              nsPresContext::AppUnitsPerCSSPixel());
   }
   return translation;
 }
 
 /**
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -105,16 +105,20 @@ namespace nsStyleTransformMatrix {
       EnsureDimensionsAreCached();
       return mWidth;
     }
     nscoord Height() {
       EnsureDimensionsAreCached();
       return mHeight;
     }
 
+    bool IsEmpty() {
+      return !mFrame;
+    }
+
   private:
     // We don't really need to prevent copying, but since none of our consumers
     // currently need to copy, preventing copying may allow us to catch some
     // cases where we use pass-by-value instead of pass-by-reference.
     TransformReferenceBox(const TransformReferenceBox&) = delete;
 
     void EnsureDimensionsAreCached();