⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 283359 in webkit


Ignore:
Timestamp:
Oct 1, 2021, 12:58:12 AM (5 years ago)
Author:
commit-queue@webkit.org
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 <kevin_turner@apple.com> 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 <kevin_turner@apple.com> 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:
Location:
trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r283358 r283359  
     12021-10-01  Kevin Turner  <kevin_turner@apple.com>
     2
     3        Add support for pow(), sqrt() and hypot() per https://drafts.csswg.org/css-values-4/#exponent-funcs.
     4        https://bugs.webkit.org/show_bug.cgi?id=203312
     5
     6        Reviewed by Simon Fraser.       
     7
     8        Tests performing CSS calculations with pow(), sqrt(), and hypot() with a range of inputs.
     9        * fast/css/calc-parsing-expected.txt:
     10        * fast/css/calc-parsing.html:
     11
    1122021-09-30  Lauro Moura  <lmoura@igalia.com>
    213
  • trunk/LayoutTests/fast/css/calc-parsing-expected.txt

    r282162 r283359  
    5353PASS getComputedStyle(element).getPropertyValue('width') is "100px"
    5454
     55element.style["width"] = "calc(100px * pow(2, pow(2, 2)))"
     56PASS element.style['width'] is "calc(1600px)"
     57PASS getComputedStyle(element).getPropertyValue('width') is "1600px"
     58
     59element.style["width"] = "calc(1px * pow(2, 3))"
     60PASS element.style['width'] is "calc(8px)"
     61PASS getComputedStyle(element).getPropertyValue('width') is "8px"
     62
     63element.style["width"] = "calc(100px * sqrt(100))"
     64PASS element.style['width'] is "calc(1000px)"
     65PASS getComputedStyle(element).getPropertyValue('width') is "1000px"
     66
     67element.style["width"] = "calc(1px * sqrt(999))"
     68PASS element.style['width'] is "calc(31.606961258558215px)"
     69PASS getComputedStyle(element).getPropertyValue('width') is "31.59375px"
     70
     71element.style["width"] = "calc(1px * pow(2, sqrt(100))"
     72PASS element.style['width'] is "calc(1024px)"
     73PASS getComputedStyle(element).getPropertyValue('width') is "1024px"
     74
     75element.style["width"] = "hypot(4px, 5px, 7px, 9px)"
     76PASS element.style['width'] is "hypot(13.076696830622021px)"
     77PASS getComputedStyle(element).getPropertyValue('width') is "13.0625px"
     78
     79element.style["width"] = "hypot(3px, 4px)"
     80PASS element.style['width'] is "hypot(5px)"
     81PASS getComputedStyle(element).getPropertyValue('width') is "5px"
     82
     83element.style["width"] = "calc(100px * hypot(3, 4))"
     84PASS element.style['width'] is "calc(500px)"
     85PASS getComputedStyle(element).getPropertyValue('width') is "500px"
     86
     87element.style["width"] = "hypot(-5px)"
     88PASS element.style['width'] is "hypot(5px)"
     89PASS getComputedStyle(element).getPropertyValue('width') is "5px"
     90
     91element.style["width"] = "calc(1px * hypot(-5))"
     92PASS element.style['width'] is "calc(5px)"
     93PASS getComputedStyle(element).getPropertyValue('width') is "5px"
     94
     95element.style["width"] = "calc(1px * hypot(10000))"
     96PASS element.style['width'] is "calc(10000px)"
     97PASS getComputedStyle(element).getPropertyValue('width') is "10000px"
     98
     99element.style["width"] = "calc(2px * sqrt(100000000))"
     100PASS element.style['width'] is "calc(20000px)"
     101PASS getComputedStyle(element).getPropertyValue('width') is "20000px"
     102
     103element.style["width"] = "calc(3px * pow(200, 4))"
     104PASS element.style['width'] is "calc(4800000000px)"
     105PASS getComputedStyle(element).getPropertyValue('width') is "33554428px"
     106
    55107element.style["width"] = "calc(sin(90deg) * 100px)"
    56108PASS 100 is 100
     
    126178
    127179element.style["width"] = "clamp(1px,2px,2px,4px)"
     180PASS element.style['width'] is "999px"
     181PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     182
     183element.style["width"] = "calc(1px * pow(1))"
     184PASS element.style['width'] is "999px"
     185PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     186
     187element.style["width"] = "calc(1px * pow(2px, 3px))"
     188PASS element.style['width'] is "999px"
     189PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     190
     191element.style["width"] = "calc(sqrt(100px)"
     192PASS element.style['width'] is "999px"
     193PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     194
     195element.style["width"] = "hypot(2px, 40%)"
     196PASS element.style['width'] is "999px"
     197PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     198
     199element.style["width"] = "hypot(2px, 3)"
     200PASS element.style['width'] is "999px"
     201PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     202
     203element.style["width"] = "hypot(3, ,4)"
     204PASS element.style['width'] is "999px"
     205PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     206
     207element.style["width"] = "calc(1px * pow(2 3))"
     208PASS element.style['width'] is "999px"
     209PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     210
     211element.style["width"] = "hypot()"
     212PASS element.style['width'] is "999px"
     213PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     214
     215element.style["width"] = "calc(pow(2))"
     216PASS element.style['width'] is "999px"
     217PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     218
     219element.style["width"] = "pow())"
     220PASS element.style['width'] is "999px"
     221PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     222
     223element.style["width"] = "calc(sqrt())"
     224PASS element.style['width'] is "999px"
     225PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     226
     227element.style["width"] = "calc(sqrt(100, 200))"
    128228PASS element.style['width'] is "999px"
    129229PASS getComputedStyle(element).getPropertyValue('width') is "999px"
     
    213313PASS getComputedStyle(element).getPropertyValue('min-width') is "clamp(100px, 0%, 1%)"
    214314
     315element.style["min-width"] = "calc(100px * pow(2, pow(2, 2)))"
     316PASS element.style['min-width'] is "calc(1600px)"
     317PASS getComputedStyle(element).getPropertyValue('min-width') is "1600px"
     318
     319element.style["min-width"] = "calc(1px * pow(2, 3))"
     320PASS element.style['min-width'] is "calc(8px)"
     321PASS getComputedStyle(element).getPropertyValue('min-width') is "8px"
     322
     323element.style["min-width"] = "calc(100px * sqrt(100))"
     324PASS element.style['min-width'] is "calc(1000px)"
     325PASS getComputedStyle(element).getPropertyValue('min-width') is "1000px"
     326
     327element.style["min-width"] = "calc(1px * sqrt(999))"
     328PASS element.style['min-width'] is "calc(31.606961258558215px)"
     329PASS getComputedStyle(element).getPropertyValue('min-width') is "31.606962203979492px"
     330
     331element.style["min-width"] = "calc(1px * pow(2, sqrt(100))"
     332PASS element.style['min-width'] is "calc(1024px)"
     333PASS getComputedStyle(element).getPropertyValue('min-width') is "1024px"
     334
     335element.style["min-width"] = "hypot(4px, 5px, 7px, 9px)"
     336PASS element.style['min-width'] is "hypot(13.076696830622021px)"
     337PASS getComputedStyle(element).getPropertyValue('min-width') is "13.076696395874023px"
     338
     339element.style["min-width"] = "hypot(3px, 4px)"
     340PASS element.style['min-width'] is "hypot(5px)"
     341PASS getComputedStyle(element).getPropertyValue('min-width') is "5px"
     342
     343element.style["min-width"] = "calc(100px * hypot(3, 4))"
     344PASS element.style['min-width'] is "calc(500px)"
     345PASS getComputedStyle(element).getPropertyValue('min-width') is "500px"
     346
     347element.style["min-width"] = "hypot(-5px)"
     348PASS element.style['min-width'] is "hypot(5px)"
     349PASS getComputedStyle(element).getPropertyValue('min-width') is "5px"
     350
     351element.style["min-width"] = "calc(1px * hypot(-5))"
     352PASS element.style['min-width'] is "calc(5px)"
     353PASS getComputedStyle(element).getPropertyValue('min-width') is "5px"
     354
     355element.style["min-width"] = "calc(1px * hypot(10000))"
     356PASS element.style['min-width'] is "calc(10000px)"
     357PASS getComputedStyle(element).getPropertyValue('min-width') is "10000px"
     358
     359element.style["min-width"] = "calc(2px * sqrt(100000000))"
     360PASS element.style['min-width'] is "calc(20000px)"
     361PASS getComputedStyle(element).getPropertyValue('min-width') is "20000px"
     362
     363element.style["min-width"] = "calc(3px * pow(200, 4))"
     364PASS element.style['min-width'] is "calc(4800000000px)"
     365PASS getComputedStyle(element).getPropertyValue('min-width') is "33554428px"
     366
    215367element.style["min-width"] = "calc(sin(90deg) * 100px)"
    216368PASS 100 is 100
     
    289441PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
    290442
     443element.style["min-width"] = "calc(1px * pow(1))"
     444PASS element.style['min-width'] is "999px"
     445PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     446
     447element.style["min-width"] = "calc(1px * pow(2px, 3px))"
     448PASS element.style['min-width'] is "999px"
     449PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     450
     451element.style["min-width"] = "calc(sqrt(100px)"
     452PASS element.style['min-width'] is "999px"
     453PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     454
     455element.style["min-width"] = "hypot(2px, 40%)"
     456PASS element.style['min-width'] is "999px"
     457PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     458
     459element.style["min-width"] = "hypot(2px, 3)"
     460PASS element.style['min-width'] is "999px"
     461PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     462
     463element.style["min-width"] = "hypot(3, ,4)"
     464PASS element.style['min-width'] is "999px"
     465PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     466
     467element.style["min-width"] = "calc(1px * pow(2 3))"
     468PASS element.style['min-width'] is "999px"
     469PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     470
     471element.style["min-width"] = "hypot()"
     472PASS element.style['min-width'] is "999px"
     473PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     474
     475element.style["min-width"] = "calc(pow(2))"
     476PASS element.style['min-width'] is "999px"
     477PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     478
     479element.style["min-width"] = "pow())"
     480PASS element.style['min-width'] is "999px"
     481PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     482
     483element.style["min-width"] = "calc(sqrt())"
     484PASS element.style['min-width'] is "999px"
     485PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     486
     487element.style["min-width"] = "calc(sqrt(100, 200))"
     488PASS element.style['min-width'] is "999px"
     489PASS getComputedStyle(element).getPropertyValue('min-width') is "999px"
     490
    291491element.style["min-width"] = "calc(sin(90px) * 100px)"
    292492PASS element.style['min-width'] is "999px"
  • trunk/LayoutTests/fast/css/calc-parsing.html

    r282162 r283359  
    4343                testExpression('max(100px,0%)', 'max(100px, 0%)', propertyName == 'width' ? '100px' : "max(100px, 0%)");
    4444                testExpression('clamp(100px,0%,1%)', 'clamp(100px, 0%, 1%)', propertyName == 'width' ? '100px' : "clamp(100px, 0%, 1%)");
     45                testExpression('calc(100px * pow(2, pow(2, 2)))', 'calc(1600px)', '1600px');
     46                testExpression('calc(1px * pow(2, 3))', 'calc(8px)', '8px')
     47                testExpression('calc(100px * sqrt(100))', 'calc(1000px)', '1000px');
     48                testExpression('calc(1px * sqrt(999))', 'calc(31.606961258558215px)', propertyName == 'width' ? '31.59375px' : '31.606962203979492px');
     49                testExpression('calc(1px * pow(2, sqrt(100))', 'calc(1024px)', '1024px');
     50                testExpression('hypot(4px, 5px, 7px, 9px)', 'hypot(13.076696830622021px)', propertyName == 'width' ? '13.0625px' : '13.076696395874023px');
     51                testExpression('hypot(3px, 4px)', 'hypot(5px)', '5px');
     52                testExpression('calc(100px * hypot(3, 4))', 'calc(500px)', '500px');
     53                testExpression('hypot(-5px)', 'hypot(5px)', '5px');
     54                testExpression('calc(1px * hypot(-5))', 'calc(5px)', '5px');
     55                testExpression('calc(1px * hypot(10000))', 'calc(10000px)', '10000px');
     56                testExpression('calc(2px * sqrt(100000000))', 'calc(20000px)', '20000px');
     57                testExpression('calc(3px * pow(200, 4))', 'calc(4800000000px)', '33554428px');
    4558                testValue('calc(sin(90deg) * 100px)', '100');
    4659                testValue('calc(sin(45deg  +  45deg ) * 100px)', '100');
     
    6679                testExpression('clamp((),,300px)', '999px', '999px');
    6780                testExpression('clamp(1px,2px,2px,4px)', '999px', '999px');
     81                testExpression('calc(1px * pow(1))', '999px', '999px');
     82                testExpression('calc(1px * pow(2px, 3px))', '999px', '999px');
     83                testExpression('calc(sqrt(100px)', '999px', '999px');
     84                testExpression('hypot(2px, 40%)', '999px', '999px');
     85                testExpression('hypot(2px, 3)', '999px', '999px');
     86                testExpression('hypot(3, ,4)', '999px', '999px');
     87                testExpression('calc(1px * pow(2 3))', '999px', '999px');
     88                testExpression('hypot()', '999px', '999px');
     89                testExpression('calc(pow(2))', '999px', '999px');
     90                testExpression('pow())', '999px', '999px', '999px');
     91                testExpression('calc(sqrt())', '999px', '999px');
     92                testExpression('calc(sqrt(100, 200))', '999px', '999px');
    6893                testExpression('calc(sin(90px) * 100px)', '999px', '999px');
    6994                testExpression('calc(sin(30deg + 1.0471967rad, 0) * 100px)', '999px', '999px');
  • trunk/Source/WebCore/ChangeLog

    r283355 r283359  
     12021-10-01  Kevin Turner  <kevin_turner@apple.com>
     2
     3        Add support for pow(), sqrt() and hypot()
     4        https://bugs.webkit.org/show_bug.cgi?id=203312
     5        <rdar://82640883>
     6       
     7        Reviewed by Simon Fraser.
     8         
     9        Implements pow(), sqrt() and hypot() functions as specified by https://drafts.csswg.org/css-values-4/#exponent-funcs.
     10
     11        Test: fast/css/calc-parsing.html
     12 
     13        * css/CSSValueKeywords.in: Adds pow, sqrt, and hypot keywords.
     14        * css/calc/CSSCalcExpressionNodeParser.cpp:
     15        (WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
     16        * css/calc/CSSCalcOperationNode.cpp:
     17        (WebCore::determineCategory):
     18        (WebCore::functionFromOperator):
     19        (WebCore::CSSCalcOperationNode::createPowOrSqrt):
     20        (WebCore::CSSCalcOperationNode::createHypot):
     21        (WebCore::CSSCalcOperationNode::canCombineAllChildren const): Ensures nodes that are identity functions cannot have their children combined.
     22        (WebCore::CSSCalcOperationNode::combineChildren): Early exit if node behaves as an identity function. Performs combine of children if node is an exponential function.
     23        (WebCore::CSSCalcOperationNode::simplifyNode): Avoid simplifying if node does not behave as an identify function. Calls combineChildren() if node is an exponential function.
     24        (WebCore::functionPrefixForOperator):
     25        (WebCore::CSSCalcOperationNode::evaluateOperator):
     26        * 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.
     27        * css/calc/CSSCalcValue.cpp:
     28        (WebCore::createCSS):
     29        (WebCore::CSSCalcValue::isCalcFunction):
     30        * platform/calc/CalcExpressionOperation.cpp:
     31        (WebCore::CalcExpressionOperation::evaluate const):
     32        * platform/calc/CalcOperator.cpp:
     33        (WebCore::operator<<):
     34        * platform/calc/CalcOperator.h:
     35
    1362021-09-30  Simon Fraser  <simon.fraser@apple.com>
    237
  • trunk/Source/WebCore/css/CSSValueKeywords.in

    r283073 r283359  
    13481348max
    13491349clamp
     1350pow
     1351sqrt
     1352hypot
    13501353sin
    13511354cos
  • trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp

    r283073 r283359  
    124124    case CSSValueMin:
    125125    case CSSValueMax:
     126    case CSSValueHypot:
    126127        maxArgumentCount = std::nullopt;
    127128        break;
     
    157158        maxArgumentCount = 2;
    158159        break;
    159     // TODO: pow, sqrt, hypot.
     160    case CSSValuePow:
     161        minArgumentCount = 2;
     162        maxArgumentCount = 2;
     163        break;
     164    case CSSValueSqrt:
     165        maxArgumentCount = 1;
     166        break;
    160167    default:
    161168        break;
     
    242249        result = CSSCalcOperationNode::createSign(CalcOperator::Sign, WTFMove(nodes));
    243250        break;
    244     // TODO: pow, sqrt, hypot
     251    case CSSValuePow:
     252        result = CSSCalcOperationNode::createPowOrSqrt(CalcOperator::Pow, WTFMove(nodes));
     253        break;
     254    case CSSValueSqrt:
     255        result = CSSCalcOperationNode::createPowOrSqrt(CalcOperator::Sqrt, WTFMove(nodes));
     256        break;
     257    case CSSValueHypot:
     258        result = CSSCalcOperationNode::createHypot(WTFMove(nodes));
     259        break;
    245260    default:
    246261        break;
  • trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp

    r283073 r283359  
    9595    case CalcOperator::Nearest:
    9696    case CalcOperator::ToZero:
     97    case CalcOperator::Pow:
     98    case CalcOperator::Sqrt:
     99    case CalcOperator::Hypot:
    97100        ASSERT_NOT_REACHED();
    98101        return CalculationCategory::Other;
     
    181184        case CalcOperator::Nearest:
    182185        case CalcOperator::ToZero:
    183             // The type of a min(), max(), or clamp() expression is the result of adding the types of its comma-separated calculations
     186        case CalcOperator::Hypot:
    184187            return CalculationCategory::Other;
     188        case CalcOperator::Pow:
     189        case CalcOperator::Sqrt:
     190            // The type of pow() and sqrt() functions must evaluate to a number.
     191            return CalculationCategory::Number;
    185192        }
    186193    }
     
    276283    return CSSCalcPrimitiveValueNode::UnitConversion::Invalid;
    277284}
    278 
    279285
    280286static CSSValueID functionFromOperator(CalcOperator op)
     
    292298    case CalcOperator::Clamp:
    293299        return CSSValueClamp;
     300    case CalcOperator::Pow:
     301        return CSSValuePow;
     302    case CalcOperator::Sqrt:
     303        return CSSValueSqrt;
     304    case CalcOperator::Hypot:
     305        return CSSValueHypot;
    294306    case CalcOperator::Sin:
    295307        return CSSValueSin;
     
    332344}
    333345
     346static std::optional<CalculationCategory> commonCategory(const Vector<Ref<CSSCalcExpressionNode>>& values)
     347{
     348    if (values.isEmpty())
     349        return std::nullopt;
     350
     351    auto expectedCategory = values[0]->category();
     352    for (size_t i = 1; i < values.size(); ++i) {
     353        if (values[i]->category() != expectedCategory)
     354            return std::nullopt;
     355    }
     356
     357    return expectedCategory;
     358}
     359
    334360RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
    335361{
     
    440466
    441467    return adoptRef(new CSSCalcOperationNode(CalculationCategory::Number, CalcOperator::Exp, WTFMove(values)));
     468}
     469
     470RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createPowOrSqrt(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values)
     471{
     472    if (op == CalcOperator::Pow && values.size() != 2)
     473        return nullptr;
     474
     475    if (op == CalcOperator::Sqrt && values.size() != 1)
     476        return nullptr;
     477
     478    if (commonCategory(values) != CalculationCategory::Number) {
     479        LOG_WITH_STREAM(Calc, stream << "Failed to create " << op << "node because unable to determine category from " << prettyPrintNodes(values));
     480        return nullptr;
     481    }
     482
     483    return adoptRef(new CSSCalcOperationNode(CalculationCategory::Number, op, WTFMove(values)));
     484}
     485
     486RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createHypot(Vector<Ref<CSSCalcExpressionNode>>&& values)
     487{
     488    auto expectedCategory = commonCategory(values);
     489
     490    if (expectedCategory == CalculationCategory::Other) {
     491        LOG_WITH_STREAM(Calc, stream << "Failed to create hypot node because unable to determine category from " << prettyPrintNodes(values));
     492        return nullptr;
     493    }
     494
     495    return adoptRef(new CSSCalcOperationNode(*expectedCategory, CalcOperator::Hypot, WTFMove(values)));
    442496}
    443497
     
    598652bool CSSCalcOperationNode::canCombineAllChildren() const
    599653{
    600     if (m_children.size() < 2)
     654    if (isIdentity() || !m_children.size())
    601655        return false;
    602656
     
    631685void CSSCalcOperationNode::combineChildren()
    632686{
     687    if (isIdentity() || !m_children.size())
     688        return;
     689
    633690    if (m_children.size() < 2) {
    634691        if (m_children.size() == 1 && isTrigNode()) {
     
    645702            m_children.append(WTFMove(newChild));
    646703        }
    647        
    648704        if (m_children.size() == 1 && isInverseTrigNode()) {
    649705            double resolvedValue = doubleValue(m_children[0]->primitiveType());
     
    652708            m_children.append(WTFMove(newChild));
    653709        }
    654         if (isSignNode()) {
     710        if (isSignNode() || isHypotNode()) {
    655711            auto combinedUnitType = m_children[0]->primitiveType();
    656712            if (calcOperator() == CalcOperator::Sign)
     
    661717            m_children.append(WTFMove(newChild));
    662718        }
     719        if (calcOperator() == CalcOperator::Sqrt) {
     720            double resolvedValue = doubleValue(m_children[0]->primitiveType());
     721            auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, CSSUnitType::CSS_NUMBER));
     722            m_children.clear();
     723            m_children.append(WTFMove(newChild));
     724        }
    663725        return;
    664726    }
    665    
     727
    666728    if (shouldSortChildren()) {
    667729        // <https://drafts.csswg.org/css-values-4/#sort-a-calculations-children>
     
    782844    }
    783845
    784     if (isMinOrMaxNode() && canCombineAllChildren()) {
     846    if ((isMinOrMaxNode() || isHypotNode()) && canCombineAllChildren()) {
    785847        auto combinedUnitType = m_children[0]->primitiveType();
    786848        auto category = calculationCategoryForCombination(combinedUnitType);
     
    794856        m_children.append(WTFMove(newChild));
    795857    }
    796    
     858
     859    if (calcOperator() == CalcOperator::Pow) {
     860        auto resolvedValue = doubleValue(m_children[0]->primitiveType());
     861        auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, CSSUnitType::CSS_NUMBER));
     862        m_children.clear();
     863        m_children.append(WTFMove(newChild));
     864    }
     865
    797866    if (calcOperator() == CalcOperator::Atan2) {
    798867        double resolvedValue = doubleValue(m_children[0]->primitiveType());
     
    872941    if (is<CSSCalcOperationNode>(rootNode)) {
    873942        auto& calcOperationNode = downcast<CSSCalcOperationNode>(rootNode.get());
    874         // Simplify operations with only one child node (other than root and operations that only need one node).
    875         if (calcOperationNode.children().size() == 1 && depth && !calcOperationNode.isTrigNode() && !calcOperationNode.isExpNode() && !calcOperationNode.isInverseTrigNode() && !calcOperationNode.isSignNode())
     943        // Identity nodes have only one child and perform no operation on their child.
     944        if (calcOperationNode.isIdentity() && depth)
    876945            return WTFMove(calcOperationNode.children()[0]);
    877946       
     
    908977        if (calcOperationNode.isRoundOperation() && depth)
    909978            calcOperationNode.combineChildren();
     979
     980        if (calcOperationNode.isHypotNode())
     981            calcOperationNode.combineChildren();
     982
     983        if (calcOperationNode.isPowOrSqrtNode() && depth)
     984            calcOperationNode.combineChildren();
     985
    910986        // If only one child remains, return the child (except at the root).
    911987        auto shouldCombineParentWithOnlyChild = [](const CSSCalcOperationNode& parent, int depth)
     
    11201196    case CalcOperator::Nearest: return "round(nearest, ";
    11211197    case CalcOperator::ToZero: return "round(to-zero, ";
     1198    case CalcOperator::Pow: return "pow(";
     1199    case CalcOperator::Sqrt: return "sqrt(";
     1200    case CalcOperator::Hypot: return "hypot(";
    11221201    }
    11231202   
     
    13281407        return std::max(min, std::min(value, max));
    13291408    }
     1409    case CalcOperator::Pow:
     1410        if (children.size() != 2)
     1411            return std::numeric_limits<double>::quiet_NaN();
     1412        return std::pow(children[0], children[1]);
     1413    case CalcOperator::Sqrt: {
     1414        if (children.size() != 1)
     1415            return std::numeric_limits<double>::quiet_NaN();
     1416        return std::sqrt(children[0]);
     1417    }
     1418    case CalcOperator::Hypot: {
     1419        if (children.isEmpty())
     1420            return std::numeric_limits<double>::quiet_NaN();
     1421        if (children.size() == 1)
     1422            return std::abs(children[0]);
     1423        double sum = 0;
     1424        for (auto child : children)
     1425            sum += (child * child);
     1426        return std::sqrt(sum);
     1427    }
    13301428    case CalcOperator::Sin: {
    13311429        if (children.size() != 1)
  • trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h

    r283073 r283359  
    3838    static RefPtr<CSSCalcOperationNode> createProduct(Vector<Ref<CSSCalcExpressionNode>>&& values);
    3939    static RefPtr<CSSCalcOperationNode> createMinOrMaxOrClamp(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory);
     40    static RefPtr<CSSCalcOperationNode> createPowOrSqrt(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
     41    static RefPtr<CSSCalcOperationNode> createHypot(Vector<Ref<CSSCalcExpressionNode>>&& values);
    4042    static RefPtr<CSSCalcOperationNode> createTrig(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
    4143    static RefPtr<CSSCalcOperationNode> createLog(Vector<Ref<CSSCalcExpressionNode>>&& values);
     
    6466    bool isRoundOperation() const { return m_operator == CalcOperator::Down || m_operator == CalcOperator::Up || m_operator == CalcOperator::ToZero || m_operator == CalcOperator::Nearest; }
    6567    bool isRoundConstant() const { return (isRoundOperation()) && !m_children.size(); }
     68    bool isHypotNode() const { return m_operator == CalcOperator::Hypot; }
     69    bool isPowOrSqrtNode() const { return m_operator == CalcOperator::Pow || m_operator == CalcOperator::Sqrt; }
    6670
    6771    void hoistChildrenWithOperator(CalcOperator);
     
    6973   
    7074    bool canCombineAllChildren() const;
     75
     76    bool isIdentity() const { return m_children.size() == 1 && (m_operator == CalcOperator::Min || m_operator == CalcOperator::Max || m_operator == CalcOperator::Add || m_operator == CalcOperator::Multiply); }
    7177
    7278    const Vector<Ref<CSSCalcExpressionNode>>& children() const { return m_children; }
  • trunk/Source/WebCore/css/calc/CSSCalcValue.cpp

    r283073 r283359  
    214214            return CSSCalcOperationNode::createSign(op, WTFMove(children));
    215215        }
     216        case CalcOperator::Sqrt:
     217        case CalcOperator::Pow: {
     218            auto children = createCSS(operationChildren, style);
     219            if (children.isEmpty())
     220                return nullptr;
     221            return CSSCalcOperationNode::createPowOrSqrt(op, WTFMove(children));
     222        }
     223        case CalcOperator::Hypot: {
     224            auto children = createCSS(operationChildren, style);
     225            if (children.isEmpty())
     226                return nullptr;
     227            return CSSCalcOperationNode::createHypot(WTFMove(children));
     228        }
    216229        case CalcOperator::Mod:
    217230        case CalcOperator::Rem:
     
    339352    case CSSValueMax:
    340353    case CSSValueClamp:
     354    case CSSValuePow:
     355    case CSSValueSqrt:
     356    case CSSValueHypot:
    341357    case CSSValueSin:
    342358    case CSSValueCos:
  • trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp

    r283073 r283359  
    9696        return std::max(min, std::min(value, max));
    9797    }
     98    case CalcOperator::Pow: {
     99        if (m_children.size() != 2)
     100            return std::numeric_limits<float>::quiet_NaN();
     101        float base = m_children[0]->evaluate(maxValue);
     102        float power = m_children[1]->evaluate(maxValue);
     103        return std::pow(base, power);
     104    }
     105    case CalcOperator::Sqrt: {
     106        if (m_children.size() != 1)
     107            return std::numeric_limits<float>::quiet_NaN();
     108        return std::sqrt(m_children[0]->evaluate(maxValue));
     109    }
     110    case CalcOperator::Hypot: {
     111        if (m_children.isEmpty())
     112            return std::numeric_limits<float>::quiet_NaN();
     113        if (m_children.size() == 1)
     114            return std::abs(m_children[0]->evaluate(maxValue));
     115        float sum = 0;
     116        for (auto& child : m_children) {
     117            float value = child->evaluate(maxValue);
     118            sum += (value * value);
     119        }
     120        return sum;
     121    }
    98122    case CalcOperator::Sin: {
    99123        if (m_children.size() != 1)
  • trunk/Source/WebCore/platform/calc/CalcOperator.cpp

    r283073 r283359  
    4141    case CalcOperator::Max: ts << "max"; break;
    4242    case CalcOperator::Clamp: ts << "clamp"; break;
     43    case CalcOperator::Pow: ts << "pow"; break;
     44    case CalcOperator::Sqrt: ts << "sqrt"; break;
     45    case CalcOperator::Hypot: ts << "hypot"; break;
    4346    case CalcOperator::Sin: ts << "sin"; break;
    4447    case CalcOperator::Cos: ts << "cos"; break;
  • trunk/Source/WebCore/platform/calc/CalcOperator.h

    r283073 r283359  
    3939    Max,
    4040    Clamp,
     41    Pow,
     42    Sqrt,
     43    Hypot,
    4144    Sin,
    4245    Cos,
Note: See TracChangeset for help on using the changeset viewer.