Title: [283359] trunk
Revision
283359
Author
[email protected]
Date
2021-10-01 00:58:12 -0700 (Fri, 01 Oct 2021)

Log Message

Source/WebCore:
Add support for pow(), sqrt() and hypot()
https://bugs.webkit.org/show_bug.cgi?id=203312
<rdar://82640883>

Patch by Kevin Turner <[email protected]> on 2021-10-01
Reviewed by Simon Fraser.

Implements pow(), sqrt() and hypot() functions as specified by https://drafts.csswg.org/css-values-4/#exponent-funcs.

Test: fast/css/calc-parsing.html

* css/CSSValueKeywords.in: Adds pow, sqrt, and hypot keywords.
* css/calc/CSSCalcExpressionNodeParser.cpp:
(WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
* css/calc/CSSCalcOperationNode.cpp:
(WebCore::determineCategory):
(WebCore::functionFromOperator):
(WebCore::CSSCalcOperationNode::createPowOrSqrt):
(WebCore::CSSCalcOperationNode::createHypot):
(WebCore::CSSCalcOperationNode::canCombineAllChildren const): Ensures nodes that are identity functions cannot have their children combined.
(WebCore::CSSCalcOperationNode::combineChildren): Early exit if node behaves as an identity function. Performs combine of children if node is an exponential function.
(WebCore::CSSCalcOperationNode::simplifyNode): Avoid simplifying if node does not behave as an identify function. Calls combineChildren() if node is an exponential function.
(WebCore::functionPrefixForOperator):
(WebCore::CSSCalcOperationNode::evaluateOperator):
* css/calc/CSSCalcOperationNode.h: Adds isExponentialFunction() and isIdentity() methods. Exponential functions include hypot, sqrt, and pow. Identity functions are min and max when they contain one child.
* css/calc/CSSCalcValue.cpp:
(WebCore::createCSS):
(WebCore::CSSCalcValue::isCalcFunction):
* platform/calc/CalcExpressionOperation.cpp:
(WebCore::CalcExpressionOperation::evaluate const):
* platform/calc/CalcOperator.cpp:
(WebCore::operator<<):
* platform/calc/CalcOperator.h:

LayoutTests:
Add support for pow(), sqrt() and hypot() per https://drafts.csswg.org/css-values-4/#exponent-funcs.
https://bugs.webkit.org/show_bug.cgi?id=203312

Patch by Kevin Turner <[email protected]> on 2021-10-01
Reviewed by Simon Fraser.

Tests performing CSS calculations with pow(), sqrt(), and hypot() with a range of inputs.
* fast/css/calc-parsing-expected.txt:
* fast/css/calc-parsing.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (283358 => 283359)


--- trunk/LayoutTests/ChangeLog	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/LayoutTests/ChangeLog	2021-10-01 07:58:12 UTC (rev 283359)
@@ -1,3 +1,14 @@
+2021-10-01  Kevin Turner  <[email protected]>
+
+        Add support for pow(), sqrt() and hypot() per https://drafts.csswg.org/css-values-4/#exponent-funcs. 
+        https://bugs.webkit.org/show_bug.cgi?id=203312
+
+        Reviewed by Simon Fraser.       
+
+        Tests performing CSS calculations with pow(), sqrt(), and hypot() with a range of inputs.
+        * fast/css/calc-parsing-expected.txt:
+        * fast/css/calc-parsing.html:
+
 2021-09-30  Lauro Moura  <[email protected]>
 
         [GLIB] Gardening some mediastream consistent failures

Modified: trunk/LayoutTests/fast/css/calc-parsing-expected.txt (283358 => 283359)


--- trunk/LayoutTests/fast/css/calc-parsing-expected.txt	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/LayoutTests/fast/css/calc-parsing-expected.txt	2021-10-01 07:58:12 UTC (rev 283359)
@@ -52,6 +52,58 @@
 PASS element.style['width'] is "clamp(100px, 0%, 1%)"
 PASS getComputedStyle(element).getPropertyValue('width') is "100px"
 
+element.style["width"] = "calc(100px * pow(2, pow(2, 2)))"
+PASS element.style['width'] is "calc(1600px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "1600px"
+
+element.style["width"] = "calc(1px * pow(2, 3))"
+PASS element.style['width'] is "calc(8px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "8px"
+
+element.style["width"] = "calc(100px * sqrt(100))"
+PASS element.style['width'] is "calc(1000px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "1000px"
+
+element.style["width"] = "calc(1px * sqrt(999))"
+PASS element.style['width'] is "calc(31.606961258558215px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "31.59375px"
+
+element.style["width"] = "calc(1px * pow(2, sqrt(100))"
+PASS element.style['width'] is "calc(1024px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "1024px"
+
+element.style["width"] = "hypot(4px, 5px, 7px, 9px)"
+PASS element.style['width'] is "hypot(13.076696830622021px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "13.0625px"
+
+element.style["width"] = "hypot(3px, 4px)"
+PASS element.style['width'] is "hypot(5px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "5px"
+
+element.style["width"] = "calc(100px * hypot(3, 4))"
+PASS element.style['width'] is "calc(500px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "500px"
+
+element.style["width"] = "hypot(-5px)"
+PASS element.style['width'] is "hypot(5px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "5px"
+
+element.style["width"] = "calc(1px * hypot(-5))"
+PASS element.style['width'] is "calc(5px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "5px"
+
+element.style["width"] = "calc(1px * hypot(10000))"
+PASS element.style['width'] is "calc(10000px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "10000px"
+
+element.style["width"] = "calc(2px * sqrt(100000000))"
+PASS element.style['width'] is "calc(20000px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "20000px"
+
+element.style["width"] = "calc(3px * pow(200, 4))"
+PASS element.style['width'] is "calc(4800000000px)"
+PASS getComputedStyle(element).getPropertyValue('width') is "33554428px"
+
 element.style["width"] = "calc(sin(90deg) * 100px)"
 PASS 100 is 100
 
@@ -128,6 +180,54 @@
 PASS element.style['width'] is "999px"
 PASS getComputedStyle(element).getPropertyValue('width') is "999px"
 
+element.style["width"] = "calc(1px * pow(1))"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "calc(1px * pow(2px, 3px))"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "calc(sqrt(100px)"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "hypot(2px, 40%)"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "hypot(2px, 3)"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "hypot(3, ,4)"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "calc(1px * pow(2 3))"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "hypot()"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "calc(pow(2))"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "pow())"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "calc(sqrt())"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
+element.style["width"] = "calc(sqrt(100, 200))"
+PASS element.style['width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('width') is "999px"
+
 element.style["width"] = "calc(sin(90px) * 100px)"
 PASS element.style['width'] is "999px"
 PASS getComputedStyle(element).getPropertyValue('width') is "999px"
@@ -212,6 +312,58 @@
 PASS element.style['min-width'] is "clamp(100px, 0%, 1%)"
 PASS getComputedStyle(element).getPropertyValue('min-width') is "clamp(100px, 0%, 1%)"
 
+element.style["min-width"] = "calc(100px * pow(2, pow(2, 2)))"
+PASS element.style['min-width'] is "calc(1600px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "1600px"
+
+element.style["min-width"] = "calc(1px * pow(2, 3))"
+PASS element.style['min-width'] is "calc(8px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "8px"
+
+element.style["min-width"] = "calc(100px * sqrt(100))"
+PASS element.style['min-width'] is "calc(1000px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "1000px"
+
+element.style["min-width"] = "calc(1px * sqrt(999))"
+PASS element.style['min-width'] is "calc(31.606961258558215px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "31.606962203979492px"
+
+element.style["min-width"] = "calc(1px * pow(2, sqrt(100))"
+PASS element.style['min-width'] is "calc(1024px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "1024px"
+
+element.style["min-width"] = "hypot(4px, 5px, 7px, 9px)"
+PASS element.style['min-width'] is "hypot(13.076696830622021px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "13.076696395874023px"
+
+element.style["min-width"] = "hypot(3px, 4px)"
+PASS element.style['min-width'] is "hypot(5px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "5px"
+
+element.style["min-width"] = "calc(100px * hypot(3, 4))"
+PASS element.style['min-width'] is "calc(500px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "500px"
+
+element.style["min-width"] = "hypot(-5px)"
+PASS element.style['min-width'] is "hypot(5px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "5px"
+
+element.style["min-width"] = "calc(1px * hypot(-5))"
+PASS element.style['min-width'] is "calc(5px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "5px"
+
+element.style["min-width"] = "calc(1px * hypot(10000))"
+PASS element.style['min-width'] is "calc(10000px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "10000px"
+
+element.style["min-width"] = "calc(2px * sqrt(100000000))"
+PASS element.style['min-width'] is "calc(20000px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "20000px"
+
+element.style["min-width"] = "calc(3px * pow(200, 4))"
+PASS element.style['min-width'] is "calc(4800000000px)"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "33554428px"
+
 element.style["min-width"] = "calc(sin(90deg) * 100px)"
 PASS 100 is 100
 
@@ -288,6 +440,54 @@
 PASS element.style['min-width'] is "999px"
 PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
 
+element.style["min-width"] = "calc(1px * pow(1))"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "calc(1px * pow(2px, 3px))"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "calc(sqrt(100px)"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "hypot(2px, 40%)"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "hypot(2px, 3)"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "hypot(3, ,4)"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "calc(1px * pow(2 3))"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "hypot()"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "calc(pow(2))"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "pow())"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "calc(sqrt())"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
+element.style["min-width"] = "calc(sqrt(100, 200))"
+PASS element.style['min-width'] is "999px"
+PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
+
 element.style["min-width"] = "calc(sin(90px) * 100px)"
 PASS element.style['min-width'] is "999px"
 PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"

Modified: trunk/LayoutTests/fast/css/calc-parsing.html (283358 => 283359)


--- trunk/LayoutTests/fast/css/calc-parsing.html	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/LayoutTests/fast/css/calc-parsing.html	2021-10-01 07:58:12 UTC (rev 283359)
@@ -42,6 +42,19 @@
                 testExpression('min(100px,0%)', 'min(100px, 0%)', propertyName == 'width' ? '0px' : "min(100px, 0%)");
                 testExpression('max(100px,0%)', 'max(100px, 0%)', propertyName == 'width' ? '100px' : "max(100px, 0%)");
                 testExpression('clamp(100px,0%,1%)', 'clamp(100px, 0%, 1%)', propertyName == 'width' ? '100px' : "clamp(100px, 0%, 1%)");
+                testExpression('calc(100px * pow(2, pow(2, 2)))', 'calc(1600px)', '1600px');
+                testExpression('calc(1px * pow(2, 3))', 'calc(8px)', '8px')
+                testExpression('calc(100px * sqrt(100))', 'calc(1000px)', '1000px');
+                testExpression('calc(1px * sqrt(999))', 'calc(31.606961258558215px)', propertyName == 'width' ? '31.59375px' : '31.606962203979492px');
+                testExpression('calc(1px * pow(2, sqrt(100))', 'calc(1024px)', '1024px');
+                testExpression('hypot(4px, 5px, 7px, 9px)', 'hypot(13.076696830622021px)', propertyName == 'width' ? '13.0625px' : '13.076696395874023px');
+                testExpression('hypot(3px, 4px)', 'hypot(5px)', '5px');
+                testExpression('calc(100px * hypot(3, 4))', 'calc(500px)', '500px');
+                testExpression('hypot(-5px)', 'hypot(5px)', '5px');
+                testExpression('calc(1px * hypot(-5))', 'calc(5px)', '5px');
+                testExpression('calc(1px * hypot(10000))', 'calc(10000px)', '10000px');
+                testExpression('calc(2px * sqrt(100000000))', 'calc(20000px)', '20000px');
+                testExpression('calc(3px * pow(200, 4))', 'calc(4800000000px)', '33554428px');
                 testValue('calc(sin(90deg) * 100px)', '100');
                 testValue('calc(sin(45deg  +  45deg ) * 100px)', '100');
                 testValue('calc(cos( 0 ) * 100px)', '100');
@@ -65,6 +78,18 @@
                 testExpression('clamp(200px,,300px)', '999px', '999px');
                 testExpression('clamp((),,300px)', '999px', '999px');
                 testExpression('clamp(1px,2px,2px,4px)', '999px', '999px');
+                testExpression('calc(1px * pow(1))', '999px', '999px');
+                testExpression('calc(1px * pow(2px, 3px))', '999px', '999px');
+                testExpression('calc(sqrt(100px)', '999px', '999px');
+                testExpression('hypot(2px, 40%)', '999px', '999px');
+                testExpression('hypot(2px, 3)', '999px', '999px');
+                testExpression('hypot(3, ,4)', '999px', '999px');
+                testExpression('calc(1px * pow(2 3))', '999px', '999px');
+                testExpression('hypot()', '999px', '999px');
+                testExpression('calc(pow(2))', '999px', '999px');
+                testExpression('pow())', '999px', '999px', '999px'); 
+                testExpression('calc(sqrt())', '999px', '999px');
+                testExpression('calc(sqrt(100, 200))', '999px', '999px');
                 testExpression('calc(sin(90px) * 100px)', '999px', '999px');
                 testExpression('calc(sin(30deg + 1.0471967rad, 0) * 100px)', '999px', '999px');
                 testExpression('calc(cos( 0 ,) * 100px)', '999px', '999px');

Modified: trunk/Source/WebCore/ChangeLog (283358 => 283359)


--- trunk/Source/WebCore/ChangeLog	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/ChangeLog	2021-10-01 07:58:12 UTC (rev 283359)
@@ -1,3 +1,38 @@
+2021-10-01  Kevin Turner  <[email protected]>
+
+        Add support for pow(), sqrt() and hypot()
+        https://bugs.webkit.org/show_bug.cgi?id=203312
+        <rdar://82640883>
+       
+        Reviewed by Simon Fraser.
+         
+        Implements pow(), sqrt() and hypot() functions as specified by https://drafts.csswg.org/css-values-4/#exponent-funcs. 
+
+        Test: fast/css/calc-parsing.html
+ 
+        * css/CSSValueKeywords.in: Adds pow, sqrt, and hypot keywords.
+        * css/calc/CSSCalcExpressionNodeParser.cpp:
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
+        * css/calc/CSSCalcOperationNode.cpp:
+        (WebCore::determineCategory):
+        (WebCore::functionFromOperator):
+        (WebCore::CSSCalcOperationNode::createPowOrSqrt):
+        (WebCore::CSSCalcOperationNode::createHypot):
+        (WebCore::CSSCalcOperationNode::canCombineAllChildren const): Ensures nodes that are identity functions cannot have their children combined.
+        (WebCore::CSSCalcOperationNode::combineChildren): Early exit if node behaves as an identity function. Performs combine of children if node is an exponential function.
+        (WebCore::CSSCalcOperationNode::simplifyNode): Avoid simplifying if node does not behave as an identify function. Calls combineChildren() if node is an exponential function.
+        (WebCore::functionPrefixForOperator):
+        (WebCore::CSSCalcOperationNode::evaluateOperator):
+        * css/calc/CSSCalcOperationNode.h: Adds isExponentialFunction() and isIdentity() methods. Exponential functions include hypot, sqrt, and pow. Identity functions are min and max when they contain one child.
+        * css/calc/CSSCalcValue.cpp:
+        (WebCore::createCSS):
+        (WebCore::CSSCalcValue::isCalcFunction):
+        * platform/calc/CalcExpressionOperation.cpp:
+        (WebCore::CalcExpressionOperation::evaluate const):
+        * platform/calc/CalcOperator.cpp:
+        (WebCore::operator<<):
+        * platform/calc/CalcOperator.h:
+
 2021-09-30  Simon Fraser  <[email protected]>
 
         Rename snapRubberBand() to have a clearer name

Modified: trunk/Source/WebCore/css/CSSValueKeywords.in (283358 => 283359)


--- trunk/Source/WebCore/css/CSSValueKeywords.in	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/css/CSSValueKeywords.in	2021-10-01 07:58:12 UTC (rev 283359)
@@ -1347,6 +1347,9 @@
 min
 max
 clamp
+pow
+sqrt
+hypot
 sin
 cos
 e

Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp (283358 => 283359)


--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp	2021-10-01 07:58:12 UTC (rev 283359)
@@ -123,6 +123,7 @@
     switch (functionID) {
     case CSSValueMin:
     case CSSValueMax:
+    case CSSValueHypot:
         maxArgumentCount = std::nullopt;
         break;
     case CSSValueClamp:
@@ -156,7 +157,13 @@
     case CSSValueAtan2:
         maxArgumentCount = 2;
         break;
-    // TODO: pow, sqrt, hypot.
+    case CSSValuePow:
+        minArgumentCount = 2;
+        maxArgumentCount = 2;
+        break;
+    case CSSValueSqrt:
+        maxArgumentCount = 1;
+        break;
     default:
         break;
     }
@@ -241,7 +248,15 @@
     case CSSValueSign:
         result = CSSCalcOperationNode::createSign(CalcOperator::Sign, WTFMove(nodes));
         break;
-    // TODO: pow, sqrt, hypot
+    case CSSValuePow:
+        result = CSSCalcOperationNode::createPowOrSqrt(CalcOperator::Pow, WTFMove(nodes));
+        break;
+    case CSSValueSqrt:
+        result = CSSCalcOperationNode::createPowOrSqrt(CalcOperator::Sqrt, WTFMove(nodes));
+        break;
+    case CSSValueHypot:
+        result = CSSCalcOperationNode::createHypot(WTFMove(nodes));
+        break;
     default:
         break;
     }

Modified: trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp (283358 => 283359)


--- trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp	2021-10-01 07:58:12 UTC (rev 283359)
@@ -94,6 +94,9 @@
     case CalcOperator::Down:
     case CalcOperator::Nearest:
     case CalcOperator::ToZero:
+    case CalcOperator::Pow:
+    case CalcOperator::Sqrt:
+    case CalcOperator::Hypot:
         ASSERT_NOT_REACHED();
         return CalculationCategory::Other;
     }
