Only allow calc() combinations as per spec.

We allowed calc expressions combining numbers and lengths, numbers and
percentages, and even a combination of numbers, percentages, and
lengths. None of those are allowed per the CSS Values and Units spec,
and it made it possible to combine user units, lengths, and percentages
into the same calc() for certain SVG CSS properties and attributes.

See also: https://github.com/w3c/csswg-drafts/issues/4874

This improves SVG interop with Firefox. Firefox does not support calc()
for numbers, though.

Bug: 1061714
Change-Id: I9e5c492122242e51064310a40e28a233530e357c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2134251
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#756226}
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.cc b/third_party/blink/renderer/core/css/css_math_expression_node.cc
index b274e03..b60c35e4 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.cc
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.cc
@@ -229,10 +229,7 @@
     case kCalcAngle:
     case kCalcFrequency:
     case kCalcPercentLength:
-    case kCalcPercentNumber:
     case kCalcTime:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
     case kCalcOther:
       NOTREACHED();
       break;
@@ -285,43 +282,26 @@
 // ------ End of CSSMathExpressionNumericLiteral member functions
 
 static const CalculationCategory kAddSubtractResult[kCalcOther][kCalcOther] = {
-    /* CalcNumber */ {kCalcNumber, kCalcLengthNumber, kCalcPercentNumber,
-                      kCalcPercentNumber, kCalcOther, kCalcOther, kCalcOther,
-                      kCalcOther, kCalcLengthNumber, kCalcPercentLengthNumber},
+    /* CalcNumber */ {kCalcNumber, kCalcOther, kCalcOther, kCalcOther,
+                      kCalcOther, kCalcOther, kCalcOther},
     /* CalcLength */
-    {kCalcLengthNumber, kCalcLength, kCalcPercentLength, kCalcOther,
-     kCalcPercentLength, kCalcOther, kCalcOther, kCalcOther, kCalcLengthNumber,
-     kCalcPercentLengthNumber},
+    {kCalcOther, kCalcLength, kCalcPercentLength, kCalcPercentLength,
+     kCalcOther, kCalcOther, kCalcOther},
     /* CalcPercent */
-    {kCalcPercentNumber, kCalcPercentLength, kCalcPercent, kCalcPercentNumber,
-     kCalcPercentLength, kCalcOther, kCalcOther, kCalcOther,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber},
-    /* CalcPercentNumber */
-    {kCalcPercentNumber, kCalcPercentLengthNumber, kCalcPercentNumber,
-     kCalcPercentNumber, kCalcPercentLengthNumber, kCalcOther, kCalcOther,
-     kCalcOther, kCalcOther, kCalcPercentLengthNumber},
+    {kCalcOther, kCalcPercentLength, kCalcPercent, kCalcPercentLength,
+     kCalcOther, kCalcOther, kCalcOther},
     /* CalcPercentLength */
-    {kCalcPercentLengthNumber, kCalcPercentLength, kCalcPercentLength,
-     kCalcPercentLengthNumber, kCalcPercentLength, kCalcOther, kCalcOther,
-     kCalcOther, kCalcOther, kCalcPercentLengthNumber},
+    {kCalcOther, kCalcPercentLength, kCalcPercentLength, kCalcPercentLength,
+     kCalcOther, kCalcOther, kCalcOther},
     /* CalcAngle  */
-    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcAngle,
-     kCalcOther, kCalcOther, kCalcOther, kCalcOther},
+    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcAngle, kCalcOther,
+     kCalcOther},
     /* CalcTime */
-    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther,
-     kCalcTime, kCalcOther, kCalcOther, kCalcOther},
+    {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcTime,
+     kCalcOther},
     /* CalcFrequency */
     {kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther, kCalcOther,
-     kCalcOther, kCalcFrequency, kCalcOther, kCalcOther},
-    /* CalcLengthNumber */
-    {kCalcLengthNumber, kCalcLengthNumber, kCalcPercentLengthNumber,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber, kCalcOther, kCalcOther,
-     kCalcOther, kCalcLengthNumber, kCalcPercentLengthNumber},
-    /* CalcPercentLengthNumber */
-    {kCalcPercentLengthNumber, kCalcPercentLengthNumber,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber,
-     kCalcPercentLengthNumber, kCalcOther, kCalcOther, kCalcOther,
-     kCalcPercentLengthNumber, kCalcPercentLengthNumber}};
+     kCalcFrequency}};
 
 static CalculationCategory DetermineCategory(
     const CSSMathExpressionNode& left_side,
@@ -729,9 +709,6 @@
     case kCalcFrequency:
       return CSSPrimitiveValue::UnitType::kHertz;
     case kCalcPercentLength:
-    case kCalcPercentNumber:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
     case kCalcOther:
       return CSSPrimitiveValue::UnitType::kUnknown;
   }
