Bug 1244590 - Part 12: Revise ComputeDistance for eUnit_Calc.
In order to computed cumulative distances for paced spacing correctly, we need
to get used values, instead of computed values, if the common unit is eUnitCalc.
MozReview-Commit-ID: J8d442aJP6O
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -512,17 +512,18 @@ KeyframeEffectReadOnly::UpdateProperties
if (mTarget) {
nsTArray<ComputedKeyframeValues> computedValues =
KeyframeUtils::GetComputedKeyframeValues(mKeyframes, mTarget->mElement,
aStyleContext);
if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
KeyframeUtils::ApplySpacing(mKeyframes, SpacingMode::paced,
mEffectOptions.mPacedProperty,
- computedValues);
+ computedValues,
+ mTarget->mElement);
}
properties =
KeyframeUtils::GetAnimationPropertiesFromKeyframes(mKeyframes,
computedValues);
}
if (mProperties == properties) {
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -386,17 +386,18 @@ RequiresAdditiveAnimation(const nsTArray
static void
DistributeRange(const Range<Keyframe>& aKeyframes,
const Range<Keyframe>& aFilteredKeyframes);
static void
PaceRange(const Range<Keyframe>& aKeyframes,
const Range<ComputedKeyframeValues>& aPacedValues,
- nsCSSProperty aProperty);
+ nsCSSProperty aProperty,
+ dom::Element* aElement);
static nsTArray<ComputedKeyframeValues>
GetPacedPropertyKeyframeValues(const nsTArray<ComputedKeyframeValues>& aValues,
nsCSSProperty aProperty);
// ------------------------------------------------------------------
//
// Public API
@@ -460,17 +461,18 @@ KeyframeUtils::GetKeyframesFromObject(JS
return keyframes;
}
/* static */ void
KeyframeUtils::ApplySpacing(nsTArray<Keyframe>& aKeyframes,
SpacingMode aSpacingMode,
nsCSSProperty aProperty,
- nsTArray<ComputedKeyframeValues>& aComputedValues)
+ nsTArray<ComputedKeyframeValues>& aComputedValues,
+ dom::Element* aElement)
{
if (aKeyframes.IsEmpty()) {
return;
}
nsTArray<ComputedKeyframeValues> pacedValues;
if (aSpacingMode == SpacingMode::paced) {
MOZ_ASSERT(IsAnimatableProperty(aProperty),
@@ -537,17 +539,17 @@ KeyframeUtils::ApplySpacing(nsTArray<Key
Range<Keyframe>(keyframeA.get(), pacedA - keyframeA + 1));
DistributeRange(Range<Keyframe>(keyframeA.get(), rangeLen),
Range<Keyframe>(pacedB.get(), keyframeB - pacedB + 1));
// c) Apply paced offsets in (Paced A, Paced B).
const size_t idx = pacedA - begin;
const size_t pacedLen = pacedB - pacedA + 1;
PaceRange(Range<Keyframe>(pacedA.get(), pacedLen),
Range<ComputedKeyframeValues>(&pacedValues[idx], pacedLen),
- aProperty);
+ aProperty, aElement);
// d) Fill null computed offsets in (Paced A, Paced B).
for (RangedPtr<Keyframe> frame = pacedA + 1; frame < pacedB; ++frame) {
if ((*frame).mComputedOffset != Keyframe::kComputedOffsetNotSet) {
continue;
}
RangedPtr<Keyframe> start = frame - 1;
RangedPtr<Keyframe> end = frame + 1;
@@ -1318,21 +1320,24 @@ DistributeRange(const Range<Keyframe>& a
* Apply paced computed offsets in (Paced A, Paced B).
*
* @param aKeyframes The sequence of keyframes between whose endpoints we should
* apply paced distribute, [Paced A, Paced B], and both Paced A & Paced B
* should be paceable.
* @param aPacedValues The sequence of computed values of the paced property.
* We get this by GetPacedPropertyKeyframeValues().
* @param aProperty The paced property.
+ * @param aElement The context element for computing the distance between each
+ * pair of keyframes.
*/
static void
PaceRange(const Range<Keyframe>& aKeyframes,
const Range<ComputedKeyframeValues>& aPacedValues,
- nsCSSProperty aProperty)
+ nsCSSProperty aProperty,
+ dom::Element* aElement)
{
const size_t len = aKeyframes.length();
if (len < 3) {
return;
}
auto IsPaceable = [](ComputedKeyframeValues& aComputedValues) {
return !aComputedValues.IsEmpty();
@@ -1379,17 +1384,18 @@ PaceRange(const Range<Keyframe>& aKeyfra
const StyleAnimationValue& preValue = prePairs[subIdx].mValue;
const StyleAnimationValue& curValue = curPairs[subIdx].mValue;
nsCSSProperty subProperty = prePairs[subIdx].mProperty;
MOZ_ASSERT(curPairs[subIdx].mProperty == subProperty,
"subProperty mismatch");
double componentDist = 0.0;
if (!StyleAnimationValue::ComputeDistance(subProperty, preValue,
- curValue, componentDist)) {
+ curValue, aElement,
+ componentDist)) {
failed = true;
break;
}
// FIXME: Any way to avoid overflow?
dist += componentDist * componentDist;
}
@@ -1398,17 +1404,18 @@ PaceRange(const Range<Keyframe>& aKeyfra
}
dist = sqrt(dist);
} else {
// If the property is longhand, we just use the 1st value.
const StyleAnimationValue& preValue = aPacedValues[preIdx][0].mValue;
const StyleAnimationValue& curValue = aPacedValues[i][0].mValue;
if (!StyleAnimationValue::ComputeDistance(aProperty, preValue,
- curValue, dist)) {
+ curValue, aElement,
+ dist)) {
failed = true;
break;
}
}
cumulativeDist[i] = cumulativeDist[i - 1] + dist;
preIdx = i;
}
--- a/dom/animation/KeyframeUtils.h
+++ b/dom/animation/KeyframeUtils.h
@@ -84,21 +84,23 @@ public:
*
* https://w3c.github.io/web-animations/#spacing-keyframes
*
* @param aKeyframes The set of keyframes to adjust.
* @param aSpacingMode The spacing mode to apply.
* @param aProperty The paced property.
* @param aComputedValues The set of computed keyframe values got by
* GetComputedKeyframeValues.
+ * @param aElement The context element for computing the cumulative distance.
*/
static void ApplySpacing(nsTArray<Keyframe>& aKeyframes,
SpacingMode aSpacingMode,
nsCSSProperty aProperty,
- nsTArray<ComputedKeyframeValues>& aComputedValues);
+ nsTArray<ComputedKeyframeValues>& aComputedValues,
+ dom::Element* aElement = nullptr);
/**
* Fills in the mComputedOffset member of each keyframe in the given array
* using distribute spacing mode.
*
* @param aKeyframes The set of keyframes to adjust.
*/
static void ApplyDistributeSpacing(nsTArray<Keyframe>& aKeyframes);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2531,17 +2531,18 @@ nsDOMWindowUtils::ComputeAnimationDistan
StyleAnimationValue v1, v2;
if (property == eCSSProperty_UNKNOWN ||
!ComputeAnimationValue(property, content->AsElement(), aValue1, v1) ||
!ComputeAnimationValue(property, content->AsElement(), aValue2, v2)) {
return NS_ERROR_ILLEGAL_VALUE;
}
- if (!StyleAnimationValue::ComputeDistance(property, v1, v2, *aResult)) {
+ if (!StyleAnimationValue::ComputeDistance(property, v1, v2, nullptr,
+ *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
@@ -16,16 +16,17 @@
#include "nsIStyleRule.h"
#include "mozilla/css/StyleRule.h"
#include "nsString.h"
#include "nsStyleContext.h"
#include "nsStyleSet.h"
#include "nsComputedDOMStyle.h"
#include "nsCSSParser.h"
#include "nsCSSPseudoElements.h"
+#include "nsROCSSPrimitiveValue.h"
#include "mozilla/css/Declaration.h"
#include "mozilla/dom/Element.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "gfxMatrix.h"
#include "gfxQuaternion.h"
#include "nsIDocument.h"
#include "nsIFrame.h"
@@ -470,23 +471,132 @@ CalcPositionCoordSquareDistance(const ns
PixelCalcValue calcVal1 = CalcBackgroundCoord(aPos1);
PixelCalcValue calcVal2 = CalcBackgroundCoord(aPos2);
float difflen = calcVal2.mLength - calcVal1.mLength;
float diffpct = calcVal2.mPercent - calcVal1.mPercent;
return difflen * difflen + diffpct * diffpct;
}
+bool
+GetContainingBlockPixelValue(const nsAString& aProperty,
+ dom::Element* aElement,
+ double& aResult)
+{
+ dom::Element* parentElement = aElement->GetParentElement();
+ if (!parentElement) {
+ return false;
+ }
+
+ nsIDocument* doc = parentElement->GetUncomposedDoc();
+ if (!doc) {
+ return false;
+ }
+
+ RefPtr<nsComputedDOMStyle> style =
+ NS_NewComputedDOMStyle(parentElement, EmptyString(), doc->GetShell());
+ if (!style) {
+ return false;
+ }
+
+ ErrorResult rv;
+ RefPtr<dom::CSSValue> cssValue = style->GetPropertyCSSValue(aProperty, rv);
+ if (rv.Failed()) {
+ return false;
+ }
+
+ nsROCSSPrimitiveValue* value = cssValue->AsPrimitiveValue();
+ if (!value || value->PrimitiveType() != nsIDOMCSSPrimitiveValue::CSS_PX) {
+ return false;
+ }
+
+ aResult = (double)value->GetFloatValue(nsIDOMCSSPrimitiveValue::CSS_PX, rv);
+ return !rv.Failed();
+}
+
+bool
+GetUsedAppUnitValue(nsCSSProperty aProperty,
+ const StyleAnimationValue& aValue,
+ dom::Element* aElement,
+ double& aResult)
+{
+ if (aValue.GetUnit() == StyleAnimationValue::eUnit_Coord) {
+ aResult = (double)aValue.GetCoordValue();
+ return true;
+ }
+
+ MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_Percent ||
+ aValue.GetUnit() == StyleAnimationValue::eUnit_Calc,
+ "Unsupported unit");
+
+ if (!aElement) {
+ return false;
+ }
+
+ double length = 0.0;
+ double percent = 0.0;
+ if (aValue.GetUnit() == StyleAnimationValue::eUnit_Percent) {
+ percent = aValue.GetPercentValue();
+ } else {
+ nsCSSValue *val = aValue.GetCSSValueValue();
+ PixelCalcValue pValue = ExtractCalcValueInternal(*val);
+ length = pValue.mLength;
+ percent = pValue.mPercent;
+ }
+
+ double base = 0.0;
+ switch (aProperty) {
+ case eCSSProperty_width:
+ case eCSSProperty_left:
+ case eCSSProperty_right:
+ case eCSSProperty_margin_left:
+ case eCSSProperty_margin_right:
+ case eCSSProperty_padding_left:
+ case eCSSProperty_padding_right:
+ case eCSSProperty_min_width:
+ case eCSSProperty_max_width:
+ if (!GetContainingBlockPixelValue(NS_LITERAL_STRING("width"),
+ aElement, base)) {
+ return false;
+ }
+ break;
+
+ case eCSSProperty_height:
+ case eCSSProperty_top:
+ case eCSSProperty_bottom:
+ case eCSSProperty_margin_top:
+ case eCSSProperty_margin_bottom:
+ case eCSSProperty_padding_top:
+ case eCSSProperty_padding_bottom:
+ case eCSSProperty_min_height:
+ case eCSSProperty_max_height:
+ if (!GetContainingBlockPixelValue(NS_LITERAL_STRING("height"),
+ aElement, base)) {
+ return false;
+ }
+ break;
+
+ default:
+ // Not supported.
+ return false;
+ }
+
+ aResult = length +
+ percent * (double)nsPresContext::CSSPixelsToAppUnits((float)base);
+ return true;
+}
+
// CLASS METHODS
// -------------
bool
StyleAnimationValue::ComputeDistance(nsCSSProperty aProperty,
const StyleAnimationValue& aStartValue,
const StyleAnimationValue& aEndValue,
+ dom::Element* aElement,
double& aDistance)
{
Unit commonUnit =
GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
switch (commonUnit) {
case eUnit_Null:
case eUnit_Auto:
@@ -580,16 +690,29 @@ StyleAnimationValue::ComputeDistance(nsC
double diffR = startR - endR;
double diffG = startG - endG;
double diffB = startB - endB;
aDistance = sqrt(diffA * diffA + diffR * diffR +
diffG * diffG + diffB * diffB);
return true;
}
case eUnit_Calc: {
+ // If passing a valid element, we will try to get used values of
+ // aStartValue and aEndValue and then calculate the distance
+ // like eUnit_Coord.
+ if (aElement) {
+ double v1 = 0.0;
+ double v2 = 0.0;
+ if (GetUsedAppUnitValue(aProperty, aStartValue, aElement, v1) &&
+ GetUsedAppUnitValue(aProperty, aEndValue, aElement, v2)) {
+ aDistance = Abs(v2 - v1);
+ return true;
+ }
+ }
+
PixelCalcValue v1 = ExtractCalcValue(aStartValue);
PixelCalcValue v2 = ExtractCalcValue(aEndValue);
float difflen = v2.mLength - v1.mLength;
float diffpct = v2.mPercent - v1.mPercent;
aDistance = sqrt(difflen * difflen + diffpct * diffpct);
return true;
}
case eUnit_ObjectPosition: {
@@ -844,16 +967,17 @@ StyleAnimationValue::ComputeDistance(nsC
(color2.GetColorValue(), StyleAnimationValue::ColorConstructor);
double colorDistance;
#ifdef DEBUG
bool ok =
#endif
StyleAnimationValue::ComputeDistance(eCSSProperty_color,
color1Value, color2Value,
+ nullptr,
colorDistance);
MOZ_ASSERT(ok, "should not fail");
squareDistance += colorDistance * colorDistance;
}
shadow1 = shadow1->mNext;
shadow2 = shadow2->mNext;
MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -68,22 +68,27 @@ 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 aElement The context element for eUnit_Calc. If it is non-null,
+ * we may use this context element to get the used values
+ * for aProperty and compute the distance by used values,
+ * instead of computed calc values.
* @param aDistance The result of the calculation.
* @return true on success, false on failure.
*/
static bool ComputeDistance(nsCSSProperty aProperty,
const StyleAnimationValue& aStartValue,
const StyleAnimationValue& aEndValue,
+ dom::Element* aElement,
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