@@ -180,8 +183,12 @@
         case CalcOperator::Down:
         case CalcOperator::Nearest:
         case CalcOperator::ToZero:
-            // The type of a min(), max(), or clamp() _expression_ is the result of adding the types of its comma-separated calculations
+        case CalcOperator::Hypot:
             return CalculationCategory::Other;
+        case CalcOperator::Pow:
+        case CalcOperator::Sqrt:
+            // The type of pow() and sqrt() functions must evaluate to a number.
+            return CalculationCategory::Number;
         }
     }
 
@@ -276,7 +283,6 @@
     return CSSCalcPrimitiveValueNode::UnitConversion::Invalid;
 }
 
-
 static CSSValueID functionFromOperator(CalcOperator op)
 {
     switch (op) {
@@ -291,6 +297,12 @@
         return CSSValueMax;
     case CalcOperator::Clamp:
         return CSSValueClamp;
+    case CalcOperator::Pow:
+        return CSSValuePow;
+    case CalcOperator::Sqrt:
+        return CSSValueSqrt;
+    case CalcOperator::Hypot:
+        return CSSValueHypot;
     case CalcOperator::Sin:
         return CSSValueSin;
     case CalcOperator::Cos:
@@ -331,6 +343,20 @@
     return CSSValueCalc;
 }
 
+static std::optional<CalculationCategory> commonCategory(const Vector<Ref<CSSCalcExpressionNode>>& values)
+{
+    if (values.isEmpty())
+        return std::nullopt;
+
+    auto expectedCategory = values[0]->category();
+    for (size_t i = 1; i < values.size(); ++i) {
+        if (values[i]->category() != expectedCategory)
+            return std::nullopt;
+    }
+
+    return expectedCategory;
+}
+
 RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
 {
     if (!leftSide || !rightSide)
@@ -441,6 +467,34 @@
     return adoptRef(new CSSCalcOperationNode(CalculationCategory::Number, CalcOperator::Exp, WTFMove(values)));
 }
 
+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createPowOrSqrt(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values)
+{
+    if (op == CalcOperator::Pow && values.size() != 2)
+        return nullptr;
+
+    if (op == CalcOperator::Sqrt && values.size() != 1)
+        return nullptr;
+
+    if (commonCategory(values) != CalculationCategory::Number) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create " << op << "node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+
+    return adoptRef(new CSSCalcOperationNode(CalculationCategory::Number, op, WTFMove(values)));
+}
+
+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createHypot(Vector<Ref<CSSCalcExpressionNode>>&& values)
+{
+    auto expectedCategory = commonCategory(values);
+
+    if (expectedCategory == CalculationCategory::Other) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create hypot node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+
+    return adoptRef(new CSSCalcOperationNode(*expectedCategory, CalcOperator::Hypot, WTFMove(values)));
+}
+
 RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createMinOrMaxOrClamp(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory)
 {
     ASSERT(op == CalcOperator::Min || op == CalcOperator::Max || op == CalcOperator::Clamp);
@@ -597,7 +651,7 @@
 
 bool CSSCalcOperationNode::canCombineAllChildren() const
 {
-    if (m_children.size() < 2)
+    if (isIdentity() || !m_children.size())
         return false;
 
     if (!is<CSSCalcPrimitiveValueNode>(m_children[0]))
@@ -630,6 +684,9 @@
 
 void CSSCalcOperationNode::combineChildren()
 {
+    if (isIdentity() || !m_children.size())
+        return;
+
     if (m_children.size() < 2) {
         if (m_children.size() == 1 && isTrigNode()) {
             double resolvedValue = doubleValue(m_children[0]->primitiveType());
@@ -644,7 +701,6 @@
             m_children.clear();
             m_children.append(WTFMove(newChild));
         }
-        
         if (m_children.size() == 1 && isInverseTrigNode()) {
             double resolvedValue = doubleValue(m_children[0]->primitiveType());
             auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, CSSUnitType::CSS_DEG));
@@ -651,7 +707,7 @@
             m_children.clear();
             m_children.append(WTFMove(newChild));
         }
-        if (isSignNode()) {
+        if (isSignNode() || isHypotNode()) {
             auto combinedUnitType = m_children[0]->primitiveType();
             if (calcOperator() == CalcOperator::Sign)
                 combinedUnitType = CSSUnitType::CSS_NUMBER;
@@ -660,9 +716,15 @@
             m_children.clear();
             m_children.append(WTFMove(newChild));
         }
+        if (calcOperator() == CalcOperator::Sqrt) {
+            double resolvedValue = doubleValue(m_children[0]->primitiveType());
+            auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, CSSUnitType::CSS_NUMBER));
+            m_children.clear();
+            m_children.append(WTFMove(newChild));
+        }
         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) {
@@ -781,7 +843,7 @@
         m_children = WTFMove(newChildren);
     }
 
-    if (isMinOrMaxNode() && canCombineAllChildren()) {
+    if ((isMinOrMaxNode() || isHypotNode()) && canCombineAllChildren()) {
         auto combinedUnitType = m_children[0]->primitiveType();
         auto category = calculationCategoryForCombination(combinedUnitType);
         if (category != CalculationCategory::Other)
@@ -793,7 +855,14 @@
         m_children.clear();
         m_children.append(WTFMove(newChild));
     }
-    
+
+    if (calcOperator() == CalcOperator::Pow) {
+        auto resolvedValue = doubleValue(m_children[0]->primitiveType());
+        auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, CSSUnitType::CSS_NUMBER));
+        m_children.clear();
+        m_children.append(WTFMove(newChild));
+    }
+
     if (calcOperator() == CalcOperator::Atan2) {
         double resolvedValue = doubleValue(m_children[0]->primitiveType());
         auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, CSSUnitType::CSS_DEG));