diff --git a/third_party/blink/renderer/core/css/css_math_expression_node.h b/third_party/blink/renderer/core/css/css_math_expression_node.h
index b1e932b6..1d1b76f 100644
--- a/third_party/blink/renderer/core/css/css_math_expression_node.h
+++ b/third_party/blink/renderer/core/css/css_math_expression_node.h
@@ -47,17 +47,14 @@
 // The order of this enum should not change since its elements are used as
 // indices in the addSubtractResult matrix.
 enum CalculationCategory {
-  kCalcNumber = 0,
+  kCalcNumber,
   kCalcLength,
   kCalcPercent,
-  kCalcPercentNumber,
   kCalcPercentLength,
   kCalcAngle,
   kCalcTime,
   kCalcFrequency,
-  kCalcLengthNumber,
-  kCalcPercentLengthNumber,
-  kCalcOther
+  kCalcOther,
 };
 
 class CORE_EXPORT CSSMathExpressionNode
@@ -121,9 +118,7 @@
 
   CalculationCategory Category() const { return category_; }
   bool HasPercentage() const {
-    return category_ == kCalcPercent || category_ == kCalcPercentNumber ||
-           category_ == kCalcPercentLength ||
-           category_ == kCalcPercentLengthNumber;
+    return category_ == kCalcPercent || category_ == kCalcPercentLength;
   }
 
   // Returns the unit type of the math expression *without doing any type
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index 1717d056..b194927 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -470,18 +470,9 @@
 
 bool CanConsumeCalcValue(CalculationCategory category,
                          CSSParserMode css_parser_mode) {
-  if (category == kCalcLength || category == kCalcPercent ||
-      category == kCalcPercentLength)
-    return true;
-
-  if (css_parser_mode != kSVGAttributeMode)
-    return false;
-
-  if (category == kCalcNumber || category == kCalcPercentNumber ||
-      category == kCalcLengthNumber || category == kCalcPercentLengthNumber)
-    return true;
-
-  return false;
+  return category == kCalcLength || category == kCalcPercent ||
+         category == kCalcPercentLength ||
+         (css_parser_mode == kSVGAttributeMode && category == kCalcNumber);
 }
 
 CSSPrimitiveValue* ConsumeLengthOrPercent(CSSParserTokenRange& range,
@@ -512,16 +503,7 @@
            value->GetDoubleValue() != 0;
   }
   const auto& math_value = To<CSSMathFunctionValue>(*value);
-  switch (math_value.Category()) {
-    case kCalcNumber:
-      return math_value.DoubleValue() != 0;
-    case kCalcPercentNumber:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
-      return true;
-    default:
-      return false;
-  }
+  return math_value.Category() == kCalcNumber && math_value.DoubleValue() != 0;
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/svg/svg_length.cc b/third_party/blink/renderer/core/svg/svg_length.cc
index 9a0108d1..ffcafb4 100644
--- a/third_party/blink/renderer/core/svg/svg_length.cc
+++ b/third_party/blink/renderer/core/svg/svg_length.cc
@@ -165,10 +165,7 @@
     case kCalcLength:
     case kCalcNumber:
     case kCalcPercent:
-    case kCalcPercentNumber:
     case kCalcPercentLength:
-    case kCalcLengthNumber:
-    case kCalcPercentLengthNumber:
       return true;
     default:
       return false;
