Support CSS sizing math functions

This commit is contained in:
Basilisk-Dev 2026-05-10 14:04:56 -04:00 committed by wuggy
commit c47cea4ddf
12 changed files with 1234 additions and 28 deletions

View file

@ -7,6 +7,8 @@
#include "mozilla/CSSStyleSheet.h"
#include <algorithm>
#include "nsIAtom.h"
#include "nsCSSRuleProcessor.h"
#include "mozilla/MemoryReporting.h"
@ -130,6 +132,153 @@ int32_t DoCompare(Numeric a, Numeric b)
return 1;
}
struct MediaQueryTypedCalcLengthResult
{
double mValue;
int32_t mExponent;
};
static bool
EvaluateMediaQueryTypedCalcLength(nsPresContext* aPresContext,
const nsCSSValue& aValue,
MediaQueryTypedCalcLengthResult& aResult)
{
switch (aValue.GetUnit()) {
case eCSSUnit_Calc: {
nsCSSValue::Array* array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 1, "unexpected length");
return EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(0),
aResult);
}
case eCSSUnit_Calc_Plus:
case eCSSUnit_Calc_Minus: {
nsCSSValue::Array* array = aValue.GetArrayValue();
MOZ_ASSERT(array->Count() == 2, "unexpected length");
MediaQueryTypedCalcLengthResult lhs;
MediaQueryTypedCalcLengthResult rhs;
if (!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(0), lhs) ||
!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(1), rhs) ||
lhs.mExponent != rhs.mExponent) {
return false;
}
aResult.mExponent = lhs.mExponent;
aResult.mValue = aValue.GetUnit() == eCSSUnit_Calc_Plus
? lhs.mValue + rhs.mValue
: lhs.mValue - rhs.mValue;
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");
MediaQueryTypedCalcLengthResult lhs;
MediaQueryTypedCalcLengthResult rhs;
if (!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(0), lhs) ||
!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(1), rhs)) {
return false;
}
if (aValue.GetUnit() == eCSSUnit_Calc_Divided) {
if (rhs.mValue == 0.0) {
return false;
}
aResult.mValue = lhs.mValue / rhs.mValue;
aResult.mExponent = lhs.mExponent - rhs.mExponent;
} else {
aResult.mValue = lhs.mValue * rhs.mValue;
aResult.mExponent = lhs.mExponent + rhs.mExponent;
}
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");
MediaQueryTypedCalcLengthResult result;
if (!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(0),
result)) {
return false;
}
if (aValue.GetUnit() == eCSSUnit_Calc_Clamp) {
MediaQueryTypedCalcLengthResult center;
MediaQueryTypedCalcLengthResult max;
if (!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(1),
center) ||
!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(2),
max) ||
result.mExponent != center.mExponent ||
result.mExponent != max.mExponent) {
return false;
}
aResult.mExponent = result.mExponent;
aResult.mValue = std::max(result.mValue,
std::min(center.mValue, max.mValue));
return true;
}
for (uint32_t i = 1; i < array->Count(); ++i) {
MediaQueryTypedCalcLengthResult item;
if (!EvaluateMediaQueryTypedCalcLength(aPresContext, array->Item(i),
item) ||
result.mExponent != item.mExponent) {
return false;
}
result.mValue = aValue.GetUnit() == eCSSUnit_Calc_Min
? std::min(result.mValue, item.mValue)
: std::max(result.mValue, item.mValue);
}
aResult = result;
return true;
}
case eCSSUnit_Number:
aResult.mValue = aValue.GetFloatValue();
aResult.mExponent = 0;
return true;
default:
break;
}
if (!aValue.IsLengthUnit()) {
return false;
}
aResult.mValue = double(nsRuleNode::CalcLengthWithInitialFont(aPresContext,
aValue));
aResult.mExponent = 1;
return true;
}
static bool
ComputeMediaQueryTypedCalcLength(nsPresContext* aPresContext,
const nsCSSValue& aValue,
nscoord& aResult)
{
if (aValue.IsLengthUnit()) {
aResult = nsRuleNode::CalcLengthWithInitialFont(aPresContext, aValue);
return true;
}
MediaQueryTypedCalcLengthResult result;
if (!EvaluateMediaQueryTypedCalcLength(aPresContext, aValue, result) ||
result.mExponent != 1) {
return false;
}
aResult = NSToCoordFloorClamped(result.mValue);
return true;
}
bool
nsMediaExpression::Matches(nsPresContext *aPresContext,
const nsCSSValue& aActualValue) const