mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 13:58:49 +00:00
+107
-1
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "nsCSSValue.h"
|
||||
#include "nsStyleCoord.h"
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
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 <class CalcOps>
|
||||
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 <class CalcOps>
|
||||
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 <class CalcOps>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "mozilla/CSSStyleSheet.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "nsIAtom.h"
|
||||
#include "nsCSSRuleProcessor.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
@@ -191,6 +193,53 @@ EvaluateMediaQueryTypedCalcLength(nsPresContext* aPresContext,
|
||||
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;
|
||||
|
||||
+134
-11
@@ -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();
|
||||
@@ -13694,7 +13750,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.
|
||||
@@ -15038,8 +15095,16 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask,
|
||||
RefPtr<nsCSSValue::Array> 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;
|
||||
@@ -15060,8 +15125,9 @@ CSSParserImpl::ParseCalc(nsCSSValue& aValue, uint32_t aVariantMask,
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExpectSymbol(')', true))
|
||||
if (!isMinMaxClamp && !ExpectSymbol(')', true)) {
|
||||
break;
|
||||
}
|
||||
|
||||
aValue.SetArrayValue(arr, eCSSUnit_Calc);
|
||||
if (aResultVariantMask) {
|
||||
@@ -15271,6 +15337,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.
|
||||
@@ -15282,14 +15356,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;
|
||||
@@ -15324,6 +15390,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<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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -477,6 +477,11 @@ enum nsCSSUnit {
|
||||
eCSSUnit_Calc_Times_L = 33, // (nsCSSValue::Array*) num * val within calc
|
||||
eCSSUnit_Calc_Times_R = 34, // (nsCSSValue::Array*) val * num within calc
|
||||
eCSSUnit_Calc_Divided = 35, // (nsCSSValue::Array*) / within calc
|
||||
// Min and Max have arrays with one or more elements. Clamp has an array
|
||||
// with exactly 3 elements: lower bound, center, upper bound.
|
||||
eCSSUnit_Calc_Min = 36, // (nsCSSValue::Array*) min() node
|
||||
eCSSUnit_Calc_Max = 37, // (nsCSSValue::Array*) max() node
|
||||
eCSSUnit_Calc_Clamp = 38, // (nsCSSValue::Array*) clamp() node
|
||||
|
||||
eCSSUnit_URL = 40, // (nsCSSValue::URL*) value
|
||||
eCSSUnit_Image = 41, // (nsCSSValue::Image*) value
|
||||
@@ -713,12 +718,12 @@ public:
|
||||
bool IsTimeUnit() const
|
||||
{ return eCSSUnit_Seconds <= mUnit && mUnit <= eCSSUnit_Milliseconds; }
|
||||
bool IsCalcUnit() const
|
||||
{ return eCSSUnit_Calc <= mUnit && mUnit <= eCSSUnit_Calc_Divided; }
|
||||
{ return eCSSUnit_Calc <= mUnit && mUnit <= eCSSUnit_Calc_Clamp; }
|
||||
|
||||
bool UnitHasStringValue() const
|
||||
{ return eCSSUnit_String <= mUnit && mUnit <= eCSSUnit_Element; }
|
||||
bool UnitHasArrayValue() const
|
||||
{ return eCSSUnit_Array <= mUnit && mUnit <= eCSSUnit_Calc_Divided; }
|
||||
{ return eCSSUnit_Array <= mUnit && mUnit <= eCSSUnit_Calc_Clamp; }
|
||||
|
||||
// Checks for the nsCSSValue being of a particular type of color unit:
|
||||
//
|
||||
|
||||
@@ -1899,6 +1899,94 @@ nsComputedDOMStyle::DoGetBackgroundColor()
|
||||
return val.forget();
|
||||
}
|
||||
|
||||
static void
|
||||
AppendCalcNodeToString(const nsStyleCoord::CalcNode* aNode,
|
||||
nsROCSSPrimitiveValue* aPrimitive,
|
||||
nsAString& aResult);
|
||||
|
||||
static void
|
||||
AppendCalcLeafToString(const nsStyleCoord::CalcNode* aNode,
|
||||
nsROCSSPrimitiveValue* aPrimitive,
|
||||
nsAString& aResult)
|
||||
{
|
||||
nsAutoString tmp;
|
||||
|
||||
if (!aNode->mHasPercent) {
|
||||
aPrimitive->SetAppUnits(aNode->mLength);
|
||||
aPrimitive->GetCssText(tmp);
|
||||
aResult.Append(tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
aResult.AppendLiteral("calc(");
|
||||
aPrimitive->SetAppUnits(aNode->mLength);
|
||||
aPrimitive->GetCssText(tmp);
|
||||
aResult.Append(tmp);
|
||||
aResult.AppendLiteral(" + ");
|
||||
aPrimitive->SetPercent(aNode->mPercent);
|
||||
aPrimitive->GetCssText(tmp);
|
||||
aResult.Append(tmp);
|
||||
aResult.Append(')');
|
||||
}
|
||||
|
||||
static void
|
||||
AppendCalcNodeToString(const nsStyleCoord::CalcNode* aNode,
|
||||
nsROCSSPrimitiveValue* aPrimitive,
|
||||
nsAString& aResult)
|
||||
{
|
||||
using Type = nsStyleCoord::CalcNode::Type;
|
||||
|
||||
switch (aNode->mType) {
|
||||
case Type::Leaf:
|
||||
AppendCalcLeafToString(aNode, aPrimitive, aResult);
|
||||
return;
|
||||
case Type::Add:
|
||||
case Type::Subtract:
|
||||
aResult.AppendLiteral("calc(");
|
||||
AppendCalcNodeToString(aNode->mChildren[0], aPrimitive, aResult);
|
||||
aResult.Append(aNode->mType == Type::Add
|
||||
? NS_LITERAL_STRING(" + ")
|
||||
: NS_LITERAL_STRING(" - "));
|
||||
AppendCalcNodeToString(aNode->mChildren[1], aPrimitive, aResult);
|
||||
aResult.Append(')');
|
||||
return;
|
||||
case Type::Multiply:
|
||||
case Type::Divide: {
|
||||
nsAutoString tmp;
|
||||
aResult.AppendLiteral("calc(");
|
||||
AppendCalcNodeToString(aNode->mChildren[0], aPrimitive, aResult);
|
||||
aResult.Append(aNode->mType == Type::Multiply
|
||||
? NS_LITERAL_STRING(" * ")
|
||||
: NS_LITERAL_STRING(" / "));
|
||||
aPrimitive->SetNumber(aNode->mNumber);
|
||||
aPrimitive->GetCssText(tmp);
|
||||
aResult.Append(tmp);
|
||||
aResult.Append(')');
|
||||
return;
|
||||
}
|
||||
case Type::Min:
|
||||
case Type::Max:
|
||||
case Type::Clamp:
|
||||
if (aNode->mType == Type::Min) {
|
||||
aResult.AppendLiteral("min(");
|
||||
} else if (aNode->mType == Type::Max) {
|
||||
aResult.AppendLiteral("max(");
|
||||
} else {
|
||||
aResult.AppendLiteral("clamp(");
|
||||
}
|
||||
for (uint32_t i = 0; i < aNode->mChildren.Length(); ++i) {
|
||||
if (i != 0) {
|
||||
aResult.AppendLiteral(", ");
|
||||
}
|
||||
AppendCalcNodeToString(aNode->mChildren[i], aPrimitive, aResult);
|
||||
}
|
||||
aResult.Append(')');
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected calc node type");
|
||||
}
|
||||
|
||||
static void
|
||||
SetValueToCalc(const nsStyleCoord::CalcValue* aCalc,
|
||||
nsROCSSPrimitiveValue* aValue)
|
||||
@@ -1925,6 +2013,21 @@ SetValueToCalc(const nsStyleCoord::CalcValue* aCalc,
|
||||
aValue->SetString(result); // not really SetString
|
||||
}
|
||||
|
||||
static void
|
||||
SetValueToCalc(const nsStyleCoord::Calc* aCalc,
|
||||
nsROCSSPrimitiveValue* aValue)
|
||||
{
|
||||
if (!aCalc->HasCalcNode()) {
|
||||
SetValueToCalc(static_cast<const nsStyleCoord::CalcValue*>(aCalc), aValue);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
|
||||
nsAutoString result;
|
||||
AppendCalcNodeToString(aCalc->mNode, val, result);
|
||||
aValue->SetString(result); // not really SetString
|
||||
}
|
||||
|
||||
static void
|
||||
AppendCSSGradientLength(const nsStyleCoord& aValue,
|
||||
nsROCSSPrimitiveValue* aPrimitive,
|
||||
|
||||
+184
-9
@@ -798,6 +798,16 @@ CalcLengthTowardZero(const nsCSSValue& aValue,
|
||||
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
|
||||
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<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) {
|
||||
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<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),
|
||||
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<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;
|
||||
} 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),
|
||||
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->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
|
||||
@@ -5108,6 +5223,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;
|
||||
@@ -5198,6 +5341,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)
|
||||
{
|
||||
|
||||
@@ -986,9 +986,22 @@ public:
|
||||
struct ComputedCalc {
|
||||
nscoord mLength;
|
||||
float mPercent;
|
||||
bool mHasPercent;
|
||||
RefPtr<nsStyleCoord::CalcNode> mNode;
|
||||
|
||||
ComputedCalc(nscoord aLength, float aPercent)
|
||||
: mLength(aLength), mPercent(aPercent) {}
|
||||
: mLength(aLength), mPercent(aPercent),
|
||||
mHasPercent(aPercent != 0.0f) {}
|
||||
|
||||
ComputedCalc(nscoord aLength, float aPercent, bool aHasPercent)
|
||||
: mLength(aLength), mPercent(aPercent),
|
||||
mHasPercent(aHasPercent) {}
|
||||
|
||||
explicit ComputedCalc(already_AddRefed<nsStyleCoord::CalcNode> aNode)
|
||||
: mLength(0), mPercent(0.0f), mHasPercent(true), mNode(aNode) {}
|
||||
|
||||
bool HasNode() const { return !!mNode; }
|
||||
already_AddRefed<nsStyleCoord::CalcNode> ToCalcNode() const;
|
||||
};
|
||||
static ComputedCalc
|
||||
SpecifiedCalcToComputedCalc(const nsCSSValue& aValue,
|
||||
|
||||
@@ -8,6 +8,150 @@
|
||||
#include "nsStyleCoord.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include <algorithm>
|
||||
|
||||
already_AddRefed<nsStyleCoord::CalcNode>
|
||||
nsStyleCoord::CalcNode::CreateLeaf(nscoord aLength, float aPercent,
|
||||
bool aHasPercent)
|
||||
{
|
||||
RefPtr<CalcNode> node = new CalcNode(Type::Leaf);
|
||||
node->mLength = aLength;
|
||||
node->mPercent = aPercent;
|
||||
node->mHasPercent = aHasPercent;
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsStyleCoord::CalcNode>
|
||||
nsStyleCoord::CalcNode::Create(Type aType)
|
||||
{
|
||||
RefPtr<CalcNode> node = new CalcNode(aType);
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
nsStyleCoord::CalcNode::CalcNode(Type aType)
|
||||
: mType(aType)
|
||||
, mLength(0)
|
||||
, mPercent(0.0f)
|
||||
, mNumber(0.0f)
|
||||
, mHasPercent(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsStyleCoord::CalcNode::HasPercent() const
|
||||
{
|
||||
if (mHasPercent) {
|
||||
return true;
|
||||
}
|
||||
for (const RefPtr<CalcNode>& child : mChildren) {
|
||||
if (child->HasPercent()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsStyleCoord::CalcNode::Equals(const CalcNode& aOther) const
|
||||
{
|
||||
if (mType != aOther.mType ||
|
||||
mLength != aOther.mLength ||
|
||||
mPercent != aOther.mPercent ||
|
||||
mNumber != aOther.mNumber ||
|
||||
mHasPercent != aOther.mHasPercent ||
|
||||
mChildren.Length() != aOther.mChildren.Length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
|
||||
if (!mChildren[i]->Equals(*aOther.mChildren[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsStyleCoord::CalcNode::HashValue(uint32_t aHash) const
|
||||
{
|
||||
aHash = mozilla::AddToHash(aHash, uint8_t(mType), mLength, mPercent,
|
||||
mNumber, mHasPercent);
|
||||
for (const RefPtr<CalcNode>& child : mChildren) {
|
||||
aHash = child->HashValue(aHash);
|
||||
}
|
||||
return aHash;
|
||||
}
|
||||
|
||||
static nscoord
|
||||
ResolveCalcNode(const nsStyleCoord::CalcNode& aNode, nscoord aPercentageBasis)
|
||||
{
|
||||
using Type = nsStyleCoord::CalcNode::Type;
|
||||
|
||||
switch (aNode.mType) {
|
||||
case Type::Leaf:
|
||||
return aNode.mLength +
|
||||
NSToCoordFloorClamped(aPercentageBasis * aNode.mPercent);
|
||||
case Type::Add:
|
||||
MOZ_ASSERT(aNode.mChildren.Length() == 2, "unexpected child count");
|
||||
return NSCoordSaturatingAdd(
|
||||
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
||||
ResolveCalcNode(*aNode.mChildren[1], aPercentageBasis));
|
||||
case Type::Subtract:
|
||||
MOZ_ASSERT(aNode.mChildren.Length() == 2, "unexpected child count");
|
||||
return NSCoordSaturatingSubtract(
|
||||
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
||||
ResolveCalcNode(*aNode.mChildren[1], aPercentageBasis), 0);
|
||||
case Type::Multiply:
|
||||
MOZ_ASSERT(aNode.mChildren.Length() == 1, "unexpected child count");
|
||||
return NSCoordSaturatingMultiply(
|
||||
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
||||
aNode.mNumber);
|
||||
case Type::Divide:
|
||||
MOZ_ASSERT(aNode.mChildren.Length() == 1, "unexpected child count");
|
||||
return NSCoordSaturatingMultiply(
|
||||
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
||||
1.0f / aNode.mNumber);
|
||||
case Type::Min: {
|
||||
MOZ_ASSERT(!aNode.mChildren.IsEmpty(), "unexpected child count");
|
||||
nscoord result = ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis);
|
||||
for (uint32_t i = 1; i < aNode.mChildren.Length(); ++i) {
|
||||
result = std::min(result,
|
||||
ResolveCalcNode(*aNode.mChildren[i],
|
||||
aPercentageBasis));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case Type::Max: {
|
||||
MOZ_ASSERT(!aNode.mChildren.IsEmpty(), "unexpected child count");
|
||||
nscoord result = ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis);
|
||||
for (uint32_t i = 1; i < aNode.mChildren.Length(); ++i) {
|
||||
result = std::max(result,
|
||||
ResolveCalcNode(*aNode.mChildren[i],
|
||||
aPercentageBasis));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case Type::Clamp:
|
||||
MOZ_ASSERT(aNode.mChildren.Length() == 3, "unexpected child count");
|
||||
return std::max(ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
||||
std::min(ResolveCalcNode(*aNode.mChildren[1],
|
||||
aPercentageBasis),
|
||||
ResolveCalcNode(*aNode.mChildren[2],
|
||||
aPercentageBasis)));
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected calc node type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsStyleCoord::Calc::Resolve(nscoord aPercentageBasis) const
|
||||
{
|
||||
if (mNode) {
|
||||
return ResolveCalcNode(*mNode, aPercentageBasis);
|
||||
}
|
||||
return mLength + NSToCoordFloorClamped(aPercentageBasis * mPercent);
|
||||
}
|
||||
|
||||
nsStyleCoord::nsStyleCoord(nsStyleUnit aUnit)
|
||||
: mUnit(aUnit)
|
||||
@@ -71,8 +215,15 @@ bool nsStyleCoord::operator==(const nsStyleCoord& aOther) const
|
||||
case eStyleUnit_Integer:
|
||||
case eStyleUnit_Enumerated:
|
||||
return mValue.mInt == aOther.mValue.mInt;
|
||||
case eStyleUnit_Calc:
|
||||
return *this->GetCalcValue() == *aOther.GetCalcValue();
|
||||
case eStyleUnit_Calc: {
|
||||
Calc* thisCalc = GetCalcValue();
|
||||
Calc* otherCalc = aOther.GetCalcValue();
|
||||
if (thisCalc->HasCalcNode() || otherCalc->HasCalcNode()) {
|
||||
return thisCalc->HasCalcNode() == otherCalc->HasCalcNode() &&
|
||||
thisCalc->mNode->Equals(*otherCalc->mNode);
|
||||
}
|
||||
return *thisCalc == *otherCalc;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(false, "unexpected unit");
|
||||
return false;
|
||||
@@ -100,13 +251,17 @@ uint32_t nsStyleCoord::HashValue(uint32_t aHash = 0) const
|
||||
case eStyleUnit_Integer:
|
||||
case eStyleUnit_Enumerated:
|
||||
return mozilla::AddToHash(aHash, mValue.mInt);
|
||||
case eStyleUnit_Calc:
|
||||
case eStyleUnit_Calc: {
|
||||
Calc* calcValue = GetCalcValue();
|
||||
if (calcValue->HasCalcNode()) {
|
||||
return calcValue->mNode->HashValue(aHash);
|
||||
}
|
||||
aHash = mozilla::AddToHash(aHash, calcValue->mLength);
|
||||
if (HasPercent()) {
|
||||
return mozilla::AddToHash(aHash, calcValue->mPercent);
|
||||
}
|
||||
return aHash;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(false, "unexpected unit");
|
||||
return aHash;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "nsCoord.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -106,7 +108,44 @@ public:
|
||||
|
||||
// If this returns true the value is definitely zero. It it returns false
|
||||
// it might be zero. So it's best used for conservative optimization.
|
||||
bool IsDefinitelyZero() const { return mLength == 0 && mPercent == 0; }
|
||||
bool IsDefinitelyZero() const {
|
||||
return mLength == 0 && mPercent == 0 && !mHasPercent;
|
||||
}
|
||||
};
|
||||
|
||||
struct CalcNode final {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CalcNode)
|
||||
|
||||
enum class Type : uint8_t {
|
||||
Leaf,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Min,
|
||||
Max,
|
||||
Clamp
|
||||
};
|
||||
|
||||
static already_AddRefed<CalcNode> CreateLeaf(nscoord aLength,
|
||||
float aPercent,
|
||||
bool aHasPercent);
|
||||
static already_AddRefed<CalcNode> Create(Type aType);
|
||||
|
||||
bool HasPercent() const;
|
||||
bool Equals(const CalcNode& aOther) const;
|
||||
uint32_t HashValue(uint32_t aHash) const;
|
||||
|
||||
Type mType;
|
||||
nscoord mLength;
|
||||
float mPercent;
|
||||
float mNumber;
|
||||
bool mHasPercent;
|
||||
nsTArray<RefPtr<CalcNode>> mChildren;
|
||||
|
||||
private:
|
||||
explicit CalcNode(Type aType);
|
||||
~CalcNode() {}
|
||||
};
|
||||
|
||||
// Reference counted calc() value. This is the type that is used to store
|
||||
@@ -115,6 +154,11 @@ public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Calc)
|
||||
Calc() {}
|
||||
|
||||
bool HasCalcNode() const { return !!mNode; }
|
||||
nscoord Resolve(nscoord aPercentageBasis) const;
|
||||
|
||||
RefPtr<CalcNode> mNode;
|
||||
|
||||
private:
|
||||
Calc(const Calc&) = delete;
|
||||
~Calc() {}
|
||||
|
||||
@@ -78,6 +78,7 @@ support-files = file_animations_with_disabled_properties.html
|
||||
[test_box_size_keywords.html]
|
||||
[test_border_width_rounding.html]
|
||||
[test_bug73586.html]
|
||||
[test_css_math_functions.html]
|
||||
[test_bug74880.html]
|
||||
[test_bug98997.html]
|
||||
[test_bug160403.html]
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test CSS sizing math functions</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="narrow" style="position:absolute; left:0; top:0; width:200px;">
|
||||
<div id="min-fill" style="height:10px; width:min(500px, 100%);"></div>
|
||||
<div id="max-half" style="height:10px; width:max(75px, 50%);"></div>
|
||||
<div id="clamp-half" style="height:10px; width:clamp(80px, 50%, 120px);"></div>
|
||||
<div id="nested" style="height:10px; width:calc(min(500px, 100%) - 20px);"></div>
|
||||
</div>
|
||||
<div id="wide" style="position:absolute; left:0; top:100px; width:700px;">
|
||||
<div id="min-cap" style="height:10px; width:min(500px, 100%);"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
ok(CSS.supports("width", "min(500px, 100%)"),
|
||||
"width accepts min() with length and percentage");
|
||||
ok(CSS.supports("width", "max(75px, 50%)"),
|
||||
"width accepts max() with length and percentage");
|
||||
ok(CSS.supports("width", "clamp(80px, 50%, 120px)"),
|
||||
"width accepts clamp() with length and percentage");
|
||||
|
||||
var probe = document.createElement("div");
|
||||
probe.style.width = "min(500px, 100%)";
|
||||
is(probe.style.width, "min(500px, 100%)",
|
||||
"specified min() serializes without being dropped");
|
||||
|
||||
is(document.getElementById("min-fill").getBoundingClientRect().width, 200,
|
||||
"min() resolves percentage against a narrow containing block");
|
||||
is(document.getElementById("min-cap").getBoundingClientRect().width, 500,
|
||||
"min() caps width when the containing block is wider than the length");
|
||||
is(document.getElementById("max-half").getBoundingClientRect().width, 100,
|
||||
"max() resolves mixed length and percentage values");
|
||||
is(document.getElementById("clamp-half").getBoundingClientRect().width, 100,
|
||||
"clamp() resolves mixed length and percentage values");
|
||||
is(document.getElementById("nested").getBoundingClientRect().width, 180,
|
||||
"math functions can participate in calc() expressions");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user