diff --git a/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html b/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html
index 2db968d..448aa01 100644
--- a/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html
+++ b/third_party/blink/web_tests/animations/svg-attribute-interpolation/svg-calc-interpolation.html
@@ -40,13 +40,13 @@
   from: '0%',
   to: '100'
 }, [
-  {at: -0.25, is: 'calc(0% + -25)'},
+  {at: -0.25, is: 'calc(0% + -25px)'},
   {at: 0, is: '0%'},
-  {at: 0.25, is: 'calc(0% + 25)'},
-  {at: 0.5, is: 'calc(0% + 50)'},
-  {at: 0.75, is: 'calc(0% + 75)'},
+  {at: 0.25, is: 'calc(0% + 25px)'},
+  {at: 0.5, is: 'calc(0% + 50px)'},
+  {at: 0.75, is: 'calc(0% + 75px)'},
   {at: 1, is: '100px'},
-  {at: 1.25, is: 'calc(0% + 125)'}
+  {at: 1.25, is: 'calc(0% + 125px)'}
 ]);
 assertAttributeInterpolation({
   property: 'cy',
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html b/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html
index c61cb65..459fc73 100644
--- a/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/svglength-additive-by-8.html
@@ -13,8 +13,8 @@
 <!-- an1: Change width from 10 to 50 in 4s -->
 <!-- an2: Change width from 10 to 100 in 4s starting at 5s -->
 <rect width="10" height="100" fill="green">
-    <animate id="an1" attributeType="XML" attributeName="width" fill="remove" by="calc(4% + 8)" begin="0s" dur="4s"/>
-        <animate id="an2" attributeType="XML" attributeName="width" additive="replace" fill="freeze" by="calc(10% + 10)" begin="5s" dur="4s"/>
+    <animate id="an1" attributeType="XML" attributeName="width" fill="remove" by="calc(4% + 8px)" begin="0s" dur="4s"/>
+        <animate id="an2" attributeType="XML" attributeName="width" additive="replace" fill="freeze" by="calc(10% + 10px)" begin="5s" dur="4s"/>
 </rect>
 
 </svg>
@@ -73,4 +73,4 @@
 
 window.animationStartsImmediately = true;
 
-</script>
\ No newline at end of file
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg
index 0c356b6..53a9640c 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-invalid.svg
@@ -17,6 +17,9 @@
 test_invalid_value("stroke-dasharray", "none 10px");
 test_invalid_value("stroke-dasharray", "20px / 30px");
 test_invalid_value("stroke-dasharray", "-40px");
+test_invalid_value("stroke-dasharray", "calc(2px + 3)");
+test_invalid_value("stroke-dasharray", "calc(10% + 5)");
+test_invalid_value("stroke-dasharray", "calc(40 + calc(3px + 6%))");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg
index e47ebc62..9326118 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dasharray-valid.svg
@@ -23,6 +23,9 @@
 test_valid_value("stroke-dasharray", "10pt 20% 30pc 40in", "10pt, 20%, 30pc, 40in");
 test_valid_value("stroke-dasharray", "10vmin, 20vmax, 30em, 40ex");
 test_valid_value("stroke-dasharray", "0, 5", ["0, 5", "0px, 5px"]); // Edge/Safari serialize numbers as lengths.
+test_valid_value("stroke-dasharray", "calc(3)");
+test_valid_value("stroke-dasharray", "calc(2 + 1)", "calc(3)");
+test_valid_value("stroke-dasharray", "calc(2 + (7 - 5))", "calc(4)");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg
index 64e2eec7..2040355 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-invalid.svg
@@ -17,6 +17,9 @@
 test_invalid_value("stroke-dashoffset", "-10.px");
 test_invalid_value("stroke-dashoffset", "30deg");
 test_invalid_value("stroke-dashoffset", "40px 50%");
+test_invalid_value("stroke-dashoffset", "calc(2px + 3)");
+test_invalid_value("stroke-dashoffset", "calc(10% + 5)");
+test_invalid_value("stroke-dashoffset", "calc(40 + calc(3px + 6%))");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
index a683e2f..2de3aab 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid-expected.txt
@@ -5,5 +5,8 @@
 FAIL e.style['stroke-dashoffset'] = "30" should set the property value assert_equals: serialization should be canonical expected "30px" but got "30"
 PASS e.style['stroke-dashoffset'] = "40Q" should set the property value
 PASS e.style['stroke-dashoffset'] = "calc(2em + 3ex)" should set the property value
+PASS e.style['stroke-dashoffset'] = "calc(3)" should set the property value
+PASS e.style['stroke-dashoffset'] = "calc(2 + 1)" should set the property value
+PASS e.style['stroke-dashoffset'] = "calc(2 + (7 - 5))" should set the property value
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
index f34774e..fe7ba12 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-dashoffset-valid.svg
@@ -19,6 +19,9 @@
 test_valid_value("stroke-dashoffset", "30", "30px");
 test_valid_value("stroke-dashoffset", "40Q", "40q");
 test_valid_value("stroke-dashoffset", "calc(2em + 3ex)");
+test_valid_value("stroke-dashoffset", "calc(3)");
+test_valid_value("stroke-dashoffset", "calc(2 + 1)", "calc(3)");
+test_valid_value("stroke-dashoffset", "calc(2 + (7 - 5))", "calc(4)");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg
index 0d3f63d..2111e37 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-invalid.svg
@@ -17,6 +17,9 @@
 test_invalid_value("stroke-width", "10px 20px");
 test_invalid_value("stroke-width", "-1px");
 test_invalid_value("stroke-width", "-10%");
+test_invalid_value("stroke-width", "calc(2px + 3)");
+test_invalid_value("stroke-width", "calc(10% + 5)");
+test_invalid_value("stroke-width", "calc(40 + calc(3px + 6%))");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
index 9ec8028..e9b028d 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid-expected.txt
@@ -6,5 +6,8 @@
 PASS e.style['stroke-width'] = "4%" should set the property value
 PASS e.style['stroke-width'] = "5vmin" should set the property value
 PASS e.style['stroke-width'] = "calc(50% + 60px)" should set the property value
+PASS e.style['stroke-width'] = "calc(3)" should set the property value
+PASS e.style['stroke-width'] = "calc(2 + 1)" should set the property value
+PASS e.style['stroke-width'] = "calc(2 + (7 - 5))" should set the property value
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
index f9078128..1ee0449 100644
--- a/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
+++ b/third_party/blink/web_tests/external/wpt/svg/painting/parsing/stroke-width-valid.svg
@@ -20,6 +20,9 @@
 test_valid_value("stroke-width", "4%");
 test_valid_value("stroke-width", "5vmin");
 test_valid_value("stroke-width", "calc(50% + 60px)");
+test_valid_value("stroke-width", "calc(3)");
+test_valid_value("stroke-width", "calc(2 + 1)", "calc(3)");
+test_valid_value("stroke-width", "calc(2 + (7 - 5))", "calc(4)");
 
   ]]></script>
 </svg>
