Issue #1826 - Add typed calc() arithmetic for media queries

This commit is contained in:
Basilisk-Dev 2026-05-06 07:58:56 -04:00 • committed by EAZYBLACK
commit 79800a9113
4 changed files with 134 additions and 17 deletions

View file

@ -318,11 +318,7 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
if (needParens) { if (needParens) {
aOps.Append("("); aOps.Append("(");
} }
if (unit == eCSSUnit_Calc_Times_L) {
aOps.AppendNumber(array->Item(0));
} else {
SerializeCalcInternal(array->Item(0), aOps); SerializeCalcInternal(array->Item(0), aOps);
}
if (needParens) { if (needParens) {
aOps.Append(")"); aOps.Append(")");
} }
@ -340,11 +336,7 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
if (needParens) { if (needParens) {
aOps.Append("("); aOps.Append("(");
} }
if (unit == eCSSUnit_Calc_Times_L) {
SerializeCalcInternal(array->Item(1), aOps); SerializeCalcInternal(array->Item(1), aOps);
} else {
aOps.AppendNumber(array->Item(1));
}
if (needParens) { if (needParens) {
aOps.Append(")"); aOps.Append(")");
} }

View file

@ -314,8 +314,11 @@ nsMediaExpression::Matches(nsPresContext *aPresContext,
"bad required value"); "bad required value");
nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont( nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont(
aPresContext, actual); aPresContext, actual);
nscoord requiredCoord = nsRuleNode::CalcLengthWithInitialFont( nscoord requiredCoord;
aPresContext, required); if (!ComputeMediaQueryTypedCalcLength(aPresContext, required,
requiredCoord)) {
return false;
}
cmp = DoCompare(actualCoord, requiredCoord); cmp = DoCompare(actualCoord, requiredCoord);
} }
break; break;

View file

