From 3426024d9c1e0669d4d7c33fd74b9e8a4c99cefb Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Sat, 6 Nov 2010 12:13:01 -0700 Subject: [PATCH] Bug 594198: In SMIL animation of length-valued attributes/properties, allow interpolation between 0 values and unitless values. r=birtles a=blocking-final+ --HG-- rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg rename : layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-no-no.svg --- content/smil/nsSMILCSSValueType.cpp | 124 +++++++++++------- content/smil/test/db_smilCSSFromBy.js | 27 +++- content/smil/test/db_smilCSSFromTo.js | 41 +++++- content/smil/test/db_smilCSSPaced.js | 50 ++++++- .../style/anim-css-strokewidth-1-by-no-no.svg | 17 +++ .../anim-css-strokewidth-1-from-by-no-no.svg | 18 +++ .../anim-css-strokewidth-1-from-to-no-no.svg | 18 +++ .../style/anim-css-strokewidth-1-to-no-no.svg | 17 +++ .../anim-css-strokewidth-4-from-by-no-no.svg | 18 +++ layout/reftests/svg/smil/style/reftest.list | 7 + 10 files changed, 279 insertions(+), 58 deletions(-) create mode 100644 layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg create mode 100644 layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg create mode 100644 layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg create mode 100644 layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg create mode 100644 layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-no-no.svg diff --git a/content/smil/nsSMILCSSValueType.cpp b/content/smil/nsSMILCSSValueType.cpp index 780187734fae..10b2f48df6b6 100644 --- a/content/smil/nsSMILCSSValueType.cpp +++ b/content/smil/nsSMILCSSValueType.cpp @@ -92,18 +92,61 @@ GetZeroValueForUnit(nsStyleAnimation::Unit aUnit) } } -static void -InvertSign(nsStyleAnimation::Value& aStyleCoord) +// 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 PR_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.) +// +// Returns PR_TRUE on success, or PR_FALSE. +static const PRBool +FinalizeStyleAnimationValues(const nsStyleAnimation::Value*& aValue1, + const nsStyleAnimation::Value*& aValue2) { - switch (aStyleCoord.GetUnit()) { + NS_ABORT_IF_FALSE(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) { + aValue1 = GetZeroValueForUnit(aValue2->GetUnit()); + return !!aValue1; // Fail if we have no zero value for this unit. + } + if (!aValue2) { + 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 nsStyleAnimation, since their Units don't match. + // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value. + if (*aValue1 == sZeroCoord && + aValue2->GetUnit() == nsStyleAnimation::eUnit_Float) { + aValue1 = &sZeroFloat; + } else if (*aValue2 == sZeroCoord && + aValue1->GetUnit() == nsStyleAnimation::eUnit_Float) { + aValue2 = &sZeroFloat; + } + + return PR_TRUE; +} + +static void +InvertSign(nsStyleAnimation::Value& aValue) +{ + switch (aValue.GetUnit()) { case nsStyleAnimation::eUnit_Coord: - aStyleCoord.SetCoordValue(-aStyleCoord.GetCoordValue()); + aValue.SetCoordValue(-aValue.GetCoordValue()); break; case nsStyleAnimation::eUnit_Percent: - aStyleCoord.SetPercentValue(-aStyleCoord.GetPercentValue()); + aValue.SetPercentValue(-aValue.GetPercentValue()); break; case nsStyleAnimation::eUnit_Float: - aStyleCoord.SetFloatValue(-aStyleCoord.GetFloatValue()); + aValue.SetFloatValue(-aValue.GetFloatValue()); break; default: NS_NOTREACHED("Calling InvertSign with an unsupported unit"); @@ -154,9 +197,6 @@ nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const if (!destWrapper) { // barely-initialized dest -- need to alloc & copy aDest.mU.mPtr = new ValueWrapper(*srcWrapper); - if (!aDest.mU.mPtr) { - return NS_ERROR_OUT_OF_MEMORY; - } } else { // both already fully-initialized -- just copy straight across *destWrapper = *srcWrapper; @@ -221,33 +261,27 @@ nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, return NS_ERROR_FAILURE; } - // Handle barely-initialized "zero" added value. - const nsStyleAnimation::Value* realValueToAdd = valueToAddWrapper ? - &valueToAddWrapper->mCSSValue : - GetZeroValueForUnit(destWrapper->mCSSValue.GetUnit()); - if (!realValueToAdd) { - // No zero value for this unit --> doesn't support addition. + const nsStyleAnimation::Value* valueToAdd = valueToAddWrapper ? + &valueToAddWrapper->mCSSValue : nsnull; + const nsStyleAnimation::Value* destValue = destWrapper ? + &destWrapper->mCSSValue : nsnull; + if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) { return NS_ERROR_FAILURE; } + // Did FinalizeStyleAnimationValues change destValue? + // If so, update outparam to use the new value. + if (destWrapper && &destWrapper->mCSSValue != destValue) { + destWrapper->mCSSValue = *destValue; + } // Handle barely-initialized "zero" destination. if (!destWrapper) { - // Need to fully initialize destination, since it's an outparam - const nsStyleAnimation::Value* zeroVal = - GetZeroValueForUnit(valueToAddWrapper->mCSSValue.GetUnit()); - if (!zeroVal) { - // No zero value for this unit --> doesn't support addition. - return NS_ERROR_FAILURE; - } aDest.mU.mPtr = destWrapper = - new ValueWrapper(property, *zeroVal, valueToAddWrapper->mPresContext); - if (!destWrapper) { - return NS_ERROR_OUT_OF_MEMORY; - } + new ValueWrapper(property, *destValue, valueToAddWrapper->mPresContext); } - return nsStyleAnimation::Add(property, destWrapper->mCSSValue, - *realValueToAdd, aCount) ? + return nsStyleAnimation::Add(property, + destWrapper->mCSSValue, *valueToAdd, aCount) ? NS_OK : NS_ERROR_FAILURE; } @@ -265,15 +299,15 @@ nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom, NS_ABORT_IF_FALSE(toWrapper, "expecting non-null endpoint"); const nsStyleAnimation::Value* fromCSSValue = fromWrapper ? - &fromWrapper->mCSSValue : - GetZeroValueForUnit(toWrapper->mCSSValue.GetUnit()); - if (!fromCSSValue) { - // No zero value for this unit --> doesn't support distance-computation. + &fromWrapper->mCSSValue : nsnull; + const nsStyleAnimation::Value* toCSSValue = &toWrapper->mCSSValue; + if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) { return NS_ERROR_FAILURE; } - return nsStyleAnimation::ComputeDistance(toWrapper->mPropID, *fromCSSValue, - toWrapper->mCSSValue, aDistance) ? + return nsStyleAnimation::ComputeDistance(toWrapper->mPropID, + *fromCSSValue, *toCSSValue, + aDistance) ? NS_OK : NS_ERROR_FAILURE; } @@ -290,27 +324,26 @@ nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal, NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type"); NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0, "unit distance value out of bounds"); + NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam"); const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal); const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal); NS_ABORT_IF_FALSE(endWrapper, "expecting non-null endpoint"); - NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam"); const nsStyleAnimation::Value* startCSSValue = startWrapper ? - &startWrapper->mCSSValue : - GetZeroValueForUnit(endWrapper->mCSSValue.GetUnit()); - if (!startCSSValue) { - // No zero value for this unit --> doesn't support interpolation. + &startWrapper->mCSSValue : nsnull; + const nsStyleAnimation::Value* endCSSValue = &endWrapper->mCSSValue; + if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) { return NS_ERROR_FAILURE; } nsStyleAnimation::Value resultValue; - if (nsStyleAnimation::Interpolate(endWrapper->mPropID, *startCSSValue, - endWrapper->mCSSValue, aUnitDistance, - resultValue)) { + if (nsStyleAnimation::Interpolate(endWrapper->mPropID, + *startCSSValue, *endCSSValue, + aUnitDistance, resultValue)) { aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue, endWrapper->mPresContext); - return aResult.mU.mPtr ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + return NS_OK; } return NS_ERROR_FAILURE; } @@ -389,11 +422,6 @@ nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID, aString, parsedValue)) { sSingleton.Init(aValue); aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue, presContext); - if (!aValue.mU.mPtr) { - // Out of memory! Destroy outparam, to leave it as nsSMILNullType, - // which indicates to our caller that we failed. - sSingleton.Destroy(aValue); - } } } diff --git a/content/smil/test/db_smilCSSFromBy.js b/content/smil/test/db_smilCSSFromBy.js index 059bfc3f5bda..2fcb93b39768 100644 --- a/content/smil/test/db_smilCSSFromBy.js +++ b/content/smil/test/db_smilCSSFromBy.js @@ -52,7 +52,26 @@ var _fromByTestLists = midComp: "rgb(65, 60, 55)", toComp: "rgb(80, 70, 60)"}), ], + lengthNoUnits: [ + new AnimTestcaseFromBy("0", "50", { fromComp: "0px", // 0 acts like 0px + midComp: "25px", + toComp: "50px"}), + new AnimTestcaseFromBy("30", "10", { fromComp: "30px", + midComp: "35px", + toComp: "40px"}), + ], + lengthNoUnitsSVG: [ + new AnimTestcaseFromBy("0", "50", { fromComp: "0px", // 0 acts like 0px + midComp: "25", + toComp: "50"}), + new AnimTestcaseFromBy("30", "10", { fromComp: "30", + midComp: "35", + toComp: "40"}), + ], lengthPx: [ + new AnimTestcaseFromBy("0", "8px", { fromComp: "0px", // 0 acts like 0px + midComp: "4px", + toComp: "8px"}), new AnimTestcaseFromBy("1px", "10px", { midComp: "6px", toComp: "11px"}), ], opacity: [ @@ -120,7 +139,9 @@ var gFromByBundles = new AnimTestcaseFromBy("10px serif", "normal normal 400 100px / 10px monospace"), ]), - new TestcaseBundle(gPropList.font_size, _fromByTestLists.lengthPx), + new TestcaseBundle(gPropList.font_size, + [].concat(_fromByTestLists.lengthNoUnits, + _fromByTestLists.lengthPx)), new TestcaseBundle(gPropList.font_size_adjust, [ // These testcases implicitly have no effect, because font-size-adjust is // non-additive (and is declared as such in db_smilCSSPropertyList.js) @@ -149,5 +170,7 @@ var gFromByBundles = new AnimTestcaseFromBy("10", "5"), new AnimTestcaseFromBy("1", "2, 3"), ]), - new TestcaseBundle(gPropList.stroke_width, _fromByTestLists.lengthPx), + new TestcaseBundle(gPropList.stroke_width, + [].concat(_fromByTestLists.lengthNoUnitsSVG, + _fromByTestLists.lengthPx)) ]; diff --git a/content/smil/test/db_smilCSSFromTo.js b/content/smil/test/db_smilCSSFromTo.js index 4aa0784711a0..db8a36a4a868 100644 --- a/content/smil/test/db_smilCSSFromTo.js +++ b/content/smil/test/db_smilCSSFromTo.js @@ -105,7 +105,33 @@ var _fromToTestLists = { "#gradB\") rgb(0, 0, 255)" }, "need support for URI-based paints"), ], + lengthNoUnits: [ + new AnimTestcaseFromTo("0", "20", { fromComp: "0px", + midComp: "10px", + toComp: "20px"}), + new AnimTestcaseFromTo("50", "0", { fromComp: "50px", + midComp: "25px", + toComp: "0px"}), + new AnimTestcaseFromTo("30", "80", { fromComp: "30px", + midComp: "55px", + toComp: "80px"}), + ], + lengthNoUnitsSVG: [ + new AnimTestcaseFromTo("0", "20", { fromComp: "0px", // 0 acts like 0px + midComp: "10", + toComp: "20"}), + new AnimTestcaseFromTo("50", "0", { fromComp: "50", + midComp: "25", + toComp: "0px"}), // 0 acts like 0px + new AnimTestcaseFromTo("30", "80", { fromComp: "30", + midComp: "55", + toComp: "80"}), + ], lengthPx: [ + new AnimTestcaseFromTo("0", "12px", { fromComp: "0px", // 0 acts like 0px + midComp: "6px"}), + new AnimTestcaseFromTo("16px", "0", { midComp: "8px", + toComp: "0px"}), // 0 acts like 0px new AnimTestcaseFromTo("10px", "20px", { midComp: "15px"}), new AnimTestcaseFromTo("41px", "1px", { midComp: "21px"}), ], @@ -266,7 +292,8 @@ var gFromToBundles = [ new AnimTestcaseFromTo("cursive", "monospace"), ]), new TestcaseBundle(gPropList.font_size, - [].concat(_fromToTestLists.lengthPx, [ + [].concat(_fromToTestLists.lengthNoUnits, + _fromToTestLists.lengthPx, [ new AnimTestcaseFromTo("10px", "40%", { midComp: "15px", toComp: "20px" }), new AnimTestcaseFromTo("160%", "80%", { fromComp: "80px", @@ -331,7 +358,8 @@ var gFromToBundles = [ toComp: "optimizespeed" }), ]), new TestcaseBundle(gPropList.letter_spacing, - [].concat(_fromToTestLists.lengthPx, + [].concat(_fromToTestLists.lengthNoUnits, + _fromToTestLists.lengthPx, _fromToTestLists.lengthPxPctSVG)), new TestcaseBundle(gPropList.letter_spacing, _fromToTestLists.lengthPctSVG, @@ -388,7 +416,8 @@ var gFromToBundles = [ midComp: "1, 3, 3, 5, 5, 2, 2, 4, 4, 6"}), ])), new TestcaseBundle(gPropList.stroke_dashoffset, - [].concat(_fromToTestLists.lengthPx, + [].concat(_fromToTestLists.lengthNoUnitsSVG, + _fromToTestLists.lengthPx, _fromToTestLists.lengthPxPctSVG, _fromToTestLists.lengthPctSVG)), new TestcaseBundle(gPropList.stroke_linecap, [ @@ -405,7 +434,8 @@ var gFromToBundles = [ ]), new TestcaseBundle(gPropList.stroke_opacity, _fromToTestLists.opacity), new TestcaseBundle(gPropList.stroke_width, - [].concat(_fromToTestLists.lengthPx, + [].concat(_fromToTestLists.lengthNoUnitsSVG, + _fromToTestLists.lengthPx, _fromToTestLists.lengthPxPctSVG, _fromToTestLists.lengthPctSVG, [ new AnimTestcaseFromTo("inherit", "7px", @@ -438,7 +468,8 @@ var gFromToBundles = [ new AnimTestcaseFromTo("hidden", "collapse"), ]), new TestcaseBundle(gPropList.word_spacing, - [].concat(_fromToTestLists.lengthPx, + [].concat(_fromToTestLists.lengthNoUnits, + _fromToTestLists.lengthPx, _fromToTestLists.lengthPxPctSVG)), new TestcaseBundle(gPropList.word_spacing, _fromToTestLists.lengthPctSVG, diff --git a/content/smil/test/db_smilCSSPaced.js b/content/smil/test/db_smilCSSPaced.js index 130df9bef614..ce429eb3a8ad 100644 --- a/content/smil/test/db_smilCSSPaced.js +++ b/content/smil/test/db_smilCSSPaced.js @@ -92,6 +92,47 @@ var _pacedTestLists = }, "need support for URI-based paints"), ], + lengthNoUnits : [ + new AnimTestcasePaced("2; 0; 4", + { comp0: "2px", + comp1_6: "1px", + comp1_3: "0px", + comp2_3: "2px", + comp1: "4px" + }), + new AnimTestcasePaced("10; 12; 8", + { comp0: "10px", + comp1_6: "11px", + comp1_3: "12px", + comp2_3: "10px", + comp1: "8px" + }), + ], + lengthNoUnitsSVG : [ + new AnimTestcasePaced("2; 0; 4", + { comp0: "2", + comp1_6: "1", + comp1_3: "0px", // 0 acts like 0px + comp2_3: "2", + comp1: "4" + }), + new AnimTestcasePaced("10; 12; 8", + { comp0: "10", + comp1_6: "11", + comp1_3: "12", + comp2_3: "10", + comp1: "8" + }), + ], + lengthPx : [ + new AnimTestcasePaced("0; 2px; 6px", + { comp0: "0px", // 0 acts like 0px + comp1_6: "1px", + comp1_3: "2px", + comp2_3: "4px", + comp1: "6px" + }), + ], lengthPx : [ new AnimTestcasePaced("0px; 2px; 6px", { comp0: "0px", @@ -223,7 +264,8 @@ var gPacedBundles = [].concat(_pacedTestLists.color, _pacedTestLists.paintServer)), new TestcaseBundle(gPropList.font_size, - [].concat(_pacedTestLists.lengthPx, [ + [].concat(_pacedTestLists.lengthNoUnits, + _pacedTestLists.lengthPx, [ new AnimTestcasePaced("20%; 24%; 16%", { comp0: "10px", comp1_6: "11px", @@ -280,11 +322,13 @@ var gPacedBundles = }), ])), new TestcaseBundle(gPropList.stroke_dashoffset, - [].concat(_pacedTestLists.lengthPx, + [].concat(_pacedTestLists.lengthNoUnitsSVG, + _pacedTestLists.lengthPx, _pacedTestLists.lengthPctSVG, _pacedTestLists.lengthPxPctSVG)), new TestcaseBundle(gPropList.stroke_width, - [].concat(_pacedTestLists.lengthPx, + [].concat(_pacedTestLists.lengthNoUnitsSVG, + _pacedTestLists.lengthPx, _pacedTestLists.lengthPctSVG, _pacedTestLists.lengthPxPctSVG)), // XXXdholbert TODO: test 'stroke-dasharray' once we support animating it diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg new file mode 100644 index 000000000000..9eb3d4bafdce --- /dev/null +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg new file mode 100644 index 000000000000..9e3a4db7ba1c --- /dev/null +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg new file mode 100644 index 000000000000..fc4e0a8eff8c --- /dev/null +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg new file mode 100644 index 000000000000..0137274bd90d --- /dev/null +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-no-no.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-no-no.svg new file mode 100644 index 000000000000..43845db4f7e4 --- /dev/null +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-no-no.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/layout/reftests/svg/smil/style/reftest.list b/layout/reftests/svg/smil/style/reftest.list index 76810b9f7b74..e01f5a5290f6 100644 --- a/layout/reftests/svg/smil/style/reftest.list +++ b/layout/reftests/svg/smil/style/reftest.list @@ -102,6 +102,12 @@ fails == anim-css-strokedasharray-1.svg anim-css-strokedasharray-1-ref.svg == anim-css-strokewidth-1-from-to-px-px.svg anim-css-strokewidth-1-ref.svg == anim-css-strokewidth-1-to-px-px.svg anim-css-strokewidth-1-ref.svg +# 'stroke-width' property, from/by/to with unitless values only +== anim-css-strokewidth-1-by-no-no.svg anim-css-strokewidth-1-ref.svg +== anim-css-strokewidth-1-from-by-no-no.svg anim-css-strokewidth-1-ref.svg +== anim-css-strokewidth-1-from-to-no-no.svg anim-css-strokewidth-1-ref.svg +== anim-css-strokewidth-1-to-no-no.svg anim-css-strokewidth-1-ref.svg + # 'stroke-width' property, from/by/to with percent values # XXXdholbert the mixed pct + px tests fail right now, because we need calc() # in order to interpolate between pct and non-pct values, and we don't yet @@ -144,6 +150,7 @@ fails == anim-css-strokewidth-1-to-px-pct.svg anim-css-strokewidth-1-ref.s == anim-css-strokewidth-4-from-by-px-px.svg anim-css-strokewidth-4-ref.svg == anim-css-strokewidth-4-from-by-px-em.svg anim-css-strokewidth-4-ref.svg == anim-css-strokewidth-4-from-by-em-em.svg anim-css-strokewidth-4-ref.svg +== anim-css-strokewidth-4-from-by-no-no.svg anim-css-strokewidth-4-ref.svg # check correct handling of "!important" values == important-set-1.svg anim-standard-ref.svg