@@ -871,8 +940,8 @@
     // using its children, expressed in the result’s canonical unit.
     if (is<CSSCalcOperationNode>(rootNode)) {
         auto& calcOperationNode = downcast<CSSCalcOperationNode>(rootNode.get());
-        // Simplify operations with only one child node (other than root and operations that only need one node).
-        if (calcOperationNode.children().size() == 1 && depth && !calcOperationNode.isTrigNode() && !calcOperationNode.isExpNode() && !calcOperationNode.isInverseTrigNode() && !calcOperationNode.isSignNode())
+        // Identity nodes have only one child and perform no operation on their child.
+        if (calcOperationNode.isIdentity() && depth)
             return WTFMove(calcOperationNode.children()[0]);
         
         if (calcOperationNode.isCalcSumNode()) {
@@ -907,6 +976,13 @@
         
         if (calcOperationNode.isRoundOperation() && depth)
             calcOperationNode.combineChildren();
+
+        if (calcOperationNode.isHypotNode())
+            calcOperationNode.combineChildren();
+
+        if (calcOperationNode.isPowOrSqrtNode() && depth)
+            calcOperationNode.combineChildren();
+
         // If only one child remains, return the child (except at the root).
         auto shouldCombineParentWithOnlyChild = [](const CSSCalcOperationNode& parent, int depth)
         {
@@ -1119,6 +1195,9 @@
     case CalcOperator::Down: return "round(down, ";
     case CalcOperator::Nearest: return "round(nearest, ";
     case CalcOperator::ToZero: return "round(to-zero, ";
+    case CalcOperator::Pow: return "pow(";
+    case CalcOperator::Sqrt: return "sqrt(";
+    case CalcOperator::Hypot: return "hypot(";
     }
     
     return "";
@@ -1327,6 +1406,25 @@
         double max = children[2];
         return std::max(min, std::min(value, max));
     }
+    case CalcOperator::Pow:
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        return std::pow(children[0], children[1]);
+    case CalcOperator::Sqrt: {
+        if (children.size() != 1)
+            return std::numeric_limits<double>::quiet_NaN();
+        return std::sqrt(children[0]);
+    }
+    case CalcOperator::Hypot: {
+        if (children.isEmpty())
+            return std::numeric_limits<double>::quiet_NaN();
+        if (children.size() == 1)
+            return std::abs(children[0]);
+        double sum = 0;
+        for (auto child : children)
+            sum += (child * child);
+        return std::sqrt(sum);
+    }
     case CalcOperator::Sin: {
         if (children.size() != 1)
             return std::numeric_limits<double>::quiet_NaN();

Modified: trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h (283358 => 283359)


--- trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h	2021-10-01 07:58:12 UTC (rev 283359)
@@ -37,6 +37,8 @@
     static RefPtr<CSSCalcOperationNode> createSum(Vector<Ref<CSSCalcExpressionNode>>&& values);
     static RefPtr<CSSCalcOperationNode> createProduct(Vector<Ref<CSSCalcExpressionNode>>&& values);
     static RefPtr<CSSCalcOperationNode> createMinOrMaxOrClamp(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory);
+    static RefPtr<CSSCalcOperationNode> createPowOrSqrt(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
+    static RefPtr<CSSCalcOperationNode> createHypot(Vector<Ref<CSSCalcExpressionNode>>&& values);
     static RefPtr<CSSCalcOperationNode> createTrig(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
     static RefPtr<CSSCalcOperationNode> createLog(Vector<Ref<CSSCalcExpressionNode>>&& values);
     static RefPtr<CSSCalcOperationNode> createExp(Vector<Ref<CSSCalcExpressionNode>>&& values);
@@ -63,6 +65,8 @@
     bool isSteppedNode() const { return m_operator == CalcOperator::Mod || m_operator == CalcOperator::Rem || m_operator == CalcOperator::Round; }
     bool isRoundOperation() const { return m_operator == CalcOperator::Down || m_operator == CalcOperator::Up || m_operator == CalcOperator::ToZero || m_operator == CalcOperator::Nearest; }
     bool isRoundConstant() const { return (isRoundOperation()) && !m_children.size(); }
+    bool isHypotNode() const { return m_operator == CalcOperator::Hypot; }
+    bool isPowOrSqrtNode() const { return m_operator == CalcOperator::Pow || m_operator == CalcOperator::Sqrt; }
 
     void hoistChildrenWithOperator(CalcOperator);
     void combineChildren();
@@ -69,6 +73,8 @@
     
     bool canCombineAllChildren() const;
 
+    bool isIdentity() const { return m_children.size() == 1 && (m_operator == CalcOperator::Min || m_operator == CalcOperator::Max || m_operator == CalcOperator::Add || m_operator == CalcOperator::Multiply); }
+
     const Vector<Ref<CSSCalcExpressionNode>>& children() const { return m_children; }
     Vector<Ref<CSSCalcExpressionNode>>& children() { return m_children; }
 

Modified: trunk/Source/WebCore/css/calc/CSSCalcValue.cpp (283358 => 283359)


--- trunk/Source/WebCore/css/calc/CSSCalcValue.cpp	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/css/calc/CSSCalcValue.cpp	2021-10-01 07:58:12 UTC (rev 283359)
@@ -213,6 +213,19 @@
                 return nullptr;
             return CSSCalcOperationNode::createSign(op, WTFMove(children));
         }
+        case CalcOperator::Sqrt:
+        case CalcOperator::Pow: {
+            auto children = createCSS(operationChildren, style);
+            if (children.isEmpty())
+                return nullptr;
+            return CSSCalcOperationNode::createPowOrSqrt(op, WTFMove(children));
+        }
+        case CalcOperator::Hypot: {
+            auto children = createCSS(operationChildren, style);
+            if (children.isEmpty())
+                return nullptr;
+            return CSSCalcOperationNode::createHypot(WTFMove(children));
+        }
         case CalcOperator::Mod:
         case CalcOperator::Rem:
         case CalcOperator::Round: {
@@ -338,6 +351,9 @@
     case CSSValueMin:
     case CSSValueMax:
     case CSSValueClamp:
+    case CSSValuePow:
+    case CSSValueSqrt:
+    case CSSValueHypot:
     case CSSValueSin:
     case CSSValueCos:
     case CSSValueTan:

Modified: trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp (283358 => 283359)


--- trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp	2021-10-01 07:58:12 UTC (rev 283359)
@@ -95,6 +95,30 @@
         float max = m_children[2]->evaluate(maxValue);
         return std::max(min, std::min(value, max));
     }
+    case CalcOperator::Pow: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<float>::quiet_NaN();
+        float base = m_children[0]->evaluate(maxValue);
+        float power = m_children[1]->evaluate(maxValue);
+        return std::pow(base, power);
+    }
+    case CalcOperator::Sqrt: {
+        if (m_children.size() != 1)
+            return std::numeric_limits<float>::quiet_NaN();
+        return std::sqrt(m_children[0]->evaluate(maxValue));
+    }
+    case CalcOperator::Hypot: {
+        if (m_children.isEmpty())
+            return std::numeric_limits<float>::quiet_NaN();
+        if (m_children.size() == 1)
+            return std::abs(m_children[0]->evaluate(maxValue));
+        float sum = 0;
+        for (auto& child : m_children) {
+            float value = child->evaluate(maxValue);
+            sum += (value * value);
+        }
+        return sum;
+    }
     case CalcOperator::Sin: {
         if (m_children.size() != 1)
             return std::numeric_limits<double>::quiet_NaN();

Modified: trunk/Source/WebCore/platform/calc/CalcOperator.cpp (283358 => 283359)


--- trunk/Source/WebCore/platform/calc/CalcOperator.cpp	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/platform/calc/CalcOperator.cpp	2021-10-01 07:58:12 UTC (rev 283359)
@@ -40,6 +40,9 @@
     case CalcOperator::Min: ts << "min"; break;
     case CalcOperator::Max: ts << "max"; break;
     case CalcOperator::Clamp: ts << "clamp"; break;
+    case CalcOperator::Pow: ts << "pow"; break;
+    case CalcOperator::Sqrt: ts << "sqrt"; break;
+    case CalcOperator::Hypot: ts << "hypot"; break;
     case CalcOperator::Sin: ts << "sin"; break;
     case CalcOperator::Cos: ts << "cos"; break;
     case CalcOperator::Tan: ts << "tan"; break;

Modified: trunk/Source/WebCore/platform/calc/CalcOperator.h (283358 => 283359)


--- trunk/Source/WebCore/platform/calc/CalcOperator.h	2021-10-01 04:18:17 UTC (rev 283358)
+++ trunk/Source/WebCore/platform/calc/CalcOperator.h	2021-10-01 07:58:12 UTC (rev 283359)
@@ -38,6 +38,9 @@
     Min = 0,
     Max,
     Clamp,
+    Pow,
+    Sqrt,
+    Hypot,
     Sin,
     Cos,
     Tan,
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to