mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 07:18:39 +09:00
Support CSS sizing math functions
This commit is contained in:
parent
7766f8f53d
commit
c47cea4ddf
12 changed files with 1234 additions and 28 deletions
|
|
@ -197,11 +197,213 @@ OKLabToSRGBColor(float aL, float aA, float aB, float aAlpha)
|
|||
float g = mozilla::clamped(LinearSRGBToEncoded(linearG), 0.0f, 1.0f);
|
||||
float b = mozilla::clamped(LinearSRGBToEncoded(linearB), 0.0f, 1.0f);
|
||||
|
||||
return NS_RGBA(
|
||||
NSToIntRound(r * 255.0f),
|
||||
NSToIntRound(g * 255.0f),
|
||||
NSToIntRound(b * 255.0f),
|
||||
nsStyleUtil::FloatToColorComponent(mozilla::clamped(aAlpha, 0.0f, 1.0f)));
|
||||
if (aIdent.LowerCaseEqualsLiteral("-infinity")) {
|
||||
aValue = -std::numeric_limits<float>::infinity();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCalcNumberFunctionName(const nsAString& aIdent)
|
||||
{
|
||||
return aIdent.LowerCaseEqualsLiteral("min") ||
|
||||
aIdent.LowerCaseEqualsLiteral("max") ||
|
||||
aIdent.LowerCaseEqualsLiteral("clamp");
|
||||
}
|
||||
|
||||
static void
|
||||
WrapCalcNumberValue(nsCSSValue& aValue, float aNumber)
|
||||
{
|
||||
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
|
||||
arr->Item(0).SetFloatValue(aNumber, eCSSUnit_Number);
|
||||
aValue.SetArrayValue(arr, eCSSUnit_Calc);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsFiniteCalcNumber(float aValue)
|
||||
{
|
||||
return !std::isnan(aValue) && !std::isinf(aValue);
|
||||
}
|
||||
|
||||
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_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;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (aValue.IsLengthUnit()) {
|
||||
aExponent = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
NormalizeCalcForVariant(nsCSSValue& aValue,
|
||||
uint32_t aPropertyVariantMask,
|
||||
uint32_t aResultVariantMask,
|
||||
bool aSawSpecialNumericValues = false)
|
||||
{
|
||||
if (ShouldPreserveCalcValue(aPropertyVariantMask)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aResultVariantMask == VARIANT_NUMBER) {
|
||||
ReduceNumberCalcOps ops;
|
||||
float value = mozilla::css::ComputeCalc(aValue, ops);
|
||||
|
||||
if (aPropertyVariantMask & VARIANT_INTEGER) {
|
||||
if (!IsFiniteCalcNumber(value)) {
|
||||
return false;
|
||||
}
|
||||
aValue.SetIntValue(RoundFloatToCSSInteger(value), eCSSUnit_Integer);
|
||||
} else if ((aPropertyVariantMask & VARIANT_OPACITY) &&
|
||||
aSawSpecialNumericValues) {
|
||||
WrapCalcNumberValue(aValue, value);
|
||||
} else {
|
||||
aValue.SetFloatValue(value, eCSSUnit_Number);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aResultVariantMask & VARIANT_PERCENT) {
|
||||
ReducePercentageCalcOps ops;
|
||||
float value = mozilla::css::ComputeCalc(aValue, ops);
|
||||
|
||||
if (aPropertyVariantMask & VARIANT_OPACITY) {
|
||||
if (aSawSpecialNumericValues) {
|
||||
WrapCalcNumberValue(aValue, value);
|
||||
} else {
|
||||
aValue.SetFloatValue(value, eCSSUnit_Number);
|
||||
}
|
||||
} else {
|
||||
aValue.SetPercentValue(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aResultVariantMask & VARIANT_ANGLE) {
|
||||
ReduceDimensionCalcOps ops(ReduceDimensionCalcOps::DimensionType::Angle);
|
||||
aValue.SetFloatValue(mozilla::css::ComputeCalc(aValue, ops), eCSSUnit_Degree);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aResultVariantMask & VARIANT_TIME) {
|
||||
ReduceDimensionCalcOps ops(ReduceDimensionCalcOps::DimensionType::Time);
|
||||
aValue.SetFloatValue(mozilla::css::ComputeCalc(aValue, ops), eCSSUnit_Seconds);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aResultVariantMask & VARIANT_FREQUENCY) {
|
||||
ReduceDimensionCalcOps ops(ReduceDimensionCalcOps::DimensionType::Frequency);
|
||||
aValue.SetFloatValue(mozilla::css::ComputeCalc(aValue, ops), eCSSUnit_Hertz);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("unsupported calc result type");
|
||||
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 &&
|
||||
|
|
@ -1023,6 +1225,10 @@ 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();
|
||||
|
||||
// For "flex" shorthand property, defined in CSS Flexbox spec
|
||||
|
|
@ -13431,7 +13637,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.
|
||||
|
|
@ -14770,11 +14977,39 @@ CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask)
|
|||
// The toplevel of a calc() is always an nsCSSValue::Array of length 1.
|
||||
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
|
||||
|
||||
if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask))
|
||||
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 (!ExpectSymbol(')', true))
|
||||
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 (!isMinMaxClamp && !ExpectSymbol(')', true)) {
|
||||
break;
|
||||
}
|
||||
|
||||
aValue.SetArrayValue(arr, eCSSUnit_Calc);
|
||||
mUnitlessLengthQuirk = oldUnitlessLengthQuirk;
|
||||
|
|
@ -14948,6 +15183,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.
|
||||
|
|
@ -14959,6 +15202,16 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
if ((aVariantMask & VARIANT_NUMBER) != 0 &&
|
||||
mToken.mType == eCSSToken_Ident) {
|
||||
float specialValue;
|
||||
if (IsCalcSpecialNumberIdent(mToken.mIdent, specialValue)) {
|
||||
mCalcHasSpecialNumericValues = true;
|
||||
aValue.SetFloatValue(specialValue, eCSSUnit_Number);
|
||||
aVariantMask = VARIANT_NUMBER;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// ... or just a value
|
||||
UngetToken();
|
||||
// Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
|
||||
|
|
@ -14983,6 +15236,139 @@ 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<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
|
||||
CSSParserImpl::ParseCalcNumberExpressionValue(float& aValue)
|
||||
{
|
||||
nsCSSValue expression;
|
||||
uint32_t variantMask = VARIANT_NUMBER;
|
||||
if (!ParseCalcAdditiveExpression(expression, variantMask) ||
|
||||
variantMask != VARIANT_NUMBER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReduceNumberCalcOps ops;
|
||||
aValue = mozilla::css::ComputeCalc(expression, ops);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CSSParserImpl::ParseCalcNumberFunction(nsCSSValue& aValue,
|
||||
uint32_t& aVariantMask)
|
||||
{
|
||||
MOZ_ASSERT(mToken.mType == eCSSToken_Function, "expected function token");
|
||||
MOZ_ASSERT(IsCalcNumberFunctionName(mToken.mIdent),
|
||||
"unexpected calc() number function");
|
||||
|
||||
float result;
|
||||
|
||||
if (mToken.mIdent.LowerCaseEqualsLiteral("clamp")) {
|
||||
float minValue;
|
||||
float centerValue;
|
||||
float maxValue;
|
||||
if (!ParseCalcNumberExpressionValue(minValue) ||
|
||||
!ExpectSymbol(',', true) ||
|
||||
!ParseCalcNumberExpressionValue(centerValue) ||
|
||||
!ExpectSymbol(',', true) ||
|
||||
!ParseCalcNumberExpressionValue(maxValue) ||
|
||||
!ExpectSymbol(')', true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::isnan(minValue) || std::isnan(centerValue) ||
|
||||
std::isnan(maxValue)) {
|
||||
result = std::numeric_limits<float>::quiet_NaN();
|
||||
} else {
|
||||
result = std::max(minValue, std::min(centerValue, maxValue));
|
||||
}
|
||||
} else {
|
||||
const bool isMax = mToken.mIdent.LowerCaseEqualsLiteral("max");
|
||||
bool sawComma = false;
|
||||
|
||||
if (!ParseCalcNumberExpressionValue(result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (ExpectSymbol(',', true)) {
|
||||
sawComma = true;
|
||||
float candidate;
|
||||
if (!ParseCalcNumberExpressionValue(candidate)) {
|
||||
return false;
|
||||
}
|
||||
if (std::isnan(result) || std::isnan(candidate)) {
|
||||
result = std::numeric_limits<float>::quiet_NaN();
|
||||
} else {
|
||||
result = isMax ? std::max(result, candidate)
|
||||
: std::min(result, candidate);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sawComma || !ExpectSymbol(')', true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aValue.SetFloatValue(result, eCSSUnit_Number);
|
||||
aVariantMask = VARIANT_NUMBER;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function consumes all consecutive whitespace and returns whether
|
||||
// there was any.
|
||||
bool
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue