From 15a322effa1ae63d1b3dc5d8cb98d92195b27fbd Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sat, 12 Jun 2021 01:12:39 +0000 Subject: [PATCH] Issue #1781 - Part 1: support calc() in stroke-dashoffset CSS This adds basic calc() support to stroke-dashoffset. It does not provide CSS animation (yet, todo for part 2) --- dom/svg/SVGContentUtils.cpp | 8 +++ layout/style/nsCSSPropList.h | 2 +- layout/style/nsRuleNode.cpp | 111 +++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp index 7f372c9b42..21b98daba8 100644 --- a/dom/svg/SVGContentUtils.cpp +++ b/dom/svg/SVGContentUtils.cpp @@ -833,6 +833,14 @@ SVGContentUtils::CoordToFloat(nsSVGElement *aContent, SVGSVGElement* ctx = aContent->GetCtx(); return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f; } + case eStyleUnit_Calc: { + MOZ_ASSERT(aCoord.GetCalcValue(), "Invalid calc value"); + nsStyleCoord::Calc* calc = aCoord.GetCalcValue(); + SVGSVGElement* ctx = aContent->GetCtx(); + float len = nsPresContext::AppUnitsToFloatCSSPixels(calc->mLength); + return ctx ? len + calc->mPercent * ctx->GetLength(SVGContentUtils::XY) + : len; + } default: return 0.0f; } diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index e78dafcc5c..f0cab6b131 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -3816,7 +3816,7 @@ CSS_PROP_SVG( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_NUMBERS_ARE_PIXELS, "", - VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD, + VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD | VARIANT_CALC, kStrokeContextValueKTable, offsetof(nsStyleSVG, mStrokeDashoffset), eStyleAnimType_Coord) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 04715aa99a..74ba732885 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -4492,6 +4492,15 @@ struct LengthNumberCalcObj bool mIsNumber; }; +struct RealNumberComputedCalc +{ + // We use float for mLength, so it can support real numbers. + float mLength = 0.0f; + float mPercent = 0.0f; + bool mIsNumber = false; +}; + + struct LengthNumberCalcOps : public css::NumbersAlreadyNormalizedOps { typedef LengthNumberCalcObj result_type; @@ -4574,6 +4583,96 @@ struct LengthNumberCalcOps : public css::NumbersAlreadyNormalizedOps } }; +// This is like LengthNumberCalcOps, but then for real/float. +struct LengthPercentNumberCalcOps : public css::NumbersAlreadyNormalizedOps +{ + typedef RealNumberComputedCalc result_type; + + nsStyleContext* const mContext; + nsPresContext* const mPresContext; + RuleNodeCacheConditions& mConditions; + bool mHasPercent = false; + + LengthPercentNumberCalcOps(nsStyleContext* aContext, + nsPresContext* aPresContext, + RuleNodeCacheConditions& aConditions) + : mContext(aContext), + mPresContext(aPresContext), + mConditions(aConditions) { } + + result_type + MergeAdditive(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + MOZ_ASSERT(aValue1.mIsNumber == aValue2.mIsNumber); + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Plus || + aCalcFunction == eCSSUnit_Calc_Minus, + "unexpected unit"); + + result_type result; + result.mIsNumber = aValue1.mIsNumber; + if (aCalcFunction == eCSSUnit_Calc_Plus) { + result.mLength = aValue1.mLength + aValue2.mLength; + result.mPercent = aValue1.mPercent + aValue2.mPercent; + } else { + result.mLength = aValue2.mLength == NS_IEEEPositiveInfinity() ? + 0.0f : + aValue1.mLength - aValue2.mLength; + result.mPercent = aValue1.mPercent - aValue2.mPercent; + } + return result; + } + + result_type + MergeMultiplicativeL(nsCSSUnit aCalcFunction, + float aValue1, result_type aValue2) + { + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, + "unexpected unit"); + result_type result; + result.mLength = aValue1 * aValue2.mLength; + result.mPercent = aValue1 * aValue2.mPercent; + result.mIsNumber = aValue2.mIsNumber; + return result; + } + + result_type + MergeMultiplicativeR(nsCSSUnit aCalcFunction, + result_type aValue1, float aValue2) + { + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_TimesR || + aCalcFunction == eCSSUnit_Divided, + "unexpected unit"); + result_type result; + if (aCalcFunction == eCSSUnit_Calc_Divided) { + aValue2 = 1.0f / aValue2; + } + result.mLength = aValue1.mLength * aValue2; + result.mPercent = aValue1.mPercent * aValue2; + result.mIsNumber = aValue1.mIsNumber; + return result; + } + + result_type + ComputeLeafValue(const nsCSSValue& aValue) + { + result_type result; + if (aValue.IsLengthUnit()) { + result.mLength = CalcLength(aValue, mContext, mPresContext, mConditions); + } else if (aValue.GetUnit() == eCSSUnit_Percent) { + result.mPercent = aValue.GetPercentValue(); + mHasPercent = true; + } else if (aValue.GetUnit() == eCSSUnit_Number) { + result.mLength = aValue.GetFloatValue(); + result.mIsNumber = true; + } else { + MOZ_ASSERT_UNREACHABLE("unexpected unit"); + result.mLength = CalcLength(aValue, mContext, mPresContext, mConditions); + } + return result; + } +}; + struct SetLineHeightCalcOps : public LengthNumberCalcOps { SetLineHeightCalcOps(nsStyleContext* aStyleContext, @@ -9626,6 +9725,18 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, strokeDashoffsetValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE); if (svg->StrokeDashoffsetFromObject()) { svg->mStrokeDashoffset.SetCoordValue(0); + } else if (strokeDashoffsetValue->IsCalcUnit()) { + LengthPercentNumberCalcOps ops(aContext, mPresContext, conditions); + RealNumberComputedCalc obj = css::ComputeCalc(*strokeDashoffsetValue, ops); + if (obj.mIsNumber) { + svg->mStrokeDashoffset.SetFactorValue(obj.mLength); + } else { + nsStyleCoord::Calc* calcObj = new nsStyleCoord::Calc; + calcObj->mLength = NSToCoordRoundWithClamp(obj.mLength); + calcObj->mPercent = obj.mPercent; + calcObj->mHasPercent = ops.mHasPercent; + svg->mStrokeDashoffset.SetCalcValue(calcObj); + } } else { SetCoord(*aRuleData->ValueForStrokeDashoffset(), svg->mStrokeDashoffset, parentSVG->mStrokeDashoffset,