@ -262,6 +262,65 @@ RoundFloatToCSSInteger(float aValue)
return int32_t(rounded); return int32_t(rounded);
} }
static bool
GetCalcLengthTypedArithmeticExponent(const nsCSSValue& aValue,
int32_t& aExponent)
{
switch (aValue.GetUnit()) {
case eCSSUnit_Calc: {
nsCSSValue::Array* array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 1, "unexpected length");
return GetCalcLengthTypedArithmeticExponent(array->Item(0), aExponent);
}
case eCSSUnit_Calc_Plus:
case eCSSUnit_Calc_Minus: {
nsCSSValue::Array* array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 2, "unexpected length");
int32_t lhsExponent;
int32_t rhsExponent;
if (!GetCalcLengthTypedArithmeticExponent(array->Item(0), lhsExponent) ||
!GetCalcLengthTypedArithmeticExponent(array->Item(1), rhsExponent) ||
lhsExponent != rhsExponent) {
return false;
}
aExponent = lhsExponent;
return true;
}
case eCSSUnit_Calc_Times_L:
case eCSSUnit_Calc_Times_R:
case eCSSUnit_Calc_Divided: {
nsCSSValue::Array* array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 2, "unexpected length");
int32_t lhsExponent;
int32_t rhsExponent;
if (!GetCalcLengthTypedArithmeticExponent(array->Item(0), lhsExponent) ||
!GetCalcLengthTypedArithmeticExponent(array->Item(1), rhsExponent)) {
return false;
}
aExponent = aValue.GetUnit() == eCSSUnit_Calc_Divided
? lhsExponent - rhsExponent
: lhsExponent + rhsExponent;
return true;
}
case eCSSUnit_Number:
aExponent = 0;
return true;
default:
break;
}
if (aValue.IsLengthUnit()) {
aExponent = 1;
return true;
}
return false;
}
static bool static bool
NormalizeCalcForVariant(nsCSSValue& aValue, NormalizeCalcForVariant(nsCSSValue& aValue,
uint32_t aPropertyVariantMask, uint32_t aPropertyVariantMask,
@ -1727,6 +1786,7 @@ protected:
// not be set to false if any nsCSSValues created during parsing can escape // not be set to false if any nsCSSValues created during parsing can escape
// out of the parser. // out of the parser.
bool mSheetPrincipalRequired; bool mSheetPrincipalRequired;
bool mCalcAllowsTypedArithmetic;
// This enum helps us track whether we've unprefixed "display: -webkit-box" // This enum helps us track whether we've unprefixed "display: -webkit-box"
// (treating it as "display: flex") in an earlier declaration within a series // (treating it as "display: flex") in an earlier declaration within a series
@ -1831,6 +1891,7 @@ CSSParserImpl::CSSParserImpl()
mInFailingSupportsRule(false), mInFailingSupportsRule(false),
mSuppressErrors(false), mSuppressErrors(false),
mSheetPrincipalRequired(true), mSheetPrincipalRequired(true),
mCalcAllowsTypedArithmetic(false),
mWebkitBoxUnprefixState(eNotParsingDecls), mWebkitBoxUnprefixState(eNotParsingDecls),
mNextFree(nullptr) mNextFree(nullptr)
{ {
@ -3998,8 +4059,12 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery)
bool rv = false; bool rv = false;
switch (feature->mValueType) { switch (feature->mValueType) {
case nsMediaFeature::eLength: case nsMediaFeature::eLength:
{
AutoRestore<bool> autoRestore(mCalcAllowsTypedArithmetic);
mCalcAllowsTypedArithmetic = true;
rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LCALC, rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LCALC,
nullptr); nullptr);
}
break; break;
case nsMediaFeature::eInteger: case nsMediaFeature::eInteger:
case nsMediaFeature::eBoolInteger: case nsMediaFeature::eBoolInteger:
@ -14848,6 +14913,25 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask,
if (!ParseCalcAdditiveExpression(arr->Item(0), resultVariantMask)) if (!ParseCalcAdditiveExpression(arr->Item(0), resultVariantMask))
break; break;
if (mCalcAllowsTypedArithmetic) {
int32_t exponent;
if (!GetCalcLengthTypedArithmeticExponent(arr->Item(0), exponent)) {
break;
}
if (aVariantMask & VARIANT_NUMBER) {
if (exponent == 0) {
resultVariantMask = VARIANT_NUMBER;
} else if (exponent == 1) {
resultVariantMask &= ~int32_t(VARIANT_NUMBER);
} else {
break;
}
} else if (exponent != 1) {
break;
}
}
if (!ExpectSymbol(')', true)) if (!ExpectSymbol(')', true))
break; break;
@ -14928,13 +15012,16 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
bool *aHadFinalWS) bool *aHadFinalWS)
{ {
MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
bool allowTypedArithmetic = mCalcAllowsTypedArithmetic;
bool gotValue = false; // already got the part with the unit bool gotValue = false; // already got the part with the unit
bool afterDivision = false; bool afterDivision = false;
nsCSSValue *storage = &aValue; nsCSSValue *storage = &aValue;
for (;;) { for (;;) {
uint32_t variantMask; uint32_t variantMask;
if (afterDivision || gotValue) { if (allowTypedArithmetic) {
variantMask = aVariantMask | VARIANT_NUMBER;
} else if (afterDivision || gotValue) {
variantMask = VARIANT_NUMBER; variantMask = VARIANT_NUMBER;
} else { } else {
variantMask = aVariantMask | VARIANT_NUMBER; variantMask = aVariantMask | VARIANT_NUMBER;
@ -14955,7 +15042,7 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
if (number == 0.0 && afterDivision) if (number == 0.0 && afterDivision)
return false; return false;
storage->SetFloatValue(number, eCSSUnit_Number); storage->SetFloatValue(number, eCSSUnit_Number);
} else { } else if (!allowTypedArithmetic) {
gotValue = true; gotValue = true;
if (storage != &aValue) { if (storage != &aValue) {
@ -14977,7 +15064,10 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
} }
nsCSSUnit unit; nsCSSUnit unit;
if (mToken.IsSymbol('*')) { if (mToken.IsSymbol('*')) {
unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L; unit = allowTypedArithmetic
? eCSSUnit_Calc_Times_R
: (gotValue ? eCSSUnit_Calc_Times_R
: eCSSUnit_Calc_Times_L);
afterDivision = false; afterDivision = false;
} else if (mToken.IsSymbol('/')) { } else if (mToken.IsSymbol('/')) {
unit = eCSSUnit_Calc_Divided; unit = eCSSUnit_Calc_Divided;
@ -14996,6 +15086,23 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
// Adjust aVariantMask (see comments above function) to reflect which // Adjust aVariantMask (see comments above function) to reflect which
// option we took. // option we took.
if (allowTypedArithmetic) {
int32_t exponent;
if (!GetCalcLengthTypedArithmeticExponent(aValue, exponent)) {
return false;
}
if (aVariantMask & VARIANT_NUMBER) {
if (exponent == 0) {
aVariantMask = VARIANT_NUMBER;
} else {
aVariantMask &= ~int32_t(VARIANT_NUMBER);
}
}
return true;
}
if (aVariantMask & VARIANT_NUMBER) { if (aVariantMask & VARIANT_NUMBER) {
if (gotValue) { if (gotValue) {
aVariantMask &= ~int32_t(VARIANT_NUMBER); aVariantMask &= ~int32_t(VARIANT_NUMBER);

View file

@ -321,6 +321,21 @@ function run() {
(Math.floor(value/em_size) - 1) + "rem)"); (Math.floor(value/em_size) - 1) + "rem)");
} }
change_state(function() {
iframe_style.width = "100px";
iframe_style.height = "10px";
});
should_apply("all and (width: calc(100px / 1em * 1em))");
should_apply("all and (width: calc((50vh * 5em) / 4px))");
should_apply("all and (width: calc(10vw / 10px * 1000vh))");
should_apply("all and (width: calc(8000vh * 1vw / 1em * 8px / 40vh))");
should_not_apply("all and (width: calc(100px / 1em * 2em))");
change_state(function() {
iframe_style.width = width_val + "px";
iframe_style.height = height_val + "px";
});
change_state(function() { change_state(function() {
iframe_style.width = "0"; iframe_style.width = "0";
}); });