diff --git a/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html b/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html
index 9a76d709..d983f5d 100644
--- a/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html
+++ b/third_party/blink/web_tests/svg/custom/calc-expression-with-zoom.html
@@ -3,6 +3,6 @@
    body { zoom: 200%; }
 </style>
 <svg id="svg" width="500" height="500" viewBox='0 0 1000 1000'>
-    <rect width='calc(50px + 50)' height='100' fill='green'/>
+    <rect width='calc(50px + 50px)' height='100' fill='green'/>
     <rect x='110' width='calc(50 + 50)' height='100' fill='green'/>
 </svg> 
diff --git a/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg b/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg
index fe98961..55e9c79 100644
--- a/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg
+++ b/third_party/blink/web_tests/svg/custom/rect-calc-corner-radii.svg
@@ -1,5 +1,5 @@
 <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
     <rect  x="50" y="50"  width="400" height="400" rx="calc(-50)"  ry="calc(-50)"  fill="#00ff00" />
-    <rect x="150" y="100" width="200" height="300" rx="calc(20% - 50)" ry="50" fill="#0000ff" />
+    <rect x="150" y="100" width="200" height="300" rx="calc(20% - 50px)" ry="50" fill="#0000ff" />
     <rect x="200" y="150" width="100" height="200" rx="50" ry="-100" fill="#ff0000" />
 </svg>
diff --git a/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html b/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html
index 3d4a704..7b718975 100644
--- a/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html
+++ b/third_party/blink/web_tests/svg/dom/SVGLength-calc-in-attr.html
@@ -82,10 +82,10 @@
     assert_calc_expression("calc(10mm + 10mm)", (20 * cssPixelsPerMillimeter));
     assert_calc_expression("calc(20mm)", (20 * cssPixelsPerMillimeter));
     assert_calc_expression("calc(10 + 10)", 20);
-    assert_calc_expression("calc(10mm + 10)", (10 * cssPixelsPerMillimeter) + 10);
-    assert_calc_expression("calc(10% + 10)", (10 * viewportWidthPercent()) + 10);
-    assert_calc_expression("calc(1cm + 2in + 1cm + 2)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
-    assert_calc_expression("calc(1cm + 2 + 1cm + 2in)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
-    assert_calc_expression("calc(10% + 10 + 2% + 10pc)", (12 * viewportWidthPercent()) + 10 + (10 * cssPixelsPerPica));
+    assert_calc_expression("calc(10mm + 10px)", (10 * cssPixelsPerMillimeter) + 10);
+    assert_calc_expression("calc(10% + 10px)", (10 * viewportWidthPercent()) + 10);
+    assert_calc_expression("calc(1cm + 2in + 1cm + 2px)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
+    assert_calc_expression("calc(1cm + 2px + 1cm + 2in)", (2 * cssPixelsPerInch) + (2 * cssPixelsPerCentimeter) + 2);
+    assert_calc_expression("calc(10% + 10px + 2% + 10pc)", (12 * viewportWidthPercent()) + 10 + (10 * cssPixelsPerPica));
 }, "Tests calc() on presentation and non-presentation attr in svgLength");
 </script>