calc() serialization doesn't match the spec
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Dec 2019 02:22:35 +0000 (02:22 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Dec 2019 02:22:35 +0000 (02:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=203442
LayoutTests/imported/w3c:

<rdar://problem/56639402>

Reviewed by Dean Jackson.

New results. Current WPT don't reflect the new serialization rules yet, so we expect
some failing results here.

* web-platform-tests/css/css-align/gaps/column-gap-parsing-001-expected.txt:
* web-platform-tests/css/css-align/gaps/gap-parsing-001-expected.txt:
* web-platform-tests/css/css-align/gaps/grid-column-gap-parsing-001-expected.txt:
* web-platform-tests/css/css-align/gaps/grid-gap-parsing-001-expected.txt:
* web-platform-tests/css/css-align/gaps/grid-row-gap-parsing-001-expected.txt:
* web-platform-tests/css/css-align/gaps/row-gap-parsing-001-expected.txt:
* web-platform-tests/css/css-box/parsing/padding-valid-expected.txt:
* web-platform-tests/css/css-shapes/animation/shape-outside-interpolation-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-margin-003-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-circle-010-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-circle-011-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-ellipse-010-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-ellipse-011-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-inset-008-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-inset-009-expected.txt:
* web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-polygon-006-expected.txt:
* web-platform-tests/css/css-values/calc-background-position-002-expected.txt:
* web-platform-tests/css/css-values/calc-background-position-003-expected.txt:
* web-platform-tests/css/css-values/calc-nesting-002-expected.txt:
* web-platform-tests/css/css-values/calc-serialization-002-expected.txt:
* web-platform-tests/css/css-values/calc-serialization-expected.txt:
* web-platform-tests/css/css-values/minmax-angle-computed-expected.txt:
* web-platform-tests/css/css-values/minmax-angle-serialize-expected.txt:
* web-platform-tests/css/css-values/minmax-length-percent-serialize-expected.txt:
* web-platform-tests/css/css-values/minmax-length-serialize-expected.txt:
* web-platform-tests/css/css-values/minmax-percentage-serialize-expected.txt:
* web-platform-tests/css/css-values/minmax-time-computed-expected.txt:
* web-platform-tests/css/css-values/minmax-time-serialize-expected.txt:

Source/WebCore:

<rdar://problem/56639402>

Reviewed by Dean Jackson.

These changes implement the calc() simplification and serialization rules described in
https://drafts.csswg.org/css-values-4/#calc-internal. They also make it easy to add the
new math functions in future.

The major changes are:

1. Create Negate and Invert nodes to handle subtraction and division, so that
   Product and Sum are the only math operators that are tracked, which makes
   it possible to sort child nodes for simplification.

2. After parsing, do a bottom-up simplification parse following
   <https://drafts.csswg.org/css-values-4/#calc-simplification>, which sorts child nodes
   according to <https://drafts.csswg.org/css-values-4/#sort-a-calculations-children>,
   combines child nodes where possible, and hoists child nodes up to their parents were
   possible. Simplification always preserves a calc-like function at the root.

   This simplification also does unit canonicalization (e.g. all absolute lengths get px units).
   To make unit conversion more explicit, pass a CSSUnitType into doubleValue() methods so that
   it's clear what units the resulting value is in.

3. Serialize according to <https://drafts.csswg.org/css-values-4/#calc-serialize>
   which fixes bugs with nested parentheses.

This patch does not completely implement the type checking and conversion rules described in <https://drafts.csswg.org/css-values-4/#calc-type-checking>
and <https://drafts.css-houdini.org/css-typed-om-1/#numeric-typing>.

Tests: fast/css/calc-parsing-limits.html
       fast/css/calc-parsing.html

* css/CSSCalculationValue.cpp:
(WebCore::calculationCategoryForCombination):
(WebCore::canonicalUnitTypeForCalculationCategory):
(WebCore::functionFromOperator):
(WebCore::CSSCalcPrimitiveValueNode::isNumericValue const):
(WebCore::CSSCalcPrimitiveValueNode::isNegative const):
(WebCore::CSSCalcPrimitiveValueNode::negate):
(WebCore::CSSCalcPrimitiveValueNode::invert):
(WebCore::CSSCalcPrimitiveValueNode::add):
(WebCore::CSSCalcPrimitiveValueNode::multiply):
(WebCore::CSSCalcPrimitiveValueNode::convertToUnitType):
(WebCore::CSSCalcPrimitiveValueNode::canonicalizeUnit):
(WebCore::CSSCalcPrimitiveValueNode::doubleValue const):
(WebCore::CSSCalcNegateNode::createCalcExpression const):
(WebCore::CSSCalcNegateNode::dump const):
(WebCore::CSSCalcInvertNode::createCalcExpression const):
(WebCore::CSSCalcInvertNode::dump const):
(WebCore::categoryForInvert):
(WebCore::determineCategory):
(WebCore::CSSCalcOperationNode::create):
(WebCore::CSSCalcOperationNode::createSum):
(WebCore::CSSCalcOperationNode::createProduct):
(WebCore::CSSCalcOperationNode::hoistChildrenWithOperator):
(WebCore::sortingCategoryForType):
(WebCore::sortingCategory):
(WebCore::primitiveTypeForCombination):
(WebCore::conversionToAddValuesWithTypes):
(WebCore::CSSCalcOperationNode::canCombineAllChildren const):
(WebCore::CSSCalcOperationNode::combineChildren):
(WebCore::CSSCalcOperationNode::simplify):
(WebCore::CSSCalcOperationNode::simplifyRecursive):
(WebCore::CSSCalcOperationNode::simplifyNode):
(WebCore::CSSCalcOperationNode::primitiveType const):
(WebCore::CSSCalcOperationNode::doubleValue const):
(WebCore::CSSCalcOperationNode::computeLengthPx const):
(WebCore::CSSCalcOperationNode::buildCSSText):
(WebCore::functionPrefixForOperator):
(WebCore::CSSCalcOperationNode::buildCSSTextRecursive):
(WebCore::CSSCalcOperationNode::evaluateOperator):
(WebCore::CSSCalcExpressionNodeParser::parseCalc):
(WebCore::checkDepthAndIndex):
(WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
(WebCore::CSSCalcExpressionNodeParser::parseValue):
(WebCore::CSSCalcExpressionNodeParser::parseCalcValue):
(WebCore::CSSCalcExpressionNodeParser::parseCalcProduct):
(WebCore::CSSCalcExpressionNodeParser::parseCalcSum):
(WebCore::createCSS):
(WebCore::CSSCalcValue::customCSSText const):
(WebCore::CSSCalcValue::doubleValue const):
(WebCore::CSSCalcValue::create):
(WebCore::CSSCalcOperationNode::createSimplified): Deleted.
(WebCore::CSSCalcOperationNode::buildCssText): Deleted.
(WebCore::CSSCalcOperationNode::customCSSText const): Deleted.
(WebCore::CSSCalcExpressionNodeParser::parseValueTerm): Deleted.
(WebCore::CSSCalcExpressionNodeParser::parseValueMultiplicativeExpression): Deleted.
(WebCore::CSSCalcExpressionNodeParser::parseAdditiveValueExpression): Deleted.
(WebCore::CSSCalcExpressionNodeParser::parseMinMaxExpression): Deleted.
(WebCore::CSSCalcExpressionNodeParser::parseValueExpression): Deleted.
* css/CSSCalculationValue.h:
* css/CSSPrimitiveValue.cpp:
(WebCore::CSSPrimitiveValue::unitTypeString):
* css/CSSPrimitiveValue.h:
* platform/CalculationValue.cpp:
(WebCore::CalcExpressionNegation::evaluate const):
(WebCore::CalcExpressionNegation::operator== const):
(WebCore::CalcExpressionNegation::dump const):
(WebCore::operator==):
(WebCore::CalcExpressionInversion::evaluate const):
(WebCore::CalcExpressionInversion::dump const):
(WebCore::CalcExpressionInversion::operator== const):
(WebCore::CalcExpressionOperation::evaluate const):
(WebCore::operator<<):
* platform/CalculationValue.h:

LayoutTests:

Reviewed by Dean Jackson.

New results, mainly because of new simplification rules.

* css3/calc/cssom-expected.txt:
* css3/calc/simplification-expected.txt:
* css3/flexbox/flex-property-parsing-expected.txt:
* css3/flexbox/flex-property-parsing.html:
* css3/scroll-snap/scroll-snap-property-computed-style-expected.txt:
* css3/scroll-snap/scroll-snap-property-computed-style.js:
* css3/scroll-snap/scroll-snap-property-parsing-expected.txt:
* css3/scroll-snap/scroll-snap-property-parsing.js:
* css3/shapes/shape-outside/values/support/parsing-utils.js:
* fast/css-grid-layout/grid-columns-rows-get-set-expected.txt:
* fast/css-grid-layout/grid-columns-rows-get-set-multiple-expected.txt:
* fast/css-grid-layout/non-grid-columns-rows-get-set-expected.txt:
* fast/css-grid-layout/non-grid-columns-rows-get-set-multiple-expected.txt:
* fast/css-grid-layout/resources/grid-columns-rows-get-set-multiple.js:
* fast/css-grid-layout/resources/grid-columns-rows-get-set.js:
* fast/css-grid-layout/resources/non-grid-columns-rows-get-set-multiple.js:
* fast/css-grid-layout/resources/non-grid-columns-rows-get-set.js:
* fast/css/calc-parsing-expected.txt: Added.
* fast/css/calc-parsing-limits-expected.txt: Added.
* fast/css/calc-parsing-limits.html: Added.
* fast/css/calc-parsing.html: Added.
* fast/css/calc-with-angle-time-frequency-expected.txt:
* fast/css/calc-with-angle-time-frequency.html:
* fast/css/column-width-calculated-value-expected.txt:
* fast/css/column-width-calculated-value.html:
* fast/css/turn-angle-double-expected.txt:
* fast/css/turn-angle-double.html:
* fast/shapes/parsing/parsing-shape-outside-expected.txt:
* fast/shapes/parsing/parsing-test-utils.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@253079 268f45cc-cd09-0410-ab3c-d52691b4dbfc

77 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/calc/cssom-expected.txt
LayoutTests/css3/calc/simplification-expected.txt
LayoutTests/css3/flexbox/flex-property-parsing-expected.txt
LayoutTests/css3/flexbox/flex-property-parsing.html
LayoutTests/css3/scroll-snap/scroll-snap-property-computed-style-expected.txt
LayoutTests/css3/scroll-snap/scroll-snap-property-computed-style.js
LayoutTests/css3/scroll-snap/scroll-snap-property-parsing-expected.txt
LayoutTests/css3/scroll-snap/scroll-snap-property-parsing.js
LayoutTests/css3/shapes/shape-outside/values/support/parsing-utils.js
LayoutTests/fast/css-grid-layout/grid-columns-rows-get-set-expected.txt
LayoutTests/fast/css-grid-layout/grid-columns-rows-get-set-multiple-expected.txt
LayoutTests/fast/css-grid-layout/non-grid-columns-rows-get-set-expected.txt
LayoutTests/fast/css-grid-layout/non-grid-columns-rows-get-set-multiple-expected.txt
LayoutTests/fast/css-grid-layout/resources/grid-columns-rows-get-set-multiple.js
LayoutTests/fast/css-grid-layout/resources/grid-columns-rows-get-set.js
LayoutTests/fast/css-grid-layout/resources/non-grid-columns-rows-get-set-multiple.js
LayoutTests/fast/css-grid-layout/resources/non-grid-columns-rows-get-set.js
LayoutTests/fast/css/calc-parsing-expected.txt [new file with mode: 0644]
LayoutTests/fast/css/calc-parsing-limits-expected.txt [new file with mode: 0644]
LayoutTests/fast/css/calc-parsing-limits.html [new file with mode: 0644]
LayoutTests/fast/css/calc-parsing.html [new file with mode: 0644]
LayoutTests/fast/css/calc-with-angle-time-frequency-expected.txt
LayoutTests/fast/css/calc-with-angle-time-frequency.html
LayoutTests/fast/css/column-width-calculated-value-expected.txt
LayoutTests/fast/css/column-width-calculated-value.html
LayoutTests/fast/css/turn-angle-double-expected.txt
LayoutTests/fast/css/turn-angle-double.html
LayoutTests/fast/shapes/parsing/parsing-shape-outside-expected.txt
LayoutTests/fast/shapes/parsing/parsing-test-utils.js
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/css/css-align/gaps/column-gap-parsing-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-align/gaps/gap-parsing-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-align/gaps/grid-column-gap-parsing-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-align/gaps/grid-gap-parsing-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-align/gaps/grid-row-gap-parsing-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-align/gaps/row-gap-parsing-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-box/parsing/padding-valid-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/animation/shape-outside-interpolation-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-margin-003-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-circle-010-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-circle-011-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-ellipse-010-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-ellipse-011-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-inset-008-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-inset-009-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-polygon-006-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/calc-background-position-002-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/calc-background-position-003-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/calc-nesting-002-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/calc-serialization-002-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/calc-serialization-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/getComputedStyle-border-radius-001-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/getComputedStyle-border-radius-003-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-angle-computed-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-angle-serialize-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-length-percent-serialize-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-length-serialize-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-number-serialize-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-percentage-serialize-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-time-computed-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-values/minmax-time-serialize-expected.txt
LayoutTests/imported/w3c/web-platform-tests/svg/painting/parsing/stroke-dasharray-computed-expected.txt
LayoutTests/legacy-animation-engine/fast/css/calc-with-angle-time-frequency-expected.txt
LayoutTests/media/modern-media-controls/tracks-support/tracks-support-captions-offset-with-controls-bar-expected.txt
LayoutTests/media/modern-media-controls/tracks-support/tracks-support-captions-offset-with-controls-bar.html
Source/WebCore/ChangeLog
Source/WebCore/css/CSSCalculationValue.cpp
Source/WebCore/css/CSSCalculationValue.h
Source/WebCore/css/CSSPrimitiveValue.cpp
Source/WebCore/css/CSSPrimitiveValue.h
Source/WebCore/css/CSSUnits.cpp
Source/WebCore/css/CSSUnits.h
Source/WebCore/platform/CalculationValue.cpp
Source/WebCore/platform/CalculationValue.h
Source/WebCore/svg/SVGLengthValue.cpp
WebKit.xcworkspace/xcshareddata/xcschemes/All Source.xcscheme

index f22c238..4b5f313 100644 (file)
         * fast/text/international/unicode-bidi-other-neutrals-expected.html: Added.
         * fast/text/international/unicode-bidi-other-neutrals.html: Added.
 
+2019-12-03  Simon Fraser  <simon.fraser@apple.com>
+
+        calc() serialization doesn't match the spec
+        https://bugs.webkit.org/show_bug.cgi?id=203442
+
+        Reviewed by Dean Jackson.
+
+        New results, mainly because of new simplification rules.
+
+        * css3/calc/cssom-expected.txt:
+        * css3/calc/simplification-expected.txt:
+        * css3/flexbox/flex-property-parsing-expected.txt:
+        * css3/flexbox/flex-property-parsing.html:
+        * css3/scroll-snap/scroll-snap-property-computed-style-expected.txt:
+        * css3/scroll-snap/scroll-snap-property-computed-style.js:
+        * css3/scroll-snap/scroll-snap-property-parsing-expected.txt:
+        * css3/scroll-snap/scroll-snap-property-parsing.js:
+        * css3/shapes/shape-outside/values/support/parsing-utils.js:
+        * fast/css-grid-layout/grid-columns-rows-get-set-expected.txt:
+        * fast/css-grid-layout/grid-columns-rows-get-set-multiple-expected.txt:
+        * fast/css-grid-layout/non-grid-columns-rows-get-set-expected.txt:
+        * fast/css-grid-layout/non-grid-columns-rows-get-set-multiple-expected.txt:
+        * fast/css-grid-layout/resources/grid-columns-rows-get-set-multiple.js:
+        * fast/css-grid-layout/resources/grid-columns-rows-get-set.js:
+        * fast/css-grid-layout/resources/non-grid-columns-rows-get-set-multiple.js:
+        * fast/css-grid-layout/resources/non-grid-columns-rows-get-set.js:
+        * fast/css/calc-parsing-expected.txt: Added.
+        * fast/css/calc-parsing-limits-expected.txt: Added.
+        * fast/css/calc-parsing-limits.html: Added.
+        * fast/css/calc-parsing.html: Added.
+        * fast/css/calc-with-angle-time-frequency-expected.txt:
+        * fast/css/calc-with-angle-time-frequency.html:
+        * fast/css/column-width-calculated-value-expected.txt:
+        * fast/css/column-width-calculated-value.html:
+        * fast/css/turn-angle-double-expected.txt:
+        * fast/css/turn-angle-double.html:
+        * fast/shapes/parsing/parsing-shape-outside-expected.txt:
+        * fast/shapes/parsing/parsing-test-utils.js:
+
 2019-12-03  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         fast/events/touch/ios/passive-by-default-on-document-and-window.html is a flaky failure
index e94d293..886a3d0 100644 (file)
@@ -5,9 +5,9 @@ This tests calc() and the CSSOM
 100% => calc(100%)
 100% - 10px => calc(100% - 10px)
 10px + 10px * 5 => calc(60px)
-5px + 2em + 6in => calc((5px + 2em) + 6in)
+5px + 2em + 6in => calc(2em + 581px)
 100% - 10px / 2 => calc(100% - 5px)
-1px + 2em - 3rem + 4in => calc(((1px + 2em) - 3rem) + 4in)
+1px + 2em - 3rem + 4in => calc(2em + 385px - 3rem)
 100px * (1 + 2 * 3 - 4 / 5) => calc(620px)
 (100px) + 200px => calc(300px)
 ((((((((((100px)))))))))) => calc(100px)
index 16c44e2..5a892a9 100644 (file)
@@ -3,19 +3,19 @@ This tests parse time simplification in calc()
 100px * (25 + 5) => calc(3000px)
 100em * (25 - 5) => calc(2000em)
 100ex * (2 * 5 - 5) => calc(500ex)
-100cm * (5 - 4 / 5) => calc(420cm)
-100mm * (2.4 * 5 - 8 / 5) => calc(1040mm)
-100in * (6 * (5 - 4) / 8) => calc(75in)
+100cm * (5 - 4 / 5) => calc(15874.015748031496px)
+100mm * (2.4 * 5 - 8 / 5) => calc(3930.708661417323px)
+100in * (6 * (5 - 4) / 8) => calc(7200px)
 1px * (3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + 1/(1 + 1/(1 + 1/(1 + 1)))))))) => calc(3.1415926535583574px)
-100pc * 20 + 100rem * 10 - 100ch * 5 + 100pc => calc(((2000pc + 1000rem) - 500ch) + 100pc)
-((100px + 20 * 5px) * 10 - 5 * (10em * 5 + 10em)) * 2 => calc((2000px - 300em) * 2)
+100pc * 20 + 100rem * 10 - 100ch * 5 + 100pc => calc(-500ch + 33600px + 1000rem)
+((100px + 20 * 5px) * 10 - 5 * (10em * 5 + 10em)) * 2 => calc(2 * (-300em + 2000px))
 100px + 1in => calc(196px)
 10 * 10px + 0.5 * 2in => calc(196px)
-100px + 1in + 10% => calc(196px + 10%)
+100px + 1in + 10% => calc(10% + 196px)
 100px - 1in => calc(4px)
-50cm + 50cm => calc(100cm)
+50cm + 50cm => calc(3779.5275590551178px)
 50cm + 10in + 100mm => calc(3227.7165354330705px)
-100px + 1em => calc(100px + 1em)
-100px + 1em + 100px => calc((100px + 1em) + 100px)
+100px + 1em => calc(1em + 100px)
+100px + 1em + 100px => calc(1em + 200px)
 1em + 1rem => calc(1em + 1rem)
 
index 3f93757..72953e8 100644 (file)
@@ -69,10 +69,10 @@ PASS flexitem.style.flex is "1.75 2 3px"
 PASS getComputedStyle(flexitem).flex is "1.75 2 3px"
 PASS flexitem.style.flex is "1 2 3px"
 PASS getComputedStyle(flexitem).flex is "1 2 3px"
-PASS flexitem.style.flex is "4 3 calc(20px + 40%)"
-PASS getComputedStyle(flexitem).flex is "4 3 calc(20px + 40%)"
-PASS flexitem.style.flex is "1 2 calc(10px + 50%)"
-PASS getComputedStyle(flexitem).flex is "1 2 calc(10px + 50%)"
+PASS flexitem.style.flex is "4 3 calc(40% + 20px)"
+PASS getComputedStyle(flexitem).flex is "4 3 calc(40% + 20px)"
+PASS flexitem.style.flex is "1 2 calc(50% + 10px)"
+PASS getComputedStyle(flexitem).flex is "1 2 calc(50% + 10px)"
 PASS flexitem.style.flex is "0 0 auto"
 PASS getComputedStyle(flexitem).flex is "0 0 auto"
 PASS flexitem.style.flex is "0 1 auto"
index 3ddb4c7..dd40b01 100644 (file)
@@ -150,12 +150,12 @@ shouldBeEqualToString('flexitem.style.flex', '1 2 3px');
 shouldBeEqualToString('getComputedStyle(flexitem).flex', '1 2 3px');
 
 flexitem.style.flex = 'calc(20px + 40%) 4 3';
-shouldBeEqualToString('flexitem.style.flex', '4 3 calc(20px + 40%)');
-shouldBeEqualToString('getComputedStyle(flexitem).flex', '4 3 calc(20px + 40%)');
+shouldBeEqualToString('flexitem.style.flex', '4 3 calc(40% + 20px)');
+shouldBeEqualToString('getComputedStyle(flexitem).flex', '4 3 calc(40% + 20px)');
 
 flexitem.style.flex = '1 2 calc(10px + 50%)';
-shouldBeEqualToString('flexitem.style.flex', '1 2 calc(10px + 50%)');
-shouldBeEqualToString('getComputedStyle(flexitem).flex', '1 2 calc(10px + 50%)');
+shouldBeEqualToString('flexitem.style.flex', '1 2 calc(50% + 10px)');
+shouldBeEqualToString('getComputedStyle(flexitem).flex', '1 2 calc(50% + 10px)');
 
 flexitem.style.flex = 'auto 0 0';
 shouldBeEqualToString('flexitem.style.flex', '0 0 auto');
index 5f4b45d..50e3134 100644 (file)
@@ -127,11 +127,11 @@ PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-rig
 PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-bottom') is '20%'
 
 calc expression: `calc(50px + 10%) 20px`
-PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding') is 'calc(50px + 10%) 20px'
-PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-top') is 'calc(50px + 10%)'
+PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding') is 'calc(10% + 50px) 20px'
+PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-top') is 'calc(10% + 50px)'
 PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-left') is '20px'
 PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-right') is '20px'
-PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-bottom') is 'calc(50px + 10%)'
+PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding-bottom') is 'calc(10% + 50px)'
 
 various units: `1em 5mm 2in 4cm`
 PASS window.getComputedStyle(document.body).getPropertyValue('scroll-padding') is '16px 18.89763832092285px 192px 151.1811065673828px'
