From 7e45a248533e912c76064daa05a446560c0290ff Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Sun, 10 May 2026 14:04:56 -0400 Subject: [PATCH] Support CSS sizing math functions Prereq for parts of #2404 --- layout/style/CSSCalc.h | 108 +++++++++++++++++++- layout/style/nsCSSParser.cpp | 145 ++++++++++++++++++++++++-- layout/style/nsCSSValue.cpp | 7 +- layout/style/nsRuleNode.cpp | 193 +++++++++++++++++++++++++++++++++-- 4 files changed, 431 insertions(+), 22 deletions(-) diff --git a/layout/style/CSSCalc.h b/layout/style/CSSCalc.h index 665fcd3613..0dd1c37dd3 100644 --- a/layout/style/CSSCalc.h +++ b/layout/style/CSSCalc.h @@ -6,6 +6,7 @@ #include "nsCSSValue.h" #include "nsStyleCoord.h" +#include #include namespace mozilla { @@ -46,6 +47,13 @@ namespace css { * result_type aValue1, float aValue2); * * result_type + * MergeMinMax(nsCSSUnit aCalcFunction, + * result_type aValue1, result_type aValue2); + * + * result_type + * MergeClamp(result_type aMin, result_type aCenter, result_type aMax); + * + * result_type * ComputeLeafValue(const input_type& aValue); * * float @@ -70,6 +78,8 @@ namespace css { * MergeAdditive for Plus and Minus * MergeMultiplicativeL for Times_L (number * value) * MergeMultiplicativeR for Times_R (value * number) and Divided + * MergeMinMax for Min and Max + * MergeClamp for Clamp */ template static typename CalcOps::result_type @@ -104,6 +114,25 @@ ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) float rhs = aOps.ComputeNumber(arr->Item(1)); return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs); } + case eCSSUnit_Calc_Min: + case eCSSUnit_Calc_Max: { + typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); + MOZ_ASSERT(arr->Count() >= 1, "unexpected length"); + typename CalcOps::result_type result = ComputeCalc(arr->Item(0), aOps); + for (uint32_t i = 1; i < arr->Count(); ++i) { + typename CalcOps::result_type next = ComputeCalc(arr->Item(i), aOps); + result = aOps.MergeMinMax(CalcOps::GetUnit(aValue), result, next); + } + return result; + } + case eCSSUnit_Calc_Clamp: { + typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); + MOZ_ASSERT(arr->Count() == 3, "unexpected length"); + typename CalcOps::result_type min = ComputeCalc(arr->Item(0), aOps); + typename CalcOps::result_type center = ComputeCalc(arr->Item(1), aOps); + typename CalcOps::result_type max = ComputeCalc(arr->Item(2), aOps); + return aOps.MergeClamp(min, center, max); + } default: { return aOps.ComputeLeafValue(aValue); } @@ -168,6 +197,23 @@ struct BasicCoordCalcOps } return NSCoordSaturatingMultiply(aValue1, aValue2); } + + result_type + MergeMinMax(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + if (aCalcFunction == eCSSUnit_Calc_Min) { + return std::min(aValue1, aValue2); + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Max, "unexpected unit"); + return std::max(aValue1, aValue2); + } + + result_type + MergeClamp(result_type aMin, result_type aCenter, result_type aMax) + { + return std::max(aMin, std::min(aCenter, aMax)); + } }; struct BasicFloatCalcOps @@ -206,6 +252,23 @@ struct BasicFloatCalcOps "unexpected unit"); return aValue1 / aValue2; } + + result_type + MergeMinMax(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + if (aCalcFunction == eCSSUnit_Calc_Min) { + return std::min(aValue1, aValue2); + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Max, "unexpected unit"); + return std::max(aValue1, aValue2); + } + + result_type + MergeClamp(result_type aMin, result_type aCenter, result_type aMax) + { + return std::max(aMin, std::min(aCenter, aMax)); + } }; /** @@ -255,8 +318,20 @@ template static void SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) { - aOps.Append("calc("); nsCSSUnit unit = CalcOps::GetUnit(aValue); + if (unit == eCSSUnit_Calc) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + MOZ_ASSERT(array->Count() == 1, "unexpected length"); + nsCSSUnit childUnit = CalcOps::GetUnit(array->Item(0)); + if (childUnit == eCSSUnit_Calc_Min || + childUnit == eCSSUnit_Calc_Max || + childUnit == eCSSUnit_Calc_Clamp) { + SerializeCalcInternal(array->Item(0), aOps); + return; + } + } + + aOps.Append("calc("); if (unit == eCSSUnit_Calc) { const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); MOZ_ASSERT(array->Count() == 1, "unexpected length"); @@ -282,6 +357,14 @@ IsCalcMultiplicativeUnit(nsCSSUnit aUnit) aUnit == eCSSUnit_Calc_Divided; } +static inline bool +IsCalcMinMaxClampUnit(nsCSSUnit aUnit) +{ + return aUnit == eCSSUnit_Calc_Min || + aUnit == eCSSUnit_Calc_Max || + aUnit == eCSSUnit_Calc_Clamp; +} + // Serialize a non-toplevel value in a calc() tree. See big comment // above. template @@ -340,6 +423,29 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps) if (needParens) { aOps.Append(")"); } + } else if (IsCalcMinMaxClampUnit(unit)) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + MOZ_ASSERT(unit != eCSSUnit_Calc_Clamp || array->Count() == 3, + "unexpected length"); + MOZ_ASSERT(unit == eCSSUnit_Calc_Clamp || array->Count() >= 1, + "unexpected length"); + + if (unit == eCSSUnit_Calc_Min) { + aOps.Append("min("); + } else if (unit == eCSSUnit_Calc_Max) { + aOps.Append("max("); + } else { + MOZ_ASSERT(unit == eCSSUnit_Calc_Clamp, "unexpected unit"); + aOps.Append("clamp("); + } + + for (uint32_t i = 0; i < array->Count(); ++i) { + if (i != 0) { + aOps.Append(", "); + } + SerializeCalcInternal(array->Item(i), aOps); + } + aOps.Append(")"); } else { aOps.AppendLeafValue(aValue); } diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index d5c1fc3728..327aa9c29a 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -348,6 +348,31 @@ GetCalcLengthTypedArithmeticExponent(const nsCSSValue& aValue, return true; } + case eCSSUnit_Calc_Min: + case eCSSUnit_Calc_Max: + case eCSSUnit_Calc_Clamp: { + nsCSSValue::Array* array = aValue.GetArrayValue(); + MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Calc_Clamp || + array->Count() == 3, "unexpected length"); + MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Calc_Clamp || + array->Count() >= 1, "unexpected length"); + + int32_t exponent; + if (!GetCalcLengthTypedArithmeticExponent(array->Item(0), exponent)) { + return false; + } + for (uint32_t i = 1; i < array->Count(); ++i) { + int32_t itemExponent; + if (!GetCalcLengthTypedArithmeticExponent(array->Item(i), + itemExponent) || + itemExponent != exponent) { + return false; + } + } + aExponent = exponent; + return true; + } + case eCSSUnit_Number: aExponent = 0; return true; @@ -430,6 +455,35 @@ NormalizeCalcForVariant(nsCSSValue& aValue, return false; } +static bool +MergeCalcFunctionVariantMask(uint32_t& aMergedMask, uint32_t aItemMask) +{ + MOZ_ASSERT(aItemMask != 0, "unexpected empty item mask"); + if (aMergedMask == 0) { + aMergedMask = aItemMask; + return true; + } + + const bool mergedIsNumber = (aMergedMask & VARIANT_NUMBER) != 0; + const bool itemIsNumber = (aItemMask & VARIANT_NUMBER) != 0; + if (mergedIsNumber || itemIsNumber) { + return mergedIsNumber == itemIsNumber; + } + + const uint32_t lengthPercentMask = VARIANT_LENGTH | VARIANT_PERCENT; + if ((aMergedMask & lengthPercentMask) && (aItemMask & lengthPercentMask)) { + aMergedMask = (aMergedMask | aItemMask) & lengthPercentMask; + return true; + } + + if ((aMergedMask & aItemMask) != 0) { + aMergedMask &= aItemMask; + return true; + } + + return false; +} + static_assert(css::eAuthorSheetFeatures == 0 && css::eUserSheetFeatures == 1 && css::eAgentSheetFeatures == 2, @@ -1251,6 +1305,8 @@ protected: uint32_t& aVariantMask, bool *aHadFinalWS); bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask); + bool ParseCalcMinMaxClampFunction(nsCSSValue& aValue, + uint32_t& aVariantMask); bool ParseCalcNumberExpressionValue(float& aValue); bool ParseCalcNumberFunction(nsCSSValue& aValue, uint32_t& aVariantMask); bool RequireWhitespace(); @@ -13708,7 +13764,8 @@ CSSParserImpl::IsCalcFunctionToken(const nsCSSToken& aToken) const { return aToken.mType == eCSSToken_Function && (aToken.mIdent.LowerCaseEqualsLiteral("calc") || - aToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")); + aToken.mIdent.LowerCaseEqualsLiteral("-moz-calc") || + IsCalcNumberFunctionName(aToken.mIdent)); } // Parse one item of the background shorthand property. @@ -15052,8 +15109,16 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask, RefPtr arr = nsCSSValue::Array::Create(1); uint32_t resultVariantMask = aVariantMask; - if (!ParseCalcAdditiveExpression(arr->Item(0), resultVariantMask)) + const bool isMinMaxClamp = + mToken.mType == eCSSToken_Function && + IsCalcNumberFunctionName(mToken.mIdent); + if (isMinMaxClamp) { + if (!ParseCalcMinMaxClampFunction(arr->Item(0), resultVariantMask)) { + break; + } + } else if (!ParseCalcAdditiveExpression(arr->Item(0), resultVariantMask)) { break; + } if (mCalcAllowsTypedArithmetic) { int32_t exponent; @@ -15074,8 +15139,9 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask, } } - if (!ExpectSymbol(')', true)) + if (!isMinMaxClamp && !ExpectSymbol(')', true)) { break; + } aValue.SetArrayValue(arr, eCSSUnit_Calc); if (aResultVariantMask) { @@ -15285,6 +15351,14 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask) MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); if (!GetToken(true)) return false; + if (mToken.mType == eCSSToken_Function && + IsCalcNumberFunctionName(mToken.mIdent)) { + if (!ParseCalcMinMaxClampFunction(aValue, aVariantMask)) { + SkipUntil(')'); + return false; + } + return true; + } // Either an additive expression in parentheses... if (mToken.IsSymbol('(') || // Treat nested calc() as plain parenthesis. @@ -15296,14 +15370,6 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask) } return true; } - if (mToken.mType == eCSSToken_Function && - IsCalcNumberFunctionName(mToken.mIdent)) { - if (!ParseCalcNumberFunction(aValue, aVariantMask)) { - SkipUntil(')'); - return false; - } - return true; - } if ((aVariantMask & VARIANT_NUMBER) != 0 && mToken.mType == eCSSToken_Ident) { float specialValue; @@ -15338,6 +15404,63 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask) return true; } +bool +CSSParserImpl::ParseCalcMinMaxClampFunction(nsCSSValue& aValue, + uint32_t& aVariantMask) +{ + MOZ_ASSERT(mToken.mType == eCSSToken_Function, "expected function token"); + MOZ_ASSERT(IsCalcNumberFunctionName(mToken.mIdent), + "unexpected calc() math function"); + + nsCSSUnit unit; + if (mToken.mIdent.LowerCaseEqualsLiteral("min")) { + unit = eCSSUnit_Calc_Min; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("max")) { + unit = eCSSUnit_Calc_Max; + } else { + MOZ_ASSERT(mToken.mIdent.LowerCaseEqualsLiteral("clamp"), + "unexpected calc() math function"); + unit = eCSSUnit_Calc_Clamp; + } + + AutoTArray arguments; + uint32_t mergedVariantMask = 0; + + for (;;) { + nsCSSValue* argument = arguments.AppendElement(); + uint32_t argumentVariantMask = aVariantMask; + if (!ParseCalcAdditiveExpression(*argument, argumentVariantMask) || + !MergeCalcFunctionVariantMask(mergedVariantMask, + argumentVariantMask)) { + return false; + } + + if (!ExpectSymbol(',', true)) { + break; + } + } + + const uint32_t argumentCount = arguments.Length(); + if ((unit == eCSSUnit_Calc_Clamp && argumentCount != 3) || + (unit != eCSSUnit_Calc_Clamp && argumentCount == 0) || + !ExpectSymbol(')', true)) { + return false; + } + + if (unit != eCSSUnit_Calc_Clamp && argumentCount == 1) { + aValue = arguments[0]; + } else { + RefPtr array = nsCSSValue::Array::Create(argumentCount); + for (uint32_t i = 0; i < argumentCount; ++i) { + array->Item(i) = arguments[i]; + } + aValue.SetArrayValue(array, unit); + } + + aVariantMask = mergedVariantMask; + return true; +} + bool CSSParserImpl::ParseCalcNumberExpressionValue(float& aValue) { diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index 4a3e9b1b4f..e63c630b10 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -1693,7 +1693,6 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult, aResult.Append(')'); } else if (IsCalcUnit()) { - MOZ_ASSERT(GetUnit() == eCSSUnit_Calc, "unexpected unit"); if (!AppendNormalizedLengthPercentCalcToString(*this, aProperty, aResult, aSerialization)) { CSSValueSerializeCalcOps ops(aProperty, aResult, aSerialization); @@ -2266,6 +2265,9 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult, case eCSSUnit_Calc_Times_L: break; case eCSSUnit_Calc_Times_R: break; case eCSSUnit_Calc_Divided: break; + case eCSSUnit_Calc_Min: break; + case eCSSUnit_Calc_Max: break; + case eCSSUnit_Calc_Clamp: break; case eCSSUnit_Integer: break; case eCSSUnit_Enumerated: break; case eCSSUnit_EnumColor: break; @@ -2398,6 +2400,9 @@ nsCSSValue::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const case eCSSUnit_Calc_Times_L: case eCSSUnit_Calc_Times_R: case eCSSUnit_Calc_Divided: + case eCSSUnit_Calc_Min: + case eCSSUnit_Calc_Max: + case eCSSUnit_Calc_Clamp: break; // URL diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 0c64d20f72..a6fb600e01 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -798,6 +798,16 @@ CalcLengthTowardZero(const nsCSSValue& aValue, AppUnitRounding::TowardZero); } +already_AddRefed +nsRuleNode::ComputedCalc::ToCalcNode() const +{ + if (mNode) { + RefPtr node = mNode; + return node.forget(); + } + return nsStyleCoord::CalcNode::CreateLeaf(mLength, mPercent, mHasPercent); +} + /* static */ nscoord nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext, const nsCSSValue& aValue) @@ -830,27 +840,42 @@ struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps { if (aValue.GetUnit() == eCSSUnit_Percent) { mHasPercent = true; - return result_type(0, aValue.GetPercentValue()); + return result_type(0, aValue.GetPercentValue(), true); } return result_type(CalcLength(aValue, mContext, mPresContext, mConditions), - 0.0f); + 0.0f, false); } result_type MergeAdditive(nsCSSUnit aCalcFunction, result_type aValue1, result_type aValue2) { + if (aValue1.HasNode() || aValue2.HasNode()) { + RefPtr node = + nsStyleCoord::CalcNode::Create( + aCalcFunction == eCSSUnit_Calc_Plus + ? nsStyleCoord::CalcNode::Type::Add + : nsStyleCoord::CalcNode::Type::Subtract); + node->mChildren.AppendElement(aValue1.ToCalcNode()); + node->mChildren.AppendElement(aValue2.ToCalcNode()); + node->mHasPercent = node->HasPercent(); + mHasPercent = mHasPercent || node->mHasPercent; + return result_type(node.forget()); + } + if (aCalcFunction == eCSSUnit_Calc_Plus) { return result_type(NSCoordSaturatingAdd(aValue1.mLength, aValue2.mLength), - aValue1.mPercent + aValue2.mPercent); + aValue1.mPercent + aValue2.mPercent, + aValue1.mHasPercent || aValue2.mHasPercent); } MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, "min() and max() are not allowed in calc() on transform"); return result_type(NSCoordSaturatingSubtract(aValue1.mLength, aValue2.mLength, 0), - aValue1.mPercent - aValue2.mPercent); + aValue1.mPercent - aValue2.mPercent, + aValue1.mHasPercent || aValue2.mHasPercent); } result_type @@ -859,8 +884,19 @@ struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps { MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, "unexpected unit"); + if (aValue2.HasNode()) { + RefPtr node = + nsStyleCoord::CalcNode::Create(nsStyleCoord::CalcNode::Type::Multiply); + node->mChildren.AppendElement(aValue2.ToCalcNode()); + node->mNumber = aValue1; + node->mHasPercent = node->HasPercent(); + mHasPercent = mHasPercent || node->mHasPercent; + return result_type(node.forget()); + } + return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1), - aValue1 * aValue2.mPercent); + aValue1 * aValue2.mPercent, + aValue2.mHasPercent); } result_type @@ -871,10 +907,89 @@ struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps aCalcFunction == eCSSUnit_Calc_Divided, "unexpected unit"); if (aCalcFunction == eCSSUnit_Calc_Divided) { + if (aValue1.HasNode()) { + RefPtr node = + nsStyleCoord::CalcNode::Create(nsStyleCoord::CalcNode::Type::Divide); + node->mChildren.AppendElement(aValue1.ToCalcNode()); + node->mNumber = aValue2; + node->mHasPercent = node->HasPercent(); + mHasPercent = mHasPercent || node->mHasPercent; + return result_type(node.forget()); + } aValue2 = 1.0f / aValue2; + } else if (aValue1.HasNode()) { + RefPtr node = + nsStyleCoord::CalcNode::Create(nsStyleCoord::CalcNode::Type::Multiply); + node->mChildren.AppendElement(aValue1.ToCalcNode()); + node->mNumber = aValue2; + node->mHasPercent = node->HasPercent(); + mHasPercent = mHasPercent || node->mHasPercent; + return result_type(node.forget()); } return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2), - aValue1.mPercent * aValue2); + aValue1.mPercent * aValue2, + aValue1.mHasPercent); + } + + result_type + MergeMinMax(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + const bool hasNode = aValue1.HasNode() || aValue2.HasNode(); + const bool hasPercent = aValue1.mHasPercent || aValue2.mHasPercent; + if (!hasNode && !hasPercent) { + if (aCalcFunction == eCSSUnit_Calc_Min) { + return result_type(std::min(aValue1.mLength, aValue2.mLength), + 0.0f, false); + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Max, "unexpected unit"); + return result_type(std::max(aValue1.mLength, aValue2.mLength), + 0.0f, false); + } + + RefPtr node = + nsStyleCoord::CalcNode::Create( + aCalcFunction == eCSSUnit_Calc_Min + ? nsStyleCoord::CalcNode::Type::Min + : nsStyleCoord::CalcNode::Type::Max); + if (aValue1.HasNode() && + aValue1.mNode->mType == node->mType) { + node->mChildren.AppendElements(aValue1.mNode->mChildren); + } else { + node->mChildren.AppendElement(aValue1.ToCalcNode()); + } + if (aValue2.HasNode() && + aValue2.mNode->mType == node->mType) { + node->mChildren.AppendElements(aValue2.mNode->mChildren); + } else { + node->mChildren.AppendElement(aValue2.ToCalcNode()); + } + node->mHasPercent = node->HasPercent(); + mHasPercent = mHasPercent || node->mHasPercent; + return result_type(node.forget()); + } + + result_type + MergeClamp(result_type aMin, result_type aCenter, result_type aMax) + { + const bool hasNode = aMin.HasNode() || aCenter.HasNode() || aMax.HasNode(); + const bool hasPercent = aMin.mHasPercent || + aCenter.mHasPercent || + aMax.mHasPercent; + if (!hasNode && !hasPercent) { + return result_type(std::max(aMin.mLength, + std::min(aCenter.mLength, aMax.mLength)), + 0.0f, false); + } + + RefPtr node = + nsStyleCoord::CalcNode::Create(nsStyleCoord::CalcNode::Type::Clamp); + node->mChildren.AppendElement(aMin.ToCalcNode()); + node->mChildren.AppendElement(aCenter.ToCalcNode()); + node->mChildren.AppendElement(aMax.ToCalcNode()); + node->mHasPercent = node->HasPercent(); + mHasPercent = mHasPercent || node->mHasPercent; + return result_type(node.forget()); } }; @@ -892,7 +1007,8 @@ SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord, calcObj->mLength = vals.mLength; calcObj->mPercent = vals.mPercent; - calcObj->mHasPercent = ops.mHasPercent; + calcObj->mHasPercent = vals.mHasPercent || ops.mHasPercent; + calcObj->mNode = vals.mNode; aCoord.SetCalcValue(calcObj); } @@ -915,8 +1031,7 @@ nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue, nscoord aPercentageBasis) { nsStyleCoord::Calc* calc = aValue.GetCalcValue(); - return calc->mLength + - NSToCoordFloorClamped(aPercentageBasis * calc->mPercent); + return calc->Resolve(aPercentageBasis); } /* static */ nscoord @@ -5110,6 +5225,34 @@ struct LengthNumberCalcOps : public css::NumbersAlreadyNormalizedOps return result; } + result_type + MergeMinMax(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + MOZ_ASSERT(aValue1.mIsNumber == aValue2.mIsNumber); + LengthNumberCalcObj result; + result.mIsNumber = aValue1.mIsNumber; + if (aCalcFunction == eCSSUnit_Calc_Min) { + result.mValue = std::min(aValue1.mValue, aValue2.mValue); + return result; + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Max, "unexpected unit"); + result.mValue = std::max(aValue1.mValue, aValue2.mValue); + return result; + } + + result_type + MergeClamp(result_type aMin, result_type aCenter, result_type aMax) + { + MOZ_ASSERT(aMin.mIsNumber == aCenter.mIsNumber && + aCenter.mIsNumber == aMax.mIsNumber); + LengthNumberCalcObj result; + result.mIsNumber = aCenter.mIsNumber; + result.mValue = std::max(aMin.mValue, + std::min(aCenter.mValue, aMax.mValue)); + return result; + } + result_type ComputeLeafValue(const nsCSSValue& aValue) { LengthNumberCalcObj result; @@ -5200,6 +5343,38 @@ struct LengthPercentNumberCalcOps : public css::NumbersAlreadyNormalizedOps return result; } + result_type + MergeMinMax(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + MOZ_ASSERT(aValue1.mIsNumber == aValue2.mIsNumber); + result_type result; + result.mIsNumber = aValue1.mIsNumber; + if (aCalcFunction == eCSSUnit_Calc_Min) { + result.mLength = std::min(aValue1.mLength, aValue2.mLength); + result.mPercent = std::min(aValue1.mPercent, aValue2.mPercent); + } else { + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Max, "unexpected unit"); + result.mLength = std::max(aValue1.mLength, aValue2.mLength); + result.mPercent = std::max(aValue1.mPercent, aValue2.mPercent); + } + return result; + } + + result_type + MergeClamp(result_type aMin, result_type aCenter, result_type aMax) + { + MOZ_ASSERT(aMin.mIsNumber == aCenter.mIsNumber && + aCenter.mIsNumber == aMax.mIsNumber); + result_type result; + result.mIsNumber = aCenter.mIsNumber; + result.mLength = std::max(aMin.mLength, + std::min(aCenter.mLength, aMax.mLength)); + result.mPercent = std::max(aMin.mPercent, + std::min(aCenter.mPercent, aMax.mPercent)); + return result; + } + result_type ComputeLeafValue(const nsCSSValue& aValue) {