Modified: trunk/Source/WebCore/css/CSSCalculationValue.cpp (251594 => 251595)
--- trunk/Source/WebCore/css/CSSCalculationValue.cpp 2019-10-25 16:20:22 UTC (rev 251594)
+++ trunk/Source/WebCore/css/CSSCalculationValue.cpp 2019-10-25 17:16:23 UTC (rev 251595)
@@ -149,34 +149,7 @@
return false;
}
-String CSSCalcValue::customCSSText() const
-{
- auto _expression_ = m_expression->customCSSText();
- if (_expression_[0] == '(')
- return makeString("calc", _expression_);
- return makeString("calc(", _expression_, ')');
-}
-bool CSSCalcValue::equals(const CSSCalcValue& other) const
-{
- return compareCSSValue(m_expression, other.m_expression);
-}
-
-inline double CSSCalcValue::clampToPermittedRange(double value) const
-{
- return m_shouldClampToNonNegative && value < 0 ? 0 : value;
-}
-
-double CSSCalcValue::doubleValue() const
-{
- return clampToPermittedRange(m_expression->doubleValue());
-}
-
-double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
-{
- return clampToPermittedRange(m_expression->computeLengthPx(conversionData));
-}
-
class CSSCalcPrimitiveValue final : public CSSCalcExpressionNode {
WTF_MAKE_FAST_ALLOCATED;
public:
@@ -198,98 +171,111 @@
return !m_value->doubleValue();
}
+ bool equals(const CSSCalcExpressionNode& other) const final;
+ Type type() const final { return CssCalcPrimitiveValue; }
+
+ CSSPrimitiveValue::UnitType primitiveType() const final
+ {
+ return CSSPrimitiveValue::UnitType(m_value->primitiveType());
+ }
+
+ std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const final;
+ double doubleValue() const final;
+
+ double computeLengthPx(const CSSToLengthConversionData&) const final;
+ void collectDirectComputationalDependencies(HashSet<CSSPropertyID>&) const final;
+ void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>&) const final;
+
String customCSSText() const final
{
return m_value->cssText();
}
- std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
+private:
+ explicit CSSCalcPrimitiveValue(Ref<CSSPrimitiveValue>&& value, bool isInteger)
+ : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitType)value->primitiveType()), isInteger)
+ , m_value(WTFMove(value))
{
- switch (category()) {
- case CalculationCategory::Number:
- return makeUnique<CalcExpressionNumber>(m_value->floatValue());
- case CalculationCategory::Length:
- return makeUnique<CalcExpressionLength>(Length(m_value->computeLength<float>(conversionData), WebCore::Fixed));
- case CalculationCategory::Percent:
- case CalculationCategory::PercentLength: {
- return makeUnique<CalcExpressionLength>(m_value->convertToLength<FixedFloatConversion | PercentConversion>(conversionData));
- }
- // Only types that could be part of a Length _expression_ can be converted
- // to a CalcExpressionNode. CalculationCategory::PercentNumber makes no sense as a Length.
- case CalculationCategory::PercentNumber:
- case CalculationCategory::Angle:
- case CalculationCategory::Time:
- case CalculationCategory::Frequency:
- case CalculationCategory::Other:
- ASSERT_NOT_REACHED();
- }
- ASSERT_NOT_REACHED();
- return nullptr;
}
- double doubleValue() const final
- {
- if (hasDoubleValue(primitiveType()))
- return m_value->doubleValue();
+ Ref<CSSPrimitiveValue> m_value;
+};
+
+std::unique_ptr<CalcExpressionNode> CSSCalcPrimitiveValue::createCalcExpression(const CSSToLengthConversionData& conversionData) const
+{
+ switch (category()) {
+ case CalculationCategory::Number:
+ return makeUnique<CalcExpressionNumber>(m_value->floatValue());
+ case CalculationCategory::Length:
+ return makeUnique<CalcExpressionLength>(Length(m_value->computeLength<float>(conversionData), WebCore::Fixed));
+ case CalculationCategory::Percent:
+ case CalculationCategory::PercentLength: {
+ return makeUnique<CalcExpressionLength>(m_value->convertToLength<FixedFloatConversion | PercentConversion>(conversionData));
+ }
+ // Only types that could be part of a Length _expression_ can be converted
+ // to a CalcExpressionNode. CalculationCategory::PercentNumber makes no sense as a Length.
+ case CalculationCategory::PercentNumber:
+ case CalculationCategory::Angle:
+ case CalculationCategory::Time:
+ case CalculationCategory::Frequency:
+ case CalculationCategory::Other:
ASSERT_NOT_REACHED();
- return 0;
}
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
- double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
- {
- switch (category()) {
- case CalculationCategory::Length:
- return m_value->computeLength<double>(conversionData);
- case CalculationCategory::Percent:
- case CalculationCategory::Number:
- return m_value->doubleValue();
- case CalculationCategory::PercentLength:
- case CalculationCategory::PercentNumber:
- case CalculationCategory::Angle:
- case CalculationCategory::Time:
- case CalculationCategory::Frequency:
- case CalculationCategory::Other:
- ASSERT_NOT_REACHED();
- break;
- }
+double CSSCalcPrimitiveValue::doubleValue() const
+{
+ if (hasDoubleValue(primitiveType()))
+ return m_value->doubleValue();
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+double CSSCalcPrimitiveValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
+{
+ switch (category()) {
+ case CalculationCategory::Length:
+ return m_value->computeLength<double>(conversionData);
+ case CalculationCategory::Percent:
+ case CalculationCategory::Number:
+ return m_value->doubleValue();
+ case CalculationCategory::PercentLength:
+ case CalculationCategory::PercentNumber:
+ case CalculationCategory::Angle:
+ case CalculationCategory::Time:
+ case CalculationCategory::Frequency:
+ case CalculationCategory::Other:
ASSERT_NOT_REACHED();
- return 0;
+ break;
}
+ ASSERT_NOT_REACHED();
+ return 0;
+}
- void collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const final
- {
- m_value->collectDirectComputationalDependencies(values);
- }
+void CSSCalcPrimitiveValue::collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const
+{
+ m_value->collectDirectComputationalDependencies(values);
+}
- void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const final
- {
- m_value->collectDirectRootComputationalDependencies(values);
- }
+void CSSCalcPrimitiveValue::collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const
+{
+ m_value->collectDirectRootComputationalDependencies(values);
+}
- bool equals(const CSSCalcExpressionNode& other) const final
- {
- if (type() != other.type())
- return false;
+bool CSSCalcPrimitiveValue::equals(const CSSCalcExpressionNode& other) const
+{
+ if (type() != other.type())
+ return false;
- return compareCSSValue(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
- }
+ return compareCSSValue(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
+}
- Type type() const final { return CssCalcPrimitiveValue; }
- CSSPrimitiveValue::UnitType primitiveType() const final
- {
- return CSSPrimitiveValue::UnitType(m_value->primitiveType());
- }
-private:
- explicit CSSCalcPrimitiveValue(Ref<CSSPrimitiveValue>&& value, bool isInteger)
- : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitType)value->primitiveType()), isInteger)
- , m_value(WTFMove(value))
- {
- }
- Ref<CSSPrimitiveValue> m_value;
-};
+
static const CalculationCategory addSubtractResult[static_cast<unsigned>(CalculationCategory::Angle)][static_cast<unsigned>(CalculationCategory::Angle)] = {
// CalculationCategory::Number CalculationCategory::Length CalculationCategory::Percent CalculationCategory::PercentNumber CalculationCategory::PercentLength
{ CalculationCategory::Number, CalculationCategory::Other, CalculationCategory::PercentNumber, CalculationCategory::PercentNumber, CalculationCategory::Other }, // CalculationCategory::Number
@@ -386,345 +372,356 @@
class CSSCalcOperation final : public CSSCalcExpressionNode {
WTF_MAKE_FAST_ALLOCATED;
public:
- static RefPtr<CSSCalcOperation> create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+ static RefPtr<CSSCalcOperation> create(CalcOperator, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide);
+ static RefPtr<CSSCalcOperation> createMinOrMax(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory);
+ static RefPtr<CSSCalcExpressionNode> createSimplified(CalcOperator, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide);
+
+private:
+ CSSCalcOperation(CalculationCategory category, CalcOperator op, Ref<CSSCalcExpressionNode>&& leftSide, Ref<CSSCalcExpressionNode>&& rightSide)
+ : CSSCalcExpressionNode(category, isIntegerResult(op, leftSide.get(), rightSide.get()))
+ , m_operator(op)
{
- if (!leftSide || !rightSide)
- return nullptr;
+ m_children.reserveInitialCapacity(2);
+ m_children.uncheckedAppend(WTFMove(leftSide));
+ m_children.uncheckedAppend(WTFMove(rightSide));
+ }
- ASSERT(leftSide->category() < CalculationCategory::Other);
- ASSERT(rightSide->category() < CalculationCategory::Other);
+ CSSCalcOperation(CalculationCategory category, CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& children)
+ : CSSCalcExpressionNode(category, isIntegerResult(op, children))
+ , m_operator(op)
+ , m_children(WTFMove(children))
+ {
+ }
- auto newCategory = determineCategory(*leftSide, *rightSide, op);
- if (newCategory == CalculationCategory::Other)
- return nullptr;
+ Type type() const final { return CssCalcOperation; }
- return adoptRef(new CSSCalcOperation(newCategory, op, leftSide.releaseNonNull(), rightSide.releaseNonNull()));
+ bool isZero() const final
+ {
+ return !doubleValue();
}
- static RefPtr<CSSCalcOperation> createMinOrMax(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory)
- {
- ASSERT(op == CalcOperator::Min || op == CalcOperator::Max);
+ bool equals(const CSSCalcExpressionNode&) const final;
- Optional<CalculationCategory> category = WTF::nullopt;
- for (auto& value : values) {
- auto valueCategory = resolvedTypeForMinOrMax(value->category(), destinationCategory);
+ std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const final;
- ASSERT(valueCategory < CalculationCategory::Other);
- if (!category) {
- if (valueCategory == CalculationCategory::Other)
- return nullptr;
- category = valueCategory;
- }
+ CSSPrimitiveValue::UnitType primitiveType() const final;
+ double doubleValue() const final;
+ double computeLengthPx(const CSSToLengthConversionData&) const final;
- if (category != valueCategory) {
- if (isSamePair(category.value(), valueCategory, CalculationCategory::Length, CalculationCategory::PercentLength)) {
- category = CalculationCategory::PercentLength;
- continue;
- }
- if (isSamePair(category.value(), valueCategory, CalculationCategory::Number, CalculationCategory::PercentNumber)) {
- category = CalculationCategory::PercentNumber;
- continue;
- }
- return nullptr;
- }
- }
+ void collectDirectComputationalDependencies(HashSet<CSSPropertyID>&) const final;
+ void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>&) const final;
- return adoptRef(new CSSCalcOperation(category.value(), op, WTFMove(values)));
+ String customCSSText() const final;
+
+ static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode& leftSide, CSSCalcExpressionNode& rightSide)
+ {
+ if (leftSide.category() == CalculationCategory::Number)
+ return &leftSide;
+ if (rightSide.category() == CalculationCategory::Number)
+ return &rightSide;
+ return nullptr;
}
- static RefPtr<CSSCalcExpressionNode> createSimplified(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+ double evaluate(const Vector<double>& children) const
{
- if (!leftSide || !rightSide)
- return nullptr;
+ return evaluateOperator(m_operator, children);
+ }
- auto leftCategory = leftSide->category();
- auto rightCategory = rightSide->category();
- ASSERT(leftCategory < CalculationCategory::Other);
- ASSERT(rightCategory < CalculationCategory::Other);
+ static double evaluateOperator(CalcOperator, const Vector<double>&);
+ static String buildCssText(Vector<String>, CalcOperator);
- bool isInteger = isIntegerResult(op, *leftSide, *rightSide);
+ const CalcOperator m_operator;
+ Vector<Ref<CSSCalcExpressionNode>> m_children;
+};
- // Simplify numbers.
- if (leftCategory == CalculationCategory::Number && rightCategory == CalculationCategory::Number) {
- CSSPrimitiveValue::UnitType evaluationType = CSSPrimitiveValue::CSS_NUMBER;
- return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), evaluationType, isInteger);
- }
+RefPtr<CSSCalcOperation> CSSCalcOperation::create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+{
+ if (!leftSide || !rightSide)
+ return nullptr;
- // Simplify addition and subtraction between same types.
- if (op == CalcOperator::Add || op == CalcOperator::Subtract) {
- if (leftCategory == rightSide->category()) {
- CSSPrimitiveValue::UnitType leftType = leftSide->primitiveType();
- if (hasDoubleValue(leftType)) {
- CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType();
- if (leftType == rightType)
- return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), leftType, isInteger);
- CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
- if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
- CSSPrimitiveValue::UnitType canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
- if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) {
- double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
- double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
- return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftValue, rightValue }), canonicalType, isInteger);
- }
- }
- }
- }
- } else {
- // Simplify multiplying or dividing by a number for simplifiable types.
- ASSERT(op == CalcOperator::Multiply || op == CalcOperator::Divide);
- auto* numberSide = getNumberSide(*leftSide, *rightSide);
- if (!numberSide)
- return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
- if (numberSide == leftSide && op == CalcOperator::Divide)
- return nullptr;
- auto& otherSide = leftSide == numberSide ? *rightSide : *leftSide;
+ ASSERT(leftSide->category() < CalculationCategory::Other);
+ ASSERT(rightSide->category() < CalculationCategory::Other);
- double number = numberSide->doubleValue();
- if (!std::isfinite(number))
- return nullptr;
- if (op == CalcOperator::Divide && !number)
- return nullptr;
+ auto newCategory = determineCategory(*leftSide, *rightSide, op);
+ if (newCategory == CalculationCategory::Other)
+ return nullptr;
- auto otherType = otherSide.primitiveType();
- if (hasDoubleValue(otherType))
- return CSSCalcPrimitiveValue::create(evaluateOperator(op, { otherSide.doubleValue(), number }), otherType, isInteger);
- }
+ return adoptRef(new CSSCalcOperation(newCategory, op, leftSide.releaseNonNull(), rightSide.releaseNonNull()));
+}
- return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
- }
+RefPtr<CSSCalcOperation> CSSCalcOperation::createMinOrMax(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory)
+{
+ ASSERT(op == CalcOperator::Min || op == CalcOperator::Max);
-private:
- bool isZero() const final
- {
- return !doubleValue();
- }
+ Optional<CalculationCategory> category = WTF::nullopt;
+ for (auto& value : values) {
+ auto valueCategory = resolvedTypeForMinOrMax(value->category(), destinationCategory);
- std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
- {
- Vector<std::unique_ptr<CalcExpressionNode>> nodes;
- nodes.reserveInitialCapacity(m_children.size());
-
- for (auto& child : m_children) {
- auto node = child->createCalcExpression(conversionData);
- if (!node)
+ ASSERT(valueCategory < CalculationCategory::Other);
+ if (!category) {
+ if (valueCategory == CalculationCategory::Other)
return nullptr;
- nodes.uncheckedAppend(WTFMove(node));
+ category = valueCategory;
}
- return makeUnique<CalcExpressionOperation>(WTFMove(nodes), m_operator);
- }
- double doubleValue() const final
- {
- Vector<double> doubleValues;
- for (auto& child : m_children)
- doubleValues.append(child->doubleValue());
- return evaluate(doubleValues);
+ if (category != valueCategory) {
+ if (isSamePair(category.value(), valueCategory, CalculationCategory::Length, CalculationCategory::PercentLength)) {
+ category = CalculationCategory::PercentLength;
+ continue;
+ }
+ if (isSamePair(category.value(), valueCategory, CalculationCategory::Number, CalculationCategory::PercentNumber)) {
+ category = CalculationCategory::PercentNumber;
+ continue;
+ }
+ return nullptr;
+ }
}
- double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
- {
- Vector<double> doubleValues;
- for (auto& child : m_children)
- doubleValues.append(child->computeLengthPx(conversionData));
- return evaluate(doubleValues);
- }
+ return adoptRef(new CSSCalcOperation(category.value(), op, WTFMove(values)));
+}
- void collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const final
- {
- for (auto& child : m_children)
- child->collectDirectComputationalDependencies(values);
- }
+RefPtr<CSSCalcExpressionNode> CSSCalcOperation::createSimplified(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+{
+ if (!leftSide || !rightSide)
+ return nullptr;
- void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const final
- {
- for (auto& child : m_children)
- child->collectDirectRootComputationalDependencies(values);
+ auto leftCategory = leftSide->category();
+ auto rightCategory = rightSide->category();
+ ASSERT(leftCategory < CalculationCategory::Other);
+ ASSERT(rightCategory < CalculationCategory::Other);
+
+ bool isInteger = isIntegerResult(op, *leftSide, *rightSide);
+
+ // Simplify numbers.
+ if (leftCategory == CalculationCategory::Number && rightCategory == CalculationCategory::Number) {
+ CSSPrimitiveValue::UnitType evaluationType = CSSPrimitiveValue::CSS_NUMBER;
+ return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), evaluationType, isInteger);
}
- static String buildCssText(Vector<String> childExpressions, CalcOperator op)
- {
- StringBuilder result;
- result.append('(');
- switch (op) {
- case CalcOperator::Add:
- case CalcOperator::Subtract:
- case CalcOperator::Multiply:
- case CalcOperator::Divide:
- ASSERT(childExpressions.size() == 2);
- result.append(childExpressions[0]);
- result.append(' ');
- result.append(static_cast<char>(op));
- result.append(' ');
- result.append(childExpressions[1]);
- break;
- case CalcOperator::Min:
- case CalcOperator::Max:
- ASSERT(!childExpressions.isEmpty());
- const char* functionName = op == CalcOperator::Min ? "min(" : "max(";
- result.append(functionName);
- result.append(childExpressions[0]);
- for (size_t i = 1; i < childExpressions.size(); ++i) {
- result.append(',');
- result.append(' ');
- result.append(childExpressions[i]);
+ // Simplify addition and subtraction between same types.
+ if (op == CalcOperator::Add || op == CalcOperator::Subtract) {
+ if (leftCategory == rightSide->category()) {
+ CSSPrimitiveValue::UnitType leftType = leftSide->primitiveType();
+ if (hasDoubleValue(leftType)) {
+ CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType();
+ if (leftType == rightType)
+ return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), leftType, isInteger);
+ CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
+ if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
+ CSSPrimitiveValue::UnitType canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
+ if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) {
+ double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
+ double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
+ return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftValue, rightValue }), canonicalType, isInteger);
+ }
+ }
}
- result.append(')');
}
- result.append(')');
+ } else {
+ // Simplify multiplying or dividing by a number for simplifiable types.
+ ASSERT(op == CalcOperator::Multiply || op == CalcOperator::Divide);
+ auto* numberSide = getNumberSide(*leftSide, *rightSide);
+ if (!numberSide)
+ return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
+ if (numberSide == leftSide && op == CalcOperator::Divide)
+ return nullptr;
+ auto& otherSide = leftSide == numberSide ? *rightSide : *leftSide;
- return result.toString();
- }
+ double number = numberSide->doubleValue();
+ if (!std::isfinite(number))
+ return nullptr;
+ if (op == CalcOperator::Divide && !number)
+ return nullptr;
- String customCSSText() const final
- {
- Vector<String> cssTexts;
- for (auto& child : m_children)
- cssTexts.append(child->customCSSText());
- return buildCssText(cssTexts, m_operator);
+ auto otherType = otherSide.primitiveType();
+ if (hasDoubleValue(otherType))
+ return CSSCalcPrimitiveValue::create(evaluateOperator(op, { otherSide.doubleValue(), number }), otherType, isInteger);
}
- bool equals(const CSSCalcExpressionNode& exp) const final
- {
- if (type() != exp.type())
- return false;
+ return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
+}
- const CSSCalcOperation& other = static_cast<const CSSCalcOperation&>(exp);
-
- if (m_children.size() != other.m_children.size() || m_operator != other.m_operator)
- return false;
-
- for (size_t i = 0; i < m_children.size(); ++i) {
- if (!compareCSSValue(m_children[i], other.m_children[i]))
- return false;
- }
- return true;
- }
-
- Type type() const final { return CssCalcOperation; }
-
- CSSPrimitiveValue::UnitType primitiveType() const final
- {
- switch (category()) {
- case CalculationCategory::Number:
+CSSPrimitiveValue::UnitType CSSCalcOperation::primitiveType() const
+{
+ switch (category()) {
+ case CalculationCategory::Number:
#if !ASSERT_DISABLED
- for (auto& child : m_children)
- ASSERT(child->category() == CalculationCategory::Number);
+ for (auto& child : m_children)
+ ASSERT(child->category() == CalculationCategory::Number);
#endif
- return CSSPrimitiveValue::CSS_NUMBER;
- case CalculationCategory::Length:
- case CalculationCategory::Percent: {
- if (m_children.isEmpty())
+ return CSSPrimitiveValue::CSS_NUMBER;
+ case CalculationCategory::Length:
+ case CalculationCategory::Percent: {
+ if (m_children.isEmpty())
+ return CSSPrimitiveValue::CSS_UNKNOWN;
+ if (m_children.size() == 2) {
+ if (m_children[0]->category() == CalculationCategory::Number)
+ return m_children[1]->primitiveType();
+ if (m_children[1]->category() == CalculationCategory::Number)
+ return m_children[0]->primitiveType();
+ }
+ CSSPrimitiveValue::UnitType firstType = m_children[0]->primitiveType();
+ for (auto& child : m_children) {
+ if (firstType != child->primitiveType())
return CSSPrimitiveValue::CSS_UNKNOWN;
- if (m_children.size() == 2) {
- if (m_children[0]->category() == CalculationCategory::Number)
- return m_children[1]->primitiveType();
- if (m_children[1]->category() == CalculationCategory::Number)
- return m_children[0]->primitiveType();
- }
- CSSPrimitiveValue::UnitType firstType = m_children[0]->primitiveType();
- for (auto& child : m_children) {
- if (firstType != child->primitiveType())
- return CSSPrimitiveValue::CSS_UNKNOWN;
- }
- return firstType;
}
- case CalculationCategory::Angle:
- return CSSPrimitiveValue::CSS_DEG;
- case CalculationCategory::Time:
- return CSSPrimitiveValue::CSS_MS;
- case CalculationCategory::Frequency:
- return CSSPrimitiveValue::CSS_HZ;
- case CalculationCategory::PercentLength:
- case CalculationCategory::PercentNumber:
- case CalculationCategory::Other:
- return CSSPrimitiveValue::CSS_UNKNOWN;
- }
- ASSERT_NOT_REACHED();
+ return firstType;
+ }
+ case CalculationCategory::Angle:
+ return CSSPrimitiveValue::CSS_DEG;
+ case CalculationCategory::Time:
+ return CSSPrimitiveValue::CSS_MS;
+ case CalculationCategory::Frequency:
+ return CSSPrimitiveValue::CSS_HZ;
+ case CalculationCategory::PercentLength:
+ case CalculationCategory::PercentNumber:
+ case CalculationCategory::Other:
return CSSPrimitiveValue::CSS_UNKNOWN;
}
+ ASSERT_NOT_REACHED();
+ return CSSPrimitiveValue::CSS_UNKNOWN;
+}
- CSSCalcOperation(CalculationCategory category, CalcOperator op, Ref<CSSCalcExpressionNode>&& leftSide, Ref<CSSCalcExpressionNode>&& rightSide)
- : CSSCalcExpressionNode(category, isIntegerResult(op, leftSide.get(), rightSide.get()))
- , m_operator(op)
- {
- m_children.reserveInitialCapacity(2);
- m_children.uncheckedAppend(WTFMove(leftSide));
- m_children.uncheckedAppend(WTFMove(rightSide));
- }
+std::unique_ptr<CalcExpressionNode> CSSCalcOperation::createCalcExpression(const CSSToLengthConversionData& conversionData) const
+{
+ Vector<std::unique_ptr<CalcExpressionNode>> nodes;
+ nodes.reserveInitialCapacity(m_children.size());
- CSSCalcOperation(CalculationCategory category, CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& children)
- : CSSCalcExpressionNode(category, isIntegerResult(op, children))
- , m_operator(op)
- , m_children(WTFMove(children))
- {
+ for (auto& child : m_children) {
+ auto node = child->createCalcExpression(conversionData);
+ if (!node)
+ return nullptr;
+ nodes.uncheckedAppend(WTFMove(node));
}
+ return makeUnique<CalcExpressionOperation>(WTFMove(nodes), m_operator);
+}
- static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode& leftSide, CSSCalcExpressionNode& rightSide)
- {
- if (leftSide.category() == CalculationCategory::Number)
- return &leftSide;
- if (rightSide.category() == CalculationCategory::Number)
- return &rightSide;
- return nullptr;
- }
+double CSSCalcOperation::doubleValue() const
+{
+ Vector<double> doubleValues;
+ for (auto& child : m_children)
+ doubleValues.append(child->doubleValue());
+ return evaluate(doubleValues);
+}
- double evaluate(const Vector<double>& children) const
- {
- return evaluateOperator(m_operator, children);
- }
+double CSSCalcOperation::computeLengthPx(const CSSToLengthConversionData& conversionData) const
+{
+ Vector<double> doubleValues;
+ for (auto& child : m_children)
+ doubleValues.append(child->computeLengthPx(conversionData));
+ return evaluate(doubleValues);
+}
- static double evaluateOperator(CalcOperator op, const Vector<double>& children)
- {
- switch (op) {
- case CalcOperator::Add:
- ASSERT(children.size() == 2);
- return children[0] + children[1];
- case CalcOperator::Subtract:
- ASSERT(children.size() == 2);
- return children[0] - children[1];
- case CalcOperator::Multiply:
- ASSERT(children.size() == 2);
- return children[0] * children[1];
- case CalcOperator::Divide:
- ASSERT(children.size() == 1 || children.size() == 2);
- if (children.size() == 1)
- return std::numeric_limits<double>::quiet_NaN();
- return children[0] / children[1];
- case CalcOperator::Min: {
- if (children.isEmpty())
- return std::numeric_limits<double>::quiet_NaN();
- double minimum = children[0];
- for (auto child : children)
- minimum = std::min(minimum, child);
- return minimum;
+void CSSCalcOperation::collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const
+{
+ for (auto& child : m_children)
+ child->collectDirectComputationalDependencies(values);
+}
+
+void CSSCalcOperation::collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const
+{
+ for (auto& child : m_children)
+ child->collectDirectRootComputationalDependencies(values);
+}
+
+String CSSCalcOperation::buildCssText(Vector<String> childExpressions, CalcOperator op)
+{
+ StringBuilder result;
+ result.append('(');
+ switch (op) {
+ case CalcOperator::Add:
+ case CalcOperator::Subtract:
+ case CalcOperator::Multiply:
+ case CalcOperator::Divide:
+ ASSERT(childExpressions.size() == 2);
+ result.append(childExpressions[0]);
+ result.append(' ');
+ result.append(static_cast<char>(op));
+ result.append(' ');
+ result.append(childExpressions[1]);
+ break;
+ case CalcOperator::Min:
+ case CalcOperator::Max:
+ ASSERT(!childExpressions.isEmpty());
+ const char* functionName = op == CalcOperator::Min ? "min(" : "max(";
+ result.append(functionName);
+ result.append(childExpressions[0]);
+ for (size_t i = 1; i < childExpressions.size(); ++i) {
+ result.append(',');
+ result.append(' ');
+ result.append(childExpressions[i]);
}
- case CalcOperator::Max: {
- if (children.isEmpty())
- return std::numeric_limits<double>::quiet_NaN();
- double maximum = children[0];
- for (auto child : children)
- maximum = std::max(maximum, child);
- return maximum;
- }
- }
- ASSERT_NOT_REACHED();
- return 0;
+ result.append(')');
}
+ result.append(')');
- const CalcOperator m_operator;
- Vector<Ref<CSSCalcExpressionNode>> m_children;
-};
+ return result.toString();
+}
-static ParseState checkDepthAndIndex(int* depth, CSSParserTokenRange tokens)
+String CSSCalcOperation::customCSSText() const
{
- (*depth)++;
- if (tokens.atEnd())
- return NoMoreTokens;
- if (*depth > maxExpressionDepth)
- return TooDeep;
- return OK;
+ Vector<String> cssTexts;
+ for (auto& child : m_children)
+ cssTexts.append(child->customCSSText());
+ return buildCssText(cssTexts, m_operator);
}
+bool CSSCalcOperation::equals(const CSSCalcExpressionNode& exp) const
+{
+ if (type() != exp.type())
+ return false;
+
+ const CSSCalcOperation& other = static_cast<const CSSCalcOperation&>(exp);
+
+ if (m_children.size() != other.m_children.size() || m_operator != other.m_operator)
+ return false;
+
+ for (size_t i = 0; i < m_children.size(); ++i) {
+ if (!compareCSSValue(m_children[i], other.m_children[i]))
+ return false;
+ }
+ return true;
+}
+
+double CSSCalcOperation::evaluateOperator(CalcOperator op, const Vector<double>& children)
+{
+ switch (op) {
+ case CalcOperator::Add:
+ ASSERT(children.size() == 2);
+ return children[0] + children[1];
+ case CalcOperator::Subtract:
+ ASSERT(children.size() == 2);
+ return children[0] - children[1];
+ case CalcOperator::Multiply:
+ ASSERT(children.size() == 2);
+ return children[0] * children[1];
+ case CalcOperator::Divide:
+ ASSERT(children.size() == 1 || children.size() == 2);
+ if (children.size() == 1)
+ return std::numeric_limits<double>::quiet_NaN();
+ return children[0] / children[1];
+ case CalcOperator::Min: {
+ if (children.isEmpty())
+ return std::numeric_limits<double>::quiet_NaN();
+ double minimum = children[0];
+ for (auto child : children)
+ minimum = std::min(minimum, child);
+ return minimum;
+ }
+ case CalcOperator::Max: {
+ if (children.isEmpty())
+ return std::numeric_limits<double>::quiet_NaN();
+ double maximum = children[0];
+ for (auto child : children)
+ maximum = std::max(maximum, child);
+ return maximum;
+ }
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+
class CSSCalcExpressionNodeParser {
public:
explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
@@ -731,19 +728,7 @@
: m_destinationCategory(destinationCategory)
{ }
- RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens, CSSValueID function)
- {
- Value result;
- tokens.consumeWhitespace();
- bool ok = false;
- if (function == CSSValueCalc || function == CSSValueWebkitCalc)
- ok = parseValueExpression(tokens, 0, &result);
- else if (function == CSSValueMin || function == CSSValueMax)
- ok = parseMinMaxExpression(tokens, function, 0, &result);
- if (!ok || !tokens.atEnd())
- return nullptr;
- return result.value;
- }
+ RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange, CSSValueID function);
private:
struct Value {
@@ -756,143 +741,176 @@
return token.delimiter();
return 0;
}
+
+ bool parseValue(CSSParserTokenRange&, Value* result);
+ bool parseValueTerm(CSSParserTokenRange&, int depth, Value* result);
+ bool parseValueMultiplicativeExpression(CSSParserTokenRange&, int depth, Value* result);
+ bool parseAdditiveValueExpression(CSSParserTokenRange&, int depth, Value* result);
+ bool parseMinMaxExpression(CSSParserTokenRange&, CSSValueID minMaxFunction, int depth, Value* result);
+ bool parseValueExpression(CSSParserTokenRange&, int depth, Value* result);
+
+ CalculationCategory m_destinationCategory;
+};
+
+
+RefPtr<CSSCalcExpressionNode> CSSCalcExpressionNodeParser::parseCalc(CSSParserTokenRange tokens, CSSValueID function)
+{
+ Value result;
+ tokens.consumeWhitespace();
+ bool ok = false;
+ if (function == CSSValueCalc || function == CSSValueWebkitCalc)
+ ok = parseValueExpression(tokens, 0, &result);
+ else if (function == CSSValueMin || function == CSSValueMax)
+ ok = parseMinMaxExpression(tokens, function, 0, &result);
+ if (!ok || !tokens.atEnd())
+ return nullptr;
+ return result.value;
+}
+
+bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, Value* result)
+{
+ CSSParserToken token = tokens.consumeIncludingWhitespace();
+ if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
+ return false;
- bool parseValue(CSSParserTokenRange& tokens, Value* result)
- {
- CSSParserToken token = tokens.consumeIncludingWhitespace();
- if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
- return false;
-
- CSSPrimitiveValue::UnitType type = token.unitType();
- if (unitCategory(type) == CalculationCategory::Other)
- return false;
-
- bool isInteger = token.numericValueType() == IntegerValueType || (token.numericValueType() == NumberValueType && token.numericValue() == trunc(token.numericValue()));
- result->value = CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(token.numericValue(), type), isInteger);
-
- return true;
- }
+ CSSPrimitiveValue::UnitType type = token.unitType();
+ if (unitCategory(type) == CalculationCategory::Other)
+ return false;
- bool parseValueTerm(CSSParserTokenRange& tokens, int depth, Value* result)
- {
- if (checkDepthAndIndex(&depth, tokens) != OK)
- return false;
+ bool isInteger = token.numericValueType() == IntegerValueType || (token.numericValueType() == NumberValueType && token.numericValue() == trunc(token.numericValue()));
+ result->value = CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(token.numericValue(), type), isInteger);
+
+ return true;
+}
- auto functionId = tokens.peek().functionId();
-
- if (tokens.peek().type() == LeftParenthesisToken || functionId == CSSValueCalc) {
- CSSParserTokenRange innerRange = tokens.consumeBlock();
- tokens.consumeWhitespace();
- innerRange.consumeWhitespace();
- return parseValueExpression(innerRange, depth, result);
- }
+static ParseState checkDepthAndIndex(int* depth, CSSParserTokenRange tokens)
+{
+ (*depth)++;
+ if (tokens.atEnd())
+ return NoMoreTokens;
+ if (*depth > maxExpressionDepth)
+ return TooDeep;
+ return OK;
+}
- if (functionId == CSSValueMax || functionId == CSSValueMin) {
- CSSParserTokenRange innerRange = tokens.consumeBlock();
- tokens.consumeWhitespace();
- innerRange.consumeWhitespace();
- return parseMinMaxExpression(innerRange, functionId, depth, result);
- }
-
- return parseValue(tokens, result);
+bool CSSCalcExpressionNodeParser::parseValueTerm(CSSParserTokenRange& tokens, int depth, Value* result)
+{
+ if (checkDepthAndIndex(&depth, tokens) != OK)
+ return false;
+
+ auto functionId = tokens.peek().functionId();
+
+ if (tokens.peek().type() == LeftParenthesisToken || functionId == CSSValueCalc) {
+ CSSParserTokenRange innerRange = tokens.consumeBlock();
+ tokens.consumeWhitespace();
+ innerRange.consumeWhitespace();
+ return parseValueExpression(innerRange, depth, result);
}
+
+ if (functionId == CSSValueMax || functionId == CSSValueMin) {
+ CSSParserTokenRange innerRange = tokens.consumeBlock();
+ tokens.consumeWhitespace();
+ innerRange.consumeWhitespace();
+ return parseMinMaxExpression(innerRange, functionId, depth, result);
+ }
- bool parseValueMultiplicativeExpression(CSSParserTokenRange& tokens, int depth, Value* result)
- {
- if (checkDepthAndIndex(&depth, tokens) != OK)
- return false;
+ return parseValue(tokens, result);
+}
+
+bool CSSCalcExpressionNodeParser::parseValueMultiplicativeExpression(CSSParserTokenRange& tokens, int depth, Value* result)
+{
+ if (checkDepthAndIndex(&depth, tokens) != OK)
+ return false;
+
+ if (!parseValueTerm(tokens, depth, result))
+ return false;
+
+ while (!tokens.atEnd()) {
+ char operatorCharacter = operatorValue(tokens.peek());
+ if (operatorCharacter != static_cast<char>(CalcOperator::Multiply) && operatorCharacter != static_cast<char>(CalcOperator::Divide))
+ break;
+ tokens.consumeIncludingWhitespace();
- if (!parseValueTerm(tokens, depth, result))
+ Value rhs;
+ if (!parseValueTerm(tokens, depth, &rhs))
return false;
- while (!tokens.atEnd()) {
- char operatorCharacter = operatorValue(tokens.peek());
- if (operatorCharacter != static_cast<char>(CalcOperator::Multiply) && operatorCharacter != static_cast<char>(CalcOperator::Divide))
- break;
- tokens.consumeIncludingWhitespace();
-
- Value rhs;
- if (!parseValueTerm(tokens, depth, &rhs))
- return false;
-
- result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
+ result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
- if (!result->value)
- return false;
- }
-
- return true;
+ if (!result->value)
+ return false;
}
- bool parseAdditiveValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
- {
- if (checkDepthAndIndex(&depth, tokens) != OK)
+ return true;
+}
+
+bool CSSCalcExpressionNodeParser::parseAdditiveValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
+{
+ if (checkDepthAndIndex(&depth, tokens) != OK)
+ return false;
+
+ if (!parseValueMultiplicativeExpression(tokens, depth, result))
+ return false;
+
+ while (!tokens.atEnd()) {
+ char operatorCharacter = operatorValue(tokens.peek());
+ if (operatorCharacter != static_cast<char>(CalcOperator::Add) && operatorCharacter != static_cast<char>(CalcOperator::Subtract))
+ break;
+ if ((&tokens.peek() - 1)->type() != WhitespaceToken)
+ return false; // calc(1px+ 2px) is invalid
+ tokens.consume();
+ if (tokens.peek().type() != WhitespaceToken)
+ return false; // calc(1px +2px) is invalid
+ tokens.consumeIncludingWhitespace();
+
+ Value rhs;
+ if (!parseValueMultiplicativeExpression(tokens, depth, &rhs))
return false;
- if (!parseValueMultiplicativeExpression(tokens, depth, result))
+ result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
+ if (!result->value)
return false;
-
- while (!tokens.atEnd()) {
- char operatorCharacter = operatorValue(tokens.peek());
- if (operatorCharacter != static_cast<char>(CalcOperator::Add) && operatorCharacter != static_cast<char>(CalcOperator::Subtract))
- break;
- if ((&tokens.peek() - 1)->type() != WhitespaceToken)
- return false; // calc(1px+ 2px) is invalid
- tokens.consume();
- if (tokens.peek().type() != WhitespaceToken)
- return false; // calc(1px +2px) is invalid
- tokens.consumeIncludingWhitespace();
-
- Value rhs;
- if (!parseValueMultiplicativeExpression(tokens, depth, &rhs))
- return false;
-
- result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
- if (!result->value)
- return false;
- }
-
- return true;
}
+
+ return true;
+}
- bool parseMinMaxExpression(CSSParserTokenRange& tokens, CSSValueID minMaxFunction, int depth, Value* result)
- {
- if (checkDepthAndIndex(&depth, tokens) != OK)
+bool CSSCalcExpressionNodeParser::parseMinMaxExpression(CSSParserTokenRange& tokens, CSSValueID minMaxFunction, int depth, Value* result)
+{
+ if (checkDepthAndIndex(&depth, tokens) != OK)
+ return false;
+
+ CalcOperator op = (minMaxFunction == CSSValueMin) ? CalcOperator::Min : CalcOperator::Max;
+
+ Value value;
+ if (!parseValueExpression(tokens, depth, &value))
+ return false;
+
+ Vector<Ref<CSSCalcExpressionNode>> nodes;
+ nodes.append(value.value.releaseNonNull());
+
+ while (!tokens.atEnd()) {
+ tokens.consumeWhitespace();
+ if (tokens.consume().type() != CommaToken)
return false;
+ tokens.consumeWhitespace();
- CalcOperator op = (minMaxFunction == CSSValueMin) ? CalcOperator::Min : CalcOperator::Max;
-
- Value value;
if (!parseValueExpression(tokens, depth, &value))
return false;
- Vector<Ref<CSSCalcExpressionNode>> nodes;
nodes.append(value.value.releaseNonNull());
+ }
- while (!tokens.atEnd()) {
- tokens.consumeWhitespace();
- if (tokens.consume().type() != CommaToken)
- return false;
- tokens.consumeWhitespace();
+ result->value = CSSCalcOperation::createMinOrMax(op, WTFMove(nodes), m_destinationCategory);
+ return result->value;
+}
- if (!parseValueExpression(tokens, depth, &value))
- return false;
+bool CSSCalcExpressionNodeParser::parseValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
+{
+ return parseAdditiveValueExpression(tokens, depth, result);
+}
- nodes.append(value.value.releaseNonNull());
- }
- result->value = CSSCalcOperation::createMinOrMax(op, WTFMove(nodes), m_destinationCategory);
- return result->value;
- }
-
- bool parseValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
- {
- return parseAdditiveValueExpression(tokens, depth, result);
- }
-
- CalculationCategory m_destinationCategory;
-};
-
static inline RefPtr<CSSCalcOperation> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
{
return CSSCalcOperation::create(CalcOperator::Multiply, createCSS(length, style),
@@ -963,6 +981,34 @@
return nullptr;
}
+String CSSCalcValue::customCSSText() const
+{
+ auto _expression_ = m_expression->customCSSText();
+ if (_expression_[0] == '(')
+ return makeString("calc", _expression_);
+ return makeString("calc(", _expression_, ')');
+}
+
+bool CSSCalcValue::equals(const CSSCalcValue& other) const
+{
+ return compareCSSValue(m_expression, other.m_expression);
+}
+
+inline double CSSCalcValue::clampToPermittedRange(double value) const
+{
+ return m_shouldClampToNonNegative && value < 0 ? 0 : value;
+}
+
+double CSSCalcValue::doubleValue() const
+{
+ return clampToPermittedRange(m_expression->doubleValue());
+}
+
+double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
+{
+ return clampToPermittedRange(m_expression->computeLengthPx(conversionData));
+}
+
RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
{
CSSCalcExpressionNodeParser parser(destinationCategory);