index 672fc60..c360ef3 100644 (file)
@@ -59,7 +59,7 @@ testComputedScrollSnapRule("single length", "scroll-padding", "10px", "10px", {
 testComputedScrollSnapRule("two percentages", "scroll-padding", "10% 20%", "10% 20%", { top: "10%", left: "20%", right: "20%", bottom: "10%" });
 testComputedScrollSnapRule("three lengths", "scroll-padding", "1px 2px 3px", "1px 2px 3px", { top: "1px", left: "2px", right: "2px", bottom: "3px" });
 testComputedScrollSnapRule("four values", "scroll-padding", "50px 10% 20% 50px", "50px 10% 20% 50px", { top: "50px", left: "50px", right: "10%", bottom: "20%" });
-testComputedScrollSnapRule("calc expression", "scroll-padding", "calc(50px + 10%) 20px", "calc(50px + 10%) 20px", { top: "calc(50px + 10%)", left: "20px", right: "20px", bottom: "calc(50px + 10%)" });
+testComputedScrollSnapRule("calc expression", "scroll-padding", "calc(50px + 10%) 20px", "calc(10% + 50px) 20px", { top: "calc(10% + 50px)", left: "20px", right: "20px", bottom: "calc(10% + 50px)" });
 testComputedScrollSnapRule("various units", "scroll-padding", "1em 5mm 2in 4cm", "16px 18.89763832092285px 192px 151.1811065673828px", { top: "16px", left: "151.1811065673828px", right: "18.89763832092285px", bottom: "192px" });
 testComputedScrollSnapRule("subpixel values", "scroll-padding", "10.4375px 6.5px", "10.4375px 6.5px", { top: "10.4375px", left: "6.5px", right: "6.5px", bottom: "10.4375px" });
 
index 23d7a26..73849ff 100644 (file)
@@ -64,10 +64,10 @@ PASS cssRule.type is 1
 PASS declaration.length is 4
 PASS declaration.getPropertyValue('scroll-padding') is '50px 10% 20% 50px'
 
-calc expression : calc(50px + 10%) 20px
+calc expression : calc(10% + 50px) 20px
 PASS cssRule.type is 1
 PASS declaration.length is 4
-PASS declaration.getPropertyValue('scroll-padding') is 'calc(50px + 10%) 20px'
+PASS declaration.getPropertyValue('scroll-padding') is 'calc(10% + 50px) 20px'
 
 various units : 1em 5mm 2in 4cm
 PASS cssRule.type is 1
index 86e8b31..513d797 100644 (file)
@@ -32,7 +32,7 @@ testScrollSnapRule("single length", "scroll-padding", "10px", "10px", 4);
 testScrollSnapRule("two percentages", "scroll-padding", "10% 20%", "10% 20%", 4);
 testScrollSnapRule("three lengths", "scroll-padding", "1px 2px 3px", "1px 2px 3px", 4);
 testScrollSnapRule("four values", "scroll-padding", "50px 10% 20% 50px", "50px 10% 20% 50px", 4);
-testScrollSnapRule("calc expression", "scroll-padding", "calc(50px + 10%) 20px", "calc(50px + 10%) 20px", 4);
+testScrollSnapRule("calc expression", "scroll-padding", "calc(10% + 50px) 20px", "calc(10% + 50px) 20px", 4);
 testScrollSnapRule("various units", "scroll-padding", "1em 5mm 2in 4cm", "1em 5mm 2in 4cm", 4);
 testScrollSnapRule("subpixel values", "scroll-padding", "10.4375px 6.5px", "10.4375px 6.5px", 4);
 testScrollSnapRule("single length", "scroll-snap-margin", "10px", "10px", 4);
index 2e98c74..312df98 100644 (file)
@@ -831,7 +831,7 @@ var validPolygons = [
 
 // [test value, expected property value, expected computed style]
 var calcTestValues = [
-    ["calc(10in)", "calc(10in)", "960px"],
+    ["calc(10in)", "calc(960px)", "960px"],
     ["calc(10in + 20px)", "calc(980px)", "980px"],
     ["calc(30%)", "calc(30%)", "30%"],
     ["calc(100%/4)", "calc(25%)", "25%"],
@@ -841,8 +841,8 @@ var calcTestValues = [
     // the computed value of a ‘calc()’ expression can be represented as either a number or a tuple
     // of a dimension and a percentage.
     // http://www.w3.org/TR/css3-values/#calc-notation
-    ["calc(25%*3 - 10in)", "calc(75% - 10in)", ["calc(75% - 960px)", "calc(-960px + 75%)"]],
-    ["calc((12.5%*6 + 10in) / 4)", "calc((75% + 10in) / 4)", ["calc((75% + 960px) / 4)", "calc(240px + 18.75%)"]]
+    ["calc(25%*3 - 10in)", "calc(75% - 960px)", ["calc(75% - 960px)", "calc(-960px + 75%)"]],
+    ["calc((12.5%*6 + 10in) / 4)", "calc(0.25 * (75% + 960px))", ["calc(0.25 * (75% + 960px))", "calc(240px + 18.75%)"]]
 ]
 
 return {
index f71e54e..46c6782 100644 (file)
@@ -137,13 +137,13 @@ PASS element.style.gridTemplateRows is "calc(75px)"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "370px"
 PASS element.style.gridTemplateColumns is "calc(50% - 30px)"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-rows') is "135px"
-PASS element.style.gridTemplateRows is "calc(75px + 10%)"
+PASS element.style.gridTemplateRows is "calc(10% + 75px)"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "200px"
 PASS element.style.gridTemplateColumns is "minmax(25%, calc(30px))"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-rows') is "450px"
 PASS element.style.gridTemplateRows is "minmax(calc(75%), 40px)"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "110px"
-PASS element.style.gridTemplateColumns is "minmax(10%, calc(30px + 10%))"
+PASS element.style.gridTemplateColumns is "minmax(10%, calc(10% + 30px))"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-rows') is "200px"
 PASS element.style.gridTemplateRows is "minmax(calc(25% - 50px), 200px)"
 
index cf10b39..4e979bc 100644 (file)
@@ -77,7 +77,7 @@ PASS element.style.gridTemplateColumns is "calc(25px) calc(2em)"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-rows') is "0px 60px"
 PASS element.style.gridTemplateRows is "auto calc(10%)"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "345px 92px"
-PASS element.style.gridTemplateColumns is "calc(25px + 40%) minmax(min-content, calc(10% + 12px))"
+PASS element.style.gridTemplateColumns is "calc(40% + 25px) minmax(min-content, calc(10% + 12px))"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-rows') is "100px 0px"
 PASS element.style.gridTemplateRows is "minmax(calc(75% - 350px), max-content) auto"
 PASS getComputedStyle(element, '').getPropertyValue('grid-template-columns') is "0px 16px"
index 3dd0ef8..cc0890f 100644 (file)
@@ -31,7 +31,7 @@ PASS window.getComputedStyle(gridWithCalcComplex, '').getPropertyValue('grid-tem
 PASS window.getComputedStyle(gridWithCalcInsideMinMax, '').getPropertyValue('grid-template-columns') is "minmax(10%, 15px)"
 PASS window.getComputedStyle(gridWithCalcInsideMinMax, '').getPropertyValue('grid-template-rows') is "minmax(20px, 50%)"
 PASS window.getComputedStyle(gridWithCalcComplexInsideMinMax, '').getPropertyValue('grid-template-columns') is "minmax(10%, calc(50% + 15px))"
-PASS window.getComputedStyle(gridWithCalcComplexInsideMinMax, '').getPropertyValue('grid-template-rows') is "minmax(calc(20px + 10%), 50%)"
+PASS window.getComputedStyle(gridWithCalcComplexInsideMinMax, '').getPropertyValue('grid-template-rows') is "minmax(calc(10% + 20px), 50%)"
 PASS window.getComputedStyle(gridWithAutoInsideMinMax, '').getPropertyValue('grid-template-columns') is "minmax(auto, 20px)"
 PASS window.getComputedStyle(gridWithAutoInsideMinMax, '').getPropertyValue('grid-template-rows') is "minmax(min-content, auto)"
 
index b0fc5ad..92271c2 100644 (file)
@@ -32,9 +32,9 @@ PASS window.getComputedStyle(gridWithCalcCalc, '').getPropertyValue('grid-templa
 PASS window.getComputedStyle(gridWithCalcCalc, '').getPropertyValue('grid-template-rows') is "150px 75px"
 PASS window.getComputedStyle(gridWithCalcAndFixed, '').getPropertyValue('grid-template-columns') is "50% 80px"
 PASS window.getComputedStyle(gridWithCalcAndFixed, '').getPropertyValue('grid-template-rows') is "88px 25%"
-PASS window.getComputedStyle(gridWithCalcAndMinMax, '').getPropertyValue('grid-template-columns') is "calc(30px + 20%) minmax(min-content, 80px)"
+PASS window.getComputedStyle(gridWithCalcAndMinMax, '').getPropertyValue('grid-template-columns') is "calc(20% + 30px) minmax(min-content, 80px)"
 PASS window.getComputedStyle(gridWithCalcAndMinMax, '').getPropertyValue('grid-template-rows') is "minmax(25%, max-content) calc(10% - 7px)"
-PASS window.getComputedStyle(gridWithCalcInsideMinMax, '').getPropertyValue('grid-template-columns') is "minmax(calc(23px + 10%), 400px) 120px"
+PASS window.getComputedStyle(gridWithCalcInsideMinMax, '').getPropertyValue('grid-template-columns') is "minmax(calc(10% + 23px), 400px) 120px"
 PASS window.getComputedStyle(gridWithCalcInsideMinMax, '').getPropertyValue('grid-template-rows') is "150px minmax(5%, calc(50% - 125px))"
 PASS window.getComputedStyle(gridWithAutoInsideMinMax, '').getPropertyValue('grid-template-columns') is "minmax(auto, 20px) 10%"
 PASS window.getComputedStyle(gridWithAutoInsideMinMax, '').getPropertyValue('grid-template-rows') is "max-content minmax(min-content, auto)"
index 73e8069..a6bac94 100644 (file)
@@ -37,7 +37,7 @@ testGridTemplatesSetJSValues("auto 16em 22px", "56% 10em auto", "0px 160px 22px"
 testGridTemplatesSetJSValues("16em minmax(16px, 20px)", "minmax(10%, 15%) auto", "160px 20px", "90px 0px");
 testGridTemplatesSetJSValues("16em 2fr", "14fr auto", "160px 640px", "600px 0px");
 testGridTemplatesSetJSValues("calc(25px) calc(2em)", "auto calc(10%)", "25px 20px", "0px 60px", "calc(25px) calc(2em)", "auto calc(10%)");
-testGridTemplatesSetJSValues("calc(25px + 40%) minmax(min-content, calc(10% + 12px))", "minmax(calc(75% - 350px), max-content) auto", "345px 92px", "100px 0px", "calc(25px + 40%) minmax(min-content, calc(10% + 12px))", "minmax(calc(75% - 350px), max-content) auto");
+testGridTemplatesSetJSValues("calc(25px + 40%) minmax(min-content, calc(10% + 12px))", "minmax(calc(75% - 350px), max-content) auto", "345px 92px", "100px 0px", "calc(40% + 25px) minmax(min-content, calc(10% + 12px))", "minmax(calc(75% - 350px), max-content) auto");
 testGridTemplatesSetJSValues("auto minmax(16px, auto)", "minmax(auto, 15%) 10vw", "0px 16px", "90px 80px");
 
 debug("");
index 0a5959f..194c261 100644 (file)
@@ -70,9 +70,9 @@ testGridTemplatesSetJSValues(".0000fr", "0fr", "0px", "0px", "0fr", "0fr");
 debug("");
 debug("Test getting and setting grid-template-columns and grid-template-rows to calc() values through JS");
 testGridTemplatesSetJSValues("calc(150px)", "calc(75px)", "150px", "75px");
-testGridTemplatesSetJSValues("calc(50% - 30px)", "calc(75px + 10%)", "370px", "135px");
+testGridTemplatesSetJSValues("calc(50% - 30px)", "calc(10% + 75px)", "370px", "135px");
 testGridTemplatesSetJSValues("minmax(25%, calc(30px))", "minmax(calc(75%), 40px)", "200px", "450px", "minmax(25%, calc(30px))", "minmax(calc(75%), 40px)");
-testGridTemplatesSetJSValues("minmax(10%, calc(30px + 10%))", "minmax(calc(25% - 50px), 200px)", "110px", "200px", "minmax(10%, calc(30px + 10%))", "minmax(calc(25% - 50px), 200px)");
+testGridTemplatesSetJSValues("minmax(10%, calc(30px + 10%))", "minmax(calc(25% - 50px), 200px)", "110px", "200px", "minmax(10%, calc(10% + 30px))", "minmax(calc(25% - 50px), 200px)");
 
 debug("");
 debug("Test setting grid-template-columns and grid-template-rows to bad values through JS");
index dcf1ebf..966b64c 100644 (file)
@@ -15,8 +15,8 @@ testGridTemplatesValues(document.getElementById("gridWithFractionFraction"), "1f
 testGridTemplatesValues(document.getElementById("gridWithFractionMinMax"), "minmax(min-content, 45px) 2fr", "3fr minmax(14px, max-content)");
 testGridTemplatesValues(document.getElementById("gridWithCalcCalc"), "200px 100px", "150px 75px");
 testGridTemplatesValues(document.getElementById("gridWithCalcAndFixed"), "50% 80px", "88px 25%");
-testGridTemplatesValues(document.getElementById("gridWithCalcAndMinMax"), "calc(30px + 20%) minmax(min-content, 80px)", "minmax(25%, max-content) calc(10% - 7px)");
-testGridTemplatesValues(document.getElementById("gridWithCalcInsideMinMax"), "minmax(calc(23px + 10%), 400px) 120px", "150px minmax(5%, calc(50% - 125px))");
+testGridTemplatesValues(document.getElementById("gridWithCalcAndMinMax"), "calc(20% + 30px) minmax(min-content, 80px)", "minmax(25%, max-content) calc(10% - 7px)");
+testGridTemplatesValues(document.getElementById("gridWithCalcInsideMinMax"), "minmax(calc(10% + 23px), 400px) 120px", "150px minmax(5%, calc(50% - 125px))");
 testGridTemplatesValues(document.getElementById("gridWithAutoInsideMinMax"), "minmax(auto, 20px) 10%", "max-content minmax(min-content, auto)");
 
 debug("");
index 6ba2fce..e8f7073 100644 (file)
@@ -14,7 +14,7 @@ testGridTemplatesValues(document.getElementById("gridWithFraction"), "1fr", "2fr
 testGridTemplatesValues(document.getElementById("gridWithCalc"), "150px", "75px");
 testGridTemplatesValues(document.getElementById("gridWithCalcComplex"), "calc(50% + 150px)", "calc(65% + 75px)");
 testGridTemplatesValues(document.getElementById("gridWithCalcInsideMinMax"), "minmax(10%, 15px)", "minmax(20px, 50%)");
-testGridTemplatesValues(document.getElementById("gridWithCalcComplexInsideMinMax"), "minmax(10%, calc(50% + 15px))", "minmax(calc(20px + 10%), 50%)");
+testGridTemplatesValues(document.getElementById("gridWithCalcComplexInsideMinMax"), "minmax(10%, calc(50% + 15px))", "minmax(calc(10% + 20px), 50%)");
 testGridTemplatesValues(document.getElementById("gridWithAutoInsideMinMax"), "minmax(auto, 20px)", "minmax(min-content, auto)");
 
 debug("");
diff --git a/LayoutTests/fast/css/calc-parsing-expected.txt b/LayoutTests/fast/css/calc-parsing-expected.txt
new file mode 100644 (file)
index 0000000..0cf92bf
--- /dev/null
@@ -0,0 +1,57 @@
+Tests parsing of various valid and invalid calc expressions.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+testDiv.style["width"] = "calc(100px)"
+PASS testDiv.style['width'] is "calc(100px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "100px"
+
+testDiv.style["width"] = "max(100px + 200px)"
+PASS testDiv.style['width'] is "max(300px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "300px"
+
+testDiv.style["width"] = "max(100px , 200px)"
+PASS testDiv.style['width'] is "max(200px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "200px"
+
+testDiv.style["width"] = "max(100px,200px)"
+PASS testDiv.style['width'] is "max(200px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "200px"
+
+testDiv.style["width"] = "calc(100px, 200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "calc(100px  200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "calc(100px ( 200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "min(100px 200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "max(100px 200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "max(100px,, 200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "max(100px, , 200px)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "max(100px, 200px,)"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css/calc-parsing-limits-expected.txt b/LayoutTests/fast/css/calc-parsing-limits-expected.txt
new file mode 100644 (file)
index 0000000..230808c
--- /dev/null
@@ -0,0 +1,25 @@
+Tests parsing of various valid and invalid calc expressions.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+testDiv.style["width"] = "calc((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((100px))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"
+PASS testDiv.style['width'] is "calc(100px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "100px"
+
+testDiv.style["width"] = "calc((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((100px))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"
+PASS testDiv.style['width'] is "999px"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "999px"
+
+testDiv.style["width"] = "calc( 1000px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px - 1px)"
+PASS testDiv.style['width'] is "calc(898px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "898px"
+
+testDiv.style["width"] = "calc( 1000px / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01 / 1.01)"
+PASS testDiv.style['width'] is "calc(362.42644086767854px)"
+PASS window.getComputedStyle(testDiv).getPropertyValue('width') is "362.421875px"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css/calc-parsing-limits.html b/LayoutTests/fast/css/calc-parsing-limits.html
new file mode 100644 (file)
index 0000000..0bfb38a
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <script src="../../resources/js-test-pre.js"></script>
+        <div id="testDiv"></div>
+        <script>
+            description("Tests parsing of various valid and invalid calc expressions.");
+
+            var testDiv = document.getElementById("testDiv");
+            
+            function calcWithNestedParens(content, parenCount)
+            {
+                let result = "calc";
+                for (let i = 0; i < parenCount; ++i)
+                    result += '(';
+            
+                result += content;
+                for (let i = 0; i < parenCount; ++i)
+                    result += ')';
+
+                return result;
+            }
+            
+            function calcWithManySubtractions(a, b, parenCount)
+            {
+                let result = "calc( " + a;
+                for (let i = 0; i < parenCount; ++i)
+                    result += ' - ' + b;
+            
+                result += ')'
+                return result;
+            }
+
+            function calcWithManyDivisions(a, b, parenCount)
+            {
+                let result = "calc( " + a;
+                for (let i = 0; i < parenCount; ++i)
+                    result += ' / ' + b;
+            
+                result += ')'
+                return result;
+            }
+
+            function testExpression(expression, specifiedValue, computedValue)
+            {
+                debug('');
+                testDiv.style["width"] = '999px';
+
+                evalAndLog(`testDiv.style["width"] = "${expression}"`);
+                shouldBeEqualToString("testDiv.style['width']", `${specifiedValue}`);
+                shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('width')", `${computedValue}`);
+            }
+
+            testExpression(calcWithNestedParens('100px', 100), 'calc(100px)', '100px');
+            testExpression(calcWithNestedParens('100px', 102), '999px', '999px');
+
+            testExpression(calcWithManySubtractions('1000px', '1px', 102), 'calc(898px)', '898px');
+            testExpression(calcWithManyDivisions('1000px', '1.01', 102), 'calc(362.42644086767854px)', '362.421875px');
+        </script>
+        <script src="../../resources/js-test-post.js"></script>
+    </body>
+</html>
diff --git a/LayoutTests/fast/css/calc-parsing.html b/LayoutTests/fast/css/calc-parsing.html
new file mode 100644 (file)
index 0000000..b82121f
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+    <body>
+        <script src="../../resources/js-test-pre.js"></script>
+        <div id="testDiv"></div>
+        <script>
+            description("Tests parsing of various valid and invalid calc expressions.");
+
+            var testDiv = document.getElementById("testDiv");
+            
+            function testExpression(expression, specifiedValue, computedValue)
+            {
+                debug('');
+                testDiv.style["width"] = '999px';
+
+                evalAndLog(`testDiv.style["width"] = "${expression}"`);
+                shouldBeEqualToString("testDiv.style['width']", `${specifiedValue}`);
+                shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('width')", `${computedValue}`);
+            }
+
+            // Valid expressions.
+            testExpression('calc(100px)', 'calc(100px)', '100px');
+            testExpression('max(100px + 200px)', 'max(300px)', '300px');
+            testExpression('max(100px , 200px)', 'max(200px)', '200px');
+            testExpression('max(100px,200px)', 'max(200px)', '200px');
+
+            // Non-parsing expression.
+            testExpression('calc(100px, 200px)', '999px', '999px');
+            testExpression('calc(100px  200px)', '999px', '999px');
+
+            testExpression('calc(100px ( 200px)', '999px', '999px');
+
+            testExpression('min(100px 200px)', '999px', '999px');
+            testExpression('max(100px 200px)', '999px', '999px');
+            testExpression('max(100px,, 200px)', '999px', '999px');
+            testExpression('max(100px, , 200px)', '999px', '999px');
+            testExpression('max(100px, 200px,)', '999px', '999px');
+        </script>
+        <script src="../../resources/js-test-post.js"></script>
+    </body>
+</html>
index a93b4d5..73b1edf 100644 (file)
@@ -7,13 +7,13 @@ testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300deg/2))'
 PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(150deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(150deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300rad/2))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(150rad))"
+PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(8594.366926962348deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(8594.366926962348deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300grad/2))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(150grad))"
+PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(135deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(135deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(3turn/2))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(1.5turn))"
+PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(540deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(540deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(1turn + 10deg))'
 PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(370deg))"
@@ -22,13 +22,13 @@ testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300deg + 30deg))'
 PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(330deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(330deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(2turn + 1turn))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(3turn))"
+PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(1080deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(1080deg)"
 testDiv.style['transition-delay'] = 'calc(300ms/2)'
 PASS testDiv.style['transition-delay'] is "calc(150ms)"
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "0.15s"
 testDiv.style['transition-delay'] = 'calc(300s/2)'
-PASS testDiv.style['transition-delay'] is "calc(150s)"
+PASS testDiv.style['transition-delay'] is "calc(150000ms)"
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "150s"
 testDiv.style['transition-delay'] = 'calc(10s + 100ms)'
 PASS testDiv.style['transition-delay'] is "calc(10100ms)"
@@ -37,7 +37,7 @@ testDiv.style['transition-delay'] = 'calc(100ms + 10s)'
 PASS testDiv.style['transition-delay'] is "calc(10100ms)"
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "10.1s"
 testDiv.style['transition-delay'] = 'calc(4s + 1s)'
-PASS testDiv.style['transition-delay'] is "calc(5s)"
+PASS testDiv.style['transition-delay'] is "calc(5000ms)"
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "5s"
 testDiv.style['transition-delay'] = 'calc(4ms + 1ms)'
 PASS testDiv.style['transition-delay'] is "calc(5ms)"
index e92f241..49136ef 100644 (file)
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter')", "hue-rotate(150deg)");
 
             evalAndLog("testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300rad/2))'");
-            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(150rad))");
+            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(8594.366926962348deg))");
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter')", "hue-rotate(8594.366926962348deg)");
 
             evalAndLog("testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300grad/2))'");
-            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(150grad))");
+            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(135deg))");
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter')", "hue-rotate(135deg)");
 
             evalAndLog("testDiv.style['-webkit-filter'] = 'hue-rotate(calc(3turn/2))'");
-            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(1.5turn))");
+            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(540deg))");
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter')", "hue-rotate(540deg)");
 
             evalAndLog("testDiv.style['-webkit-filter'] = 'hue-rotate(calc(1turn + 10deg))'");
@@ -34,7 +34,7 @@
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter')", "hue-rotate(330deg)");
 
             evalAndLog("testDiv.style['-webkit-filter'] = 'hue-rotate(calc(2turn + 1turn))'");
-            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(3turn))");
+            shouldBeEqualToString("testDiv.style['-webkit-filter']", "hue-rotate(calc(1080deg))");
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter')", "hue-rotate(1080deg)");
 
             // Time units.
@@ -43,7 +43,7 @@
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('transition-delay')", "0.15s");
 
             evalAndLog("testDiv.style['transition-delay'] = 'calc(300s/2)'");
-            shouldBeEqualToString("testDiv.style['transition-delay']", "calc(150s)");
+            shouldBeEqualToString("testDiv.style['transition-delay']", "calc(150000ms)");
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('transition-delay')", "150s");
 
             evalAndLog("testDiv.style['transition-delay'] = 'calc(10s + 100ms)'");
@@ -55,7 +55,7 @@
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('transition-delay')", "10.1s");
 
             evalAndLog("testDiv.style['transition-delay'] = 'calc(4s + 1s)'");
-            shouldBeEqualToString("testDiv.style['transition-delay']", "calc(5s)");
+            shouldBeEqualToString("testDiv.style['transition-delay']", "calc(5000ms)");
             shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('transition-delay')", "5s");
 
             evalAndLog("testDiv.style['transition-delay'] = 'calc(4ms + 1ms)'");
index 62e3a1b..f77ed24 100644 (file)
@@ -3,10 +3,6 @@ Tests assigning a calculated value to 'column-width' CSS property.
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS testDiv.style['column-width'] is ""
-testDiv.style['column-width'] = 'calc(100px * 2)'
-PASS testDiv.style['column-width'] is "calc(200px)"
-PASS window.getComputedStyle(testDiv).getPropertyValue('column-width') is "200px"
 0px is not a valid value
 testDiv.style['column-width'] = 'calc(0px * 2)'
 PASS testDiv.style['column-width'] is "calc(0px)"
index 6324f8f..d4c122a 100644 (file)
@@ -7,10 +7,10 @@ description("Tests assigning a calculated value to 'column-width' CSS property."
 
 var testDiv = document.getElementById("testDiv");
 
-shouldBeEmptyString("testDiv.style['column-width']");
-evalAndLog("testDiv.style['column-width'] = 'calc(100px * 2)'");
-shouldBeEqualToString("testDiv.style['column-width']", "calc(200px)");
-shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('column-width')", "200px");
+// shouldBeEmptyString("testDiv.style['column-width']");
+// evalAndLog("testDiv.style['column-width'] = 'calc(100px * 2)'");
+// shouldBeEqualToString("testDiv.style['column-width']", "calc(200px)");
+// shouldBeEqualToString("window.getComputedStyle(testDiv).getPropertyValue('column-width')", "200px");
 
 debug("0px is not a valid value");
 evalAndLog("testDiv.style['column-width'] = 'calc(0px * 2)'");
index 47e202c..19ed2e8 100644 (file)
@@ -7,7 +7,7 @@ PASS testDiv.style['-webkit-transform'] is ""
 testDiv.style['-webkit-transform'] = 'rotate(0.5turn)'
 PASS testDiv.style['-webkit-transform'] is "rotate(0.5turn)"
 testDiv.style['-webkit-transform'] = 'rotate(calc(2turn * 1.5))'
-PASS testDiv.style['-webkit-transform'] is "rotate(calc(3turn))"
+PASS testDiv.style['-webkit-transform'] is "rotate(calc(1080deg))"
 testDiv.style['-webkit-transform'] = 'rotate(calc(1turn * 2 + 2.5deg))'
 PASS testDiv.style['-webkit-transform'] is "rotate(calc(722.5deg))"
 PASS successfullyParsed is true
index 35984cc..f62cd41 100644 (file)
@@ -13,7 +13,7 @@ evalAndLog("testDiv.style['-webkit-transform'] = 'rotate(0.5turn)'");
 shouldBeEqualToString("testDiv.style['-webkit-transform']", "rotate(0.5turn)");
 
 evalAndLog("testDiv.style['-webkit-transform'] = 'rotate(calc(2turn * 1.5))'");
-shouldBeEqualToString("testDiv.style['-webkit-transform']", "rotate(calc(3turn))");
+shouldBeEqualToString("testDiv.style['-webkit-transform']", "rotate(calc(1080deg))");
 
 evalAndLog("testDiv.style['-webkit-transform'] = 'rotate(calc(1turn * 2 + 2.5deg))'");
 shouldBeEqualToString("testDiv.style['-webkit-transform']", "rotate(calc(722.5deg))");
index 1a60a8b..0876ed5 100644 (file)
@@ -65,9 +65,9 @@ PASS getCSSText("-webkit-shape-outside", "inset(10px 20px 30px 40px round 5px 6p
 PASS getComputedStyleValue("-webkit-shape-outside", "inset(10px 20px 30px 40px round 5px 6px 7px 8px / 50px 60px 70px 80px)") is "inset(10px 20px 30px 40px round 5px 6px 7px 8px / 50px 60px 70px 80px)"
 PASS getCSSText("-webkit-shape-outside", "inset(10px round 0px / 10px)") is "inset(10px round 0px / 10px)"
 PASS getComputedStyleValue("-webkit-shape-outside", "inset(10px round 0px / 10px)") is "inset(10px round 0px / 10px)"
-PASS getCSSText("-webkit-shape-outside", "inset(calc(25%*3 - 10in) 0 0 0)") is "inset(calc(75% - 10in) 0px 0px)"
+PASS getCSSText("-webkit-shape-outside", "inset(calc(25%*3 - 10in) 0 0 0)") is "inset(calc(75% - 960px) 0px 0px)"
 PASS getComputedStyleValue("-webkit-shape-outside", "inset(calc(25%*3 - 10in) 0 0 0)") is "inset(calc(75% - 960px) 0px 0px)"
-PASS getCSSText("-webkit-shape-outside", "inset(10px 10px 10px 10px round calc(25%*3 - 10in))") is "inset(10px round calc(75% - 10in))"
+PASS getCSSText("-webkit-shape-outside", "inset(10px 10px 10px 10px round calc(25%*3 - 10in))") is "inset(10px round calc(75% - 960px))"
 PASS getComputedStyleValue("-webkit-shape-outside", "inset(10px 10px 10px 10px round calc(25%*3 - 10in))") is "inset(10px round calc(75% - 960px))"
 PASS getCSSText("-webkit-shape-outside", "circle()") is "circle(at 50% 50%)"
 PASS getComputedStyleValue("-webkit-shape-outside", "circle()") is "circle(at 50% 50%)"
@@ -135,9 +135,9 @@ PASS getCSSText("-webkit-shape-outside", "polygon(evenodd, 10px 20px, 30px 40px,
 PASS getComputedStyleValue("-webkit-shape-outside", "polygon(evenodd, 10px 20px, 30px 40px, 40px 50px)") is "polygon(evenodd, 10px 20px, 30px 40px, 40px 50px)"
 PASS getCSSText("-webkit-shape-outside", "polygon(nonzero, 10px 20px, 30px 40px, 40px 50px)") is "polygon(10px 20px, 30px 40px, 40px 50px)"
 PASS getComputedStyleValue("-webkit-shape-outside", "polygon(nonzero, 10px 20px, 30px 40px, 40px 50px)") is "polygon(10px 20px, 30px 40px, 40px 50px)"
-PASS getCSSText("-webkit-shape-outside", "polygon(10px calc(10in))") is "polygon(10px calc(10in))"
+PASS getCSSText("-webkit-shape-outside", "polygon(10px calc(10in))") is "polygon(10px calc(960px))"
 PASS getComputedStyleValue("-webkit-shape-outside", "polygon(10px calc(10in))") is "polygon(10px 960px)"
-PASS getCSSText("-webkit-shape-outside", "polygon(calc(10in) 10px)") is "polygon(calc(10in) 10px)"
+PASS getCSSText("-webkit-shape-outside", "polygon(calc(10in) 10px)") is "polygon(calc(960px) 10px)"
 PASS getComputedStyleValue("-webkit-shape-outside", "polygon(calc(10in) 10px)") is "polygon(960px 10px)"
 PASS getCSSText("-webkit-shape-outside", "content-box") is "content-box"
 PASS getComputedStyleValue("-webkit-shape-outside", "content-box") is "content-box"
index 35c58b3..5b55733 100644 (file)
@@ -38,8 +38,8 @@ var validShapeValues = [
     "inset(10px 20px 30px 40px round 5px 6px 7px 8px / 50px 60px 70px 80px)",
     "inset(10px round 0px / 10px)",
 
-    ["inset(calc(25%*3 - 10in) 0 0 0)", "inset(calc(75% - 10in) 0px 0px)", "inset(calc(75% - 960px) 0px 0px)"],
-    ["inset(10px 10px 10px 10px round calc(25%*3 - 10in))", "inset(10px round calc(75% - 10in))", "inset(10px round calc(75% - 960px))"],
+    ["inset(calc(25%*3 - 10in) 0 0 0)", "inset(calc(75% - 960px) 0px 0px)", "inset(calc(75% - 960px) 0px 0px)"],
+    ["inset(10px 10px 10px 10px round calc(25%*3 - 10in))", "inset(10px round calc(75% - 960px))", "inset(10px round calc(75% - 960px))"],
 
     ["circle()", "circle(at 50% 50%)", "circle(at 50% 50%)"],
     ["circle(farthest-side)", "circle(farthest-side at 50% 50%)", "circle(farthest-side at 50% 50%)"],
@@ -77,8 +77,8 @@ var validShapeValues = [
     ["polygon(10px 20px, 30px 40px, 40px 50px)", "polygon(10px 20px, 30px 40px, 40px 50px)"],
     ["polygon(evenodd, 10px 20px, 30px 40px, 40px 50px)", "polygon(evenodd, 10px 20px, 30px 40px, 40px 50px)"],
     ["polygon(nonzero, 10px 20px, 30px 40px, 40px 50px)", "polygon(10px 20px, 30px 40px, 40px 50px)"],
-    ["polygon(10px calc(10in))", "polygon(10px calc(10in))", "polygon(10px 960px)"],
-    ["polygon(calc(10in) 10px)", "polygon(calc(10in) 10px)", "polygon(960px 10px)"],
+    ["polygon(10px calc(10in))", "polygon(10px calc(960px))", "polygon(10px 960px)"],
+    ["polygon(calc(10in) 10px)", "polygon(calc(960px) 10px)", "polygon(960px 10px)"],
 
     "content-box",
     "padding-box",
index 12b84a9..f4a36f9 100644 (file)
         * resources/import-expectations.json:
         * web-platform-tests/html/webappapis/scripting/events/*: Updated.
 
+2019-12-03  Simon Fraser  <simon.fraser@apple.com>
+
+        calc() serialization doesn't match the spec
+        https://bugs.webkit.org/show_bug.cgi?id=203442
+        <rdar://problem/56639402>
+
+        Reviewed by Dean Jackson.
+
+        New results. Current WPT don't reflect the new serialization rules yet, so we expect
+        some failing results here.
+
+        * web-platform-tests/css/css-align/gaps/column-gap-parsing-001-expected.txt:
+        * web-platform-tests/css/css-align/gaps/gap-parsing-001-expected.txt:
+        * web-platform-tests/css/css-align/gaps/grid-column-gap-parsing-001-expected.txt:
+        * web-platform-tests/css/css-align/gaps/grid-gap-parsing-001-expected.txt:
+        * web-platform-tests/css/css-align/gaps/grid-row-gap-parsing-001-expected.txt:
+        * web-platform-tests/css/css-align/gaps/row-gap-parsing-001-expected.txt:
+        * web-platform-tests/css/css-box/parsing/padding-valid-expected.txt:
+        * web-platform-tests/css/css-shapes/animation/shape-outside-interpolation-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-margin-003-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-circle-010-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-circle-011-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-ellipse-010-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-ellipse-011-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-inset-008-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-inset-009-expected.txt:
+        * web-platform-tests/css/css-shapes/shape-outside/values/shape-outside-polygon-006-expected.txt:
+        * web-platform-tests/css/css-values/calc-background-position-002-expected.txt:
+        * web-platform-tests/css/css-values/calc-background-position-003-expected.txt:
+        * web-platform-tests/css/css-values/calc-nesting-002-expected.txt:
+        * web-platform-tests/css/css-values/calc-serialization-002-expected.txt:
+        * web-platform-tests/css/css-values/calc-serialization-expected.txt:
+        * web-platform-tests/css/css-values/minmax-angle-computed-expected.txt:
+        * web-platform-tests/css/css-values/minmax-angle-serialize-expected.txt:
+        * web-platform-tests/css/css-values/minmax-length-percent-serialize-expected.txt:
+        * web-platform-tests/css/css-values/minmax-length-serialize-expected.txt:
+        * web-platform-tests/css/css-values/minmax-percentage-serialize-expected.txt:
+        * web-platform-tests/css/css-values/minmax-time-computed-expected.txt:
+        * web-platform-tests/css/css-values/minmax-time-serialize-expected.txt:
+
 2019-12-03  Zan Dobersek  <zdobersek@igalia.com>
 
         Implement createImageBitmap(OffscreenCanvas)
index daaa5e6..1f513f6 100644 (file)
@@ -5,7 +5,7 @@ PASS column-gap accepts em
 PASS column-gap accepts vw 
 PASS column-gap accepts percentage 
 PASS column-gap accepts calc() 
-PASS column-gap accepts calc() mixing fixed and percentage values 
+FAIL column-gap accepts calc() mixing fixed and percentage values assert_equals: expected "calc(5px + 10%)" but got "calc(10% + 5px)"
 PASS Initial column-gap is 'normal' 
 PASS Initial column-gap is 'normal' 2 
 PASS Initial inherited column-gap is 'normal' 
index 31301ea..2b958cb 100644 (file)
@@ -10,7 +10,7 @@ PASS gap accepts vw and vh
 PASS gap accepts percentage 
 PASS gap accepts percentage 2 
 PASS gap accepts calc() 
-PASS gap accepts calc() mixing fixed and percentage values 
+FAIL gap accepts calc() mixing fixed and percentage values assert_equals: expected "calc(5px + 10%)" but got "calc(10% + 5px)"
 PASS gap accepts calc() 2 
 PASS Initial gap is 'normal' 
 PASS Initial gap is 'normal' 2 
index 9ae926a..4e0db65 100644 (file)
@@ -5,7 +5,7 @@ PASS grid-column-gap accepts em
 PASS grid-column-gap accepts vw 
 PASS grid-column-gap accepts percentage 
 PASS grid-column-gap accepts calc() 
-PASS grid-column-gap accepts calc() mixing fixed and percentage values 
+FAIL grid-column-gap accepts calc() mixing fixed and percentage values assert_equals: expected "calc(5px + 10%)" but got "calc(10% + 5px)"
 PASS Initial grid-column-gap is 'normal' 
 PASS Initial grid-column-gap is 'normal' 2 
 PASS Initial inherited grid-column-gap is 'normal' 
index bd0d427..36a5b45 100644 (file)
@@ -10,7 +10,7 @@ PASS grid-gap accepts vw and vh
 PASS grid-gap accepts percentage 
 PASS grid-gap accepts percentage 2 
 PASS grid-gap accepts calc() 
-PASS grid-gap accepts calc() mixing fixed and percentage values 
+FAIL grid-gap accepts calc() mixing fixed and percentage values assert_equals: expected "calc(5px + 10%)" but got "calc(10% + 5px)"
 PASS grid-gap accepts calc() 2 
 PASS Initial grid-gap is 'normal' 
 PASS Initial grid-gap is 'normal' 2 
index a2978d2..04dc5b2 100644 (file)
@@ -5,7 +5,7 @@ PASS grid-row-gap accepts em
 PASS grid-row-gap accepts vw 
 PASS grid-row-gap accepts percentage 
 PASS grid-row-gap accepts calc() 
-PASS grid-row-gap accepts calc() mixing fixed and percentage values 
+FAIL grid-row-gap accepts calc() mixing fixed and percentage values assert_equals: expected "calc(5px + 10%)" but got "calc(10% + 5px)"
 PASS Initial grid-row-gap is 'normal' 
 PASS Initial grid-row-gap is 'normal' 2 
 PASS Initial inherited grid-row-gap is 'normal' 
index a5a6dbe..3bf1c1a 100644 (file)
@@ -5,7 +5,7 @@ PASS row-gap accepts em
 PASS row-gap accepts vw 
 PASS row-gap accepts percentage 
 PASS row-gap accepts calc() 
-PASS row-gap accepts calc() mixing fixed and percentage values 
+FAIL row-gap accepts calc() mixing fixed and percentage values assert_equals: expected "calc(5px + 10%)" but got "calc(10% + 5px)"
 PASS Initial row-gap is 'normal' 
 PASS Initial row-gap is 'normal' 2 
 PASS Initial inherited row-gap is 'normal' 
index ad63d2e..2a552de 100644 (file)
@@ -9,5 +9,5 @@ PASS e.style['padding-left'] = "40px" should set the property value
 PASS e.style['padding'] = "20%" should set the property value 
 PASS e.style['padding'] = "10px 20% 30% 40px" should set the property value 
 PASS e.style['padding-right'] = "20%" should set the property value 
-PASS e.style['padding-right'] = "calc(2em + 3%)" should set the property value 
+FAIL e.style['padding-right'] = "calc(2em + 3%)" should set the property value assert_equals: serialization should be canonical expected "calc(2em + 3%)" but got "calc(3% + 2em)"
 
index 83aed77..eff8045 100644 (file)
@@ -255,16 +255,16 @@ PASS Web Animations: property <shape-outside> from [none] to [ellipse(100% 100%
 PASS Web Animations: property <shape-outside> from [none] to [ellipse(100% 100% at 0% 0%)] at (0.6) should be [ellipse(100% 100% at 0% 0%)] 
 PASS Web Animations: property <shape-outside> from [none] to [ellipse(100% 100% at 0% 0%)] at (1) should be [ellipse(100% 100% at 0% 0%)] 
 PASS Web Animations: property <shape-outside> from [none] to [ellipse(100% 100% at 0% 0%)] at (1.5) should be [ellipse(100% 100% at 0% 0%)] 
-FAIL CSS Transitions: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( - 20px + 100 % ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
-FAIL CSS Transitions: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( - 25px + 100 % ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
-FAIL CSS Transitions: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( - 30px + 100 % ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
-FAIL CSS Transitions with transition: all: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( - 20px + 100 % ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
-FAIL CSS Transitions with transition: all: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( - 25px + 100 % ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
-FAIL CSS Transitions with transition: all: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( - 30px + 100 % ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
-FAIL CSS Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( - 20px + 100 % ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
-FAIL CSS Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( - 25px + 100 % ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
-FAIL CSS Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( - 30px + 100 % ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
-FAIL Web Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( - 20px + 100 % ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
-FAIL Web Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( - 25px + 100 % ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
-FAIL Web Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( - 30px + 100 % ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
+FAIL CSS Transitions: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( 100 % - 20px ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
+FAIL CSS Transitions: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( 100 % - 25px ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
+FAIL CSS Transitions: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( 100 % - 30px ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
+FAIL CSS Transitions with transition: all: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( 100 % - 20px ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
+FAIL CSS Transitions with transition: all: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( 100 % - 25px ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
+FAIL CSS Transitions with transition: all: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( 100 % - 30px ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
+FAIL CSS Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( 100 % - 20px ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
+FAIL CSS Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( 100 % - 25px ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
+FAIL CSS Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( 100 % - 30px ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
+FAIL Web Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.25) should be [circle(30% at 90% calc(-20px + 100%))] assert_equals: expected "circle ( 30 % at 90 % calc ( 100 % - 20px ) ) " but got "circle ( 30 % at 90 % calc ( ( ( 100 % - 15px ) * 0.75 ) + ( ( 100 % - 35px ) * 0.25 ) ) ) "
+FAIL Web Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.5) should be [circle(35% at 85% calc(-25px + 100%))] assert_equals: expected "circle ( 35 % at 85 % calc ( 100 % - 25px ) ) " but got "circle ( 35 % at 85 % calc ( ( ( 100 % - 15px ) * 0.5 ) + ( ( 100 % - 35px ) * 0.5 ) ) ) "
+FAIL Web Animations: property <shape-outside> from [circle(25% at right 5% bottom 15px)] to [circle(45% at right 25% bottom 35px)] at (0.75) should be [circle(40% at 80% calc(-30px + 100%))] assert_equals: expected "circle ( 40 % at 80 % calc ( 100 % - 30px ) ) " but got "circle ( 40 % at 80 % calc ( ( ( 100 % - 15px ) * 0.25 ) + ( ( 100 % - 35px ) * 0.75 ) ) ) "
 
index 9068c62..8427038 100644 (file)
@@ -1,16 +1,16 @@
 
-PASS calc(10in) - inline style 
+FAIL calc(10in) - inline style assert_equals: expected "calc(10in)" but got "calc(960px)"
 PASS calc(10in + 20px) - inline style 
 PASS calc(30%) - inline style 
 PASS calc(100%/4) - inline style 
 PASS calc(25%*3) - inline style 
-PASS calc(25%*3 - 10in) - inline style 
-PASS calc((12.5%*6 + 10in) / 4) - inline style 
+FAIL calc(25%*3 - 10in) - inline style assert_equals: expected "calc(75% - 10in)" but got "calc(75% - 960px)"
+FAIL calc((12.5%*6 + 10in) / 4) - inline style assert_equals: expected "calc((75% + 10in) / 4)" but got "calc(0.25 * (75% + 960px))"
 PASS calc(10in) - computed style 
 PASS calc(10in + 20px) - computed style 
 PASS calc(30%) - computed style 
 PASS calc(100%/4) - computed style 
 PASS calc(25%*3) - computed style 
 PASS calc(25%*3 - 10in) - computed style 
-PASS calc((12.5%*6 + 10in) / 4) - computed style 
+FAIL calc((12.5%*6 + 10in) / 4) - computed style assert_in_array: value "calc(0.25 * (75% + 960px))" not in array ["calc((75% + 960px) / 4)", "calc(18.75% + 240px)"]
 
index 59ab3a1..3299665 100644 (file)
@@ -1,16 +1,16 @@
 
-PASS circle(calc(10in)) - inline style 
+FAIL circle(calc(10in)) - inline style assert_equals: expected "circle(calc(10in) at 50% 50%)" but got "circle(calc(960px) at 50% 50%)"
 PASS circle(calc(10in + 20px)) - inline style 
 PASS circle(calc(30%)) - inline style 
 PASS circle(calc(100%/4)) - inline style 
 PASS circle(calc(25%*3)) - inline style 
-PASS circle(calc(25%*3 - 10in)) - inline style 
-PASS circle(calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL circle(calc(25%*3 - 10in)) - inline style assert_equals: expected "circle(calc(75% - 10in) at 50% 50%)" but got "circle(calc(75% - 960px) at 50% 50%)"
+FAIL circle(calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "circle(calc((75% + 10in) / 4) at 50% 50%)" but got "circle(calc(0.25 * (75% + 960px)) at 50% 50%)"
 PASS circle(calc(10in)) - computed style 
 PASS circle(calc(10in + 20px)) - computed style 
 PASS circle(calc(30%)) - computed style 
 PASS circle(calc(100%/4)) - computed style 
 PASS circle(calc(25%*3)) - computed style 
 PASS circle(calc(25%*3 - 10in)) - computed style 
-PASS circle(calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL circle(calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "circle(calc(0.25 * (75% + 960px)) at 50% 50%)" not in array ["circle(calc((75% + 960px) / 4) at 50% 50%)", "circle(calc(18.75% + 240px) at 50% 50%)"]
 
index 8f56d93..9846172 100644 (file)
@@ -1,16 +1,16 @@
 
-PASS circle(at calc(10in)) - inline style 
+FAIL circle(at calc(10in)) - inline style assert_equals: expected "circle(at calc(10in) 50%)" but got "circle(at calc(960px) 50%)"
 PASS circle(at calc(10in + 20px)) - inline style 
 PASS circle(at calc(30%)) - inline style 
 PASS circle(at calc(100%/4)) - inline style 
 PASS circle(at calc(25%*3)) - inline style 
-PASS circle(at calc(25%*3 - 10in)) - inline style 
-PASS circle(at calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL circle(at calc(25%*3 - 10in)) - inline style assert_equals: expected "circle(at calc(75% - 10in) 50%)" but got "circle(at calc(75% - 960px) 50%)"
+FAIL circle(at calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "circle(at calc((75% + 10in) / 4) 50%)" but got "circle(at calc(0.25 * (75% + 960px)) 50%)"
 PASS circle(at calc(10in)) - computed style 
 PASS circle(at calc(10in + 20px)) - computed style 
 PASS circle(at calc(30%)) - computed style 
 PASS circle(at calc(100%/4)) - computed style 
 PASS circle(at calc(25%*3)) - computed style 
 PASS circle(at calc(25%*3 - 10in)) - computed style 
-PASS circle(at calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL circle(at calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "circle(at calc(0.25 * (75% + 960px)) 50%)" not in array ["circle(at calc((75% + 960px) / 4) 50%)", "circle(at calc(18.75% + 240px) 50%)"]
 
index 1319e1c..635e062 100644 (file)
@@ -1,30 +1,30 @@
 
-PASS ellipse(farthest-side calc(10in)) - inline style 
+FAIL ellipse(farthest-side calc(10in)) - inline style assert_equals: expected "ellipse(farthest-side calc(10in) at 50% 50%)" but got "ellipse(farthest-side calc(960px) at 50% 50%)"
 PASS ellipse(farthest-side calc(10in + 20px)) - inline style 
 PASS ellipse(farthest-side calc(30%)) - inline style 
 PASS ellipse(farthest-side calc(100%/4)) - inline style 
 PASS ellipse(farthest-side calc(25%*3)) - inline style 
-PASS ellipse(farthest-side calc(25%*3 - 10in)) - inline style 
-PASS ellipse(farthest-side calc((12.5%*6 + 10in) / 4)) - inline style 
-PASS ellipse(calc(10in) calc(10in)) - inline style 
+FAIL ellipse(farthest-side calc(25%*3 - 10in)) - inline style assert_equals: expected "ellipse(farthest-side calc(75% - 10in) at 50% 50%)" but got "ellipse(farthest-side calc(75% - 960px) at 50% 50%)"
+FAIL ellipse(farthest-side calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "ellipse(farthest-side calc((75% + 10in) / 4) at 50% 50%)" but got "ellipse(farthest-side calc(0.25 * (75% + 960px)) at 50% 50%)"
+FAIL ellipse(calc(10in) calc(10in)) - inline style assert_equals: expected "ellipse(calc(10in) calc(10in) at 50% 50%)" but got "ellipse(calc(960px) calc(960px) at 50% 50%)"
 PASS ellipse(calc(10in + 20px) calc(10in + 20px)) - inline style 
 PASS ellipse(calc(30%) calc(30%)) - inline style 
 PASS ellipse(calc(100%/4) calc(100%/4)) - inline style 
 PASS ellipse(calc(25%*3) calc(25%*3)) - inline style 
-PASS ellipse(calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style 
-PASS ellipse(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL ellipse(calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style assert_equals: expected "ellipse(calc(75% - 10in) calc(75% - 10in) at 50% 50%)" but got "ellipse(calc(75% - 960px) calc(75% - 960px) at 50% 50%)"
+FAIL ellipse(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "ellipse(calc((75% + 10in) / 4) calc((75% + 10in) / 4) at 50% 50%)" but got "ellipse(calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)) at 50% 50%)"
 PASS ellipse(farthest-side calc(10in)) - computed style 
 PASS ellipse(farthest-side calc(10in + 20px)) - computed style 
 PASS ellipse(farthest-side calc(30%)) - computed style 
 PASS ellipse(farthest-side calc(100%/4)) - computed style 
 PASS ellipse(farthest-side calc(25%*3)) - computed style 
 PASS ellipse(farthest-side calc(25%*3 - 10in)) - computed style 
-PASS ellipse(farthest-side calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL ellipse(farthest-side calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "ellipse(farthest-side calc(0.25 * (75% + 960px)) at 50% 50%)" not in array ["ellipse(farthest-side calc((75% + 960px) / 4) at 50% 50%)", "ellipse(farthest-side calc(18.75% + 240px) at 50% 50%)"]
 PASS ellipse(calc(10in) calc(10in)) - computed style 
 PASS ellipse(calc(10in + 20px) calc(10in + 20px)) - computed style 
 PASS ellipse(calc(30%) calc(30%)) - computed style 
 PASS ellipse(calc(100%/4) calc(100%/4)) - computed style 
 PASS ellipse(calc(25%*3) calc(25%*3)) - computed style 
 PASS ellipse(calc(25%*3 - 10in) calc(25%*3 - 10in)) - computed style 
-PASS ellipse(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL ellipse(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "ellipse(calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)) at 50% 50%)" not in array ["ellipse(calc((75% + 960px) / 4) calc((75% + 960px) / 4) at 50% 50%)", "ellipse(calc(18.75% + 240px) calc(18.75% + 240px) at 50% 50%)"]
 
index 67f0cbc..d6760bb 100644 (file)
@@ -1,30 +1,30 @@
 
-PASS ellipse(at calc(10in) 50%) - inline style 
+FAIL ellipse(at calc(10in) 50%) - inline style assert_equals: expected "ellipse(at calc(10in) 50%)" but got "ellipse(at calc(960px) 50%)"
 PASS ellipse(at calc(10in + 20px) 50%) - inline style 
 PASS ellipse(at calc(30%) 50%) - inline style 
 PASS ellipse(at calc(100%/4) 50%) - inline style 
 PASS ellipse(at calc(25%*3) 50%) - inline style 
-PASS ellipse(at calc(25%*3 - 10in) 50%) - inline style 
-PASS ellipse(at calc((12.5%*6 + 10in) / 4) 50%) - inline style 
-PASS ellipse(closest-side farthest-side at calc(10in) calc(10in)) - inline style 
+FAIL ellipse(at calc(25%*3 - 10in) 50%) - inline style assert_equals: expected "ellipse(at calc(75% - 10in) 50%)" but got "ellipse(at calc(75% - 960px) 50%)"
+FAIL ellipse(at calc((12.5%*6 + 10in) / 4) 50%) - inline style assert_equals: expected "ellipse(at calc((75% + 10in) / 4) 50%)" but got "ellipse(at calc(0.25 * (75% + 960px)) 50%)"
+FAIL ellipse(closest-side farthest-side at calc(10in) calc(10in)) - inline style assert_equals: expected "ellipse(closest-side farthest-side at calc(10in) calc(10in))" but got "ellipse(closest-side farthest-side at calc(960px) calc(960px))"
 PASS ellipse(closest-side farthest-side at calc(10in + 20px) calc(10in + 20px)) - inline style 
 PASS ellipse(closest-side farthest-side at calc(30%) calc(30%)) - inline style 
 PASS ellipse(closest-side farthest-side at calc(100%/4) calc(100%/4)) - inline style 
 PASS ellipse(closest-side farthest-side at calc(25%*3) calc(25%*3)) - inline style 
-PASS ellipse(closest-side farthest-side at calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style 
-PASS ellipse(closest-side farthest-side at calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL ellipse(closest-side farthest-side at calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style assert_equals: expected "ellipse(closest-side farthest-side at calc(75% - 10in) calc(75% - 10in))" but got "ellipse(closest-side farthest-side at calc(75% - 960px) calc(75% - 960px))"
+FAIL ellipse(closest-side farthest-side at calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "ellipse(closest-side farthest-side at calc((75% + 10in) / 4) calc((75% + 10in) / 4))" but got "ellipse(closest-side farthest-side at calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)))"
 PASS ellipse(at calc(10in) 50%) - computed style 
 PASS ellipse(at calc(10in + 20px) 50%) - computed style 
 PASS ellipse(at calc(30%) 50%) - computed style 
 PASS ellipse(at calc(100%/4) 50%) - computed style 
 PASS ellipse(at calc(25%*3) 50%) - computed style 
 PASS ellipse(at calc(25%*3 - 10in) 50%) - computed style 
-PASS ellipse(at calc((12.5%*6 + 10in) / 4) 50%) - computed style 
+FAIL ellipse(at calc((12.5%*6 + 10in) / 4) 50%) - computed style assert_in_array: value "ellipse(at calc(0.25 * (75% + 960px)) 50%)" not in array ["ellipse(at calc((75% + 960px) / 4) 50%)", "ellipse(at calc(18.75% + 240px) 50%)"]
 PASS ellipse(closest-side farthest-side at calc(10in) calc(10in)) - computed style 
 PASS ellipse(closest-side farthest-side at calc(10in + 20px) calc(10in + 20px)) - computed style 
 PASS ellipse(closest-side farthest-side at calc(30%) calc(30%)) - computed style 
 PASS ellipse(closest-side farthest-side at calc(100%/4) calc(100%/4)) - computed style 
 PASS ellipse(closest-side farthest-side at calc(25%*3) calc(25%*3)) - computed style 
 PASS ellipse(closest-side farthest-side at calc(25%*3 - 10in) calc(25%*3 - 10in)) - computed style 
-PASS ellipse(closest-side farthest-side at calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL ellipse(closest-side farthest-side at calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "ellipse(closest-side farthest-side at calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)))" not in array ["ellipse(closest-side farthest-side at calc((75% + 960px) / 4) calc((75% + 960px) / 4))", "ellipse(closest-side farthest-side at calc(18.75% + 240px) calc(18.75% + 240px))"]
 
index ba03e49..6c62c9b 100644 (file)
@@ -1,30 +1,30 @@
 
-PASS inset(calc(10in)) - inline style 
+FAIL inset(calc(10in)) - inline style assert_equals: expected "inset(calc(10in))" but got "inset(calc(960px))"
 PASS inset(calc(10in + 20px)) - inline style 
 PASS inset(calc(30%)) - inline style 
 PASS inset(calc(100%/4)) - inline style 
 PASS inset(calc(25%*3)) - inline style 
-PASS inset(calc(25%*3 - 10in)) - inline style 
-PASS inset(calc((12.5%*6 + 10in) / 4)) - inline style 
-PASS inset(calc(10in) calc(10in)) - inline style 
+FAIL inset(calc(25%*3 - 10in)) - inline style assert_equals: expected "inset(calc(75% - 10in))" but got "inset(calc(75% - 960px))"
+FAIL inset(calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "inset(calc((75% + 10in) / 4))" but got "inset(calc(0.25 * (75% + 960px)))"
+FAIL inset(calc(10in) calc(10in)) - inline style assert_equals: expected "inset(calc(10in))" but got "inset(calc(960px))"
 PASS inset(calc(10in + 20px) calc(10in + 20px)) - inline style 
 PASS inset(calc(30%) calc(30%)) - inline style 
 PASS inset(calc(100%/4) calc(100%/4)) - inline style 
 PASS inset(calc(25%*3) calc(25%*3)) - inline style 
-PASS inset(calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style 
-PASS inset(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL inset(calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style assert_equals: expected "inset(calc(75% - 10in))" but got "inset(calc(75% - 960px))"
+FAIL inset(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "inset(calc((75% + 10in) / 4))" but got "inset(calc(0.25 * (75% + 960px)))"
 PASS inset(calc(10in)) - computed style 
 PASS inset(calc(10in + 20px)) - computed style 
 PASS inset(calc(30%)) - computed style 
 PASS inset(calc(100%/4)) - computed style 
 PASS inset(calc(25%*3)) - computed style 
 PASS inset(calc(25%*3 - 10in)) - computed style 
-PASS inset(calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL inset(calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "inset(calc(0.25 * (75% + 960px)))" not in array ["inset(calc((75% + 960px) / 4))", "inset(calc(18.75% + 240px))"]
 PASS inset(calc(10in) calc(10in)) - computed style 
 PASS inset(calc(10in + 20px) calc(10in + 20px)) - computed style 
 PASS inset(calc(30%) calc(30%)) - computed style 
 PASS inset(calc(100%/4) calc(100%/4)) - computed style 
 PASS inset(calc(25%*3) calc(25%*3)) - computed style 
 PASS inset(calc(25%*3 - 10in) calc(25%*3 - 10in)) - computed style 
-PASS inset(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL inset(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "inset(calc(0.25 * (75% + 960px)))" not in array ["inset(calc((75% + 960px) / 4))", "inset(calc(18.75% + 240px))"]
 
index 467898c..94a20f7 100644 (file)
@@ -1,30 +1,30 @@
 
-PASS inset(10px 10px 10px 10px round calc(10in)) - inline style 
+FAIL inset(10px 10px 10px 10px round calc(10in)) - inline style assert_equals: expected "inset(10px round calc(10in))" but got "inset(10px round calc(960px))"
 PASS inset(10px 10px 10px 10px round calc(10in + 20px)) - inline style 
 PASS inset(10px 10px 10px 10px round calc(30%)) - inline style 
 PASS inset(10px 10px 10px 10px round calc(100%/4)) - inline style 
 PASS inset(10px 10px 10px 10px round calc(25%*3)) - inline style 
-PASS inset(10px 10px 10px 10px round calc(25%*3 - 10in)) - inline style 
-PASS inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4)) - inline style 
-PASS inset(10px 10px 10px 10px round calc(10in) calc(10in)) - inline style 
+FAIL inset(10px 10px 10px 10px round calc(25%*3 - 10in)) - inline style assert_equals: expected "inset(10px round calc(75% - 10in))" but got "inset(10px round calc(75% - 960px))"
+FAIL inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "inset(10px round calc((75% + 10in) / 4))" but got "inset(10px round calc(0.25 * (75% + 960px)))"
+FAIL inset(10px 10px 10px 10px round calc(10in) calc(10in)) - inline style assert_equals: expected "inset(10px round calc(10in))" but got "inset(10px round calc(960px))"
 PASS inset(10px 10px 10px 10px round calc(10in + 20px) calc(10in + 20px)) - inline style 
 PASS inset(10px 10px 10px 10px round calc(30%) calc(30%)) - inline style 
 PASS inset(10px 10px 10px 10px round calc(100%/4) calc(100%/4)) - inline style 
 PASS inset(10px 10px 10px 10px round calc(25%*3) calc(25%*3)) - inline style 
-PASS inset(10px 10px 10px 10px round calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style 
-PASS inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL inset(10px 10px 10px 10px round calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style assert_equals: expected "inset(10px round calc(75% - 10in))" but got "inset(10px round calc(75% - 960px))"
+FAIL inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "inset(10px round calc((75% + 10in) / 4))" but got "inset(10px round calc(0.25 * (75% + 960px)))"
 PASS inset(10px 10px 10px 10px round calc(10in)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(10in + 20px)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(30%)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(100%/4)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(25%*3)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(25%*3 - 10in)) - computed style 
-PASS inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "inset(10px round calc(0.25 * (75% + 960px)))" not in array ["inset(10px round calc((75% + 960px) / 4))", "inset(10px round calc(18.75% + 240px))"]
 PASS inset(10px 10px 10px 10px round calc(10in) calc(10in)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(10in + 20px) calc(10in + 20px)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(30%) calc(30%)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(100%/4) calc(100%/4)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(25%*3) calc(25%*3)) - computed style 
 PASS inset(10px 10px 10px 10px round calc(25%*3 - 10in) calc(25%*3 - 10in)) - computed style 
-PASS inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL inset(10px 10px 10px 10px round calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "inset(10px round calc(0.25 * (75% + 960px)))" not in array ["inset(10px round calc((75% + 960px) / 4))", "inset(10px round calc(18.75% + 240px))"]
 
index 5fed73a..cb5eeb6 100644 (file)
@@ -1,30 +1,30 @@
 
-PASS polygon(calc(10in) calc(10in)) - inline style 
+FAIL polygon(calc(10in) calc(10in)) - inline style assert_equals: expected "polygon(calc(10in) calc(10in))" but got "polygon(calc(960px) calc(960px))"
 PASS polygon(calc(10in + 20px) calc(10in + 20px)) - inline style 
 PASS polygon(calc(30%) calc(30%)) - inline style 
 PASS polygon(calc(100%/4) calc(100%/4)) - inline style 
 PASS polygon(calc(25%*3) calc(25%*3)) - inline style 
-PASS polygon(calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style 
-PASS polygon(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style 
-PASS polygon(evenodd, calc(10in) calc(10in), calc(10in) calc(10in)) - inline style 
+FAIL polygon(calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style assert_equals: expected "polygon(calc(75% - 10in) calc(75% - 10in))" but got "polygon(calc(75% - 960px) calc(75% - 960px))"
+FAIL polygon(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "polygon(calc((75% + 10in) / 4) calc((75% + 10in) / 4))" but got "polygon(calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)))"
+FAIL polygon(evenodd, calc(10in) calc(10in), calc(10in) calc(10in)) - inline style assert_equals: expected "polygon(evenodd, calc(10in) calc(10in), calc(10in) calc(10in))" but got "polygon(evenodd, calc(960px) calc(960px), calc(960px) calc(960px))"
 PASS polygon(evenodd, calc(10in + 20px) calc(10in + 20px), calc(10in + 20px) calc(10in + 20px)) - inline style 
 PASS polygon(evenodd, calc(30%) calc(30%), calc(30%) calc(30%)) - inline style 
 PASS polygon(evenodd, calc(100%/4) calc(100%/4), calc(100%/4) calc(100%/4)) - inline style 
 PASS polygon(evenodd, calc(25%*3) calc(25%*3), calc(25%*3) calc(25%*3)) - inline style 
-PASS polygon(evenodd, calc(25%*3 - 10in) calc(25%*3 - 10in), calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style 
-PASS polygon(evenodd, calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4), calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style 
+FAIL polygon(evenodd, calc(25%*3 - 10in) calc(25%*3 - 10in), calc(25%*3 - 10in) calc(25%*3 - 10in)) - inline style assert_equals: expected "polygon(evenodd, calc(75% - 10in) calc(75% - 10in), calc(75% - 10in) calc(75% - 10in))" but got "polygon(evenodd, calc(75% - 960px) calc(75% - 960px), calc(75% - 960px) calc(75% - 960px))"
+FAIL polygon(evenodd, calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4), calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - inline style assert_equals: expected "polygon(evenodd, calc((75% + 10in) / 4) calc((75% + 10in) / 4), calc((75% + 10in) / 4) calc((75% + 10in) / 4))" but got "polygon(evenodd, calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)), calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)))"
 PASS polygon(calc(10in) calc(10in)) - computed style 
 PASS polygon(calc(10in + 20px) calc(10in + 20px)) - computed style 
 PASS polygon(calc(30%) calc(30%)) - computed style 
 PASS polygon(calc(100%/4) calc(100%/4)) - computed style 
 PASS polygon(calc(25%*3) calc(25%*3)) - computed style 
 PASS polygon(calc(25%*3 - 10in) calc(25%*3 - 10in)) - computed style 
-PASS polygon(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL polygon(calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "polygon(calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)))" not in array ["polygon(calc((75% + 960px) / 4) calc((75% + 960px) / 4))", "polygon(calc(18.75% + 240px) calc(18.75% + 240px))"]
 PASS polygon(evenodd, calc(10in) calc(10in), calc(10in) calc(10in)) - computed style 
 PASS polygon(evenodd, calc(10in + 20px) calc(10in + 20px), calc(10in + 20px) calc(10in + 20px)) - computed style 
 PASS polygon(evenodd, calc(30%) calc(30%), calc(30%) calc(30%)) - computed style 
 PASS polygon(evenodd, calc(100%/4) calc(100%/4), calc(100%/4) calc(100%/4)) - computed style 
 PASS polygon(evenodd, calc(25%*3) calc(25%*3), calc(25%*3) calc(25%*3)) - computed style 
 PASS polygon(evenodd, calc(25%*3 - 10in) calc(25%*3 - 10in), calc(25%*3 - 10in) calc(25%*3 - 10in)) - computed style 
-PASS polygon(evenodd, calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4), calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style 
+FAIL polygon(evenodd, calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4), calc((12.5%*6 + 10in) / 4) calc((12.5%*6 + 10in) / 4)) - computed style assert_in_array: value "polygon(evenodd, calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)), calc(0.25 * (75% + 960px)) calc(0.25 * (75% + 960px)))" not in array ["polygon(evenodd, calc((75% + 960px) / 4) calc((75% + 960px) / 4), calc((75% + 960px) / 4) calc((75% + 960px) / 4))", "polygon(evenodd, calc(18.75% + 240px) calc(18.75% + 240px), calc(18.75% + 240px) calc(18.75% + 240px))"]
 
index 3e78081..60ca2e9 100644 (file)
@@ -3,6 +3,6 @@ PASS testing background-position: calc(2px + 3px) calc(4px + 5px)
 PASS testing background-position: calc(18px - 7px) calc(19px - 7px) 
 PASS testing background-position: calc(4 * 5px) calc(6px * 4) 
 PASS testing background-position: calc(100px / 4) calc(119px / 7) 
-FAIL testing background-position: calc(6px + 21%) calc(7em + 22%) assert_equals: expected "calc(21% + 6px) calc(22% + 112px)" but got "calc(6px + 21%) calc(112px + 22%)"
-FAIL testing background-position: calc(-8px + 23%) calc(-9em + 24%) assert_equals: expected "calc(23% - 8px) calc(24% - 144px)" but got "calc(-8px + 23%) calc(-144px + 24%)"
+PASS testing background-position: calc(6px + 21%) calc(7em + 22%) 
+PASS testing background-position: calc(-8px + 23%) calc(-9em + 24%) 
 
index 3d1732d..60ca2e9 100644 (file)
@@ -3,6 +3,6 @@ PASS testing background-position: calc(2px + 3px) calc(4px + 5px)
 PASS testing background-position: calc(18px - 7px) calc(19px - 7px) 
 PASS testing background-position: calc(4 * 5px) calc(6px * 4) 
 PASS testing background-position: calc(100px / 4) calc(119px / 7) 
-FAIL testing background-position: calc(6px + 21%) calc(7em + 22%) assert_equals: expected "calc(21% + 6px) calc(22% + 7em)" but got "calc(6px + 21%) calc(7em + 22%)"
-FAIL testing background-position: calc(-8px + 23%) calc(-9em + 24%) assert_equals: expected "calc(23% - 8px) calc(24% - 9em)" but got "calc(-8px + 23%) calc(-9em + 24%)"
+PASS testing background-position: calc(6px + 21%) calc(7em + 22%) 
+PASS testing background-position: calc(-8px + 23%) calc(-9em + 24%) 
 
index 419ff9d..a6ef57e 100644 (file)
@@ -4,5 +4,5 @@ PASS testing calc(calc(100px))
 PASS testing calc(calc(2) * calc(50px) 
 PASS testing calc(calc(150px*2/3) 
 PASS testing calc(calc(2 * calc(calc(3)) + 4) * 10px) 
-FAIL testing calc(50px + calc(40%)) assert_equals: expected "calc(40% + 50px)" but got "calc(50px + 40%)"
+PASS testing calc(50px + calc(40%)) 
 
index f444013..a5f4fda 100644 (file)
@@ -1,13 +1,13 @@
 
-FAIL testing calc(1vh + 2px + 3%) assert_equals: expected "calc(3% + 2px + 1vh)" but got "calc((1vh + 2px) + 3%)"
+PASS testing calc(1vh + 2px + 3%) 
 PASS testing calc(4px + 1vh) 
-FAIL testing calc(5px + 6em + 1vh) assert_equals: expected "calc(6em + 5px + 1vh)" but got "calc((5px + 6em) + 1vh)"
-FAIL testing calc(1vh - 7px) assert_equals: expected "calc(-7px + 1vh)" but got "calc(1vh - 7px)"
-FAIL testing calc(-8px + 9em + 1vh) assert_equals: expected "calc(9em - 8px + 1vh)" but got "calc((-8px + 9em) + 1vh)"
-FAIL testing calc(1pc + 1in + 1vh + 10%) assert_equals: expected "calc(10% + 112px + 1vh)" but got "calc((112px + 1vh) + 10%)"
-FAIL testing calc(25.4q + 1vh + 12%) assert_equals: expected "calc(12% + 24px + 1vh)" but got "calc((25.4q + 1vh) + 12%)"
-FAIL testing calc(1em + 1.27cm + 13% + 3em) assert_equals: expected "calc(13% + 4em + 48px)" but got "calc(((1em + 1.27cm) + 13%) + 3em)"
-FAIL testing calc(1vmin - 14%) assert_equals: expected "calc(-14% + 1vmin)" but got "calc(1vmin - 14%)"
-FAIL testing calc(15vw + 16vmin - 17vh) assert_equals: expected "calc(-17vh + 16vmin + 15vw)" but got "calc((15vw + 16vmin) - 17vh)"
+PASS testing calc(5px + 6em + 1vh) 
+PASS testing calc(1vh - 7px) 
+PASS testing calc(-8px + 9em + 1vh) 
+PASS testing calc(1pc + 1in + 1vh + 10%) 
+PASS testing calc(25.4q + 1vh + 12%) 
+PASS testing calc(1em + 1.27cm + 13% + 3em) 
+PASS testing calc(1vmin - 14%) 
+PASS testing calc(15vw + 16vmin - 17vh) 
 PASS testing calc(-80px + 25.4mm) 
 
index e37e512..aa4c15d 100644 (file)
@@ -1,3 +1,3 @@
 
-FAIL calc() serialization assert_equals: Serialization of calc(10px + 1vmin + 10%) expected "calc(10% + 10px + 1vmin)" but got "calc((10px + 1vmin) + 10%)"
+PASS calc() serialization 
 
index 201b5ab..bc3df13 100644 (file)
@@ -1,7 +1,7 @@
 
-FAIL testing border-top-left-radius: calc(10px + 25%) calc(20px + 25%) assert_equals: expected "calc(25% + 10px) calc(25% + 20px)" but got "calc(10px + 25%) calc(20px + 25%)"
-FAIL testing border-top-right-radius: calc(1em + 25%) assert_equals: expected "calc(25% + 16px)" but got "calc(16px + 25%)"
+PASS testing border-top-left-radius: calc(10px + 25%) calc(20px + 25%) 
+PASS testing border-top-right-radius: calc(1em + 25%) 
 PASS testing border-bottom-right-radius: calc(25%) 
 PASS testing border-bottom-left-radius: calc(25px); 
-FAIL testing border-radius shorthand assert_equals: expected "calc(25% + 10px) calc(25% + 16px) 25% 25px / calc(25% + 20px) calc(25% + 16px) 25% 25px" but got "calc(10px + 25%) calc(16px + 25%) 25% 25px / calc(20px + 25%) calc(16px + 25%) 25% 25px"
+PASS testing border-radius shorthand 
 
index b10db50..c7486eb 100644 (file)
@@ -1,3 +1,3 @@
 
-FAIL testing border-radius shorthand assert_equals: expected "calc(1% + 1px) calc(2% + 2px) calc(3% + 3px) calc(4% + 4px) / calc(5% + 5px) calc(6% + 6px) calc(7% + 7px) calc(8% + 8px)" but got "calc(1px + 1%) calc(2px + 2%) calc(3px + 3%) calc(4px + 4%) / calc(5px + 5%) calc(6px + 6%) calc(7px + 7%) calc(8px + 8%)"
+PASS testing border-radius shorthand 
 
index 42e9695..95bd6d2 100644 (file)
@@ -1,34 +1,34 @@
 
 PASS Property transform value 'rotate(min(1deg))' computes to 'matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)' 
-FAIL Property transform value 'rotate(min(1grad))' computes to 'matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)' assert_equals: expected "matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
-FAIL Property transform value 'rotate(min(1rad))' computes to 'matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)' assert_equals: expected "matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
-FAIL Property transform value 'rotate(min(1turn))' computes to 'matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)' assert_equals: expected "matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
+PASS Property transform value 'rotate(min(1grad))' computes to 'matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)' 
+PASS Property transform value 'rotate(min(1rad))' computes to 'matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)' 
+PASS Property transform value 'rotate(min(1turn))' computes to 'matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)' 
 PASS Property transform value 'rotate(max(1deg))' computes to 'matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)' 
-FAIL Property transform value 'rotate(max(1grad))' computes to 'matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)' assert_equals: expected "matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
-FAIL Property transform value 'rotate(max(1rad))' computes to 'matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)' assert_equals: expected "matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
-FAIL Property transform value 'rotate(max(1turn))' computes to 'matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)' assert_equals: expected "matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
+PASS Property transform value 'rotate(max(1grad))' computes to 'matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)' 
+PASS Property transform value 'rotate(max(1rad))' computes to 'matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)' 
+PASS Property transform value 'rotate(max(1turn))' computes to 'matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)' 
 PASS Property transform value 'rotate(min(1deg, 2deg))' computes to 'matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)' 
-FAIL Property transform value 'rotate(min(1grad, 2grad))' computes to 'matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)' assert_equals: expected "matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
-FAIL Property transform value 'rotate(min(1rad, 2rad))' computes to 'matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)' assert_equals: expected "matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
-FAIL Property transform value 'rotate(min(1turn, 2turn))' computes to 'matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)' assert_equals: expected "matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)" but got "matrix(0.9998476951563913, 0.01745240643728351, -0.01745240643728351, 0.9998476951563913, 0, 0)"
+PASS Property transform value 'rotate(min(1grad, 2grad))' computes to 'matrix(0.9998766324816606, 0.015707317311820675, -0.015707317311820675, 0.9998766324816606, 0, 0)' 
+PASS Property transform value 'rotate(min(1rad, 2rad))' computes to 'matrix(0.5403023058681398, 0.8414709848078965, -0.8414709848078965, 0.5403023058681398, 0, 0)' 
+PASS Property transform value 'rotate(min(1turn, 2turn))' computes to 'matrix(1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0)' 
 PASS Property transform value 'rotate(max(1deg, 2deg))' computes to 'matrix(0.9993908270190958, 0.03489949670250097, -0.03489949670250097, 0.9993908270190958, 0, 0)' 
-FAIL Property transform value 'rotate(max(1grad, 2grad))' computes to 'matrix(0.9995065603657316, 0.03141075907812829, -0.03141075907812829, 0.9995065603657316, 0, 0)' assert_equals: expected "matrix(0.9995065603657316, 0.03141075907812829, -0.03141075907812829, 0.9995065603657316, 0, 0)" but got "matrix(0.9993908270190958, 0.03489949670250097, -0.03489949670250097, 0.9993908270190958, 0, 0)"
-FAIL Property transform value 'rotate(max(1rad, 2rad))' computes to 'matrix(-0.4161468365471424, 0.9092974268256817, -0.9092974268256817, -0.4161468365471424, 0, 0)' assert_equals: expected "matrix(-0.4161468365471424, 0.9092974268256817, -0.9092974268256817, -0.4161468365471424, 0, 0)" but got "matrix(0.9993908270190958, 0.03489949670250097, -0.03489949670250097, 0.9993908270190958, 0, 0)"
-FAIL Property transform value 'rotate(max(1turn, 2turn))' computes to 'matrix(1, -4.898587196589413e-16, 4.898587196589413e-16, 1, 0, 0)' assert_equals: expected "matrix(1, -4.898587196589413e-16, 4.898587196589413e-16, 1, 0, 0)" but got "matrix(0.9993908270190958, 0.03489949670250097, -0.03489949670250097, 0.9993908270190958, 0, 0)"
-FAIL Property transform value 'rotate(min(90deg, 0.26turn))' computes to 'matrix(6.123233995736766e-17, 1, -1, 6.123233995736766e-17, 0, 0)' assert_equals: expected "matrix(6.123233995736766e-17, 1, -1, 6.123233995736766e-17, 0, 0)" but got "matrix(0.9999897039488793, 0.00453784048117526, -0.00453784048117526, 0.9999897039488793, 0, 0)"
-FAIL Property transform value 'rotate(min(1.57rad, 95deg))' computes to 'matrix(0.0007963267107332633, 0.9999996829318346, -0.9999996829318346, 0.0007963267107332633, 0, 0)' assert_equals: expected "matrix(0.0007963267107332633, 0.9999996829318346, -0.9999996829318346, 0.0007963267107332633, 0, 0)" but got "matrix(0.9996245977511435, 0.027398240287735538, -0.027398240287735538, 0.9996245977511435, 0, 0)"
+PASS Property transform value 'rotate(max(1grad, 2grad))' computes to 'matrix(0.9995065603657316, 0.03141075907812829, -0.03141075907812829, 0.9995065603657316, 0, 0)' 
+PASS Property transform value 'rotate(max(1rad, 2rad))' computes to 'matrix(-0.4161468365471424, 0.9092974268256817, -0.9092974268256817, -0.4161468365471424, 0, 0)' 
+PASS Property transform value 'rotate(max(1turn, 2turn))' computes to 'matrix(1, -4.898587196589413e-16, 4.898587196589413e-16, 1, 0, 0)' 
+PASS Property transform value 'rotate(min(90deg, 0.26turn))' computes to 'matrix(6.123233995736766e-17, 1, -1, 6.123233995736766e-17, 0, 0)' 
+FAIL Property transform value 'rotate(min(1.57rad, 95deg))' computes to 'matrix(0.0007963267107332633, 0.9999996829318346, -0.9999996829318346, 0.0007963267107332633, 0, 0)' assert_equals: expected "matrix(0.0007963267107332633, 0.9999996829318346, -0.9999996829318346, 0.0007963267107332633, 0, 0)" but got "matrix(0.0007963267107334854, 0.9999996829318346, -0.9999996829318346, 0.0007963267107334854, 0, 0)"
 PASS Property transform value 'rotate(max(91deg, 0.25turn))' computes to 'matrix(-0.017452406437283473, 0.9998476951563913, -0.9998476951563913, -0.017452406437283473, 0, 0)' 
-FAIL Property transform value 'rotate(max(1.58rad, 90deg))' computes to 'matrix(-0.009203543268808559, 0.9999576464987401, -0.9999576464987401, -0.009203543268808559, 0, 0)' assert_equals: expected "matrix(-0.009203543268808559, 0.9999576464987401, -0.9999576464987401, -0.009203543268808559, 0, 0)" but got "matrix(6.123233995736766e-17, 1, -1, 6.123233995736766e-17, 0, 0)"
-FAIL Property transform value 'rotate(min(270deg, max(0.25turn, 3.14rad)))' computes to 'matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)' assert_equals: expected "matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)" but got "matrix(0.998498672858271, 0.05477590985343363, -0.05477590985343363, 0.998498672858271, 0, 0)"
-FAIL Property transform value 'rotate(max(0.25turn, min(270deg, 3.14rad)))' computes to 'matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)' assert_equals: expected "matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)" but got "matrix(0.998498672858271, 0.05477590985343363, -0.05477590985343363, 0.998498672858271, 0, 0)"
-FAIL Property transform value 'rotate(calc(min(90deg, 1.58rad) + 0.25turn))' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' assert_equals: expected "matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)" but got "matrix(-0.027572707274697524, 0.9996198006309919, -0.9996198006309919, -0.027572707274697524, 0, 0)"
-FAIL Property transform value 'rotate(calc(min(90deg, 1.58rad) - 0.125turn))' computes to 'matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)' assert_equals: expected "matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)" but got "matrix(0.7263347879241293, -0.6873410913449087, 0.6873410913449087, 0.7263347879241293, 0, 0)"
-FAIL Property transform value 'rotate(calc(min(90deg, 1.58rad) * 2)' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' assert_equals: expected "matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)" but got "matrix(0.9984794916270877, 0.055124448297579676, -0.055124448297579676, 0.9984794916270877, 0, 0)"
-FAIL Property transform value 'rotate(calc(min(90deg, 1.58rad) / 2)' computes to 'matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)' assert_equals: expected "matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)" but got "matrix(0.9999049456400823, 0.013787664214945474, -0.013787664214945474, 0.9999049456400823, 0, 0)"
+PASS Property transform value 'rotate(max(1.58rad, 90deg))' computes to 'matrix(-0.009203543268808559, 0.9999576464987401, -0.9999576464987401, -0.009203543268808559, 0, 0)' 
+FAIL Property transform value 'rotate(min(270deg, max(0.25turn, 3.14rad)))' computes to 'matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)' assert_equals: expected "matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)" but got "matrix(-0.9999987317275395, 0.0015926529164872723, -0.0015926529164872723, -0.9999987317275395, 0, 0)"
+FAIL Property transform value 'rotate(max(0.25turn, min(270deg, 3.14rad)))' computes to 'matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)' assert_equals: expected "matrix(-0.9999987317275395, 0.0015926529164868282, -0.0015926529164868282, -0.9999987317275395, 0, 0)" but got "matrix(-0.9999987317275395, 0.0015926529164872723, -0.0015926529164872723, -0.9999987317275395, 0, 0)"
+PASS Property transform value 'rotate(calc(min(90deg, 1.58rad) + 0.25turn))' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' 
+PASS Property transform value 'rotate(calc(min(90deg, 1.58rad) - 0.125turn))' computes to 'matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)' 
+PASS Property transform value 'rotate(calc(min(90deg, 1.58rad) * 2)' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' 
+PASS Property transform value 'rotate(calc(min(90deg, 1.58rad) / 2)' computes to 'matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)' 
 PASS Property transform value 'rotate(calc(max(90deg, 1.56rad) + 0.25turn))' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' 
 PASS Property transform value 'rotate(calc(max(90deg, 1.56rad) - 0.125turn))' computes to 'matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)' 
 PASS Property transform value 'rotate(calc(max(90deg, 1.56rad) * 2)' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' 
 PASS Property transform value 'rotate(calc(max(90deg, 1.56rad) / 2)' computes to 'matrix(0.7071067811865476, 0.7071067811865475, -0.7071067811865475, 0.7071067811865476, 0, 0)' 
-FAIL Property transform value 'rotate(calc(min(90deg, 1.58rad) + max(0.25turn, 99grad)))' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' assert_equals: expected "matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)" but got "matrix(-0.18360823024919293, 0.9829995003990385, -0.9829995003990385, -0.18360823024919293, 0, 0)"
-FAIL Property transform value 'rotate(calc(min(90deg, 1.58rad) - max(0.25turn, 99grad)))' computes to 'matrix(1, 0, 0, 1, 0, 0)' assert_equals: expected "matrix(1, 0, 0, 1, 0, 0)" but got "matrix(-0.12914174726147007, -0.9916261438234949, 0.9916261438234949, -0.12914174726147007, 0, 0)"
+PASS Property transform value 'rotate(calc(min(90deg, 1.58rad) + max(0.25turn, 99grad)))' computes to 'matrix(-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0)' 
+PASS Property transform value 'rotate(calc(min(90deg, 1.58rad) - max(0.25turn, 99grad)))' computes to 'matrix(1, 0, 0, 1, 0, 0)' 
 
index 554f12f..8bd7a1c 100644 (file)
@@ -1,20 +1,20 @@
 
-FAIL e.style['transform'] = "rotate(min(1deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1deg))" but got "rotate(calc(min(1deg)))"
-FAIL e.style['transform'] = "rotate(min(1rad))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1rad))" but got "rotate(calc(min(1rad)))"
-FAIL e.style['transform'] = "rotate(min(1turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1turn))" but got "rotate(calc(min(1turn)))"
-FAIL e.style['transform'] = "rotate(min(1grad))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1grad))" but got "rotate(calc(min(1grad)))"
-FAIL e.style['transform'] = "rotate(max(1deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1deg))" but got "rotate(calc(max(1deg)))"
-FAIL e.style['transform'] = "rotate(max(1rad))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1rad))" but got "rotate(calc(max(1rad)))"
-FAIL e.style['transform'] = "rotate(max(1turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1turn))" but got "rotate(calc(max(1turn)))"
-FAIL e.style['transform'] = "rotate(max(1grad))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1grad))" but got "rotate(calc(max(1grad)))"
-FAIL e.style['transform'] = "rotate(min(1deg, 2deg, 3deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1deg, 2deg, 3deg))" but got "rotate(calc(min(1deg, 2deg, 3deg)))"
-FAIL e.style['transform'] = "rotate(min(3deg, 2deg, 1deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(3deg, 2deg, 1deg))" but got "rotate(calc(min(3deg, 2deg, 1deg)))"
-FAIL e.style['transform'] = "rotate(min(90deg, 1.57rad, 0.25turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(90deg, 1.57rad, 0.25turn))" but got "rotate(calc(min(90deg, 1.57rad, 0.25turn)))"
-FAIL e.style['transform'] = "rotate(min(0.25turn, 1.57rad, 90deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(0.25turn, 1.57rad, 90deg))" but got "rotate(calc(min(0.25turn, 1.57rad, 90deg)))"
-FAIL e.style['transform'] = "rotate(max(1deg, 2deg, 3deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1deg, 2deg, 3deg))" but got "rotate(calc(max(1deg, 2deg, 3deg)))"
-FAIL e.style['transform'] = "rotate(max(3deg, 2deg, 1deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(3deg, 2deg, 1deg))" but got "rotate(calc(max(3deg, 2deg, 1deg)))"
-FAIL e.style['transform'] = "rotate(max(90deg, 1.57rad, 0.25turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(90deg, 1.57rad, 0.25turn))" but got "rotate(calc(max(90deg, 1.57rad, 0.25turn)))"
-FAIL e.style['transform'] = "rotate(max(0.25turn, 1.57rad, 90deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(0.25turn, 1.57rad, 90deg))" but got "rotate(calc(max(0.25turn, 1.57rad, 90deg)))"
+PASS e.style['transform'] = "rotate(min(1deg))" should set the property value 
+FAIL e.style['transform'] = "rotate(min(1rad))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1rad))" but got "rotate(min(57.29577951308232deg))"
+FAIL e.style['transform'] = "rotate(min(1turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1turn))" but got "rotate(min(360deg))"
+FAIL e.style['transform'] = "rotate(min(1grad))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1grad))" but got "rotate(min(0.9deg))"
+PASS e.style['transform'] = "rotate(max(1deg))" should set the property value 
+FAIL e.style['transform'] = "rotate(max(1rad))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1rad))" but got "rotate(max(57.29577951308232deg))"
+FAIL e.style['transform'] = "rotate(max(1turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1turn))" but got "rotate(max(360deg))"
+FAIL e.style['transform'] = "rotate(max(1grad))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1grad))" but got "rotate(max(0.9deg))"
+FAIL e.style['transform'] = "rotate(min(1deg, 2deg, 3deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(1deg, 2deg, 3deg))" but got "rotate(min(1deg))"
+FAIL e.style['transform'] = "rotate(min(3deg, 2deg, 1deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(3deg, 2deg, 1deg))" but got "rotate(min(1deg))"
+FAIL e.style['transform'] = "rotate(min(90deg, 1.57rad, 0.25turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(90deg, 1.57rad, 0.25turn))" but got "rotate(min(89.95437383553924deg))"
+FAIL e.style['transform'] = "rotate(min(0.25turn, 1.57rad, 90deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(min(0.25turn, 1.57rad, 90deg))" but got "rotate(min(89.95437383553924deg))"
+FAIL e.style['transform'] = "rotate(max(1deg, 2deg, 3deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(1deg, 2deg, 3deg))" but got "rotate(max(3deg))"
+FAIL e.style['transform'] = "rotate(max(3deg, 2deg, 1deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(3deg, 2deg, 1deg))" but got "rotate(max(3deg))"
+FAIL e.style['transform'] = "rotate(max(90deg, 1.57rad, 0.25turn))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(90deg, 1.57rad, 0.25turn))" but got "rotate(max(90deg))"
+FAIL e.style['transform'] = "rotate(max(0.25turn, 1.57rad, 90deg))" should set the property value assert_equals: serialization should be canonical expected "rotate(max(0.25turn, 1.57rad, 90deg))" but got "rotate(max(90deg))"
 FAIL e.style['transform'] = "rotate(calc(min(1deg) + min(2deg)))" should set the property value assert_equals: serialization should be canonical expected "rotate(calc(min(1deg) + min(2deg)))" but got "rotate(calc(3deg))"
 FAIL e.style['transform'] = "rotate(calc(max(1deg) + max(2deg)))" should set the property value assert_equals: serialization should be canonical expected "rotate(calc(max(1deg) + max(2deg)))" but got "rotate(calc(3deg))"
 FAIL e.style['transform'] = "rotate(calc(1rad + min(1deg)))" should set the property value assert_equals: serialization should be canonical expected "rotate(calc(1rad + min(1deg)))" but got "rotate(calc(58.29577951308232deg))"
index 644a8d3..079faa9 100644 (file)
@@ -1,62 +1,62 @@
 
-FAIL e.style['margin-left'] = "min(1px + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1px + 1%)" but got "calc(min((1px + 1%)))"
-FAIL e.style['margin-left'] = "min(1cm + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1cm + 1%)" but got "calc(min((1cm + 1%)))"
-FAIL e.style['margin-left'] = "min(1mm + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1mm + 1%)" but got "calc(min((1mm + 1%)))"
-FAIL e.style['margin-left'] = "min(1Q + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1q + 1%)" but got "calc(min((1q + 1%)))"
-FAIL e.style['margin-left'] = "min(1in + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1in + 1%)" but got "calc(min((1in + 1%)))"
-FAIL e.style['margin-left'] = "min(1pc + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1pc + 1%)" but got "calc(min((1pc + 1%)))"
-FAIL e.style['margin-left'] = "min(1pt + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1pt + 1%)" but got "calc(min((1pt + 1%)))"
-FAIL e.style['margin-left'] = "min(1em + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1em + 1%)" but got "calc(min((1em + 1%)))"
-FAIL e.style['margin-left'] = "min(1ex + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1ex + 1%)" but got "calc(min((1ex + 1%)))"
-FAIL e.style['margin-left'] = "min(1ch + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1ch + 1%)" but got "calc(min((1ch + 1%)))"
-FAIL e.style['margin-left'] = "min(1rem + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1rem + 1%)" but got "calc(min((1rem + 1%)))"
-FAIL e.style['margin-left'] = "min(1vh + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vh + 1%)" but got "calc(min((1vh + 1%)))"
-FAIL e.style['margin-left'] = "min(1vw + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vw + 1%)" but got "calc(min((1vw + 1%)))"
-FAIL e.style['margin-left'] = "min(1vmin + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vmin + 1%)" but got "calc(min((1vmin + 1%)))"
-FAIL e.style['margin-left'] = "min(1vmax + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vmax + 1%)" but got "calc(min((1vmax + 1%)))"
-FAIL e.style['margin-left'] = "max(1px + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1px + 1%)" but got "calc(max((1px + 1%)))"
-FAIL e.style['margin-left'] = "max(1cm + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1cm + 1%)" but got "calc(max((1cm + 1%)))"
-FAIL e.style['margin-left'] = "max(1mm + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1mm + 1%)" but got "calc(max((1mm + 1%)))"
-FAIL e.style['margin-left'] = "max(1Q + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1q + 1%)" but got "calc(max((1q + 1%)))"
-FAIL e.style['margin-left'] = "max(1in + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1in + 1%)" but got "calc(max((1in + 1%)))"
-FAIL e.style['margin-left'] = "max(1pc + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1pc + 1%)" but got "calc(max((1pc + 1%)))"
-FAIL e.style['margin-left'] = "max(1pt + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1pt + 1%)" but got "calc(max((1pt + 1%)))"
-FAIL e.style['margin-left'] = "max(1em + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1em + 1%)" but got "calc(max((1em + 1%)))"
-FAIL e.style['margin-left'] = "max(1ex + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1ex + 1%)" but got "calc(max((1ex + 1%)))"
-FAIL e.style['margin-left'] = "max(1ch + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1ch + 1%)" but got "calc(max((1ch + 1%)))"
-FAIL e.style['margin-left'] = "max(1rem + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1rem + 1%)" but got "calc(max((1rem + 1%)))"
-FAIL e.style['margin-left'] = "max(1vh + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vh + 1%)" but got "calc(max((1vh + 1%)))"
-FAIL e.style['margin-left'] = "max(1vw + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vw + 1%)" but got "calc(max((1vw + 1%)))"
-FAIL e.style['margin-left'] = "max(1vmin + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vmin + 1%)" but got "calc(max((1vmin + 1%)))"
-FAIL e.style['margin-left'] = "max(1vmax + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vmax + 1%)" but got "calc(max((1vmax + 1%)))"
-FAIL e.style['margin-left'] = "min(20px, 10%)" should set the property value assert_equals: serialization should be canonical expected "min(20px, 10%)" but got "calc(min(20px, 10%))"
-FAIL e.style['margin-left'] = "min(1em, 10%)" should set the property value assert_equals: serialization should be canonical expected "min(1em, 10%)" but got "calc(min(1em, 10%))"
-FAIL e.style['margin-left'] = "max(20px, 10%)" should set the property value assert_equals: serialization should be canonical expected "max(20px, 10%)" but got "calc(max(20px, 10%))"
-FAIL e.style['margin-left'] = "max(1em, 10%)" should set the property value assert_equals: serialization should be canonical expected "max(1em, 10%)" but got "calc(max(1em, 10%))"
-FAIL e.style['margin-left'] = "min(10%, 20px)" should set the property value assert_equals: serialization should be canonical expected "min(10%, 20px)" but got "calc(min(10%, 20px))"
-FAIL e.style['margin-left'] = "min(10%, 1em)" should set the property value assert_equals: serialization should be canonical expected "min(10%, 1em)" but got "calc(min(10%, 1em))"
-FAIL e.style['margin-left'] = "max(10%, 20px)" should set the property value assert_equals: serialization should be canonical expected "max(10%, 20px)" but got "calc(max(10%, 20px))"
-FAIL e.style['margin-left'] = "max(10%, 1em)" should set the property value assert_equals: serialization should be canonical expected "max(10%, 1em)" but got "calc(max(10%, 1em))"
-FAIL e.style['margin-left'] = "min(10% + 30px, 5% + 60px)" should set the property value assert_equals: serialization should be canonical expected "min(10% + 30px, 5% + 60px)" but got "calc(min((10% + 30px), (5% + 60px)))"
-FAIL e.style['margin-left'] = "max(10% + 2em, 5% + 1em)" should set the property value assert_equals: serialization should be canonical expected "max(10% + 2em, 5% + 1em)" but got "calc(max((10% + 2em), (5% + 1em)))"
-FAIL e.style['margin-left'] = "calc(min(10%) + max(1em) + min(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(min(10%) + max(1em) + min(20px))" but got "calc(((min(10%)) + (max(1em))) + (min(20px)))"
-FAIL e.style['margin-left'] = "calc(max(20px) + min(1em) + max(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(max(20px) + min(1em) + max(10%))" but got "calc(((max(20px)) + (min(1em))) + (max(10%)))"
-FAIL e.style['margin-left'] = "calc(max(10%) + min(1em) + max(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(max(10%) + min(1em) + max(20px))" but got "calc(((max(10%)) + (min(1em))) + (max(20px)))"
-FAIL e.style['margin-left'] = "calc(min(20px) + max(1em) + min(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(min(20px) + max(1em) + min(10%))" but got "calc(((min(20px)) + (max(1em))) + (min(10%)))"
-FAIL e.style['margin-left'] = "calc(20px + min(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(20px + min(10%))" but got "calc(20px + (min(10%)))"
-FAIL e.style['margin-left'] = "calc(10% + min(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(20px))" but got "calc(10% + (min(20px)))"
-FAIL e.style['margin-left'] = "calc(1em + min(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(1em + min(10%))" but got "calc(1em + (min(10%)))"
-FAIL e.style['margin-left'] = "calc(10% + min(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(1em))" but got "calc(10% + (min(1em)))"
-FAIL e.style['margin-left'] = "calc(min(10%) + 20px)" should set the property value assert_equals: serialization should be canonical expected "calc(20px + min(10%))" but got "calc((min(10%)) + 20px)"
-FAIL e.style['margin-left'] = "calc(min(20px) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(20px))" but got "calc((min(20px)) + 10%)"
-FAIL e.style['margin-left'] = "calc(min(10%) + 1em)" should set the property value assert_equals: serialization should be canonical expected "calc(1em + min(10%))" but got "calc((min(10%)) + 1em)"
-FAIL e.style['margin-left'] = "calc(min(1em) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(1em))" but got "calc((min(1em)) + 10%)"
-FAIL e.style['margin-left'] = "calc(20px + max(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(20px + max(10%))" but got "calc(20px + (max(10%)))"
-FAIL e.style['margin-left'] = "calc(10% + max(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(20px))" but got "calc(10% + (max(20px)))"
-FAIL e.style['margin-left'] = "calc(1em + max(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(1em + max(10%))" but got "calc(1em + (max(10%)))"
-FAIL e.style['margin-left'] = "calc(10% + max(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(1em))" but got "calc(10% + (max(1em)))"
-FAIL e.style['margin-left'] = "calc(max(10%) + 20px)" should set the property value assert_equals: serialization should be canonical expected "calc(20px + max(10%))" but got "calc((max(10%)) + 20px)"
-FAIL e.style['margin-left'] = "calc(max(20px) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(20px))" but got "calc((max(20px)) + 10%)"
-FAIL e.style['margin-left'] = "calc(max(10%) + 1em)" should set the property value assert_equals: serialization should be canonical expected "calc(1em + max(10%))" but got "calc((max(10%)) + 1em)"
-FAIL e.style['margin-left'] = "calc(max(1em) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(1em))" but got "calc((max(1em)) + 10%)"
+FAIL e.style['margin-left'] = "min(1px + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1px + 1%)" but got "min(1% + 1px)"
+FAIL e.style['margin-left'] = "min(1cm + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1cm + 1%)" but got "min(1% + 37.79527559055118px)"
+FAIL e.style['margin-left'] = "min(1mm + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1mm + 1%)" but got "min(1% + 3.7795275590551185px)"
+FAIL e.style['margin-left'] = "min(1Q + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1q + 1%)" but got "min(1% + 0.9448818897637796px)"
+FAIL e.style['margin-left'] = "min(1in + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1in + 1%)" but got "min(1% + 96px)"
+FAIL e.style['margin-left'] = "min(1pc + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1pc + 1%)" but got "min(1% + 16px)"
+FAIL e.style['margin-left'] = "min(1pt + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1pt + 1%)" but got "min(1% + 1.3333333333333333px)"
+FAIL e.style['margin-left'] = "min(1em + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1em + 1%)" but got "min(1% + 1em)"
+FAIL e.style['margin-left'] = "min(1ex + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1ex + 1%)" but got "min(1% + 1ex)"
+FAIL e.style['margin-left'] = "min(1ch + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1ch + 1%)" but got "min(1% + 1ch)"
+FAIL e.style['margin-left'] = "min(1rem + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1rem + 1%)" but got "min(1% + 1rem)"
+FAIL e.style['margin-left'] = "min(1vh + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vh + 1%)" but got "min(1% + 1vh)"
+FAIL e.style['margin-left'] = "min(1vw + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vw + 1%)" but got "min(1% + 1vw)"
+FAIL e.style['margin-left'] = "min(1vmin + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vmin + 1%)" but got "min(1% + 1vmin)"
+FAIL e.style['margin-left'] = "min(1vmax + 1%)" should set the property value assert_equals: serialization should be canonical expected "min(1vmax + 1%)" but got "min(1% + 1vmax)"
+FAIL e.style['margin-left'] = "max(1px + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1px + 1%)" but got "max(1% + 1px)"
+FAIL e.style['margin-left'] = "max(1cm + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1cm + 1%)" but got "max(1% + 37.79527559055118px)"
+FAIL e.style['margin-left'] = "max(1mm + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1mm + 1%)" but got "max(1% + 3.7795275590551185px)"
+FAIL e.style['margin-left'] = "max(1Q + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1q + 1%)" but got "max(1% + 0.9448818897637796px)"
+FAIL e.style['margin-left'] = "max(1in + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1in + 1%)" but got "max(1% + 96px)"
+FAIL e.style['margin-left'] = "max(1pc + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1pc + 1%)" but got "max(1% + 16px)"
+FAIL e.style['margin-left'] = "max(1pt + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1pt + 1%)" but got "max(1% + 1.3333333333333333px)"
+FAIL e.style['margin-left'] = "max(1em + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1em + 1%)" but got "max(1% + 1em)"
+FAIL e.style['margin-left'] = "max(1ex + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1ex + 1%)" but got "max(1% + 1ex)"
+FAIL e.style['margin-left'] = "max(1ch + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1ch + 1%)" but got "max(1% + 1ch)"
+FAIL e.style['margin-left'] = "max(1rem + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1rem + 1%)" but got "max(1% + 1rem)"
+FAIL e.style['margin-left'] = "max(1vh + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vh + 1%)" but got "max(1% + 1vh)"
+FAIL e.style['margin-left'] = "max(1vw + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vw + 1%)" but got "max(1% + 1vw)"
+FAIL e.style['margin-left'] = "max(1vmin + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vmin + 1%)" but got "max(1% + 1vmin)"
+FAIL e.style['margin-left'] = "max(1vmax + 1%)" should set the property value assert_equals: serialization should be canonical expected "max(1vmax + 1%)" but got "max(1% + 1vmax)"
+PASS e.style['margin-left'] = "min(20px, 10%)" should set the property value 
+PASS e.style['margin-left'] = "min(1em, 10%)" should set the property value 
+PASS e.style['margin-left'] = "max(20px, 10%)" should set the property value 
+PASS e.style['margin-left'] = "max(1em, 10%)" should set the property value 
+PASS e.style['margin-left'] = "min(10%, 20px)" should set the property value 
+PASS e.style['margin-left'] = "min(10%, 1em)" should set the property value 
+PASS e.style['margin-left'] = "max(10%, 20px)" should set the property value 
+PASS e.style['margin-left'] = "max(10%, 1em)" should set the property value 
+PASS e.style['margin-left'] = "min(10% + 30px, 5% + 60px)" should set the property value 
+PASS e.style['margin-left'] = "max(10% + 2em, 5% + 1em)" should set the property value 
+FAIL e.style['margin-left'] = "calc(min(10%) + max(1em) + min(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(min(10%) + max(1em) + min(20px))" but got "calc(10% + 1em + 20px)"
+FAIL e.style['margin-left'] = "calc(max(20px) + min(1em) + max(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(max(20px) + min(1em) + max(10%))" but got "calc(10% + 1em + 20px)"
+FAIL e.style['margin-left'] = "calc(max(10%) + min(1em) + max(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(max(10%) + min(1em) + max(20px))" but got "calc(10% + 1em + 20px)"
+FAIL e.style['margin-left'] = "calc(min(20px) + max(1em) + min(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(min(20px) + max(1em) + min(10%))" but got "calc(10% + 1em + 20px)"
+FAIL e.style['margin-left'] = "calc(20px + min(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(20px + min(10%))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(10% + min(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(20px))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(1em + min(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(1em + min(10%))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(10% + min(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(1em))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(min(10%) + 20px)" should set the property value assert_equals: serialization should be canonical expected "calc(20px + min(10%))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(min(20px) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(20px))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(min(10%) + 1em)" should set the property value assert_equals: serialization should be canonical expected "calc(1em + min(10%))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(min(1em) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(1em))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(20px + max(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(20px + max(10%))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(10% + max(20px))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(20px))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(1em + max(10%))" should set the property value assert_equals: serialization should be canonical expected "calc(1em + max(10%))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(10% + max(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(1em))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(max(10%) + 20px)" should set the property value assert_equals: serialization should be canonical expected "calc(20px + max(10%))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(max(20px) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(20px))" but got "calc(10% + 20px)"
+FAIL e.style['margin-left'] = "calc(max(10%) + 1em)" should set the property value assert_equals: serialization should be canonical expected "calc(1em + max(10%))" but got "calc(10% + 1em)"
+FAIL e.style['margin-left'] = "calc(max(1em) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(1em))" but got "calc(10% + 1em)"
 
index bc2e648..ad062b6 100644 (file)
@@ -1,46 +1,46 @@
 
-FAIL e.style['letter-spacing'] = "min(1px)" should set the property value assert_equals: serialization should be canonical expected "min(1px)" but got "calc(min(1px))"
-FAIL e.style['letter-spacing'] = "min(1cm)" should set the property value assert_equals: serialization should be canonical expected "min(1cm)" but got "calc(min(1cm))"
-FAIL e.style['letter-spacing'] = "min(1mm)" should set the property value assert_equals: serialization should be canonical expected "min(1mm)" but got "calc(min(1mm))"
-FAIL e.style['letter-spacing'] = "min(1Q)" should set the property value assert_equals: serialization should be canonical expected "min(1q)" but got "calc(min(1q))"
-FAIL e.style['letter-spacing'] = "min(1in)" should set the property value assert_equals: serialization should be canonical expected "min(1in)" but got "calc(min(1in))"
-FAIL e.style['letter-spacing'] = "min(1pc)" should set the property value assert_equals: serialization should be canonical expected "min(1pc)" but got "calc(min(1pc))"
-FAIL e.style['letter-spacing'] = "min(1pt)" should set the property value assert_equals: serialization should be canonical expected "min(1pt)" but got "calc(min(1pt))"
-FAIL e.style['letter-spacing'] = "min(1em)" should set the property value assert_equals: serialization should be canonical expected "min(1em)" but got "calc(min(1em))"
-FAIL e.style['letter-spacing'] = "min(1ex)" should set the property value assert_equals: serialization should be canonical expected "min(1ex)" but got "calc(min(1ex))"
-FAIL e.style['letter-spacing'] = "min(1ch)" should set the property value assert_equals: serialization should be canonical expected "min(1ch)" but got "calc(min(1ch))"
-FAIL e.style['letter-spacing'] = "min(1rem)" should set the property value assert_equals: serialization should be canonical expected "min(1rem)" but got "calc(min(1rem))"
-FAIL e.style['letter-spacing'] = "min(1vh)" should set the property value assert_equals: serialization should be canonical expected "min(1vh)" but got "calc(min(1vh))"
-FAIL e.style['letter-spacing'] = "min(1vw)" should set the property value assert_equals: serialization should be canonical expected "min(1vw)" but got "calc(min(1vw))"
-FAIL e.style['letter-spacing'] = "min(1vmin)" should set the property value assert_equals: serialization should be canonical expected "min(1vmin)" but got "calc(min(1vmin))"
-FAIL e.style['letter-spacing'] = "min(1vmax)" should set the property value assert_equals: serialization should be canonical expected "min(1vmax)" but got "calc(min(1vmax))"
-FAIL e.style['letter-spacing'] = "max(1px)" should set the property value assert_equals: serialization should be canonical expected "max(1px)" but got "calc(max(1px))"
-FAIL e.style['letter-spacing'] = "max(1cm)" should set the property value assert_equals: serialization should be canonical expected "max(1cm)" but got "calc(max(1cm))"
-FAIL e.style['letter-spacing'] = "max(1mm)" should set the property value assert_equals: serialization should be canonical expected "max(1mm)" but got "calc(max(1mm))"
-FAIL e.style['letter-spacing'] = "max(1Q)" should set the property value assert_equals: serialization should be canonical expected "max(1q)" but got "calc(max(1q))"
-FAIL e.style['letter-spacing'] = "max(1in)" should set the property value assert_equals: serialization should be canonical expected "max(1in)" but got "calc(max(1in))"
-FAIL e.style['letter-spacing'] = "max(1pc)" should set the property value assert_equals: serialization should be canonical expected "max(1pc)" but got "calc(max(1pc))"
-FAIL e.style['letter-spacing'] = "max(1pt)" should set the property value assert_equals: serialization should be canonical expected "max(1pt)" but got "calc(max(1pt))"
-FAIL e.style['letter-spacing'] = "max(1em)" should set the property value assert_equals: serialization should be canonical expected "max(1em)" but got "calc(max(1em))"
-FAIL e.style['letter-spacing'] = "max(1ex)" should set the property value assert_equals: serialization should be canonical expected "max(1ex)" but got "calc(max(1ex))"
-FAIL e.style['letter-spacing'] = "max(1ch)" should set the property value assert_equals: serialization should be canonical expected "max(1ch)" but got "calc(max(1ch))"
-FAIL e.style['letter-spacing'] = "max(1rem)" should set the property value assert_equals: serialization should be canonical expected "max(1rem)" but got "calc(max(1rem))"
-FAIL e.style['letter-spacing'] = "max(1vh)" should set the property value assert_equals: serialization should be canonical expected "max(1vh)" but got "calc(max(1vh))"
-FAIL e.style['letter-spacing'] = "max(1vw)" should set the property value assert_equals: serialization should be canonical expected "max(1vw)" but got "calc(max(1vw))"
-FAIL e.style['letter-spacing'] = "max(1vmin)" should set the property value assert_equals: serialization should be canonical expected "max(1vmin)" but got "calc(max(1vmin))"
-FAIL e.style['letter-spacing'] = "max(1vmax)" should set the property value assert_equals: serialization should be canonical expected "max(1vmax)" but got "calc(max(1vmax))"
-FAIL e.style['letter-spacing'] = "min(10px, 20px, 30px)" should set the property value assert_equals: serialization should be canonical expected "min(10px, 20px, 30px)" but got "calc(min(10px, 20px, 30px))"
-FAIL e.style['letter-spacing'] = "min(30px, 20px, 10px)" should set the property value assert_equals: serialization should be canonical expected "min(30px, 20px, 10px)" but got "calc(min(30px, 20px, 10px))"
-FAIL e.style['letter-spacing'] = "min(20px, 1em, 10vw)" should set the property value assert_equals: serialization should be canonical expected "min(20px, 1em, 10vw)" but got "calc(min(20px, 1em, 10vw))"
-FAIL e.style['letter-spacing'] = "min(10vw, 1em, 20px)" should set the property value assert_equals: serialization should be canonical expected "min(10vw, 1em, 20px)" but got "calc(min(10vw, 1em, 20px))"
-FAIL e.style['letter-spacing'] = "max(10px, 20px, 30px)" should set the property value assert_equals: serialization should be canonical expected "max(10px, 20px, 30px)" but got "calc(max(10px, 20px, 30px))"
-FAIL e.style['letter-spacing'] = "max(30px, 20px, 10px)" should set the property value assert_equals: serialization should be canonical expected "max(30px, 20px, 10px)" but got "calc(max(30px, 20px, 10px))"
-FAIL e.style['letter-spacing'] = "max(20px, 1em, 10vw)" should set the property value assert_equals: serialization should be canonical expected "max(20px, 1em, 10vw)" but got "calc(max(20px, 1em, 10vw))"
-FAIL e.style['letter-spacing'] = "max(10vw, 1em, 20px)" should set the property value assert_equals: serialization should be canonical expected "max(10vw, 1em, 20px)" but got "calc(max(10vw, 1em, 20px))"
-FAIL e.style['letter-spacing'] = "calc(min(10px) + max(1em) + min(10vw))" should set the property value assert_equals: serialization should be canonical expected "calc(min(10px) + max(1em) + min(10vw))" but got "calc(((min(10px)) + (max(1em))) + (min(10vw)))"
-FAIL e.style['letter-spacing'] = "calc(max(1em) + min(10vw) + max(10px))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1em) + min(10vw) + max(10px))" but got "calc(((max(1em)) + (min(10vw))) + (max(10px)))"
-FAIL e.style['letter-spacing'] = "calc(10px + min(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10px + min(1em))" but got "calc(10px + (min(1em)))"
-FAIL e.style['letter-spacing'] = "calc(min(1em) + 10px)" should set the property value assert_equals: serialization should be canonical expected "calc(10px + min(1em))" but got "calc((min(1em)) + 10px)"
-FAIL e.style['letter-spacing'] = "calc(10px + max(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10px + max(1em))" but got "calc(10px + (max(1em)))"
-FAIL e.style['letter-spacing'] = "calc(max(1em) + 10px)" should set the property value assert_equals: serialization should be canonical expected "calc(10px + max(1em))" but got "calc((max(1em)) + 10px)"
+PASS e.style['letter-spacing'] = "min(1px)" should set the property value 
+FAIL e.style['letter-spacing'] = "min(1cm)" should set the property value assert_equals: serialization should be canonical expected "min(1cm)" but got "min(37.79527559055118px)"
+FAIL e.style['letter-spacing'] = "min(1mm)" should set the property value assert_equals: serialization should be canonical expected "min(1mm)" but got "min(3.7795275590551185px)"
+FAIL e.style['letter-spacing'] = "min(1Q)" should set the property value assert_equals: serialization should be canonical expected "min(1q)" but got "min(0.9448818897637796px)"
+FAIL e.style['letter-spacing'] = "min(1in)" should set the property value assert_equals: serialization should be canonical expected "min(1in)" but got "min(96px)"
+FAIL e.style['letter-spacing'] = "min(1pc)" should set the property value assert_equals: serialization should be canonical expected "min(1pc)" but got "min(16px)"
+FAIL e.style['letter-spacing'] = "min(1pt)" should set the property value assert_equals: serialization should be canonical expected "min(1pt)" but got "min(1.3333333333333333px)"
+PASS e.style['letter-spacing'] = "min(1em)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1ex)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1ch)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1rem)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1vh)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1vw)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1vmin)" should set the property value 
+PASS e.style['letter-spacing'] = "min(1vmax)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1px)" should set the property value 
+FAIL e.style['letter-spacing'] = "max(1cm)" should set the property value assert_equals: serialization should be canonical expected "max(1cm)" but got "max(37.79527559055118px)"
+FAIL e.style['letter-spacing'] = "max(1mm)" should set the property value assert_equals: serialization should be canonical expected "max(1mm)" but got "max(3.7795275590551185px)"
+FAIL e.style['letter-spacing'] = "max(1Q)" should set the property value assert_equals: serialization should be canonical expected "max(1q)" but got "max(0.9448818897637796px)"
+FAIL e.style['letter-spacing'] = "max(1in)" should set the property value assert_equals: serialization should be canonical expected "max(1in)" but got "max(96px)"
+FAIL e.style['letter-spacing'] = "max(1pc)" should set the property value assert_equals: serialization should be canonical expected "max(1pc)" but got "max(16px)"
+FAIL e.style['letter-spacing'] = "max(1pt)" should set the property value assert_equals: serialization should be canonical expected "max(1pt)" but got "max(1.3333333333333333px)"
+PASS e.style['letter-spacing'] = "max(1em)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1ex)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1ch)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1rem)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1vh)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1vw)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1vmin)" should set the property value 
+PASS e.style['letter-spacing'] = "max(1vmax)" should set the property value 
+FAIL e.style['letter-spacing'] = "min(10px, 20px, 30px)" should set the property value assert_equals: serialization should be canonical expected "min(10px, 20px, 30px)" but got "min(10px)"
+FAIL e.style['letter-spacing'] = "min(30px, 20px, 10px)" should set the property value assert_equals: serialization should be canonical expected "min(30px, 20px, 10px)" but got "min(10px)"
+PASS e.style['letter-spacing'] = "min(20px, 1em, 10vw)" should set the property value 
+PASS e.style['letter-spacing'] = "min(10vw, 1em, 20px)" should set the property value 
+FAIL e.style['letter-spacing'] = "max(10px, 20px, 30px)" should set the property value assert_equals: serialization should be canonical expected "max(10px, 20px, 30px)" but got "max(30px)"
+FAIL e.style['letter-spacing'] = "max(30px, 20px, 10px)" should set the property value assert_equals: serialization should be canonical expected "max(30px, 20px, 10px)" but got "max(30px)"
+PASS e.style['letter-spacing'] = "max(20px, 1em, 10vw)" should set the property value 
+PASS e.style['letter-spacing'] = "max(10vw, 1em, 20px)" should set the property value 
+FAIL e.style['letter-spacing'] = "calc(min(10px) + max(1em) + min(10vw))" should set the property value assert_equals: serialization should be canonical expected "calc(min(10px) + max(1em) + min(10vw))" but got "calc(1em + 10px + 10vw)"
+FAIL e.style['letter-spacing'] = "calc(max(1em) + min(10vw) + max(10px))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1em) + min(10vw) + max(10px))" but got "calc(1em + 10px + 10vw)"
+FAIL e.style['letter-spacing'] = "calc(10px + min(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10px + min(1em))" but got "calc(1em + 10px)"
+FAIL e.style['letter-spacing'] = "calc(min(1em) + 10px)" should set the property value assert_equals: serialization should be canonical expected "calc(10px + min(1em))" but got "calc(1em + 10px)"
+FAIL e.style['letter-spacing'] = "calc(10px + max(1em))" should set the property value assert_equals: serialization should be canonical expected "calc(10px + max(1em))" but got "calc(1em + 10px)"
+FAIL e.style['letter-spacing'] = "calc(max(1em) + 10px)" should set the property value assert_equals: serialization should be canonical expected "calc(10px + max(1em))" but got "calc(1em + 10px)"
 
index a28a755..0c82215 100644 (file)
@@ -1,10 +1,10 @@
 
-FAIL e.style['opacity'] = "min(1)" should set the property value assert_equals: serialization should be canonical expected "min(1)" but got "calc(min(1))"
-FAIL e.style['opacity'] = "max(1)" should set the property value assert_equals: serialization should be canonical expected "max(1)" but got "calc(max(1))"
-FAIL e.style['opacity'] = "min(1, 2, 3)" should set the property value assert_equals: serialization should be canonical expected "min(1, 2, 3)" but got "calc(min(1, 2, 3))"
-FAIL e.style['opacity'] = "min(3, 2, 1)" should set the property value assert_equals: serialization should be canonical expected "min(3, 2, 1)" but got "calc(min(3, 2, 1))"
-FAIL e.style['opacity'] = "max(1, 2, 3)" should set the property value assert_equals: serialization should be canonical expected "max(1, 2, 3)" but got "calc(max(1, 2, 3))"
-FAIL e.style['opacity'] = "max(3, 2, 1)" should set the property value assert_equals: serialization should be canonical expected "max(3, 2, 1)" but got "calc(max(3, 2, 1))"
+PASS e.style['opacity'] = "min(1)" should set the property value 
+PASS e.style['opacity'] = "max(1)" should set the property value 
+FAIL e.style['opacity'] = "min(1, 2, 3)" should set the property value assert_equals: serialization should be canonical expected "min(1, 2, 3)" but got "min(1)"
+FAIL e.style['opacity'] = "min(3, 2, 1)" should set the property value assert_equals: serialization should be canonical expected "min(3, 2, 1)" but got "min(1)"
+FAIL e.style['opacity'] = "max(1, 2, 3)" should set the property value assert_equals: serialization should be canonical expected "max(1, 2, 3)" but got "max(3)"
+FAIL e.style['opacity'] = "max(3, 2, 1)" should set the property value assert_equals: serialization should be canonical expected "max(3, 2, 1)" but got "max(3)"
 FAIL e.style['opacity'] = "calc(min(1) + min(2))" should set the property value assert_equals: serialization should be canonical expected "calc(min(1) + min(2))" but got "calc(3)"
 FAIL e.style['opacity'] = "calc(max(1) + max(2))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1) + max(2))" but got "calc(3)"
 FAIL e.style['opacity'] = "calc(1 + min(1))" should set the property value assert_equals: serialization should be canonical expected "calc(1 + min(1))" but got "calc(2)"
index 9ce9683..2d2e182 100644 (file)
@@ -1,14 +1,14 @@
 
-FAIL e.style['margin-left'] = "min(1%)" should set the property value assert_equals: serialization should be canonical expected "min(1%)" but got "calc(min(1%))"
-FAIL e.style['margin-left'] = "max(1%)" should set the property value assert_equals: serialization should be canonical expected "max(1%)" but got "calc(max(1%))"
-FAIL e.style['margin-left'] = "min(1%, 2%, 3%)" should set the property value assert_equals: serialization should be canonical expected "min(1%, 2%, 3%)" but got "calc(min(1%, 2%, 3%))"
-FAIL e.style['margin-left'] = "min(3%, 2%, 1%)" should set the property value assert_equals: serialization should be canonical expected "min(3%, 2%, 1%)" but got "calc(min(3%, 2%, 1%))"
-FAIL e.style['margin-left'] = "max(1%, 2%, 3%)" should set the property value assert_equals: serialization should be canonical expected "max(1%, 2%, 3%)" but got "calc(max(1%, 2%, 3%))"
-FAIL e.style['margin-left'] = "max(3%, 2%, 1%)" should set the property value assert_equals: serialization should be canonical expected "max(3%, 2%, 1%)" but got "calc(max(3%, 2%, 1%))"
-FAIL e.style['margin-left'] = "calc(min(1%) + min(2%))" should set the property value assert_equals: serialization should be canonical expected "calc(min(1%) + min(2%))" but got "calc((min(1%)) + (min(2%)))"
-FAIL e.style['margin-left'] = "calc(max(1%) + max(2%))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1%) + max(2%))" but got "calc((max(1%)) + (max(2%)))"
-FAIL e.style['margin-left'] = "calc(1% + min(1%))" should set the property value assert_equals: serialization should be canonical expected "calc(1% + min(1%))" but got "calc(1% + (min(1%)))"
-FAIL e.style['margin-left'] = "calc(min(1%) + 1%)" should set the property value assert_equals: serialization should be canonical expected "calc(1% + min(1%))" but got "calc((min(1%)) + 1%)"
-FAIL e.style['margin-left'] = "calc(1% + max(1%))" should set the property value assert_equals: serialization should be canonical expected "calc(1% + max(1%))" but got "calc(1% + (max(1%)))"
-FAIL e.style['margin-left'] = "calc(max(1%) + 1%)" should set the property value assert_equals: serialization should be canonical expected "calc(1% + max(1%))" but got "calc((max(1%)) + 1%)"
+PASS e.style['margin-left'] = "min(1%)" should set the property value 
+PASS e.style['margin-left'] = "max(1%)" should set the property value 
+FAIL e.style['margin-left'] = "min(1%, 2%, 3%)" should set the property value assert_equals: serialization should be canonical expected "min(1%, 2%, 3%)" but got "min(1%)"
+FAIL e.style['margin-left'] = "min(3%, 2%, 1%)" should set the property value assert_equals: serialization should be canonical expected "min(3%, 2%, 1%)" but got "min(1%)"
+FAIL e.style['margin-left'] = "max(1%, 2%, 3%)" should set the property value assert_equals: serialization should be canonical expected "max(1%, 2%, 3%)" but got "max(3%)"
+FAIL e.style['margin-left'] = "max(3%, 2%, 1%)" should set the property value assert_equals: serialization should be canonical expected "max(3%, 2%, 1%)" but got "max(3%)"
+FAIL e.style['margin-left'] = "calc(min(1%) + min(2%))" should set the property value assert_equals: serialization should be canonical expected "calc(min(1%) + min(2%))" but got "calc(3%)"
+FAIL e.style['margin-left'] = "calc(max(1%) + max(2%))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1%) + max(2%))" but got "calc(3%)"
+FAIL e.style['margin-left'] = "calc(1% + min(1%))" should set the property value assert_equals: serialization should be canonical expected "calc(1% + min(1%))" but got "calc(2%)"
+FAIL e.style['margin-left'] = "calc(min(1%) + 1%)" should set the property value assert_equals: serialization should be canonical expected "calc(1% + min(1%))" but got "calc(2%)"
+FAIL e.style['margin-left'] = "calc(1% + max(1%))" should set the property value assert_equals: serialization should be canonical expected "calc(1% + max(1%))" but got "calc(2%)"
+FAIL e.style['margin-left'] = "calc(max(1%) + 1%)" should set the property value assert_equals: serialization should be canonical expected "calc(1% + max(1%))" but got "calc(2%)"
 
index 4f698af..0493c97 100644 (file)
@@ -1,24 +1,24 @@
 
-FAIL Property transition-delay value 'min(1s)' computes to '1s' assert_equals: expected "1s" but got "0.001s"
+PASS Property transition-delay value 'min(1s)' computes to '1s' 
 PASS Property transition-delay value 'min(1ms)' computes to '0.001s' 
-FAIL Property transition-delay value 'max(1s)' computes to '1s' assert_equals: expected "1s" but got "0.001s"
+PASS Property transition-delay value 'max(1s)' computes to '1s' 
 PASS Property transition-delay value 'max(1ms)' computes to '0.001s' 
-FAIL Property transition-delay value 'min(1s, 2s)' computes to '1s' assert_equals: expected "1s" but got "0.001s"
+PASS Property transition-delay value 'min(1s, 2s)' computes to '1s' 
 PASS Property transition-delay value 'min(1ms, 2ms)' computes to '0.001s' 
-FAIL Property transition-delay value 'max(1s, 2s)' computes to '2s' assert_equals: expected "2s" but got "0.002s"
+PASS Property transition-delay value 'max(1s, 2s)' computes to '2s' 
 PASS Property transition-delay value 'max(1ms, 2ms)' computes to '0.002s' 
-FAIL Property transition-delay value 'min(1s, 1100ms)' computes to '1s' assert_equals: expected "1s" but got "0.001s"
+PASS Property transition-delay value 'min(1s, 1100ms)' computes to '1s' 
 PASS Property transition-delay value 'max(0.9s, 1000ms)' computes to '1s' 
-FAIL Property transition-delay value 'min(2s, max(1s, 1500ms))' computes to '1.5s' assert_equals: expected "1.5s" but got "0.002s"
-FAIL Property transition-delay value 'max(1000ms, min(2000ms, 1.5s))' computes to '1.5s' assert_equals: expected "1.5s" but got "1s"
-FAIL Property transition-delay value 'calc(min(0.5s, 600ms) + 500ms)' computes to '1s' assert_equals: expected "1s" but got "0.5005s"
-FAIL Property transition-delay value 'calc(min(0.5s, 600ms) - 500ms)' computes to '0s' assert_equals: expected "0s" but got "-0.4995s"
-FAIL Property transition-delay value 'calc(min(0.5s, 600ms) * 2)' computes to '1s' assert_equals: expected "1s" but got "0.001s"
-FAIL Property transition-delay value 'calc(min(0.5s, 600ms) / 2)' computes to '0.25s' assert_equals: expected "0.25s" but got "0.00025s"
-FAIL Property transition-delay value 'calc(max(0.5s, 400ms) + 500ms)' computes to '1s' assert_equals: expected "1s" but got "0.9s"
-FAIL Property transition-delay value 'calc(max(0.5s, 400ms) - 500ms)' computes to '0s' assert_equals: expected "0s" but got "-0.1s"
-FAIL Property transition-delay value 'calc(max(0.5s, 400ms) * 2)' computes to '1s' assert_equals: expected "1s" but got "0.8s"
-FAIL Property transition-delay value 'calc(max(0.5s, 400ms) / 2)' computes to '0.25s' assert_equals: expected "0.25s" but got "0.2s"
-FAIL Property transition-delay value 'calc(min(0.5s, 600ms) + max(500ms, 0.4s))' computes to '1s' assert_equals: expected "1s" but got "0.5005s"
-FAIL Property transition-delay value 'calc(min(0.5s, 600ms) - max(500ms, 0.4s))' computes to '0s' assert_equals: expected "0s" but got "-0.4995s"
+PASS Property transition-delay value 'min(2s, max(1s, 1500ms))' computes to '1.5s' 
+PASS Property transition-delay value 'max(1000ms, min(2000ms, 1.5s))' computes to '1.5s' 
+PASS Property transition-delay value 'calc(min(0.5s, 600ms) + 500ms)' computes to '1s' 
+PASS Property transition-delay value 'calc(min(0.5s, 600ms) - 500ms)' computes to '0s' 
+PASS Property transition-delay value 'calc(min(0.5s, 600ms) * 2)' computes to '1s' 
+PASS Property transition-delay value 'calc(min(0.5s, 600ms) / 2)' computes to '0.25s' 
+PASS Property transition-delay value 'calc(max(0.5s, 400ms) + 500ms)' computes to '1s' 
+PASS Property transition-delay value 'calc(max(0.5s, 400ms) - 500ms)' computes to '0s' 
+PASS Property transition-delay value 'calc(max(0.5s, 400ms) * 2)' computes to '1s' 
+PASS Property transition-delay value 'calc(max(0.5s, 400ms) / 2)' computes to '0.25s' 
+PASS Property transition-delay value 'calc(min(0.5s, 600ms) + max(500ms, 0.4s))' computes to '1s' 
+PASS Property transition-delay value 'calc(min(0.5s, 600ms) - max(500ms, 0.4s))' computes to '0s' 
 
index 6696402..db5cc4b 100644 (file)
@@ -1,22 +1,22 @@
 
-FAIL e.style['transition-delay'] = "min(1ms)" should set the property value assert_equals: serialization should be canonical expected "min(1ms)" but got "calc(min(1ms))"
-FAIL e.style['transition-delay'] = "min(1s)" should set the property value assert_equals: serialization should be canonical expected "min(1s)" but got "calc(min(1s))"
-FAIL e.style['transition-delay'] = "max(1ms)" should set the property value assert_equals: serialization should be canonical expected "max(1ms)" but got "calc(max(1ms))"
-FAIL e.style['transition-delay'] = "max(1s)" should set the property value assert_equals: serialization should be canonical expected "max(1s)" but got "calc(max(1s))"
-FAIL e.style['transition-delay'] = "min(1ms, 2ms, 3ms)" should set the property value assert_equals: serialization should be canonical expected "min(1ms, 2ms, 3ms)" but got "calc(min(1ms, 2ms, 3ms))"
-FAIL e.style['transition-delay'] = "min(3ms, 2ms, 1ms)" should set the property value assert_equals: serialization should be canonical expected "min(3ms, 2ms, 1ms)" but got "calc(min(3ms, 2ms, 1ms))"
-FAIL e.style['transition-delay'] = "max(1ms, 2ms, 3ms)" should set the property value assert_equals: serialization should be canonical expected "max(1ms, 2ms, 3ms)" but got "calc(max(1ms, 2ms, 3ms))"
-FAIL e.style['transition-delay'] = "max(3ms, 2ms, 1ms)" should set the property value assert_equals: serialization should be canonical expected "max(3ms, 2ms, 1ms)" but got "calc(max(3ms, 2ms, 1ms))"
-FAIL e.style['transition-delay'] = "min(1000ms, 1s)" should set the property value assert_equals: serialization should be canonical expected "min(1000ms, 1s)" but got "calc(min(1000ms, 1s))"
-FAIL e.style['transition-delay'] = "min(1s, 1000ms)" should set the property value assert_equals: serialization should be canonical expected "min(1s, 1000ms)" but got "calc(min(1s, 1000ms))"
-FAIL e.style['transition-delay'] = "max(1000ms, 1s)" should set the property value assert_equals: serialization should be canonical expected "max(1000ms, 1s)" but got "calc(max(1000ms, 1s))"
-FAIL e.style['transition-delay'] = "max(1s, 1000ms)" should set the property value assert_equals: serialization should be canonical expected "max(1s, 1000ms)" but got "calc(max(1s, 1000ms))"
-FAIL e.style['transition-delay'] = "calc(min(1s) + min(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(min(1s) + min(2s))" but got "calc(3ms)"
-FAIL e.style['transition-delay'] = "calc(min(2s) + min(1s))" should set the property value assert_equals: serialization should be canonical expected "calc(min(2s) + min(1s))" but got "calc(3ms)"
-FAIL e.style['transition-delay'] = "calc(max(1s) + max(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1s) + max(2s))" but got "calc(3ms)"
-FAIL e.style['transition-delay'] = "calc(max(2s) + max(1s))" should set the property value assert_equals: serialization should be canonical expected "calc(max(2s) + max(1s))" but got "calc(3ms)"
-FAIL e.style['transition-delay'] = "calc(1s + min(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(1s + min(2s))" but got "calc(1002ms)"
-FAIL e.style['transition-delay'] = "calc(min(2s) + 1s)" should set the property value assert_equals: serialization should be canonical expected "calc(1s + min(2s))" but got "calc(1002ms)"
-FAIL e.style['transition-delay'] = "calc(1s + max(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(1s + max(2s))" but got "calc(1002ms)"
-FAIL e.style['transition-delay'] = "calc(max(2s) + 1s)" should set the property value assert_equals: serialization should be canonical expected "calc(1s + max(2s))" but got "calc(1002ms)"
+PASS e.style['transition-delay'] = "min(1ms)" should set the property value 
+FAIL e.style['transition-delay'] = "min(1s)" should set the property value assert_equals: serialization should be canonical expected "min(1s)" but got "min(1000ms)"
+PASS e.style['transition-delay'] = "max(1ms)" should set the property value 
+FAIL e.style['transition-delay'] = "max(1s)" should set the property value assert_equals: serialization should be canonical expected "max(1s)" but got "max(1000ms)"
+FAIL e.style['transition-delay'] = "min(1ms, 2ms, 3ms)" should set the property value assert_equals: serialization should be canonical expected "min(1ms, 2ms, 3ms)" but got "min(1ms)"
+FAIL e.style['transition-delay'] = "min(3ms, 2ms, 1ms)" should set the property value assert_equals: serialization should be canonical expected "min(3ms, 2ms, 1ms)" but got "min(1ms)"
+FAIL e.style['transition-delay'] = "max(1ms, 2ms, 3ms)" should set the property value assert_equals: serialization should be canonical expected "max(1ms, 2ms, 3ms)" but got "max(3ms)"
+FAIL e.style['transition-delay'] = "max(3ms, 2ms, 1ms)" should set the property value assert_equals: serialization should be canonical expected "max(3ms, 2ms, 1ms)" but got "max(3ms)"
+FAIL e.style['transition-delay'] = "min(1000ms, 1s)" should set the property value assert_equals: serialization should be canonical expected "min(1000ms, 1s)" but got "min(1000ms)"
+FAIL e.style['transition-delay'] = "min(1s, 1000ms)" should set the property value assert_equals: serialization should be canonical expected "min(1s, 1000ms)" but got "min(1000ms)"
+FAIL e.style['transition-delay'] = "max(1000ms, 1s)" should set the property value assert_equals: serialization should be canonical expected "max(1000ms, 1s)" but got "max(1000ms)"
+FAIL e.style['transition-delay'] = "max(1s, 1000ms)" should set the property value assert_equals: serialization should be canonical expected "max(1s, 1000ms)" but got "max(1000ms)"
+FAIL e.style['transition-delay'] = "calc(min(1s) + min(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(min(1s) + min(2s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(min(2s) + min(1s))" should set the property value assert_equals: serialization should be canonical expected "calc(min(2s) + min(1s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(max(1s) + max(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1s) + max(2s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(max(2s) + max(1s))" should set the property value assert_equals: serialization should be canonical expected "calc(max(2s) + max(1s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(1s + min(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(1s + min(2s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(min(2s) + 1s)" should set the property value assert_equals: serialization should be canonical expected "calc(1s + min(2s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(1s + max(2s))" should set the property value assert_equals: serialization should be canonical expected "calc(1s + max(2s))" but got "calc(3000ms)"
+FAIL e.style['transition-delay'] = "calc(max(2s) + 1s)" should set the property value assert_equals: serialization should be canonical expected "calc(1s + max(2s))" but got "calc(3000ms)"
 
index cdb3d87..94d4e5a 100644 (file)
@@ -1,8 +1,8 @@
 
 PASS Property stroke-dasharray value 'none' computes to 'none' 
 PASS Property stroke-dasharray value '10' computes to '10px' 
-FAIL Property stroke-dasharray value 'calc(10px + 0.5em)' computes to '30px' assert_equals: expected "30px" but got "10.5px"
-FAIL Property stroke-dasharray value 'calc(10px - 0.5em)' computes to '0px' assert_equals: expected "0px" but got "9.5px"
+FAIL Property stroke-dasharray value 'calc(10px + 0.5em)' computes to '30px' assert_equals: expected "30px" but got "10px"
+FAIL Property stroke-dasharray value 'calc(10px - 0.5em)' computes to '0px' assert_equals: expected "0px" but got "10px"
 PASS Property stroke-dasharray value '40%' computes to '40%' 
 FAIL Property stroke-dasharray value 'calc(50% + 60px)' computes to 'calc(50% + 60px)' assert_equals: expected "calc(50% + 60px)" but got "0"
 PASS Property stroke-dasharray value '10px 20% 30px' computes to '10px, 20%, 30px' 
index a93b4d5..7a9a4b6 100644 (file)
@@ -7,13 +7,13 @@ testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300deg/2))'
 PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(150deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(150deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300rad/2))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(150rad))"
+FAIL testDiv.style['-webkit-filter'] should be hue-rotate(calc(150rad)). Was hue-rotate(calc(8594.366926962348deg)).
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(8594.366926962348deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300grad/2))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(150grad))"
+FAIL testDiv.style['-webkit-filter'] should be hue-rotate(calc(150grad)). Was hue-rotate(calc(135deg)).
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(135deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(3turn/2))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(1.5turn))"
+FAIL testDiv.style['-webkit-filter'] should be hue-rotate(calc(1.5turn)). Was hue-rotate(calc(540deg)).
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(540deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(1turn + 10deg))'
 PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(370deg))"
@@ -22,13 +22,13 @@ testDiv.style['-webkit-filter'] = 'hue-rotate(calc(300deg + 30deg))'
 PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(330deg))"
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(330deg)"
 testDiv.style['-webkit-filter'] = 'hue-rotate(calc(2turn + 1turn))'
-PASS testDiv.style['-webkit-filter'] is "hue-rotate(calc(3turn))"
+FAIL testDiv.style['-webkit-filter'] should be hue-rotate(calc(3turn)). Was hue-rotate(calc(1080deg)).
 PASS window.getComputedStyle(testDiv).getPropertyValue('-webkit-filter') is "hue-rotate(1080deg)"
 testDiv.style['transition-delay'] = 'calc(300ms/2)'
 PASS testDiv.style['transition-delay'] is "calc(150ms)"
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "0.15s"
 testDiv.style['transition-delay'] = 'calc(300s/2)'
-PASS testDiv.style['transition-delay'] is "calc(150s)"
+FAIL testDiv.style['transition-delay'] should be calc(150s). Was calc(150000ms).
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "150s"
 testDiv.style['transition-delay'] = 'calc(10s + 100ms)'
 PASS testDiv.style['transition-delay'] is "calc(10100ms)"
@@ -37,7 +37,7 @@ testDiv.style['transition-delay'] = 'calc(100ms + 10s)'
 PASS testDiv.style['transition-delay'] is "calc(10100ms)"
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "10.1s"
 testDiv.style['transition-delay'] = 'calc(4s + 1s)'
-PASS testDiv.style['transition-delay'] is "calc(5s)"
+FAIL testDiv.style['transition-delay'] should be calc(5s). Was calc(5000ms).
 PASS window.getComputedStyle(testDiv).getPropertyValue('transition-delay') is "5s"
 testDiv.style['transition-delay'] = 'calc(4ms + 1ms)'
 PASS testDiv.style['transition-delay'] is "calc(5ms)"
index f6fbd08..a9a9497 100644 (file)
@@ -3,7 +3,7 @@ Ensure that the captions container accounts for the controsl bar when visible.
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS getComputedStyle(shadowRoot.lastElementChild.firstElementChild).height is "calc((100% - 31px) - 6px)"
+PASS getComputedStyle(shadowRoot.lastElementChild.firstElementChild).height is "calc(100% - 37px)"
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3a8ae0c..97534e6 100644 (file)
@@ -15,7 +15,7 @@ const shadowRoot = window.internals.shadowRoot(media);
 media.addEventListener("play", () => {
     media.pause();
 
-    shouldBeEqualToString("getComputedStyle(shadowRoot.lastElementChild.firstElementChild).height", "calc((100% - 31px) - 6px)");
+    shouldBeEqualToString("getComputedStyle(shadowRoot.lastElementChild.firstElementChild).height", "calc(100% - 37px)");
     media.remove();
     finishJSTest();
 });
index 9089d50..d635bb9 100644 (file)
         (WebCore::collectScreenProperties):
         (WebCore::screenSupportsHighDynamicRange):
 
+2019-12-03  Simon Fraser  <simon.fraser@apple.com>
+
+        calc() serialization doesn't match the spec
+        https://bugs.webkit.org/show_bug.cgi?id=203442
+        <rdar://problem/56639402>
+
+        Reviewed by Dean Jackson.
+
+        These changes implement the calc() simplification and serialization rules described in
+        https://drafts.csswg.org/css-values-4/#calc-internal. They also make it easy to add the
+        new math functions in future.
+        
+        The major changes are:
+
+        1. Create Negate and Invert nodes to handle subtraction and division, so that
+           Product and Sum are the only math operators that are tracked, which makes
+           it possible to sort child nodes for simplification.
+
+        2. After parsing, do a bottom-up simplification parse following
+           <https://drafts.csswg.org/css-values-4/#calc-simplification>, which sorts child nodes
+           according to <https://drafts.csswg.org/css-values-4/#sort-a-calculations-children>,
+           combines child nodes where possible, and hoists child nodes up to their parents were
+           possible. Simplification always preserves a calc-like function at the root.
+           
+           This simplification also does unit canonicalization (e.g. all absolute lengths get px units).
+           To make unit conversion more explicit, pass a CSSUnitType into doubleValue() methods so that
+           it's clear what units the resulting value is in.
+           
+        3. Serialize according to <https://drafts.csswg.org/css-values-4/#calc-serialize>
+           which fixes bugs with nested parentheses.
+
+        This patch does not completely implement the type checking and conversion rules described in <https://drafts.csswg.org/css-values-4/#calc-type-checking>
+        and <https://drafts.css-houdini.org/css-typed-om-1/#numeric-typing>.
+
+        Tests: fast/css/calc-parsing-limits.html
+               fast/css/calc-parsing.html
+
+        * css/CSSCalculationValue.cpp:
+        (WebCore::calculationCategoryForCombination):
+        (WebCore::canonicalUnitTypeForCalculationCategory):
+        (WebCore::functionFromOperator):
+        (WebCore::CSSCalcPrimitiveValueNode::isNumericValue const):
+        (WebCore::CSSCalcPrimitiveValueNode::isNegative const):
+        (WebCore::CSSCalcPrimitiveValueNode::negate):
+        (WebCore::CSSCalcPrimitiveValueNode::invert):
+        (WebCore::CSSCalcPrimitiveValueNode::add):
+        (WebCore::CSSCalcPrimitiveValueNode::multiply):
+        (WebCore::CSSCalcPrimitiveValueNode::convertToUnitType):
+        (WebCore::CSSCalcPrimitiveValueNode::canonicalizeUnit):
+        (WebCore::CSSCalcPrimitiveValueNode::doubleValue const):
+        (WebCore::CSSCalcNegateNode::createCalcExpression const):
+        (WebCore::CSSCalcNegateNode::dump const):
+        (WebCore::CSSCalcInvertNode::createCalcExpression const):
+        (WebCore::CSSCalcInvertNode::dump const):
+        (WebCore::categoryForInvert):
+        (WebCore::determineCategory):
+        (WebCore::CSSCalcOperationNode::create):
+        (WebCore::CSSCalcOperationNode::createSum):
+        (WebCore::CSSCalcOperationNode::createProduct):
+        (WebCore::CSSCalcOperationNode::hoistChildrenWithOperator):
+        (WebCore::sortingCategoryForType):
+        (WebCore::sortingCategory):
+        (WebCore::primitiveTypeForCombination):
+        (WebCore::conversionToAddValuesWithTypes):
+        (WebCore::CSSCalcOperationNode::canCombineAllChildren const):
+        (WebCore::CSSCalcOperationNode::combineChildren):
+        (WebCore::CSSCalcOperationNode::simplify):
+        (WebCore::CSSCalcOperationNode::simplifyRecursive):
+        (WebCore::CSSCalcOperationNode::simplifyNode):
+        (WebCore::CSSCalcOperationNode::primitiveType const):
+        (WebCore::CSSCalcOperationNode::doubleValue const):
+        (WebCore::CSSCalcOperationNode::computeLengthPx const):
+        (WebCore::CSSCalcOperationNode::buildCSSText):
+        (WebCore::functionPrefixForOperator):
+        (WebCore::CSSCalcOperationNode::buildCSSTextRecursive):
+        (WebCore::CSSCalcOperationNode::evaluateOperator):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalc):
+        (WebCore::checkDepthAndIndex):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
+        (WebCore::CSSCalcExpressionNodeParser::parseValue):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcValue):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcProduct):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcSum):
+        (WebCore::createCSS):
+        (WebCore::CSSCalcValue::customCSSText const):
+        (WebCore::CSSCalcValue::doubleValue const):
+        (WebCore::CSSCalcValue::create):
+        (WebCore::CSSCalcOperationNode::createSimplified): Deleted.
+        (WebCore::CSSCalcOperationNode::buildCssText): Deleted.
+        (WebCore::CSSCalcOperationNode::customCSSText const): Deleted.
+        (WebCore::CSSCalcExpressionNodeParser::parseValueTerm): Deleted.
+        (WebCore::CSSCalcExpressionNodeParser::parseValueMultiplicativeExpression): Deleted.
+        (WebCore::CSSCalcExpressionNodeParser::parseAdditiveValueExpression): Deleted.
+        (WebCore::CSSCalcExpressionNodeParser::parseMinMaxExpression): Deleted.
+        (WebCore::CSSCalcExpressionNodeParser::parseValueExpression): Deleted.
+        * css/CSSCalculationValue.h:
+        * css/CSSPrimitiveValue.cpp:
+        (WebCore::CSSPrimitiveValue::unitTypeString):
+        * css/CSSPrimitiveValue.h:
+        * platform/CalculationValue.cpp:
+        (WebCore::CalcExpressionNegation::evaluate const):
+        (WebCore::CalcExpressionNegation::operator== const):
+        (WebCore::CalcExpressionNegation::dump const):
+        (WebCore::operator==):
+        (WebCore::CalcExpressionInversion::evaluate const):
+        (WebCore::CalcExpressionInversion::dump const):
+        (WebCore::CalcExpressionInversion::operator== const):
+        (WebCore::CalcExpressionOperation::evaluate const):
+        (WebCore::operator<<):
+        * platform/CalculationValue.h:
+
 2019-12-03  Tim Horton  <timothy_horton@apple.com>
 
         Start adding encoding support for DisplayList and some DisplayListItems
index c6df54d..ebef0e8 100644 (file)
@@ -46,10 +46,14 @@ static const int maxExpressionDepth = 100;
 namespace WebCore {
 class CSSCalcPrimitiveValueNode;
 class CSSCalcOperationNode;
+class CSSCalcNegateNode;
+class CSSCalcInvertNode;
 }
 
 SPECIALIZE_TYPE_TRAITS_CSSCALCEXPRESSION_NODE(CSSCalcPrimitiveValueNode, type() == WebCore::CSSCalcExpressionNode::Type::CssCalcPrimitiveValue)
 SPECIALIZE_TYPE_TRAITS_CSSCALCEXPRESSION_NODE(CSSCalcOperationNode, type() == WebCore::CSSCalcExpressionNode::Type::CssCalcOperation)
+SPECIALIZE_TYPE_TRAITS_CSSCALCEXPRESSION_NODE(CSSCalcNegateNode, type() == WebCore::CSSCalcExpressionNode::Type::CssCalcNegate)
+SPECIALIZE_TYPE_TRAITS_CSSCALCEXPRESSION_NODE(CSSCalcInvertNode, type() == WebCore::CSSCalcExpressionNode::Type::CssCalcInvert)
 
 namespace WebCore {
 
@@ -101,6 +105,63 @@ static CalculationCategory calcUnitCategory(CSSUnitType type)
     }
 }
 
+static CalculationCategory calculationCategoryForCombination(CSSUnitType type)
+{
+    switch (type) {
+    case CSSUnitType::CSS_NUMBER:
+        return CalculationCategory::Number;
+    case CSSUnitType::CSS_PX:
+    case CSSUnitType::CSS_CM:
+    case CSSUnitType::CSS_MM:
+    case CSSUnitType::CSS_IN:
+    case CSSUnitType::CSS_PT:
+    case CSSUnitType::CSS_PC:
+    case CSSUnitType::CSS_Q:
+        return CalculationCategory::Length;
+    case CSSUnitType::CSS_PERCENTAGE:
+        return CalculationCategory::Percent;
+    case CSSUnitType::CSS_DEG:
+    case CSSUnitType::CSS_RAD:
+    case CSSUnitType::CSS_GRAD:
+    case CSSUnitType::CSS_TURN:
+        return CalculationCategory::Angle;
+    case CSSUnitType::CSS_MS:
+    case CSSUnitType::CSS_S:
+        return CalculationCategory::Time;
+    case CSSUnitType::CSS_HZ:
+    case CSSUnitType::CSS_KHZ:
+        return CalculationCategory::Frequency;
+    case CSSUnitType::CSS_EMS:
+    case CSSUnitType::CSS_EXS:
+    case CSSUnitType::CSS_REMS:
+    case CSSUnitType::CSS_CHS:
+    case CSSUnitType::CSS_VW:
+    case CSSUnitType::CSS_VH:
+    case CSSUnitType::CSS_VMIN:
+    case CSSUnitType::CSS_VMAX:
+    default:
+        return CalculationCategory::Other;
+    }
+}
+
+static CSSUnitType canonicalUnitTypeForCalculationCategory(CalculationCategory category)
+{
+    switch (category) {
+    case CalculationCategory::Number: return CSSUnitType::CSS_NUMBER;
+    case CalculationCategory::Length: return CSSUnitType::CSS_PX;
+    case CalculationCategory::Percent: return CSSUnitType::CSS_PERCENTAGE;
+    case CalculationCategory::Angle: return CSSUnitType::CSS_DEG;
+    case CalculationCategory::Time: return CSSUnitType::CSS_MS;
+    case CalculationCategory::Frequency: return CSSUnitType::CSS_HZ;
+    case CalculationCategory::Other:
+    case CalculationCategory::PercentNumber:
+    case CalculationCategory::PercentLength:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+    return CSSUnitType::CSS_UNKNOWN;
+}
+
 static bool hasDoubleValue(CSSUnitType type)
 {
     switch (type) {
@@ -161,6 +222,22 @@ static bool hasDoubleValue(CSSUnitType type)
     return false;
 }
 
+static CSSValueID functionFromOperator(CalcOperator op)
+{
+    switch (op) {
+    case CalcOperator::Add:
+    case CalcOperator::Subtract:
+    case CalcOperator::Multiply:
+    case CalcOperator::Divide:
+        return CSSValueCalc;
+    case CalcOperator::Min:
+        return CSSValueMin;
+    case CalcOperator::Max:
+        return CSSValueMax;
+    }
+    return CSSValueCalc;
+}
+
 #if !LOG_DISABLED
 static String prettyPrintNode(const CSSCalcExpressionNode& node)
 {
@@ -192,6 +269,35 @@ public:
         return adoptRef(new CSSCalcPrimitiveValueNode(CSSPrimitiveValue::create(value, type)));
     }
 
+    String customCSSText() const
+    {
+        return m_value->cssText();
+    }
+
+    CSSUnitType primitiveType() const final
+    {
+        return m_value->primitiveType();
+    }
+
+    bool isNumericValue() const;
+    bool isNegative() const;
+
+    void negate();
+    void invert();
+
+    enum class UnitConversion {
+        Invalid,
+        Preserve,
+        Canonicalize
+    };
+    void add(const CSSCalcPrimitiveValueNode&, UnitConversion = UnitConversion::Preserve);
+    void multiply(double);
+
+    void convertToUnitType(CSSUnitType);
+    void canonicalizeUnit();
+
+    const CSSPrimitiveValue& value() const { return m_value.get(); }
+
 private:
     bool isZero() const final
     {
@@ -201,23 +307,13 @@ private:
     bool equals(const CSSCalcExpressionNode& other) const final;
     Type type() const final { return CssCalcPrimitiveValue; }
 
-    CSSUnitType primitiveType() const final
-    {
-        return CSSUnitType(m_value->primitiveType());
-    }
-
     std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const final;
-    double doubleValue() const final;
+    double doubleValue(CSSUnitType) 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();
-    }
-
     void dump(TextStream&) const final;
 
 private:
@@ -230,6 +326,86 @@ private:
     Ref<CSSPrimitiveValue> m_value;
 };
 
+// FIXME: Use calcUnitCategory?
+bool CSSCalcPrimitiveValueNode::isNumericValue() const
+{
+    return m_value->isLength() || m_value->isNumber() || m_value->isPercentage() || m_value->isAngle()
+        || m_value->isTime() || m_value->isResolution() || m_value->isFlex() || m_value->isFrequency();
+}
+
+bool CSSCalcPrimitiveValueNode::isNegative() const
+{
+    return isNumericValue() && m_value->doubleValue() < 0.0;
+}
+
+void CSSCalcPrimitiveValueNode::negate()
+{
+    ASSERT(isNumericValue());
+    m_value = CSSPrimitiveValue::create(0.0 - m_value->doubleValue(), m_value->primitiveType());
+}
+
+void CSSCalcPrimitiveValueNode::invert()
+{
+    ASSERT(isNumericValue());
+    if (!m_value->doubleValue())
+        m_value = CSSPrimitiveValue::create(std::numeric_limits<double>::infinity(), m_value->primitiveType());
+
+    m_value = CSSPrimitiveValue::create(1.0 / m_value->doubleValue(), m_value->primitiveType());
+}
+
+void CSSCalcPrimitiveValueNode::add(const CSSCalcPrimitiveValueNode& node, UnitConversion unitConversion)
+{
+    auto valueType = m_value->primitiveType();
+
+    switch (unitConversion) {
+    case UnitConversion::Invalid:
+        ASSERT_NOT_REACHED();
+        break;
+    case UnitConversion::Preserve:
+        ASSERT(node.primitiveType() == valueType);
+        m_value = CSSPrimitiveValue::create(m_value->doubleValue() + node.doubleValue(valueType), valueType);
+        break;
+    case UnitConversion::Canonicalize: {
+        auto valueCategory = unitCategory(valueType);
+        // FIXME: It's awkward that canonicalUnitTypeForCategory() has special handling for CSSUnitCategory::Percent.
+        auto canonicalType = valueCategory == CSSUnitCategory::Percent ? CSSUnitType::CSS_PERCENTAGE : canonicalUnitTypeForCategory(valueCategory);
+        ASSERT(canonicalType != CSSUnitType::CSS_UNKNOWN);
+        double leftValue = m_value->doubleValue(canonicalType);
+        double rightValue = node.doubleValue(canonicalType);
+        m_value = CSSPrimitiveValue::create(leftValue + rightValue, canonicalType);
+        break;
+    }
+    }
+}
+
+void CSSCalcPrimitiveValueNode::multiply(double multiplier)
+{
+    auto valueType = m_value->primitiveType();
+    ASSERT(hasDoubleValue(valueType));
+    m_value = CSSPrimitiveValue::create(m_value->doubleValue(valueType) * multiplier, valueType);
+}
+
+void CSSCalcPrimitiveValueNode::convertToUnitType(CSSUnitType unitType)
+{
+    ASSERT(unitCategory(unitType) == unitCategory(m_value->primitiveType()));
+    double newValue = m_value->doubleValue(unitType);
+    m_value = CSSPrimitiveValue::create(newValue, unitType);
+}
+
+void CSSCalcPrimitiveValueNode::canonicalizeUnit()
+{
+    auto category = calculationCategoryForCombination(m_value->primitiveType());
+    if (category == CalculationCategory::Other)
+        return;
+
+    auto canonicalType = canonicalUnitTypeForCalculationCategory(category);
+    if (canonicalType == m_value->primitiveType())
+        return;
+
+    double newValue = m_value->doubleValue(canonicalType);
+    m_value = CSSPrimitiveValue::create(newValue, canonicalType);
+}
+
 std::unique_ptr<CalcExpressionNode> CSSCalcPrimitiveValueNode::createCalcExpression(const CSSToLengthConversionData& conversionData) const
 {
     switch (category()) {
@@ -254,10 +430,17 @@ std::unique_ptr<CalcExpressionNode> CSSCalcPrimitiveValueNode::createCalcExpress
     return nullptr;
 }
 
-double CSSCalcPrimitiveValueNode::doubleValue() const
+double CSSCalcPrimitiveValueNode::doubleValue(CSSUnitType unitType) const
 {
-    if (hasDoubleValue(primitiveType()))
-        return m_value->doubleValue();
+    if (hasDoubleValue(unitType)) {
+        // FIXME: This should ASSERT(unitCategory(m_value->primitiveType()) == unitCategory(unitType)), but only when all callers are fixed (e.g. webkit.org/b/204826).
+        if (unitCategory(m_value->primitiveType()) != unitCategory(unitType)) {
+            LOG_WITH_STREAM(Calc, stream << "Calling doubleValue() with unit " << unitType << " on a node of unit type " << m_value->primitiveType() << " which is incompatible");
+            return 0;
+        }
+
+        return m_value->doubleValue(unitType);
+    }
     ASSERT_NOT_REACHED();
     return 0;
 }
@@ -303,9 +486,118 @@ bool CSSCalcPrimitiveValueNode::equals(const CSSCalcExpressionNode& other) const
 
 void CSSCalcPrimitiveValueNode::dump(TextStream& ts) const
 {
-    ts << "value " << m_value->customCSSText();
+    ts << "value " << m_value->customCSSText() << " (category: " << category() << ", type: " << primitiveType() << ")";
+}
+
+class CSSCalcNegateNode final : public CSSCalcExpressionNode {
+public:
+    static Ref<CSSCalcNegateNode> create(Ref<CSSCalcExpressionNode>&& child)
+    {
+        return adoptRef(*new CSSCalcNegateNode(WTFMove(child)));
+    }
+
+    const CSSCalcExpressionNode& child() const { return m_child.get(); }
+    CSSCalcExpressionNode& child() { return m_child.get(); }
+
+    void setChild(Ref<CSSCalcExpressionNode>&& child) { m_child = WTFMove(child); }
+
+private:
+    CSSCalcNegateNode(Ref<CSSCalcExpressionNode>&& child)
+        : CSSCalcExpressionNode(child->category())
+        , m_child(WTFMove(child))
+    {
+    }
+
+    std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const final;
+
+    bool isZero() const final { return m_child->isZero(); }
+    double doubleValue(CSSUnitType unitType) const final { return -m_child->doubleValue(unitType); }
+    double computeLengthPx(const CSSToLengthConversionData& conversionData) const final { return -m_child->computeLengthPx(conversionData); }
+    Type type() const final { return Type::CssCalcNegate; }
+    CSSUnitType primitiveType() const final { return m_child->primitiveType(); }
+
+    void collectDirectComputationalDependencies(HashSet<CSSPropertyID>& properties) const final { m_child->collectDirectComputationalDependencies(properties); }
+    void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& properties) const final { m_child->collectDirectRootComputationalDependencies(properties); }
+
+    void dump(TextStream&) const final;
+
+    Ref<CSSCalcExpressionNode> m_child;
+};
+
+std::unique_ptr<CalcExpressionNode> CSSCalcNegateNode::createCalcExpression(const CSSToLengthConversionData& conversionData) const
+{
+    auto childNode = m_child->createCalcExpression(conversionData);
+    return makeUnique<CalcExpressionNegation>(WTFMove(childNode));
+}
+
+void CSSCalcNegateNode::dump(TextStream& ts) const
+{
+    ts << "-" << m_child.get();
+}
+
+class CSSCalcInvertNode final : public CSSCalcExpressionNode {
+public:
+    static Ref<CSSCalcInvertNode> create(Ref<CSSCalcExpressionNode>&& child)
+    {
+        return adoptRef(*new CSSCalcInvertNode(WTFMove(child)));
+    }
+
+    const CSSCalcExpressionNode& child() const { return m_child.get(); }
+    CSSCalcExpressionNode& child() { return m_child.get(); }
+
+    void setChild(Ref<CSSCalcExpressionNode>&& child) { m_child = WTFMove(child); }
+
+private:
+    CSSCalcInvertNode(Ref<CSSCalcExpressionNode>&& child)
+        : CSSCalcExpressionNode(child->category())
+        , m_child(WTFMove(child))
+    {
+    }
+
+    std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const final;
+
+    bool isZero() const final { return m_child->isZero(); }
+    double doubleValue(CSSUnitType) const final;
+    double computeLengthPx(const CSSToLengthConversionData&) const final;
+    Type type() const final { return Type::CssCalcInvert; }
+    CSSUnitType primitiveType() const final { return m_child->primitiveType(); }
+
+    void collectDirectComputationalDependencies(HashSet<CSSPropertyID>& properties) const final { m_child->collectDirectComputationalDependencies(properties); }
+    void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& properties) const final { m_child->collectDirectRootComputationalDependencies(properties); }
+
+    void dump(TextStream&) const final;
+
+    Ref<CSSCalcExpressionNode> m_child;
+};
+
+std::unique_ptr<CalcExpressionNode> CSSCalcInvertNode::createCalcExpression(const CSSToLengthConversionData& conversionData) const
+{
+    auto childNode = m_child->createCalcExpression(conversionData);
+    return makeUnique<CalcExpressionInversion>(WTFMove(childNode));
+}
+
+double CSSCalcInvertNode::doubleValue(CSSUnitType unitType) const
+{
+    auto childValue = m_child->doubleValue(unitType);
+    if (!childValue)
+        return std::numeric_limits<double>::infinity();
+    return 1.0 / childValue;
+}
+
+double CSSCalcInvertNode::computeLengthPx(const CSSToLengthConversionData& conversionData) const
+{
+    auto childValue = m_child->computeLengthPx(conversionData);
+    if (!childValue)
+        return std::numeric_limits<double>::infinity();
+    return 1.0 / childValue;
 }
 
+void CSSCalcInvertNode::dump(TextStream& ts) const
+{
+    ts << "1/" << m_child.get();
+}
+
+// This is the result of the "To add two types type1 and type2, perform the following steps:" rules.
 
 static const CalculationCategory addSubtractResult[static_cast<unsigned>(CalculationCategory::Angle)][static_cast<unsigned>(CalculationCategory::Angle)] = {
 //    CalculationCategory::Number         CalculationCategory::Length         CalculationCategory::Percent        CalculationCategory::PercentNumber  CalculationCategory::PercentLength
@@ -349,6 +641,74 @@ static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSi
     return CalculationCategory::Other;
 }
 
+// FIXME: Need to implement correct category computation per:
+// <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-invert-a-type>
+// To invert a type type, perform the following steps:
+// Let result be a new type with an initially empty ordered map and an initially null percent hint
+// For each unit → exponent of type, set result[unit] to (-1 * exponent).
+static CalculationCategory categoryForInvert(CalculationCategory category)
+{
+    return category;
+}
+
+static CalculationCategory determineCategory(const Vector<Ref<CSSCalcExpressionNode>>& nodes, CalcOperator op)
+{
+    if (nodes.isEmpty())
+        return CalculationCategory::Other;
+
+    auto currentCategory = nodes[0]->category();
+
+    for (unsigned i = 1; i < nodes.size(); ++i) {
+        const auto& node = nodes[i].get();
+        
+        auto usedOperator = op;
+        if (node.type() == CSSCalcExpressionNode::Type::CssCalcInvert)
+            usedOperator = CalcOperator::Divide;
+
+        auto nextCategory = node.category();
+
+        switch (usedOperator) {
+        case CalcOperator::Add:
+        case CalcOperator::Subtract:
+            // <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-add-two-types>
+            // At a + or - sub-expression, attempt to add the types of the left and right arguments.
+            // If this returns failure, the entire calculation’s type is failure. Otherwise, the sub-expression’s type is the returned type.
+            if (currentCategory < CalculationCategory::Angle && nextCategory < CalculationCategory::Angle)
+                currentCategory = addSubtractResult[static_cast<unsigned>(currentCategory)][static_cast<unsigned>(nextCategory)];
+            else if (currentCategory != nextCategory)
+                return CalculationCategory::Other;
+            break;
+
+        case CalcOperator::Multiply:
+            // <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-multiply-two-types>
+            // At a * sub-expression, multiply the types of the left and right arguments. The sub-expression’s type is the returned result.
+            if (currentCategory != CalculationCategory::Number && nextCategory != CalculationCategory::Number)
+                return CalculationCategory::Other;
+
+            currentCategory = currentCategory == CalculationCategory::Number ? nextCategory : currentCategory;
+            break;
+
+        case CalcOperator::Divide: {
+            auto invertCategory = categoryForInvert(nextCategory);
+            
+            // At a / sub-expression, let left type be the result of finding the types of its left argument,
+            // and right type be the result of finding the types of its right argument and then inverting it.
+            // The sub-expression’s type is the result of multiplying the left type and right type.
+            if (invertCategory != CalculationCategory::Number || node.isZero())
+                return CalculationCategory::Other;
+            break;
+        }
+
+        case CalcOperator::Min:
+        case CalcOperator::Max:
+            // The type of a min(), max(), or clamp() expression is the result of adding the types of its comma-separated calculations
+            return CalculationCategory::Other;
+        }
+    }
+
+    return currentCategory;
+}
+
 static CalculationCategory resolvedTypeForMinOrMax(CalculationCategory category, CalculationCategory destinationCategory)
 {
     switch (category) {
@@ -382,8 +742,27 @@ class CSSCalcOperationNode final : public CSSCalcExpressionNode {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     static RefPtr<CSSCalcOperationNode> create(CalcOperator, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide);
+    static RefPtr<CSSCalcOperationNode> createSum(Vector<Ref<CSSCalcExpressionNode>>&& values);
+    static RefPtr<CSSCalcOperationNode> createProduct(Vector<Ref<CSSCalcExpressionNode>>&& values);
     static RefPtr<CSSCalcOperationNode> createMinOrMax(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory);
-    static RefPtr<CSSCalcExpressionNode> createSimplified(CalcOperator, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide);
+
+    static Ref<CSSCalcExpressionNode> simplify(Ref<CSSCalcExpressionNode>&&);
+
+    static void buildCSSText(const CSSCalcExpressionNode&, StringBuilder&);
+
+    CalcOperator calcOperator() const { return m_operator; }
+    bool isCalcSumNode() const { return m_operator == CalcOperator::Add; }
+    bool isCalcProductNode() const { return m_operator == CalcOperator::Multiply; }
+    bool isMinOrMaxNode() const { return m_operator == CalcOperator::Min || m_operator == CalcOperator::Max; }
+    bool shouldSortChildren() const { return isCalcSumNode() || isCalcProductNode(); }
+
+    void hoistChildrenWithOperator(CalcOperator);
+    void combineChildren();
+    
+    bool canCombineAllChildren() const;
+
+    const Vector<Ref<CSSCalcExpressionNode>>& children() const { return m_children; }
+    Vector<Ref<CSSCalcExpressionNode>>& children() { return m_children; }
 
 private:
     CSSCalcOperationNode(CalculationCategory category, CalcOperator op, Ref<CSSCalcExpressionNode>&& leftSide, Ref<CSSCalcExpressionNode>&& rightSide)
@@ -406,7 +785,7 @@ private:
 
     bool isZero() const final
     {
-        return !doubleValue();
+        return !doubleValue(primitiveType());
     }
 
     bool equals(const CSSCalcExpressionNode&) const final;
@@ -414,14 +793,12 @@ private:
     std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const final;
 
     CSSUnitType primitiveType() const final;
-    double doubleValue() const final;
+    double doubleValue(CSSUnitType) const final;
     double computeLengthPx(const CSSToLengthConversionData&) const final;
 
     void collectDirectComputationalDependencies(HashSet<CSSPropertyID>&) const final;
     void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>&) const final;
 
-    String customCSSText() const final;
-
     void dump(TextStream&) const final;
 
     static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode& leftSide, CSSCalcExpressionNode& rightSide)
@@ -439,7 +816,14 @@ private:
     }
 
     static double evaluateOperator(CalcOperator, const Vector<double>&);
-    static String buildCssText(Vector<String>, CalcOperator);
+    static Ref<CSSCalcExpressionNode> simplifyNode(Ref<CSSCalcExpressionNode>&&, int depth);
+    static Ref<CSSCalcExpressionNode> simplifyRecursive(Ref<CSSCalcExpressionNode>&&, int depth);
+    
+    enum class GroupingParens {
+        Omit,
+        Include
+    };
+    static void buildCSSTextRecursive(const CSSCalcExpressionNode&, StringBuilder&, GroupingParens = GroupingParens::Include);
 
     const CalcOperator m_operator;
     Vector<Ref<CSSCalcExpressionNode>> m_children;
@@ -450,6 +834,8 @@ RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::create(CalcOperator op, RefPt
     if (!leftSide || !rightSide)
         return nullptr;
 
+    ASSERT(op == CalcOperator::Add || op == CalcOperator::Multiply);
+
     ASSERT(leftSide->category() < CalculationCategory::Other);
     ASSERT(rightSide->category() < CalculationCategory::Other);
 
@@ -462,6 +848,35 @@ RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::create(CalcOperator op, RefPt
     return adoptRef(new CSSCalcOperationNode(newCategory, op, leftSide.releaseNonNull(), rightSide.releaseNonNull()));
 }
 
+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createSum(Vector<Ref<CSSCalcExpressionNode>>&& values)
+{
+    if (values.isEmpty())
+        return nullptr;
+
+    auto newCategory = determineCategory(values, CalcOperator::Add);
+    if (newCategory == CalculationCategory::Other) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create sum node because unable to determine category from " << prettyPrintNodes(values));
+        newCategory = determineCategory(values, CalcOperator::Add);
+        return nullptr;
+    }
+
+    return adoptRef(new CSSCalcOperationNode(newCategory, CalcOperator::Add, WTFMove(values)));
+}
+
+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createProduct(Vector<Ref<CSSCalcExpressionNode>>&& values)
+{
+    if (values.isEmpty())
+        return nullptr;
+
+    auto newCategory = determineCategory(values, CalcOperator::Multiply);
+    if (newCategory == CalculationCategory::Other) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create product node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+
+    return adoptRef(new CSSCalcOperationNode(newCategory, CalcOperator::Multiply, WTFMove(values)));
+}
+
 RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createMinOrMax(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory)
 {
     ASSERT(op == CalcOperator::Min || op == CalcOperator::Max);
@@ -495,78 +910,388 @@ RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createMinOrMax(CalcOperator o
     return adoptRef(new CSSCalcOperationNode(category.value(), op, WTFMove(values)));
 }
 
-RefPtr<CSSCalcExpressionNode> CSSCalcOperationNode::createSimplified(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+void CSSCalcOperationNode::hoistChildrenWithOperator(CalcOperator op)
 {
-    if (!leftSide || !rightSide)
-        return nullptr;
-
-    auto leftCategory = leftSide->category();
-    auto rightCategory = rightSide->category();
-    ASSERT(leftCategory < CalculationCategory::Other);
-    ASSERT(rightCategory < CalculationCategory::Other);
+    ASSERT(op == CalcOperator::Add || op == CalcOperator::Multiply);
 
-    // Simplify numbers.
-    if (leftCategory == CalculationCategory::Number && rightCategory == CalculationCategory::Number) {
-        CSSUnitType evaluationType = CSSUnitType::CSS_NUMBER;
-        return CSSCalcPrimitiveValueNode::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), evaluationType);
-    }
-
-    // Simplify addition and subtraction between same types.
-    if (op == CalcOperator::Add || op == CalcOperator::Subtract) {
-        if (leftCategory == rightSide->category()) {
-            CSSUnitType leftType = leftSide->primitiveType();
-            if (hasDoubleValue(leftType)) {
-                CSSUnitType rightType = rightSide->primitiveType();
-                if (leftType == rightType)
-                    return CSSCalcPrimitiveValueNode::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), leftType);
-                CSSUnitCategory leftUnitCategory = unitCategory(leftType);
-                if (leftUnitCategory != CSSUnitCategory::Other && leftUnitCategory == unitCategory(rightType)) {
-                    CSSUnitType canonicalType = canonicalUnitTypeForCategory(leftUnitCategory);
-                    if (canonicalType != CSSUnitType::CSS_UNKNOWN) {
-                        double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
-                        double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
-                        return CSSCalcPrimitiveValueNode::create(evaluateOperator(op, { leftValue, rightValue }), canonicalType);
-                    }
-                }
-            }
+    auto hasChildWithOperator = [&] (CalcOperator op) {
+        for (auto& child : m_children) {
+            if (is<CSSCalcOperationNode>(child.get()) && downcast<CSSCalcOperationNode>(child.get()).calcOperator() == op)
+                return true;
         }
-    } 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 false;
+    };
 
-        double number = numberSide->doubleValue();
-        if (!std::isfinite(number))
-            return nullptr;
-        if (op == CalcOperator::Divide && !number)
-            return nullptr;
+    if (!hasChildWithOperator(op))
+        return;
 
-        auto otherType = otherSide.primitiveType();
-        if (hasDoubleValue(otherType))
-            return CSSCalcPrimitiveValueNode::create(evaluateOperator(op, { otherSide.doubleValue(), number }), otherType);
+    Vector<Ref<CSSCalcExpressionNode>> newChildren;
+    for (auto& child : m_children) {
+        if (is<CSSCalcOperationNode>(child.get()) && downcast<CSSCalcOperationNode>(child.get()).calcOperator() == op) {
+            auto& children = downcast<CSSCalcOperationNode>(child.get()).children();
+            for (auto& childToMove : children)
+                newChildren.append(WTFMove(childToMove));
+        } else
+            newChildren.append(WTFMove(child));
     }
+    
+    newChildren.shrinkToFit();
+    m_children = WTFMove(newChildren);
+}
+
+enum class SortingCategory {
+    Number,
+    Percent,
+    Dimension,
+    Other
+};
 
-    return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
+static SortingCategory sortingCategoryForType(CSSUnitType unitType)
+{
+    static constexpr SortingCategory sortOrder[] = {
+        SortingCategory::Number,        // CalculationCategory::Number,
+        SortingCategory::Dimension,     // CalculationCategory::Length,
+        SortingCategory::Percent,       // CalculationCategory::Percent,
+        SortingCategory::Number,        // CalculationCategory::PercentNumber,
+        SortingCategory::Dimension,     // CalculationCategory::PercentLength,
+        SortingCategory::Dimension,     // CalculationCategory::Angle,
+        SortingCategory::Dimension,     // CalculationCategory::Time,
+        SortingCategory::Dimension,     // CalculationCategory::Frequency,
+        SortingCategory::Other,         // UOther
+    };
+
+    COMPILE_ASSERT(ARRAY_SIZE(sortOrder) == static_cast<unsigned>(CalculationCategory::Other) + 1, sortOrder_size_should_match_UnitCategory);
+    return sortOrder[static_cast<unsigned>(calcUnitCategory(unitType))];
 }
 
-CSSUnitType CSSCalcOperationNode::primitiveType() const
+static SortingCategory sortingCategory(const CSSCalcExpressionNode& node)
 {
-    switch (category()) {
-    case CalculationCategory::Number:
-#if !ASSERT_DISABLED
-        for (auto& child : m_children)
-            ASSERT(child->category() == CalculationCategory::Number);
-#endif
-        return CSSUnitType::CSS_NUMBER;
-    case CalculationCategory::Length:
+    if (is<CSSCalcPrimitiveValueNode>(node))
+        return sortingCategoryForType(node.primitiveType());
+
+    return SortingCategory::Other;
+}
+
+static CSSUnitType primitiveTypeForCombination(const CSSCalcExpressionNode& node)
+{
+    if (is<CSSCalcPrimitiveValueNode>(node))
+        return node.primitiveType();
+    
+    return CSSUnitType::CSS_UNKNOWN;
+}
+
+static CSSCalcPrimitiveValueNode::UnitConversion conversionToAddValuesWithTypes(CSSUnitType firstType, CSSUnitType secondType)
+{
+    if (firstType == CSSUnitType::CSS_UNKNOWN || secondType == CSSUnitType::CSS_UNKNOWN)
+        return CSSCalcPrimitiveValueNode::UnitConversion::Invalid;
+
+    auto firstCategory = calculationCategoryForCombination(firstType);
+
+    // Compatible types.
+    if (firstCategory != CalculationCategory::Other && firstCategory == calculationCategoryForCombination(secondType))
+        return CSSCalcPrimitiveValueNode::UnitConversion::Canonicalize;
+
+    // Matching types.
+    if (firstType == secondType && hasDoubleValue(firstType))
+        return CSSCalcPrimitiveValueNode::UnitConversion::Preserve;
+
+    return CSSCalcPrimitiveValueNode::UnitConversion::Invalid;
+}
+
+bool CSSCalcOperationNode::canCombineAllChildren() const
+{
+    if (m_children.size() < 2)
+        return false;
+
+    if (!is<CSSCalcPrimitiveValueNode>(m_children[0]))
+        return false;
+
+    auto firstUnitType = m_children[0]->primitiveType();
+    auto firstCategory = calculationCategoryForCombination(m_children[0]->primitiveType());
+
+    for (unsigned i = 1; i < m_children.size(); ++i) {
+        auto& node = m_children[i];
+
+        if (!is<CSSCalcPrimitiveValueNode>(node))
+            return false;
+
+        auto nodeUnitType = node->primitiveType();
+        auto nodeCategory = calculationCategoryForCombination(nodeUnitType);
+
+        if (nodeCategory != firstCategory)
+            return false;
+
+        if (nodeCategory == CalculationCategory::Other && nodeUnitType != firstUnitType)
+            return false;
+        
+        if (!hasDoubleValue(nodeUnitType))
+            return false;
+    }
+
+    return true;
+}
+
+void CSSCalcOperationNode::combineChildren()
+{
+    if (m_children.size() < 2)
+        return;
+
+    if (shouldSortChildren()) {
+        // <https://drafts.csswg.org/css-values-4/#sort-a-calculations-children>
+        std::stable_sort(m_children.begin(), m_children.end(), [](const auto& first, const auto& second) {
+            // Sort order: number, percentage, dimension, other.
+            SortingCategory firstCategory = sortingCategory(first.get());
+            SortingCategory secondCategory = sortingCategory(second.get());
+            
+            if (firstCategory == SortingCategory::Dimension && secondCategory == SortingCategory::Dimension) {
+                // If nodes contains any dimensions, remove them from nodes, sort them by their units, and append them to ret.
+                auto firstUnitString = CSSPrimitiveValue::unitTypeString(first->primitiveType());
+                auto secondUnitString = CSSPrimitiveValue::unitTypeString(second->primitiveType());
+                return codePointCompareLessThan(firstUnitString, secondUnitString);
+            }
+
+            return static_cast<unsigned>(firstCategory) < static_cast<unsigned>(secondCategory);
+        });
+
+        LOG_WITH_STREAM(Calc, stream << "post-sort: " << *this);
+    }
+
+    if (calcOperator() == CalcOperator::Add) {
+        // For each set of root’s children that are numeric values with identical units,
+        // remove those children and replace them with a single numeric value containing
+        // the sum of the removed nodes, and with the same unit.
+        Vector<Ref<CSSCalcExpressionNode>> newChildren;
+        newChildren.reserveInitialCapacity(m_children.size());
+        newChildren.uncheckedAppend(m_children[0].copyRef());
+
+        CSSUnitType previousType = primitiveTypeForCombination(newChildren[0].get());
+
+        for (unsigned i = 1; i < m_children.size(); ++i) {
+            auto& currentNode = m_children[i];
+            CSSUnitType currentType = primitiveTypeForCombination(currentNode.get());
+
+            auto conversionType = conversionToAddValuesWithTypes(previousType, currentType);
+            if (conversionType != CSSCalcPrimitiveValueNode::UnitConversion::Invalid) {
+                downcast<CSSCalcPrimitiveValueNode>(newChildren.last().get()).add(downcast<CSSCalcPrimitiveValueNode>(currentNode.get()), conversionType);
+                continue;
+            }
+
+            previousType = primitiveTypeForCombination(currentNode);
+            newChildren.uncheckedAppend(currentNode.copyRef());
+        }
+        
+        newChildren.shrinkToFit();
+        m_children = WTFMove(newChildren);
+        return;
+    }
+
+    if (calcOperator() == CalcOperator::Multiply) {
+        // If root has multiple children that are numbers (not percentages or dimensions),
+        // remove them and replace them with a single number containing the product of the removed nodes.
+        double multiplier = 1;
+
+        // Sorting will have put the number nodes first.
+        unsigned leadingNumberNodeCount = 0;
+        for (auto& node : m_children) {
+            auto nodeType = primitiveTypeForCombination(node.get());
+            if (nodeType != CSSUnitType::CSS_NUMBER)
+                break;
+            
+            multiplier *= node->doubleValue(CSSUnitType::CSS_NUMBER);
+            ++leadingNumberNodeCount;
+        }
+        
+        Vector<Ref<CSSCalcExpressionNode>> newChildren;
+        newChildren.reserveInitialCapacity(m_children.size());
+        
+        // If root contains only two children, one of which is a number (not a percentage or dimension) and the other of
+        // which is a Sum whose children are all numeric values, multiply all of the Sum’s children by the number, then
+        // return the Sum.
+        // The Sum's children simplification will have happened already.
+        bool didMultipy = false;
+        if (leadingNumberNodeCount && m_children.size() - leadingNumberNodeCount == 1) {
+            auto multiplicandCategory = calcUnitCategory(primitiveTypeForCombination(m_children.last().get()));
+            if (multiplicandCategory != CalculationCategory::Other) {
+                newChildren.uncheckedAppend(m_children.last().copyRef());
+                downcast<CSSCalcPrimitiveValueNode>(newChildren[0].get()).multiply(multiplier);
+                didMultipy = true;
+            }
+        }
+        
+        if (!didMultipy) {
+            if (leadingNumberNodeCount) {
+                auto multiplierNode = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(multiplier, CSSUnitType::CSS_NUMBER));
+                newChildren.uncheckedAppend(WTFMove(multiplierNode));
+            }
+
+            for (unsigned i = leadingNumberNodeCount; i < m_children.size(); ++i)
+                newChildren.uncheckedAppend(m_children[i].copyRef());
+        }
+
+        newChildren.shrinkToFit();
+        m_children = WTFMove(newChildren);
+    }
+
+    if (isMinOrMaxNode() && canCombineAllChildren()) {
+        auto combinedUnitType = m_children[0]->primitiveType();
+        auto category = calculationCategoryForCombination(combinedUnitType);
+        if (category != CalculationCategory::Other)
+            combinedUnitType = canonicalUnitTypeForCalculationCategory(category);
+
+        double resolvedValue = doubleValue(combinedUnitType);
+        auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, combinedUnitType));
+
+        m_children.clear();
+        m_children.append(WTFMove(newChild));
+    }
+}
+
+// https://drafts.csswg.org/css-values-4/#simplify-a-calculation-tree
+
+Ref<CSSCalcExpressionNode> CSSCalcOperationNode::simplify(Ref<CSSCalcExpressionNode>&& rootNode)
+{
+    return simplifyRecursive(WTFMove(rootNode), 0);
+}
+
+Ref<CSSCalcExpressionNode> CSSCalcOperationNode::simplifyRecursive(Ref<CSSCalcExpressionNode>&& rootNode, int depth)
+{
+    if (is<CSSCalcOperationNode>(rootNode)) {
+        auto& operationNode = downcast<CSSCalcOperationNode>(rootNode.get());
+        
+        auto& children = operationNode.children();
+        for (unsigned i = 0; i < children.size(); ++i) {
+            auto child = children[i].copyRef();
+            auto newNode = simplifyRecursive(WTFMove(child), depth + 1);
+            if (newNode.ptr() != children[i].ptr())
+                children[i] = WTFMove(newNode);
+        }
+    } else if (is<CSSCalcNegateNode>(rootNode)) {
+        auto& negateNode = downcast<CSSCalcNegateNode>(rootNode.get());
+        Ref<CSSCalcExpressionNode> child = negateNode.child();
+        auto newNode = simplifyRecursive(WTFMove(child), depth + 1);
+        if (newNode.ptr() != &negateNode.child())
+            negateNode.setChild(WTFMove(newNode));
+    } else if (is<CSSCalcInvertNode>(rootNode)) {
+        auto& invertNode = downcast<CSSCalcInvertNode>(rootNode.get());
+        Ref<CSSCalcExpressionNode> child = invertNode.child();
+        auto newNode = simplifyRecursive(WTFMove(child), depth + 1);
+        if (newNode.ptr() != &invertNode.child())
+            invertNode.setChild(WTFMove(newNode));
+    }
+
+    return simplifyNode(WTFMove(rootNode), depth);
+}
+
+Ref<CSSCalcExpressionNode> CSSCalcOperationNode::simplifyNode(Ref<CSSCalcExpressionNode>&& rootNode, int depth)
+{
+    if (is<CSSCalcPrimitiveValueNode>(rootNode)) {
+        // If root is a percentage that will be resolved against another value, and there is enough information
+        // available to resolve it, do so, and express the resulting numeric value in the appropriate canonical
+        // unit. Return the value.
+
+        // If root is a dimension that is not expressed in its canonical unit, and there is enough information
+        // available to convert it to the canonical unit, do so, and return the value.
+        auto& primitiveValueNode = downcast<CSSCalcPrimitiveValueNode>(rootNode.get());
+        primitiveValueNode.canonicalizeUnit();
+        return WTFMove(rootNode);
+    }
+
+    // If root is an operator node that’s not one of the calc-operator nodes, and all of its children are numeric values
+    // with enough information to computed the operation root represents, return the result of running root’s operation
+    // using its children, expressed in the result’s canonical unit.
+    if (is<CSSCalcOperationNode>(rootNode)) {
+        auto& calcOperationNode = downcast<CSSCalcOperationNode>(rootNode.get());
+        // Don't simplify at the root, otherwise we lose track of the operation for serialization.
+        if (calcOperationNode.children().size() == 1 && depth)
+            return WTFMove(calcOperationNode.children()[0]);
+        
+        if (calcOperationNode.isCalcSumNode()) {
+            calcOperationNode.hoistChildrenWithOperator(CalcOperator::Add);
+            calcOperationNode.combineChildren();
+        }
+
+        if (calcOperationNode.isCalcProductNode()) {
+            calcOperationNode.hoistChildrenWithOperator(CalcOperator::Multiply);
+            calcOperationNode.combineChildren();
+        }
+        
+        if (calcOperationNode.isMinOrMaxNode())
+            calcOperationNode.combineChildren();
+
+        // If only one child remains, return the child (except at the root).
+        auto shouldCombineParentWithOnlyChild = [](const CSSCalcOperationNode& parent, int depth)
+        {
+            if (parent.children().size() != 1)
+                return false;
+
+            // Always simplify below the root.
+            if (depth)
+                return true;
+
+            // At the root, preserve the root function by only merging nodes with the same function.
+            auto& child = parent.children().first();
+            if (!is<CSSCalcOperationNode>(child))
+                return false;
+
+            auto parentFunction = functionFromOperator(parent.calcOperator());
+            auto childFunction = functionFromOperator(downcast<CSSCalcOperationNode>(child.get()).calcOperator());
+            return childFunction == parentFunction;
+        };
+
+        if (shouldCombineParentWithOnlyChild(calcOperationNode, depth))
+            return WTFMove(calcOperationNode.children().first());
+
+        return WTFMove(rootNode);
+    }
+
+    if (is<CSSCalcNegateNode>(rootNode)) {
+        auto& childNode = downcast<CSSCalcNegateNode>(rootNode.get()).child();
+        // If root’s child is a numeric value, return an equivalent numeric value, but with the value negated (0 - value).
+        if (is<CSSCalcPrimitiveValueNode>(childNode) && downcast<CSSCalcPrimitiveValueNode>(childNode).isNumericValue()) {
+            downcast<CSSCalcPrimitiveValueNode>(childNode).negate();
+            return childNode;
+        }
+        
+        // If root’s child is a Negate node, return the child’s child.
+        if (is<CSSCalcNegateNode>(childNode))
+            return downcast<CSSCalcNegateNode>(childNode).child();
+        
+        return WTFMove(rootNode);
+    }
+
+    if (is<CSSCalcInvertNode>(rootNode)) {
+        auto& childNode = downcast<CSSCalcInvertNode>(rootNode.get()).child();
+        // If root’s child is a number (not a percentage or dimension) return the reciprocal of the child’s value.
+        if (is<CSSCalcPrimitiveValueNode>(childNode) && downcast<CSSCalcPrimitiveValueNode>(childNode).isNumericValue()) {
+            downcast<CSSCalcPrimitiveValueNode>(childNode).invert();
+            return childNode;
+        }
+
+        // If root’s child is an Invert node, return the child’s child.
+        if (is<CSSCalcInvertNode>(childNode))
+            return downcast<CSSCalcInvertNode>(childNode).child();
+
+        return WTFMove(rootNode);
+    }
+
+    return WTFMove(rootNode);
+}
+
+CSSUnitType CSSCalcOperationNode::primitiveType() const
+{
+    auto unitCategory = category();
+    switch (unitCategory) {
+    case CalculationCategory::Number:
+#if !ASSERT_DISABLED
+        for (auto& child : m_children)
+            ASSERT(child->category() == CalculationCategory::Number);
+#endif
+        return CSSUnitType::CSS_NUMBER;
+
     case CalculationCategory::Percent: {
         if (m_children.isEmpty())
             return CSSUnitType::CSS_UNKNOWN;
+
         if (m_children.size() == 2) {
             if (m_children[0]->category() == CalculationCategory::Number)
                 return m_children[1]->primitiveType();
@@ -580,12 +1305,15 @@ CSSUnitType CSSCalcOperationNode::primitiveType() const
         }
         return firstType;
     }
+
+    case CalculationCategory::Length:
     case CalculationCategory::Angle:
-        return CSSUnitType::CSS_DEG;
     case CalculationCategory::Time:
-        return CSSUnitType::CSS_MS;
     case CalculationCategory::Frequency:
-        return CSSUnitType::CSS_HZ;
+        if (m_children.size() == 1)
+            return m_children.first()->primitiveType();
+        return canonicalUnitTypeForCalculationCategory(unitCategory);
+
     case CalculationCategory::PercentLength:
     case CalculationCategory::PercentNumber:
     case CalculationCategory::Other:
@@ -609,20 +1337,23 @@ std::unique_ptr<CalcExpressionNode> CSSCalcOperationNode::createCalcExpression(c
     return makeUnique<CalcExpressionOperation>(WTFMove(nodes), m_operator);
 }
 
-double CSSCalcOperationNode::doubleValue() const
+double CSSCalcOperationNode::doubleValue(CSSUnitType unitType) const
 {
-    Vector<double> doubleValues;
-    for (auto& child : m_children)
-        doubleValues.append(child->doubleValue());
-    return evaluate(doubleValues);
+    bool allowNumbers = calcOperator() == CalcOperator::Multiply;
+
+    return evaluate(m_children.map([&] (auto& child) {
+        CSSUnitType childType = unitType;
+        if (allowNumbers && unitType != CSSUnitType::CSS_NUMBER && child->primitiveType() == CSSUnitType::CSS_NUMBER)
+            childType = CSSUnitType::CSS_NUMBER;
+        return child->doubleValue(childType);
+    }));
 }
 
 double CSSCalcOperationNode::computeLengthPx(const CSSToLengthConversionData& conversionData) const
 {
-    Vector<double> doubleValues;
-    for (auto& child : m_children)
-        doubleValues.append(child->computeLengthPx(conversionData));
-    return evaluate(doubleValues);
+    return evaluate(m_children.map([&] (auto& child) {
+        return child->computeLengthPx(conversionData);
+    }));
 }
 
 void CSSCalcOperationNode::collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const
@@ -637,51 +1368,167 @@ void CSSCalcOperationNode::collectDirectRootComputationalDependencies(HashSet<CS
         child->collectDirectRootComputationalDependencies(values);
 }
 
-String CSSCalcOperationNode::buildCssText(Vector<String> childExpressions, CalcOperator op)
+void CSSCalcOperationNode::buildCSSText(const CSSCalcExpressionNode& node, StringBuilder& builder)
+{
+    auto shouldOutputEnclosingCalc = [](const CSSCalcExpressionNode& rootNode) {
+        if (is<CSSCalcOperationNode>(rootNode)) {
+            auto& operationNode = downcast<CSSCalcOperationNode>(rootNode);
+            return operationNode.isCalcSumNode() || operationNode.isCalcProductNode();
+        }
+        return true;
+    };
+    
+    bool outputCalc = shouldOutputEnclosingCalc(node);
+    if (outputCalc)
+        builder.append("calc(");
+
+    buildCSSTextRecursive(node, builder, GroupingParens::Omit);
+
+    if (outputCalc)
+        builder.append(')');
+}
+
+static const char* functionPrefixForOperator(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]);
-        }
-        result.append(')');
+        ASSERT_NOT_REACHED();
+        return "";
+    case CalcOperator::Min: return "min(";
+    case CalcOperator::Max: return "max(";
     }
-    result.append(')');
-
-    return result.toString();
+    
+    return "";
 }
 
-String CSSCalcOperationNode::customCSSText() const
+// <https://drafts.csswg.org/css-values-4/#serialize-a-calculation-tree>
+void CSSCalcOperationNode::buildCSSTextRecursive(const CSSCalcExpressionNode& node, StringBuilder& builder, GroupingParens parens)
 {
-    Vector<String> cssTexts;
-    for (auto& child : m_children)
-        cssTexts.append(child->customCSSText());
-    return buildCssText(cssTexts, m_operator);
+    // If root is a numeric value, or a non-math function, serialize root per the normal rules for it and return the result.
+    if (is<CSSCalcPrimitiveValueNode>(node)) {
+        auto& valueNode = downcast<CSSCalcPrimitiveValueNode>(node);
+        builder.append(valueNode.customCSSText());
+        return;
+    }
+
+    if (is<CSSCalcOperationNode>(node)) {
+        auto& operationNode = downcast<CSSCalcOperationNode>(node);
+
+        if (operationNode.isCalcSumNode()) {
+            // If root is a Sum node, let s be a string initially containing "(".
+            if (parens == GroupingParens::Include)
+                builder.append('(');
+
+            // Simplification already sorted children.
+            auto& children = operationNode.children();
+            ASSERT(children.size());
+            // Serialize root’s first child, and append it to s.
+            buildCSSTextRecursive(children.first(), builder);
+
+            // For each child of root beyond the first:
+            // If child is a Negate node, append " - " to s, then serialize the Negate’s child and append the result to s.
+            // If child is a negative numeric value, append " - " to s, then serialize the negation of child as normal and append the result to s.
+            // Otherwise, append " + " to s, then serialize child and append the result to s.
+            for (unsigned i = 1; i < children.size(); ++i) {
+                auto& child = children[i];
+                if (is<CSSCalcNegateNode>(child)) {
+                    builder.append(" - ");
+                    buildCSSTextRecursive(downcast<CSSCalcNegateNode>(child.get()).child(), builder);
+                    continue;
+                }
+                
+                if (is<CSSCalcPrimitiveValueNode>(child)) {
+                    auto& primitiveValueNode = downcast<CSSCalcPrimitiveValueNode>(child.get());
+                    if (primitiveValueNode.isNegative()) {
+                        builder.append(" - ");
+                        // Serialize the negation of child.
+                        auto unitType = primitiveValueNode.value().primitiveType();
+                        builder.append(0 - primitiveValueNode.value().doubleValue(), CSSPrimitiveValue::unitTypeString(unitType));
+                        continue;
+                    }
+                }
+                
+                builder.append(" + ");
+                buildCSSTextRecursive(child, builder);
+            }
+
+            if (parens == GroupingParens::Include)
+                builder.append(')');
+            return;
+        }
+        
+        if (operationNode.isCalcProductNode()) {
+            // If root is a Product node, let s be a string initially containing "(".
+            if (parens == GroupingParens::Include)
+                builder.append('(');
+
+            // Simplification already sorted children.
+            auto& children = operationNode.children();
+            ASSERT(children.size());
+            // Serialize root’s first child, and append it to s.
+            buildCSSTextRecursive(children.first(), builder);
+
+            // For each child of root beyond the first:
+            // If child is an Invert node, append " / " to s, then serialize the Invert’s child and append the result to s.
+            // Otherwise, append " * " to s, then serialize child and append the result to s.
+            for (unsigned i = 1; i < children.size(); ++i) {
+                auto& child = children[i];
+                if (is<CSSCalcInvertNode>(child)) {
+                    builder.append(" / ");
+                    buildCSSTextRecursive(downcast<CSSCalcInvertNode>(child.get()).child(), builder);
+                    continue;
+                }
+
+                builder.append(" * ");
+                buildCSSTextRecursive(child, builder);
+            }
+
+            if (parens == GroupingParens::Include)
+                builder.append(')');
+            return;
+        }
+
+        // If root is anything but a Sum, Negate, Product, or Invert node, serialize a math function for the
+        // function corresponding to the node type, treating the node’s children as the function’s
+        // comma-separated calculation arguments, and return the result.
+        builder.append(functionPrefixForOperator(operationNode.calcOperator()));
+
+        auto& children = operationNode.children();
+        ASSERT(children.size());
+        buildCSSTextRecursive(children.first(), builder, GroupingParens::Omit);
+
+        for (unsigned i = 1; i < children.size(); ++i) {
+            builder.append(", ");
+            buildCSSTextRecursive(children[i], builder, GroupingParens::Omit);
+        }
+        
+        builder.append(')');
+        return;
+    }
+    
+    if (is<CSSCalcNegateNode>(node)) {
+        auto& negateNode = downcast<CSSCalcNegateNode>(node);
+        // If root is a Negate node, let s be a string initially containing "(-1 * ".
+        builder.append("-1 *");
+        buildCSSTextRecursive(negateNode.child(), builder);
+        return;
+    }
+    
+    if (is<CSSCalcInvertNode>(node)) {
+        auto& invertNode = downcast<CSSCalcInvertNode>(node);
+        // If root is an Invert node, let s be a string initially containing "(1 / ".
+        builder.append("1 / ");
+        buildCSSTextRecursive(invertNode.child(), builder);
+        return;
+    }
 }
 
 void CSSCalcOperationNode::dump(TextStream& ts) const
 {
-    ts << "calc operation " << m_operator << " category " << category();
+    ts << "calc operation " << m_operator << " (category: " << category() << ", type " << primitiveType() << ")";
 
     TextStream::GroupScope scope(ts);
     ts << m_children.size() << " children";
@@ -709,15 +1556,21 @@ bool CSSCalcOperationNode::equals(const CSSCalcExpressionNode& exp) const
 double CSSCalcOperationNode::evaluateOperator(CalcOperator op, const Vector<double>& children)
 {
     switch (op) {
-    case CalcOperator::Add:
-        ASSERT(children.size() == 2);
-        return children[0] + children[1];
+    case CalcOperator::Add: {
+        double sum = 0;
+        for (auto& child : children)
+            sum += child;
+        return sum;
+    }
     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::Multiply: {
+        double product = 1;
+        for (auto& child : children)
+            product *= child;
+        return product;
+    }
     case CalcOperator::Divide:
         ASSERT(children.size() == 1 || children.size() == 2);
         if (children.size() == 1)
@@ -763,24 +1616,38 @@ private:
 
     bool parseValue(CSSParserTokenRange&, RefPtr<CSSCalcExpressionNode>&);
     bool parseValueTerm(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
-    bool parseValueMultiplicativeExpression(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
-    bool parseAdditiveValueExpression(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
-    bool parseMinMaxExpression(CSSParserTokenRange&, CSSValueID minMaxFunction, int depth, RefPtr<CSSCalcExpressionNode>&);
-    bool parseValueExpression(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
+    bool parseCalcFunction(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
+    bool parseCalcSum(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
+    bool parseCalcProduct(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
+    bool parseCalcValue(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
 
     CalculationCategory m_destinationCategory;
 };
 
-
+// <https://drafts.csswg.org/css-values-4/#calc-syntax>:
+// <calc()>  = calc( <calc-sum> )
+// <min()>   = min( <calc-sum># )
+// <max()>   = max( <calc-sum># )
+// <clamp()> = clamp( <calc-sum>#{3} )
+// <sin()>   = sin( <calc-sum> )
+// <cos()>   = cos( <calc-sum> )
+// <tan()>   = tan( <calc-sum> )
+// <asin()>  = asin( <calc-sum> )
+// <acos()>  = acos( <calc-sum> )
+// <atan()>  = atan( <calc-sum> )
+// <atan2()> = atan2( <calc-sum>, <calc-sum> )
+// <pow()>   = pow( <calc-sum>, <calc-sum> )
+// <sqrt()>  = sqrt( <calc-sum> )
+// <hypot()> = hypot( <calc-sum># )
+// <calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
+// <calc-product> = <calc-value> [ [ '*' | '/' ] <calc-value> ]*
+// <calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> )
 RefPtr<CSSCalcExpressionNode> CSSCalcExpressionNodeParser::parseCalc(CSSParserTokenRange tokens, CSSValueID function)
 {
-    RefPtr<CSSCalcExpressionNode> 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);
+
+    RefPtr<CSSCalcExpressionNode> result;
+    bool ok = parseCalcFunction(tokens, function, 0, result);
     if (!ok || !tokens.atEnd())
         return nullptr;
 
@@ -789,21 +1656,11 @@ RefPtr<CSSCalcExpressionNode> CSSCalcExpressionNodeParser::parseCalc(CSSParserTo
 
     LOG_WITH_STREAM(Calc, stream << "CSSCalcExpressionNodeParser::parseCalc " << prettyPrintNode(*result));
 
-    return result;
-}
+    result = CSSCalcOperationNode::simplify(result.releaseNonNull());
 
-bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
-{
-    CSSParserToken token = tokens.consumeIncludingWhitespace();
-    if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
-        return false;
-    
-    CSSUnitType type = token.unitType();
-    if (calcUnitCategory(type) == CalculationCategory::Other)
-        return false;
-    
-    result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(token.numericValue(), type));
-    return true;
+    LOG_WITH_STREAM(Calc, stream << "CSSCalcExpressionNodeParser::parseCalc - after simplification " << prettyPrintNode(*result));
+
+    return result;
 }
 
 enum ParseState {
@@ -812,168 +1669,314 @@ enum ParseState {
     NoMoreTokens
 };
 
-static ParseState checkDepthAndIndex(int* depth, CSSParserTokenRange tokens)
+static ParseState checkDepthAndIndex(int depth, CSSParserTokenRange tokens)
 {
-    (*depth)++;
     if (tokens.atEnd())
         return NoMoreTokens;
-    if (*depth > maxExpressionDepth)
+    if (depth > maxExpressionDepth) {
+        LOG_WITH_STREAM(Calc, stream << "Depth " << depth << " exceeded maxExpressionDepth " << maxExpressionDepth);
         return TooDeep;
+    }
     return OK;
 }
 
-bool CSSCalcExpressionNodeParser::parseValueTerm(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
+bool CSSCalcExpressionNodeParser::parseCalcFunction(CSSParserTokenRange& tokens, CSSValueID functionID, int depth, RefPtr<CSSCalcExpressionNode>& result)
 {
-    if (checkDepthAndIndex(&depth, tokens) != OK)
+    if (checkDepthAndIndex(depth, tokens) != OK)
         return false;
 
-    auto functionId = tokens.peek().functionId();
-    
-    if (tokens.peek().type() == LeftParenthesisToken || functionId == CSSValueCalc) {
-        CSSParserTokenRange innerRange = tokens.consumeBlock();
+    // "arguments" refers to things between commas.
+    Optional<unsigned> maxArgumentCount;
+
+    switch (functionID) {
+    case CSSValueMin:
+    case CSSValueMax:
+        maxArgumentCount = WTF::nullopt;
+        break;
+    case CSSValueCalc:
+        maxArgumentCount = 1;
+        break;
+    // TODO: clamp, sin, cos, tan, asin, acos, atan, atan2, pow, sqrt, hypot.
+    default:
+        break;
+    }
+
+    Vector<Ref<CSSCalcExpressionNode>> nodes;
+
+    bool requireComma = false;
+    unsigned argumentCount = 0;
+    while (!tokens.atEnd()) {
         tokens.consumeWhitespace();
-        innerRange.consumeWhitespace();
-        return parseValueExpression(innerRange, depth, result);
+        if (requireComma) {
+            if (tokens.consume().type() != CommaToken)
+                return false;
+            tokens.consumeWhitespace();
+        }
+
+        RefPtr<CSSCalcExpressionNode> node;
+        if (!parseCalcSum(tokens, depth, node))
+            return false;
+
+        ++argumentCount;
+        if (maxArgumentCount && argumentCount > maxArgumentCount.value())
+            return false;
+
+        nodes.append(node.releaseNonNull());
+        requireComma = true;
     }
 
-    if (functionId == CSSValueMax || functionId == CSSValueMin) {
+    switch (functionID) {
+    case CSSValueMin:
+        result = CSSCalcOperationNode::createMinOrMax(CalcOperator::Min, WTFMove(nodes), m_destinationCategory);
+        break;
+    case CSSValueMax:
+        result = CSSCalcOperationNode::createMinOrMax(CalcOperator::Max, WTFMove(nodes), m_destinationCategory);
+        break;
+    case CSSValueWebkitCalc:
+    case CSSValueCalc:
+        result = CSSCalcOperationNode::createSum(WTFMove(nodes));
+        break;
+    // TODO: clamp, sin, cos, tan, asin, acos, atan, atan2, pow, sqrt, hypot
+    default:
+        break;
+    }
+
+    return !!result;
+}
+
+bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
+{
+    CSSParserToken token = tokens.consumeIncludingWhitespace();
+    if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
+        return false;
+    
+    auto type = token.unitType();
+    if (calcUnitCategory(type) == CalculationCategory::Other)
+        return false;
+    
+    result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(token.numericValue(), type));
+    
+    return true;
+}
+
+bool CSSCalcExpressionNodeParser::parseCalcValue(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
+{
+    if (checkDepthAndIndex(depth, tokens) != OK)
+        return false;
+
+    auto findFunctionId = [&](CSSValueID& functionId) {
+        if (tokens.peek().type() == LeftParenthesisToken) {
+            functionId = CSSValueCalc;
+            return true;
+        }
+
+        functionId = tokens.peek().functionId();
+        return CSSCalcValue::isCalcFunction(functionId);
+    };
+
+    CSSValueID functionId;
+    if (findFunctionId(functionId)) {
         CSSParserTokenRange innerRange = tokens.consumeBlock();
         tokens.consumeWhitespace();
         innerRange.consumeWhitespace();
-        return parseMinMaxExpression(innerRange, functionId, depth, result);
+        return parseCalcFunction(innerRange, functionId, depth + 1, result);
     }
-    
+
     return parseValue(tokens, result);
 }
 
-bool CSSCalcExpressionNodeParser::parseValueMultiplicativeExpression(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
+bool CSSCalcExpressionNodeParser::parseCalcProduct(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
 {
-    if (checkDepthAndIndex(&depth, tokens) != OK)
+    if (checkDepthAndIndex(depth, tokens) != OK)
         return false;
-    
-    if (!parseValueTerm(tokens, depth, result))
+
+    RefPtr<CSSCalcExpressionNode> firstValue;
+    if (!parseCalcValue(tokens, depth, firstValue))
         return false;
-    
+
+    Vector<Ref<CSSCalcExpressionNode>> nodes;
+
     while (!tokens.atEnd()) {
         char operatorCharacter = operatorValue(tokens.peek());
         if (operatorCharacter != static_cast<char>(CalcOperator::Multiply) && operatorCharacter != static_cast<char>(CalcOperator::Divide))
             break;
         tokens.consumeIncludingWhitespace();
         
-        RefPtr<CSSCalcExpressionNode> rhs;
-        if (!parseValueTerm(tokens, depth, rhs))
+        RefPtr<CSSCalcExpressionNode> nextValue;
+        if (!parseCalcValue(tokens, depth, nextValue) || !nextValue)
             return false;
-        
-        result = CSSCalcOperationNode::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result), WTFMove(rhs));
 
-        if (!result)
-            return false;
+        if (operatorCharacter == static_cast<char>(CalcOperator::Divide))
+            nextValue = CSSCalcInvertNode::create(nextValue.releaseNonNull());
+
+        if (firstValue)
+            nodes.append(firstValue.releaseNonNull());
+
+        nodes.append(nextValue.releaseNonNull());
     }
-    
-    return true;
+
+    if (nodes.isEmpty()) {
+        result = WTFMove(firstValue);
+        return !!result;
+    }
+
+    result = CSSCalcOperationNode::createProduct(WTFMove(nodes));
+    return !!result;
 }
 
-bool CSSCalcExpressionNodeParser::parseAdditiveValueExpression(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
+bool CSSCalcExpressionNodeParser::parseCalcSum(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
 {
-    if (checkDepthAndIndex(&depth, tokens) != OK)
+    if (checkDepthAndIndex(depth, tokens) != OK)
         return false;
-    
-    if (!parseValueMultiplicativeExpression(tokens, depth, result))
+
+    RefPtr<CSSCalcExpressionNode> firstValue;
+    if (!parseCalcProduct(tokens, depth, firstValue))
         return false;
-    
+
+    Vector<Ref<CSSCalcExpressionNode>> nodes;
+
     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();
-        
-        RefPtr<CSSCalcExpressionNode> rhs;
-        if (!parseValueMultiplicativeExpression(tokens, depth, rhs))
-            return false;
-        
-        result = CSSCalcOperationNode::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result), WTFMove(rhs));
-        if (!result)
-            return false;
-    }
-    
-    return true;
-}
-
-bool CSSCalcExpressionNodeParser::parseMinMaxExpression(CSSParserTokenRange& tokens, CSSValueID minMaxFunction, int depth, RefPtr<CSSCalcExpressionNode>& result)
-{
-    if (checkDepthAndIndex(&depth, tokens) != OK)
-        return false;
 
-    CalcOperator op = (minMaxFunction == CSSValueMin) ? CalcOperator::Min : CalcOperator::Max;
-
-    RefPtr<CSSCalcExpressionNode> value;
-    if (!parseValueExpression(tokens, depth, value))
-        return false;
-
-    Vector<Ref<CSSCalcExpressionNode>> nodes;
-    nodes.append(value.releaseNonNull());
+        tokens.consumeIncludingWhitespace();
 
-    while (!tokens.atEnd()) {
-        tokens.consumeWhitespace();
-        if (tokens.consume().type() != CommaToken)
+        RefPtr<CSSCalcExpressionNode> nextValue;
+        if (!parseCalcProduct(tokens, depth, nextValue) || !nextValue)
             return false;
-        tokens.consumeWhitespace();
 
-        if (!parseValueExpression(tokens, depth, value))
-            return false;
+        if (operatorCharacter == static_cast<char>(CalcOperator::Subtract))
+            nextValue = CSSCalcNegateNode::create(nextValue.releaseNonNull());
+
+        if (firstValue)
+            nodes.append(firstValue.releaseNonNull());
 
-        nodes.append(value.releaseNonNull());
+        nodes.append(nextValue.releaseNonNull());
     }
 
-    result = CSSCalcOperationNode::createMinOrMax(op, WTFMove(nodes), m_destinationCategory);
-    return result;
-}
+    if (nodes.isEmpty()) {
+        result = WTFMove(firstValue);
+        return !!result;
+    }
 
-bool CSSCalcExpressionNodeParser::parseValueExpression(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
-{
-    return parseAdditiveValueExpression(tokens, depth, result);
+    result = CSSCalcOperationNode::createSum(WTFMove(nodes));
+    return !!result;
 }
 
-
 static inline RefPtr<CSSCalcOperationNode> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
 {
     return CSSCalcOperationNode::create(CalcOperator::Multiply, createCSS(length, style),
         CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(progress, CSSUnitType::CSS_NUMBER)));
 }
 
+static Vector<Ref<CSSCalcExpressionNode>> createCSS(const Vector<std::unique_ptr<CalcExpressionNode>>& nodes, const RenderStyle& style)
+{
+    Vector<Ref<CSSCalcExpressionNode>> values;
+    values.reserveInitialCapacity(nodes.size());
+    for (auto& node : nodes) {
+        auto cssNode = createCSS(*node, style);
+        if (!cssNode)
+            return { };
+        values.uncheckedAppend(cssNode.releaseNonNull());
+    }
+    return values;
+}
+
 static RefPtr<CSSCalcExpressionNode> createCSS(const CalcExpressionNode& node, const RenderStyle& style)
 {
     switch (node.type()) {
     case CalcExpressionNodeType::Number: {
-        float value = downcast<CalcExpressionNumber>(node).value();
+        float value = downcast<CalcExpressionNumber>(node).value(); // double?
         return CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(value, CSSUnitType::CSS_NUMBER));
     }
     case CalcExpressionNodeType::Length:
         return createCSS(downcast<CalcExpressionLength>(node).length(), style);
+
+    case CalcExpressionNodeType::Negation: {
+        auto childNode = createCSS(*downcast<CalcExpressionNegation>(node).child(), style);
+        if (!childNode)
+            return nullptr;
+        return CSSCalcNegateNode::create(childNode.releaseNonNull());
+    }
+    case CalcExpressionNodeType::Inversion: {
+        auto childNode = createCSS(*downcast<CalcExpressionInversion>(node).child(), style);
+        if (!childNode)
+            return nullptr;
+        return CSSCalcInvertNode::create(childNode.releaseNonNull());
+    }
     case CalcExpressionNodeType::Operation: {
         auto& operationNode = downcast<CalcExpressionOperation>(node);
         auto& operationChildren = operationNode.children();
         CalcOperator op = operationNode.getOperator();
-        if (op == CalcOperator::Min || op == CalcOperator::Max) {
+        
+        switch (op) {
+        case CalcOperator::Add: {
+            auto children = createCSS(operationChildren, style);
+            if (children.isEmpty())
+                return nullptr;
+            return CSSCalcOperationNode::createSum(WTFMove(children));
+        } case CalcOperator::Subtract: {
+            ASSERT(operationChildren.size() == 2);
+
             Vector<Ref<CSSCalcExpressionNode>> values;
             values.reserveInitialCapacity(operationChildren.size());
-            for (auto& child : operationChildren) {
-                auto cssNode = createCSS(*child, style);
-                if (!cssNode)
-                    return nullptr;
-                values.uncheckedAppend(*cssNode);
-            }
-            return CSSCalcOperationNode::createMinOrMax(operationNode.getOperator(), WTFMove(values), CalculationCategory::Other);
+            
+            auto firstChild = createCSS(*operationChildren[0], style);
+            if (!firstChild)
+                return nullptr;
+
+            auto secondChild = createCSS(*operationChildren[1], style);
+            if (!secondChild)
+                return nullptr;
+            auto negateNode = CSSCalcNegateNode::create(secondChild.releaseNonNull());
+
+            values.append(firstChild.releaseNonNull());
+            values.append(WTFMove(negateNode));
+
+            return CSSCalcOperationNode::createSum(WTFMove(values));
         }
+        case CalcOperator::Multiply: {
+            auto children = createCSS(operationChildren, style);
+            if (children.isEmpty())
+                return nullptr;
+            return CSSCalcOperationNode::createProduct(WTFMove(children));
+        }
+        case CalcOperator::Divide: {
+            ASSERT(operationChildren.size() == 2);
+
+            Vector<Ref<CSSCalcExpressionNode>> values;
+            values.reserveInitialCapacity(operationChildren.size());
+            
+            auto firstChild = createCSS(*operationChildren[0], style);
+            if (!firstChild)
+                return nullptr;
+
+            auto secondChild = createCSS(*operationChildren[1], style);
+            if (!secondChild)
+                return nullptr;
+            auto invertNode = CSSCalcInvertNode::create(secondChild.releaseNonNull());
 
-        if (operationChildren.size() == 2)
-            return CSSCalcOperationNode::create(operationNode.getOperator(), createCSS(*operationChildren[0], style), createCSS(*operationChildren[1], style));
+            values.append(firstChild.releaseNonNull());
+            values.append(WTFMove(invertNode));
 
+            return CSSCalcOperationNode::createProduct(createCSS(operationChildren, style));
+        }
+        case CalcOperator::Min:
+        case CalcOperator::Max: {
+            auto children = createCSS(operationChildren, style);
+            if (children.isEmpty())
+                return nullptr;
+            return CSSCalcOperationNode::createMinOrMax(op, WTFMove(children), CalculationCategory::Other);
+        }
+        }
         return nullptr;
     }
     case CalcExpressionNodeType::BlendLength: {
@@ -1012,10 +2015,9 @@ static RefPtr<CSSCalcExpressionNode> createCSS(const Length& length, const Rende
 
 String CSSCalcValue::customCSSText() const
 {
-    auto expression = m_expression->customCSSText();
-    if (expression[0] == '(')
-        return makeString("calc", expression);
-    return makeString("calc(", expression, ')');
+    StringBuilder builder;
+    CSSCalcOperationNode::buildCSSText(m_expression.get(), builder);
+    return builder.toString();
 }
 
 bool CSSCalcValue::equals(const CSSCalcValue& other) const
@@ -1030,7 +2032,7 @@ inline double CSSCalcValue::clampToPermittedRange(double value) const
 
 double CSSCalcValue::doubleValue() const
 {
-    return clampToPermittedRange(m_expression->doubleValue());
+    return clampToPermittedRange(m_expression->doubleValue(primitiveType()));
 }
 
 double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
@@ -1072,9 +2074,8 @@ RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTo
     auto expression = parser.parseCalc(tokens, function);
     if (!expression)
         return nullptr;
-
     auto result = adoptRef(new CSSCalcValue(expression.releaseNonNull(), range != ValueRangeAll));
-    LOG_WITH_STREAM(Calc, stream << "CSSCalcValue::create from tokens: " << *result);
+    LOG_WITH_STREAM(Calc, stream << "CSSCalcValue::create " << *result);
     return result;
 }
     
index 7d09f7e..1e78f4a 100644 (file)
@@ -54,6 +54,9 @@ enum class CalculationCategory : uint8_t {
     Angle,
     Time,
     Frequency,
+    // TODO:
+    // Flex,
+    // Resolution
     Other
 };
 
@@ -61,15 +64,16 @@ class CSSCalcExpressionNode : public RefCounted<CSSCalcExpressionNode> {
 public:
     enum Type {
         CssCalcPrimitiveValue = 1,
-        CssCalcOperation
+        CssCalcOperation,
+        CssCalcNegate,
+        CssCalcInvert,
     };
 
     virtual ~CSSCalcExpressionNode() = default;
     virtual bool isZero() const = 0;
     virtual std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData&) const = 0;
-    virtual double doubleValue() const = 0;
+    virtual double doubleValue(CSSUnitType) const = 0;
     virtual double computeLengthPx(const CSSToLengthConversionData&) const = 0;
-    virtual String customCSSText() const = 0;
     virtual bool equals(const CSSCalcExpressionNode& other) const { return m_category == other.m_category; }
     virtual Type type() const = 0;
     virtual CSSUnitType primitiveType() const = 0;
index b328b70..81e412b 100644 (file)
@@ -201,14 +201,13 @@ CSSUnitType CSSPrimitiveValue::primitiveType() const
     switch (m_value.calc->category()) {
     case CalculationCategory::Number:
         return CSSUnitType::CSS_NUMBER;
-    case CalculationCategory::Length:
-        return CSSUnitType::CSS_PX;
     case CalculationCategory::Percent:
         return CSSUnitType::CSS_PERCENTAGE;
     case CalculationCategory::PercentNumber:
         return CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER;
     case CalculationCategory::PercentLength:
         return CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH;
+    case CalculationCategory::Length:
     case CalculationCategory::Angle:
     case CalculationCategory::Time:
     case CalculationCategory::Frequency:
@@ -907,6 +906,65 @@ NEVER_INLINE String CSSPrimitiveValue::formatNumberValue(StringView suffix) cons
     return makeString(m_value.num, suffix);
 }
 
+String CSSPrimitiveValue::unitTypeString(CSSUnitType unitType)
+{
+    switch (unitType) {
+        case CSSUnitType::CSS_PERCENTAGE: return "%";
+        case CSSUnitType::CSS_EMS: return "em";
+        case CSSUnitType::CSS_EXS: return "ex";
+        case CSSUnitType::CSS_PX: return "px";
+        case CSSUnitType::CSS_CM: return "cm";
+        case CSSUnitType::CSS_MM: return "mm";
+        case CSSUnitType::CSS_IN: return "in";
+        case CSSUnitType::CSS_PT: return "pt";
+        case CSSUnitType::CSS_PC: return "pc";
+        case CSSUnitType::CSS_DEG: return "deg";
+        case CSSUnitType::CSS_RAD: return "rad";
+        case CSSUnitType::CSS_GRAD: return "grad";
+        case CSSUnitType::CSS_MS: return "ms";
+        case CSSUnitType::CSS_S: return "s";
+        case CSSUnitType::CSS_HZ: return "hz";
+        case CSSUnitType::CSS_KHZ: return "khz";
+        case CSSUnitType::CSS_VW: return "vw";
+        case CSSUnitType::CSS_VH: return "vh";
+        case CSSUnitType::CSS_VMIN: return "vmin";
+        case CSSUnitType::CSS_VMAX: return "vmax";
+        case CSSUnitType::CSS_DPPX: return "dppx";
+        case CSSUnitType::CSS_DPI: return "dpi";
+        case CSSUnitType::CSS_DPCM: return "dpcm";
+        case CSSUnitType::CSS_FR: return "fr";
+        case CSSUnitType::CSS_Q: return "q";
+        case CSSUnitType::CSS_TURN: return "turn";
+        case CSSUnitType::CSS_REMS: return "rem";
+        case CSSUnitType::CSS_CHS: return "ch";
+
+        case CSSUnitType::CSS_UNKNOWN:
+        case CSSUnitType::CSS_NUMBER:
+        case CSSUnitType::CSS_DIMENSION:
+        case CSSUnitType::CSS_STRING:
+        case CSSUnitType::CSS_URI:
+        case CSSUnitType::CSS_IDENT:
+        case CSSUnitType::CSS_ATTR:
+        case CSSUnitType::CSS_COUNTER:
+        case CSSUnitType::CSS_RECT:
+        case CSSUnitType::CSS_RGBCOLOR:
+        case CSSUnitType::CSS_PAIR:
+        case CSSUnitType::CSS_UNICODE_RANGE:
+        case CSSUnitType::CSS_COUNTER_NAME:
+        case CSSUnitType::CSS_SHAPE:
+        case CSSUnitType::CSS_QUAD:
+        case CSSUnitType::CSS_CALC:
+        case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER:
+        case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH:
+        case CSSUnitType::CSS_FONT_FAMILY:
+        case CSSUnitType::CSS_PROPERTY_ID:
+        case CSSUnitType::CSS_VALUE_ID:
+        case CSSUnitType::CSS_QUIRKY_EMS:
+            return emptyString();
+    }
+    return emptyString();
+}
+
 ALWAYS_INLINE String CSSPrimitiveValue::formatNumberForCustomCSSText() const
 {
     switch (primitiveUnitType()) {
index 2241b70..763d420 100644 (file)
@@ -197,6 +197,7 @@ public:
     bool equals(const CSSPrimitiveValue&) const;
 
     static double conversionToCanonicalUnitsScaleFactor(CSSUnitType);
+    static String unitTypeString(CSSUnitType);
 
     static double computeNonCalcLengthDouble(const CSSToLengthConversionData&, CSSUnitType, double value);
     // True if computeNonCalcLengthDouble would produce identical results when resolved against both these styles.
index 4b7474d..1fc6dd2 100644 (file)
@@ -21,6 +21,8 @@
 #include "config.h"
 #include "CSSUnits.h"
 
+#include <wtf/text/TextStream.h>
+
 namespace WebCore {
 
 CSSUnitCategory unitCategory(CSSUnitType type)
@@ -87,4 +89,76 @@ CSSUnitType canonicalUnitType(CSSUnitType unitType)
     return canonicalUnitTypeForCategory(unitCategory(unitType));
 }
 
+TextStream& operator<<(TextStream& ts, CSSUnitCategory category)
+{
+    switch (category) {
+    case CSSUnitCategory::Number: ts << "Number"; break;
+    case CSSUnitCategory::Percent: ts << "Percent"; break;
+    case CSSUnitCategory::Length: ts << "Length"; break;
+    case CSSUnitCategory::Angle: ts << "Angle"; break;
+    case CSSUnitCategory::Time: ts << "Time"; break;
+    case CSSUnitCategory::Frequency: ts << "Frequency"; break;
+    case CSSUnitCategory::Resolution: ts << "Resolution"; break;
+    case CSSUnitCategory::Other: ts << "Other"; break;
+    }
+    return ts;
+}
+
+TextStream& operator<<(TextStream& ts, CSSUnitType unitType)
+{
+    switch (unitType) {
+    case CSSUnitType::CSS_UNKNOWN: ts << "unknown"; break;
+    case CSSUnitType::CSS_NUMBER: ts << "number"; break;
+    case CSSUnitType::CSS_PERCENTAGE: ts << "percentage"; break;
+    case CSSUnitType::CSS_EMS: ts << "ems"; break;
+    case CSSUnitType::CSS_EXS: ts << "exs"; break;
+    case CSSUnitType::CSS_PX: ts << "px"; break;
+    case CSSUnitType::CSS_CM: ts << "cm"; break;
+    case CSSUnitType::CSS_MM: ts << "mm"; break;
+    case CSSUnitType::CSS_IN: ts << "in"; break;
+    case CSSUnitType::CSS_PT: ts << "pt"; break;
+    case CSSUnitType::CSS_PC: ts << "pc"; break;
+    case CSSUnitType::CSS_DEG: ts << "deg"; break;
+    case CSSUnitType::CSS_RAD: ts << "rad"; break;
+    case CSSUnitType::CSS_GRAD: ts << "grad"; break;
+    case CSSUnitType::CSS_MS: ts << "ms"; break;
+    case CSSUnitType::CSS_S: ts << "s"; break;
+    case CSSUnitType::CSS_HZ: ts << "hz"; break;
+    case CSSUnitType::CSS_KHZ: ts << "khz"; break;
+    case CSSUnitType::CSS_DIMENSION: ts << "dimension"; break;
+    case CSSUnitType::CSS_STRING: ts << "string"; break;
+    case CSSUnitType::CSS_URI: ts << "uri"; break;
+    case CSSUnitType::CSS_IDENT: ts << "ident"; break;
+    case CSSUnitType::CSS_ATTR: ts << "attr"; break;
+    case CSSUnitType::CSS_COUNTER: ts << "counter"; break;
+    case CSSUnitType::CSS_RECT: ts << "rect"; break;
+    case CSSUnitType::CSS_RGBCOLOR: ts << "rgbcolor"; break;
+    case CSSUnitType::CSS_VW: ts << "vw"; break;
+    case CSSUnitType::CSS_VH: ts << "vh"; break;
+    case CSSUnitType::CSS_VMIN: ts << "vmin"; break;
+    case CSSUnitType::CSS_VMAX: ts << "vmax"; break;
+    case CSSUnitType::CSS_DPPX: ts << "dppx"; break;
+    case CSSUnitType::CSS_DPI: ts << "dpi"; break;
+    case CSSUnitType::CSS_DPCM: ts << "dpcm"; break;
+    case CSSUnitType::CSS_FR: ts << "fr"; break;
+    case CSSUnitType::CSS_Q: ts << "q"; break;
+    case CSSUnitType::CSS_PAIR: ts << "pair"; break;
+    case CSSUnitType::CSS_UNICODE_RANGE: ts << "unicode_range"; break;
+    case CSSUnitType::CSS_TURN: ts << "turn"; break;
+    case CSSUnitType::CSS_REMS: ts << "rems"; break;
+    case CSSUnitType::CSS_CHS: ts << "chs"; break;
+    case CSSUnitType::CSS_COUNTER_NAME: ts << "counter_name"; break;
+    case CSSUnitType::CSS_SHAPE: ts << "shape"; break;
+    case CSSUnitType::CSS_QUAD: ts << "quad"; break;
+    case CSSUnitType::CSS_CALC: ts << "calc"; break;
+    case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_NUMBER: ts << "calc_percentage_with_number"; break;
+    case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH: ts << "calc_percentage_with_length"; break;
+    case CSSUnitType::CSS_FONT_FAMILY: ts << "font_family"; break;
+    case CSSUnitType::CSS_PROPERTY_ID: ts << "property_id"; break;
+    case CSSUnitType::CSS_VALUE_ID: ts << "value_id"; break;
+    case CSSUnitType::CSS_QUIRKY_EMS: ts << "quirky_ems"; break;
+    }
+    return ts;
+}
+
 } // namespace WebCore
index 9a93bab..2380fab 100644 (file)
 
 #pragma once
 
+namespace WTF {
+class TextStream;
+}
+
 namespace WebCore {
 
 enum class CSSUnitType : uint8_t {
@@ -106,5 +110,8 @@ CSSUnitCategory unitCategory(CSSUnitType);
 CSSUnitType canonicalUnitTypeForCategory(CSSUnitCategory);
 CSSUnitType canonicalUnitType(CSSUnitType);
 
+WTF::TextStream& operator<<(WTF::TextStream&, CSSUnitCategory);
+WTF::TextStream& operator<<(WTF::TextStream&, CSSUnitType);
+
 } // namespace WebCore
 
index b5d08aa..bd72ffc 100644 (file)
@@ -68,28 +68,74 @@ float CalculationValue::evaluate(float maxValue) const
     return m_shouldClampToNonNegative && result < 0 ? 0 : result;
 }
 
+float CalcExpressionNegation::evaluate(float maxValue) const
+{
+    return -m_child->evaluate(maxValue);
+}
+
+bool CalcExpressionNegation::operator==(const CalcExpressionNode& other) const
+{
+    return is<CalcExpressionNegation>(other) && *this == downcast<CalcExpressionNegation>(other);
+}
+
+void CalcExpressionNegation::dump(TextStream& ts) const
+{
+    ts << "-(";
+    ts << *m_child;
+    ts << ")";
+}
+
+bool operator==(const CalcExpressionNegation& a, const CalcExpressionNegation& b)
+{
+    return *a.child() == *b.child();
+}
+
+float CalcExpressionInversion::evaluate(float maxValue) const
+{
+    return 1.0f / m_child->evaluate(maxValue);
+}
+
+void CalcExpressionInversion::dump(TextStream& ts) const
+{
+    ts << "1.0 / " << "(";
+    ts << *m_child;
+    ts << ")";
+}
+
+bool CalcExpressionInversion::operator==(const CalcExpressionNode& other) const
+{
+    return is<CalcExpressionInversion>(other) && *this == downcast<CalcExpressionInversion>(other);
+}
+
+bool operator==(const CalcExpressionInversion& a, const CalcExpressionInversion& b)
+{
+    return *a.child() == *b.child();
+}
+
 float CalcExpressionOperation::evaluate(float maxValue) const
 {
     switch (m_operator) {
     case CalcOperator::Add: {
-        ASSERT(m_children.size() == 2);
-        float left = m_children[0]->evaluate(maxValue);
-        float right = m_children[1]->evaluate(maxValue);
-        return left + right;
+        float sum = 0;
+        for (auto& child : m_children)
+            sum += child->evaluate(maxValue);
+        return sum;
     }
     case CalcOperator::Subtract: {
+        // FIXME
         ASSERT(m_children.size() == 2);
         float left = m_children[0]->evaluate(maxValue);
         float right = m_children[1]->evaluate(maxValue);
         return left - right;
     }
     case CalcOperator::Multiply: {
-        ASSERT(m_children.size() == 2);
-        float left = m_children[0]->evaluate(maxValue);
-        float right = m_children[1]->evaluate(maxValue);
-        return left * right;
+        float product = 1;
+        for (auto& child : m_children)
+            product *= child->evaluate(maxValue);
+        return product;
     }
     case CalcOperator::Divide: {
+        // FIXME
         ASSERT(m_children.size() == 1 || m_children.size() == 2);
         if (m_children.size() == 1)
             return std::numeric_limits<float>::quiet_NaN();
@@ -203,8 +249,8 @@ TextStream& operator<<(TextStream& ts, CalcOperator op)
     case CalcOperator::Subtract: ts << "-"; break;
     case CalcOperator::Multiply: ts << "*"; break;
     case CalcOperator::Divide: ts << "/"; break;
-    case CalcOperator::Min: ts << "max"; break;
-    case CalcOperator::Max: ts << "min"; break;
+    case CalcOperator::Min: ts << "min"; break;
+    case CalcOperator::Max: ts << "max"; break;
     }
     return ts;
 }
index 9e075d9..6f28118 100644 (file)
@@ -57,6 +57,8 @@ enum class CalcExpressionNodeType : uint8_t {
     Number,
     Length,
     Operation,
+    Negation,
+    Inversion,
     BlendLength,
 };
 
@@ -104,6 +106,48 @@ private:
     Length m_length;
 };
 
+class CalcExpressionNegation final : public CalcExpressionNode {
+public:
+    CalcExpressionNegation(std::unique_ptr<CalcExpressionNode>&& node)
+        : CalcExpressionNode(CalcExpressionNodeType::Negation)
+        , m_child(WTFMove(node))
+    {
+        ASSERT(m_child);
+    }
+
+    float evaluate(float maxValue) const final;
+    bool operator==(const CalcExpressionNode&) const final;
+    void dump(WTF::TextStream&) const final;
+    
+    const CalcExpressionNode* child() const { return m_child.get(); }
+
+private:
+    std::unique_ptr<CalcExpressionNode> m_child;
+};
+
+bool operator==(const CalcExpressionNegation&, const CalcExpressionNegation&);
+
+class CalcExpressionInversion final : public CalcExpressionNode {
+public:
+    CalcExpressionInversion(std::unique_ptr<CalcExpressionNode>&& node)
+        : CalcExpressionNode(CalcExpressionNodeType::Inversion)
+        , m_child(WTFMove(node))
+    {
+        ASSERT(m_child);
+    }
+
+    float evaluate(float maxValue) const final;
+    bool operator==(const CalcExpressionNode&) const final;
+    void dump(WTF::TextStream&) const final;
+
+    const CalcExpressionNode* child() const { return m_child.get(); }
+
+private:
+    std::unique_ptr<CalcExpressionNode> m_child;
+};
+
+bool operator==(const CalcExpressionInversion&, const CalcExpressionInversion&);
+
 class CalcExpressionOperation final : public CalcExpressionNode {
 public:
     CalcExpressionOperation(Vector<std::unique_ptr<CalcExpressionNode>>&& children, CalcOperator);
@@ -220,4 +264,6 @@ SPECIALIZE_TYPE_TRAITS_END()
 SPECIALIZE_TYPE_TRAITS_CALCEXPRESSION_NODE(CalcExpressionNumber, type() == WebCore::CalcExpressionNodeType::Number)
 SPECIALIZE_TYPE_TRAITS_CALCEXPRESSION_NODE(CalcExpressionLength, type() == WebCore::CalcExpressionNodeType::Length)
 SPECIALIZE_TYPE_TRAITS_CALCEXPRESSION_NODE(CalcExpressionOperation, type() == WebCore::CalcExpressionNodeType::Operation)
+SPECIALIZE_TYPE_TRAITS_CALCEXPRESSION_NODE(CalcExpressionNegation, type() == WebCore::CalcExpressionNodeType::Negation)
+SPECIALIZE_TYPE_TRAITS_CALCEXPRESSION_NODE(CalcExpressionInversion, type() == WebCore::CalcExpressionNodeType::Inversion)
 SPECIALIZE_TYPE_TRAITS_CALCEXPRESSION_NODE(CalcExpressionBlendLength, type() == WebCore::CalcExpressionNodeType::BlendLength)
index dfd5bbc..2314b87 100644 (file)
@@ -229,6 +229,7 @@ SVGLengthValue SVGLengthValue::blend(const SVGLengthValue& from, const SVGLength
 
 SVGLengthValue SVGLengthValue::fromCSSPrimitiveValue(const CSSPrimitiveValue& value)
 {
+    // FIXME: This needs to call value.computeLength() so it can correctly resolve non-absolute units (webkit.org/b/204826).
     SVGLengthType lengthType = primitiveTypeToLengthType(value.primitiveType());
     return lengthType == SVGLengthType::Unknown ? SVGLengthValue() : SVGLengthValue(value.floatValue(), lengthType);
 }
index fa6dee2..10a1b3c 100644 (file)
@@ -57,7 +57,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "FB39D0D01200F0E300088E69"
-               BuildableName = "libANGLE.a"
+               BuildableName = "libANGLE.dylib"
                BlueprintName = "ANGLE"
                ReferencedContainer = "container:Source/ThirdParty/ANGLE/ANGLE.xcodeproj">
             </BuildableReference>
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
       <MacroExpansion>
          <BuildableReference
             BuildableIdentifier = "primary"
             ReferencedContainer = "container:Tools/MiniBrowser/MiniBrowser.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
+      <Testables>
+      </Testables>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
             ReferencedContainer = "container:Tools/MiniBrowser/MiniBrowser.xcodeproj">
          </BuildableReference>
       </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"