Support CSS sizing math functions

Prereq for parts of #2404
This commit is contained in:
Basilisk-Dev 2026-05-10 14:04:56 -04:00 • committed by EAZYBLACK
commit 7e45a24853
4 changed files with 431 additions and 22 deletions

View file

@ -6,6 +6,7 @@
#include "nsCSSValue.h" #include "nsCSSValue.h"
#include "nsStyleCoord.h" #include "nsStyleCoord.h"
#include <algorithm>
#include <math.h> #include <math.h>
namespace mozilla { namespace mozilla {
@ -46,6 +47,13 @@ namespace css {
* result_type aValue1, float aValue2); * result_type aValue1, float aValue2);
* *
* result_type * 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); * ComputeLeafValue(const input_type& aValue);
* *
* float * float
@ -70,6 +78,8 @@ namespace css {
* MergeAdditive for Plus and Minus * MergeAdditive for Plus and Minus
* MergeMultiplicativeL for Times_L (number * value) * MergeMultiplicativeL for Times_L (number * value)
* MergeMultiplicativeR for Times_R (value * number) and Divided * MergeMultiplicativeR for Times_R (value * number) and Divided
* MergeMinMax for Min and Max
* MergeClamp for Clamp
*/ */
template <class CalcOps> template <class CalcOps>
static typename CalcOps::result_type 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)); float rhs = aOps.ComputeNumber(arr->Item(1));
return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs); 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: { default: {
return aOps.ComputeLeafValue(aValue); return aOps.ComputeLeafValue(aValue);
} }
@ -168,6 +197,23 @@ struct BasicCoordCalcOps
} }
return NSCoordSaturatingMultiply(aValue1, aValue2); 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 struct BasicFloatCalcOps
@ -206,6 +252,23 @@ struct BasicFloatCalcOps
"unexpected unit"); "unexpected unit");
return aValue1 / aValue2; 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 <class CalcOps>
static void static void
SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
{ {
aOps.Append("calc(");
nsCSSUnit unit = CalcOps::GetUnit(aValue); 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) { if (unit == eCSSUnit_Calc) {
const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 1, "unexpected length"); MOZ_ASSERT(array->Count() == 1, "unexpected length");
@ -282,6 +357,14 @@ IsCalcMultiplicativeUnit(nsCSSUnit aUnit)
aUnit == eCSSUnit_Calc_Divided; 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 // Serialize a non-toplevel value in a calc() tree. See big comment
// above. // above.
template <class CalcOps> template <class CalcOps>
@ -340,6 +423,29 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
if (needParens) { if (needParens) {
aOps.Append(")"); 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 { } else {
aOps.AppendLeafValue(aValue); aOps.AppendLeafValue(aValue);
} }

View file

@ -348,6 +348,31 @@ GetCalcLengthTypedArithmeticExponent(const nsCSSValue& aValue,
return true; 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: case eCSSUnit_Number:
aExponent = 0; aExponent = 0;
return true; return true;
@ -430,6 +455,35 @@ NormalizeCalcForVariant(nsCSSValue& aValue,
return false; 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 && static_assert(css::eAuthorSheetFeatures == 0 &&
css::eUserSheetFeatures == 1 && css::eUserSheetFeatures == 1 &&
css::eAgentSheetFeatures == 2, css::eAgentSheetFeatures == 2,
@ -1251,6 +1305,8 @@ protected:
uint32_t& aVariantMask, uint32_t& aVariantMask,
bool *aHadFinalWS); bool *aHadFinalWS);
bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask); bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask);
bool ParseCalcMinMaxClampFunction(nsCSSValue& aValue,
uint32_t& aVariantMask);
bool ParseCalcNumberExpressionValue(float& aValue); bool ParseCalcNumberExpressionValue(float& aValue);
bool ParseCalcNumberFunction(nsCSSValue& aValue, uint32_t& aVariantMask); bool ParseCalcNumberFunction(nsCSSValue& aValue, uint32_t& aVariantMask);
bool RequireWhitespace(); bool RequireWhitespace();
@ -13708,7 +13764,8 @@ CSSParserImpl::IsCalcFunctionToken(const nsCSSToken& aToken) const
{ {
return aToken.mType == eCSSToken_Function && return aToken.mType == eCSSToken_Function &&
(aToken.mIdent.LowerCaseEqualsLiteral("calc") || (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. // Parse one item of the background shorthand property.
@ -15052,8 +15109,16 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask,
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1); RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
uint32_t resultVariantMask = aVariantMask; 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; break;
}
if (mCalcAllowsTypedArithmetic) { if (mCalcAllowsTypedArithmetic) {
int32_t exponent; int32_t exponent;
@ -15074,8 +15139,9 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask,
} }
} }
if (!ExpectSymbol(')', true)) if (!isMinMaxClamp && !ExpectSymbol(')', true)) {
break; break;
}
aValue.SetArrayValue(arr, eCSSUnit_Calc); aValue.SetArrayValue(arr, eCSSUnit_Calc);
if (aResultVariantMask) { if (aResultVariantMask) {
@ -15285,6 +15351,14 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
if (!GetToken(true)) if (!GetToken(true))
return false; 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... // Either an additive expression in parentheses...
if (mToken.IsSymbol('(') || if (mToken.IsSymbol('(') ||
// Treat nested calc() as plain parenthesis. // Treat nested calc() as plain parenthesis.
@ -15296,14 +15370,6 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
} }
return true; return true;
} }
if (mToken.mType == eCSSToken_Function &&
IsCalcNumberFunctionName(mToken.mIdent)) {
if (!ParseCalcNumberFunction(aValue, aVariantMask)) {
SkipUntil(')');
return false;
}
return true;
}
if ((aVariantMask & VARIANT_NUMBER) != 0 && if ((aVariantMask & VARIANT_NUMBER) != 0 &&
mToken.mType == eCSSToken_Ident) { mToken.mType == eCSSToken_Ident) {
float specialValue; float specialValue;
@ -15338,6 +15404,63 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
return true; 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<nsCSSValue, 4> 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<nsCSSValue::Array> 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 bool
CSSParserImpl::ParseCalcNumberExpressionValue(float& aValue) CSSParserImpl::ParseCalcNumberExpressionValue(float& aValue)
{ {

View file

@ -1693,7 +1693,6 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
aResult.Append(')'); aResult.Append(')');
} }
else if (IsCalcUnit()) { else if (IsCalcUnit()) {
MOZ_ASSERT(GetUnit() == eCSSUnit_Calc, "unexpected unit");
if (!AppendNormalizedLengthPercentCalcToString(*this, aProperty, aResult, if (!AppendNormalizedLengthPercentCalcToString(*this, aProperty, aResult,
aSerialization)) { aSerialization)) {
CSSValueSerializeCalcOps ops(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_L: break;
case eCSSUnit_Calc_Times_R: break; case eCSSUnit_Calc_Times_R: break;
case eCSSUnit_Calc_Divided: 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_Integer: break;
case eCSSUnit_Enumerated: break; case eCSSUnit_Enumerated: break;
case eCSSUnit_EnumColor: break; case eCSSUnit_EnumColor: break;
@ -2398,6 +2400,9 @@ nsCSSValue::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
case eCSSUnit_Calc_Times_L: case eCSSUnit_Calc_Times_L:
case eCSSUnit_Calc_Times_R: case eCSSUnit_Calc_Times_R:
case eCSSUnit_Calc_Divided: case eCSSUnit_Calc_Divided:
case eCSSUnit_Calc_Min:
case eCSSUnit_Calc_Max:
case eCSSUnit_Calc_Clamp:
break; break;
// URL // URL

View file

@ -798,6 +798,16 @@ CalcLengthTowardZero(const nsCSSValue& aValue,
AppUnitRounding::TowardZero); AppUnitRounding::TowardZero);
} }
already_AddRefed<nsStyleCoord::CalcNode>
nsRuleNode::ComputedCalc::ToCalcNode() const
{
if (mNode) {
RefPtr<nsStyleCoord::CalcNode> node = mNode;
return node.forget();
}
return nsStyleCoord::CalcNode::CreateLeaf(mLength, mPercent, mHasPercent);
}
/* static */ nscoord /* static */ nscoord
nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext, nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext,
const nsCSSValue& aValue) const nsCSSValue& aValue)
@ -830,27 +840,42 @@ struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
{ {
if (aValue.GetUnit() == eCSSUnit_Percent) { if (aValue.GetUnit() == eCSSUnit_Percent) {
mHasPercent = true; mHasPercent = true;
return result_type(0, aValue.GetPercentValue()); return result_type(0, aValue.GetPercentValue(), true);
} }
return result_type(CalcLength(aValue, mContext, mPresContext, return result_type(CalcLength(aValue, mContext, mPresContext,
mConditions), mConditions),
0.0f); 0.0f, false);
} }
result_type result_type
MergeAdditive(nsCSSUnit aCalcFunction, MergeAdditive(nsCSSUnit aCalcFunction,
result_type aValue1, result_type aValue2) result_type aValue1, result_type aValue2)
{ {
if (aValue1.HasNode() || aValue2.HasNode()) {
RefPtr<nsStyleCoord::CalcNode> 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) { if (aCalcFunction == eCSSUnit_Calc_Plus) {
return result_type(NSCoordSaturatingAdd(aValue1.mLength, return result_type(NSCoordSaturatingAdd(aValue1.mLength,
aValue2.mLength), aValue2.mLength),
aValue1.mPercent + aValue2.mPercent); aValue1.mPercent + aValue2.mPercent,
aValue1.mHasPercent || aValue2.mHasPercent);
} }
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
"min() and max() are not allowed in calc() on transform"); "min() and max() are not allowed in calc() on transform");
return result_type(NSCoordSaturatingSubtract(aValue1.mLength, return result_type(NSCoordSaturatingSubtract(aValue1.mLength,
aValue2.mLength, 0), aValue2.mLength, 0),
aValue1.mPercent - aValue2.mPercent); aValue1.mPercent - aValue2.mPercent,
aValue1.mHasPercent || aValue2.mHasPercent);
} }
result_type result_type
@ -859,8 +884,19 @@ struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
{ {
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit"); "unexpected unit");
if (aValue2.HasNode()) {
RefPtr<nsStyleCoord::CalcNode> 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), return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1),
aValue1 * aValue2.mPercent); aValue1 * aValue2.mPercent,
aValue2.mHasPercent);
} }
result_type result_type
@ -871,10 +907,89 @@ struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
aCalcFunction == eCSSUnit_Calc_Divided, aCalcFunction == eCSSUnit_Calc_Divided,
"unexpected unit"); "unexpected unit");
if (aCalcFunction == eCSSUnit_Calc_Divided) { if (aCalcFunction == eCSSUnit_Calc_Divided) {
if (aValue1.HasNode()) {
RefPtr<nsStyleCoord::CalcNode> 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; aValue2 = 1.0f / aValue2;
} else if (aValue1.HasNode()) {
RefPtr<nsStyleCoord::CalcNode> 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), 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<nsStyleCoord::CalcNode> 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<nsStyleCoord::CalcNode> 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->mLength = vals.mLength;
calcObj->mPercent = vals.mPercent; calcObj->mPercent = vals.mPercent;
calcObj->mHasPercent = ops.mHasPercent; calcObj->mHasPercent = vals.mHasPercent || ops.mHasPercent;
calcObj->mNode = vals.mNode;
aCoord.SetCalcValue(calcObj); aCoord.SetCalcValue(calcObj);
} }
@ -915,8 +1031,7 @@ nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue,
nscoord aPercentageBasis) nscoord aPercentageBasis)
{ {
nsStyleCoord::Calc* calc = aValue.GetCalcValue(); nsStyleCoord::Calc* calc = aValue.GetCalcValue();
return calc->mLength + return calc->Resolve(aPercentageBasis);
NSToCoordFloorClamped(aPercentageBasis * calc->mPercent);
} }
/* static */ nscoord /* static */ nscoord
@ -5110,6 +5225,34 @@ struct LengthNumberCalcOps : public css::NumbersAlreadyNormalizedOps
return result; 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) result_type ComputeLeafValue(const nsCSSValue& aValue)
{ {
LengthNumberCalcObj result; LengthNumberCalcObj result;
@ -5200,6 +5343,38 @@ struct LengthPercentNumberCalcOps : public css::NumbersAlreadyNormalizedOps
return result; 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 result_type
ComputeLeafValue(const nsCSSValue& aValue) ComputeLeafValue(const nsCSSValue& aValue)
{ {