Title: [222190] trunk
Revision
222190
Author
[email protected]
Date
2017-09-18 16:36:20 -0700 (Mon, 18 Sep 2017)

Log Message

Support min() and max() in calc()
https://bugs.webkit.org/show_bug.cgi?id=167000
<rdar://problem/30153481>

Reviewed by David Hyatt.
Patch originally by Myles Maxfield.

Source/WebCore:

Add two new toplevel functions to CSS, min() and max(), which take an
arbirary number of arguments and resolve to the minimum and maximum of
the resolved value of the arguments, respectively. It is also possible
to use min() and max() inside calc(), and to use calc()-like math
inside min() and max().

* css/CSSCalculationValue.cpp:
(WebCore::determineCategory):
min and max operators don't use determineCategory; we have a specific
implementation for them in createMinOrMax.

(WebCore::resolvedTypeForMinOrMax):
The spec says that min() and max() should be marked as invalid if they
have values of more than one type, but that percentages should resolve
against the destination type before making this determination. So,
if the destination type is length, percent turns into percent-length,
and similarly for number.

(WebCore::isIntegerResult):
Add an n-way implementation of isIntegerResult.

(WebCore::isSamePair):
(WebCore::CSSCalcOperation::createMinOrMax): Create a min() or max()
operation, as long as the types of arguments are all the same. Allow
lengths to upgrade the whole operation to percent-length, and numbers
to percent-number, which will cause us to use CalculationValue and friends
in order to do proper resolution of all of the parameters instead of
just comparing their numeric values.

(WebCore::CSSCalcOperation::createCalcExpression):
(WebCore::CSSCalcOperation::doubleValue):
(WebCore::CSSCalcOperation::computeLengthPx):
(WebCore::CSSCalcOperation::customCSSText):
(WebCore::CSSCalcOperation::primitiveType):
(WebCore::CSSCalcOperation::CSSCalcOperation):
(WebCore::CSSCalcOperation::evaluate):
(WebCore::CSSCalcOperation::evaluateOperator):
Adapt to child counts greater than two.

(WebCore::CSSCalcOperation::buildCssText):
Add support for min() and max().

(WebCore::CSSCalcExpressionNodeParser::parseCalc):
parseCalc now accepts a CSSValueID parameter indicating which calc function
it should parse (calc, webkit-calc, min, or max), and delegates to either
parseValueExpression or parseMinMaxExpression.

(WebCore::CSSCalcExpressionNodeParser::operatorValue):
(WebCore::CSSCalcExpressionNodeParser::parseValue):
If min() or max() are found while parsing a value (i.e. nested inside
either calc or themselves), use parseMinMaxExpression on that subtree.

(WebCore::CSSCalcExpressionNodeParser::parseValueTerm):
(WebCore::CSSCalcExpressionNodeParser::parseValueMultiplicativeExpression):
(WebCore::CSSCalcExpressionNodeParser::parseAdditiveValueExpression):
Adjust to the CSSCalcBinaryOperation->CSSCalcOperation rename.

(WebCore::CSSCalcExpressionNodeParser::parseMinMaxExpression):
Added. Parse an arbitrary number of comma-and-whitespace-separated children.

(WebCore::createBlendHalf):
Adjust to the CSSCalcBinaryOperation->CSSCalcOperation rename.

(WebCore::createCSS):
Build the CSSCalcOperation for the platform-independent min and max operations.

(WebCore::CSSCalcValue::create):
Pass the function being parsed and the destination calc category for the
property being parsed for into create, and then into the parser so that
it can know which function it is parsing for, and what kind of result it
needs (as previously mentioned above in resolvedTypeForMinOrMax).

* css/CSSCalculationValue.h:
* css/CSSValueKeywords.in:
Add min and max functions as CSS keywords.

* css/StyleBuilderConverter.h:
(WebCore::StyleBuilderConverter::convertLength):
(WebCore::StyleBuilderConverter::convertTo100PercentMinusLength):
* platform/Length.cpp:
(WebCore::convertTo100PercentMinusLength):
Adapt to the CalcExpressionOperation constructor taking a vector of
arguments instead of two.

* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::CalcParser::CalcParser):
Store and pass the specific function being parsed down into CSSCalcValue.

(WebCore::CSSPropertyParserHelpers::consumeInteger):
(WebCore::CSSPropertyParserHelpers::consumePositiveIntegerRaw):
(WebCore::CSSPropertyParserHelpers::consumeNumberRaw):
(WebCore::CSSPropertyParserHelpers::consumeNumber):
(WebCore::CSSPropertyParserHelpers::consumeFontWeightNumber):
(WebCore::CSSPropertyParserHelpers::consumeLength):
(WebCore::CSSPropertyParserHelpers::consumePercent):
(WebCore::CSSPropertyParserHelpers::consumeLengthOrPercent):
(WebCore::CSSPropertyParserHelpers::consumeAngle):
(WebCore::CSSPropertyParserHelpers::consumeTime):
Pass the destination type into each calc parser.

* platform/CalculationValue.cpp:
(WebCore::CalcExpressionOperation::evaluate const):
(WebCore::CalcExpressionOperation::operator== const):
(WebCore::CalcExpressionOperation::dump const):
(WebCore::operator<<):
(WebCore::CalcExpressionBinaryOperation::evaluate const): Deleted.
(WebCore::CalcExpressionBinaryOperation::operator== const): Deleted.
(WebCore::CalcExpressionBinaryOperation::dump const): Deleted.
* platform/CalculationValue.h:
(WebCore::CalcExpressionOperation::CalcExpressionOperation):
(WebCore::operator==):
(WebCore::toCalcExpressionOperation):
(WebCore::CalcExpressionBinaryOperation::CalcExpressionBinaryOperation): Deleted.
(WebCore::toCalcExpressionBinaryOperation): Deleted.
Adjust to the CSSCalcBinaryOperation->CSSCalcOperation rename.
Adjust to having n>2 children.
Support min() and max() operators in various places.

LayoutTests:

* css3/calc/minmax-errors-expected.txt:
* css3/calc/minmax-errors.html:
* css3/calc/simple-minmax-expected.txt:
* css3/calc/simple-minmax.html:
Revive previously-unused tests for an earlier never-implemented version
of this feature, and add a bunch more interesting test cases from reading the spec.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (222189 => 222190)


--- trunk/LayoutTests/ChangeLog	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/LayoutTests/ChangeLog	2017-09-18 23:36:20 UTC (rev 222190)
@@ -1,3 +1,19 @@
+2017-09-18  Tim Horton  <[email protected]>
+
+        Support min() and max() in calc()
+        https://bugs.webkit.org/show_bug.cgi?id=167000
+        <rdar://problem/30153481>
+
+        Reviewed by David Hyatt.
+        Patch originally by Myles Maxfield.
+
+        * css3/calc/minmax-errors-expected.txt:
+        * css3/calc/minmax-errors.html:
+        * css3/calc/simple-minmax-expected.txt:
+        * css3/calc/simple-minmax.html:
+        Revive previously-unused tests for an earlier never-implemented version
+        of this feature, and add a bunch more interesting test cases from reading the spec.
+
 2017-09-18  Per Arne Vollan  <[email protected]>
 
         Mark imported/blink/fast/events/panScroll-panIcon.html as flaky on Windows.

Modified: trunk/LayoutTests/css3/calc/minmax-errors-expected.txt (222189 => 222190)


--- trunk/LayoutTests/css3/calc/minmax-errors-expected.txt	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/LayoutTests/css3/calc/minmax-errors-expected.txt	2017-09-18 23:36:20 UTC (rev 222190)
@@ -1,6 +1,6 @@
 All boxes below should be 100px * 100px and green.
 
-MIN
+Bare min()
 unclosed min => PASS
 unclosed min with garbage => PASS
 garbage => PASS
@@ -12,7 +12,23 @@
 mix number and length => PASS
 mix percent and number => PASS
 mix number and percent => PASS
-MAX
+
+
+min() inside calc()
+unclosed min => PASS
+unclosed min with garbage => PASS
+garbage => PASS
+extra trailing comma => PASS
+leading comma => PASS
+trailing garbage => PASS
+bad _expression_ => PASS
+mix length and number => PASS
+mix number and length => PASS
+mix percent and number => PASS
+mix number and percent => PASS
+
+
+Bare max()
 unclosed max => PASS
 unclosed max with garbage => PASS
 mix length and number => PASS
@@ -19,3 +35,12 @@
 mix number and length => PASS
 mix percent and number => PASS
 mix number and percent => PASS
+
+
+max() inside calc
+unclosed max => PASS
+unclosed max with garbage => PASS
+mix length and number => PASS
+mix number and length => PASS
+mix percent and number => PASS
+mix number and percent => PASS

Modified: trunk/LayoutTests/css3/calc/minmax-errors.html (222189 => 222190)


--- trunk/LayoutTests/css3/calc/minmax-errors.html	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/LayoutTests/css3/calc/minmax-errors.html	2017-09-18 23:36:20 UTC (rev 222190)
@@ -12,27 +12,48 @@
 
 <div id="test">
 
-MIN
-<div style="width: 100px; width: -webkit-min(">unclosed min</div>
-<div style="width: 100px; width: -webkit-min( bob">unclosed min with garbage</div>
-<div style="width: 100px; width: -webkit-min( bob );">garbage</div>
-<div style="width: 100px; width: -webkit-min(20px,);">extra trailing comma</div>
-<div style="width: 100px; width: -webkit-min(,20px);">leading comma</div>
-<div style="width: 100px; width: -webkit-min(20px, bob);">trailing garbage</div>
-<div style="width: 100px; width: -webmit-min(20px, 10px + flim);">bad _expression_</div>
-<div style="width: 100px; width: -webkit-min(256px, 120);">mix length and number</div>
-<div style="width: 100px; width: -webkit-min(256, 120px);">mix number and length</div>
-<div style="width: 100px; width: -webkit-min(50%, 150);">mix percent and number</div>
-<div style="width: 100px; width: -webkit-min(150, 50%);">mix number and percent</div>
+Bare min()
+<div style="width: 100px; width: min(">unclosed min</div>
+<div style="width: 100px; width: min( bob">unclosed min with garbage</div>
+<div style="width: 100px; width: min( bob );">garbage</div>
+<div style="width: 100px; width: min(20px,);">extra trailing comma</div>
+<div style="width: 100px; width: min(,20px);">leading comma</div>
+<div style="width: 100px; width: min(20px, bob);">trailing garbage</div>
+<div style="width: 100px; width: min(20px, 10px + flim);">bad _expression_</div>
+<div style="width: 100px; width: min(256px, 120);">mix length and number</div>
+<div style="width: 100px; width: min(256, 120px);">mix number and length</div>
+<div style="width: 100px; width: min(50%, 150);">mix percent and number</div>
+<div style="width: 100px; width: min(150, 50%);">mix number and percent</div>
 
-MAX
-<div style="width: 100px; width: -webkit-max(">unclosed max</div>
-<div style="width: 100px; width: -webkit-max( bob">unclosed max with garbage</div>
-<div style="width: 100px; width: -webkit-max(256px, 120);">mix length and number</div>
-<div style="width: 100px; width: -webkit-max(256, 120px);">mix number and length</div>
-<div style="width: 100px; width: -webkit-max(50%, 150);">mix percent and number</div>
-<div style="width: 100px; width: -webkit-max(150, 50%);">mix number and percent</div>
+<br/><br/>min() inside calc()
+<div style="width: 100px; width: calc(min(">unclosed min</div>
+<div style="width: 100px; width: calc(min( bob">unclosed min with garbage</div>
+<div style="width: 100px; width: calc(min( bob ));">garbage</div>
+<div style="width: 100px; width: calc(min(20px,));">extra trailing comma</div>
+<div style="width: 100px; width: calc(min(,20px));">leading comma</div>
+<div style="width: 100px; width: calc(min(20px, bob));">trailing garbage</div>
+<div style="width: 100px; width: calc(min(20px, 10px + flim));">bad _expression_</div>
+<div style="width: 100px; width: calc(min(256px, 120));">mix length and number</div>
+<div style="width: 100px; width: calc(min(256, 120px));">mix number and length</div>
+<div style="width: 100px; width: calc(min(50%, 150));">mix percent and number</div>
+<div style="width: 100px; width: calc(min(150, 50%));">mix number and percent</div>
 
+<br/><br/>Bare max()
+<div style="width: 100px; width: max(">unclosed max</div>
+<div style="width: 100px; width: max( bob">unclosed max with garbage</div>
+<div style="width: 100px; width: max(256px, 120);">mix length and number</div>
+<div style="width: 100px; width: max(256, 120px);">mix number and length</div>
+<div style="width: 100px; width: max(50%, 150);">mix percent and number</div>
+<div style="width: 100px; width: max(150, 50%);">mix number and percent</div>
+
+<br/><br/>max() inside calc
+<div style="width: 100px; width: calc(max(">unclosed max</div>
+<div style="width: 100px; width: calc(max( bob">unclosed max with garbage</div>
+<div style="width: 100px; width: calc(max(256px, 120));">mix length and number</div>
+<div style="width: 100px; width: calc(max(256, 120px));">mix number and length</div>
+<div style="width: 100px; width: calc(max(50%, 150));">mix percent and number</div>
+<div style="width: 100px; width: calc(max(150, 50%));">mix number and percent</div>
+
 </div>
 
 <script>

Modified: trunk/LayoutTests/css3/calc/simple-minmax-expected.txt (222189 => 222190)


--- trunk/LayoutTests/css3/calc/simple-minmax-expected.txt	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/LayoutTests/css3/calc/simple-minmax-expected.txt	2017-09-18 23:36:20 UTC (rev 222190)
@@ -1,18 +1,54 @@
 All boxes below should be 100px * 100px and green.
 
-min(100px) => FAIL: expected width of 100, but was 256
-min( 100px ) => FAIL: expected width of 100, but was 256
-min((((100px)))) => FAIL: expected width of 100, but was 256
-min(150px,100px) => FAIL: expected width of 100, but was 256
-min(150px,100px,200px) => FAIL: expected width of 100, but was 256
-min( 150px , 100px ,200px) => FAIL: expected width of 100, but was 256
-min(90px + 50px ,100px) => FAIL: expected width of 100, but was 256
-min(100%,100px) - where 100% is 200px => FAIL: expected width of 100, but was 256
-min(100px,100%) - where 100% is 200px => FAIL: expected width of 100, but was 256
-max(100px) => FAIL: expected width of 100, but was 256
-max(50px,100px) => FAIL: expected width of 100, but was 256
-max(50px,100px,20px) => FAIL: expected width of 100, but was 256
-max(120px - 50px,100px) => FAIL: expected width of 100, but was 256
-max(100%,100px) - where 100% is 50px => FAIL: expected width of 100, but was 256
-max(100px,100%) - where 100% is 50px => FAIL: expected width of 100, but was 256
-min(200px,100px) => FAIL: expected height of 100, but was 50
+Bare min()
+min(100px) => PASS
+min( 100px ) => PASS
+min((((100px)))) => PASS
+min(150px,100px) => PASS
+min(150px,100px,200px) => PASS
+min( 150px , 100px ,200px) => PASS
+min(90px + 50px ,100px) => PASS
+min(100%,100px) - where 100% is 200px => PASS
+min(50%,150px) - where 50% is 100px => PASS
+min(100% - 50px,100px) - where 100% is 200px => PASS
+min(25% + 50px,150px) - where 25% is 50px => PASS
+min(100px,100%) - where 100% is 200px => PASS
+min(200px,100px) => PASS
+
+
+min() inside calc()
+calc(min(100px)) => PASS
+calc(min(150px, 200px) - 50px) => PASS
+calc(50 + min(150px, 50px)) => PASS
+calc(min(250px, 200px) / 2) => PASS
+calc(min( 100px )) => PASS
+calc(min((((100px))))) => PASS
+calc(min(150px,100px)) => PASS
+calc(min(150px,100px,200px)) => PASS
+calc(min( 150px , 100px ,200px)) => PASS
+calc(min(90px + 50px ,100px)) => PASS
+calc(min(100%,100px)) - where 100% is 200px => PASS
+calc(min(50%,150px)) - where 50% is 100px => PASS
+calc(min(100% - 50px,100px)) - where 100% is 200px => PASS
+calc(min(25% + 50px,150px)) - where 25% is 50px => PASS
+calc(min(100px,100%)) - where 100% is 200px => PASS
+calc(min(200px,100px)) => PASS
+
+
+Bare max()
+max(100px) => PASS
+calc(max(150px, 100px) - 50px) => PASS
+max(50px,100px) => PASS
+max(50px,100px,20px) => PASS
+max(120px - 50px,100px) => PASS
+max(100%,100px) - where 100% is 50px => PASS
+max(100px,100%) - where 100% is 50px => PASS
+
+
+max() inside calc()
+calc(max(100px)) => PASS
+calc(max(50px,100px)) => PASS
+calc(max(50px,100px,20px)) => PASS
+calc(max(120px - 50px,100px)) => PASS
+calc(max(100%,100px)) - where 100% is 50px => PASS
+calc(max(100px,100%)) - where 100% is 50px => PASS

Modified: trunk/LayoutTests/css3/calc/simple-minmax.html (222189 => 222190)


--- trunk/LayoutTests/css3/calc/simple-minmax.html	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/LayoutTests/css3/calc/simple-minmax.html	2017-09-18 23:36:20 UTC (rev 222190)
@@ -19,32 +19,83 @@
 
 <div id="test">
 
-<div class="width-test" style="width: -webkit-min(100px);">min(100px)</div>
-<div class="width-test" style="width: -webkit-min( 100px );">min( 100px )</div>
-<div class="width-test" style="width: -webkit-min((((100px))));">min((((100px))))</div>
-<div class="width-test" style="width: -webkit-min(150px,100px);">min(150px,100px)</div>
-<div class="width-test" style="width: -webkit-min(150px,100px,200px);">min(150px,100px,200px)</div>
-<div class="width-test" style="width: -webkit-min(  150px ,  100px  ,200px);">min(  150px  ,  100px  ,200px)</div>
-<div class="width-test" style="width: -webkit-min(90px + 50px ,100px);">min(90px + 50px ,100px)</div>
+Bare min()
+<div class="width-test" style="width: min(100px);">min(100px)</div>
+<div class="width-test" style="width: min( 100px );">min( 100px )</div>
+<div class="width-test" style="width: min((((100px))));">min((((100px))))</div>
+<div class="width-test" style="width: min(150px,100px);">min(150px,100px)</div>
+<div class="width-test" style="width: min(150px,100px,200px);">min(150px,100px,200px)</div>
+<div class="width-test" style="width: min(  150px ,  100px  ,200px);">min(  150px  ,  100px  ,200px)</div>
+<div class="width-test" style="width: min(90px + 50px ,100px);">min(90px + 50px ,100px)</div>
 <div style="width: 200px; background-color: white;" class="wrapper">
-  <div class="width-test" style="width: -webkit-min(100%,100px);">min(100%,100px) - where 100% is 200px</div>
+  <div class="width-test" style="width: min(100%,100px);">min(100%,100px) - where 100% is 200px</div>
 </div>
 <div style="width: 200px; background-color: white;" class="wrapper">
-  <div class="width-test" style="width: -webkit-min(100px,100%);">min(100px,100%) - where 100% is 200px</div>
+  <div class="width-test" style="width: min(50%,150px);">min(50%,150px) - where 50% is 100px</div>
 </div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: min(100% - 50px,100px);">min(100% - 50px,100px) - where 100% is 200px</div>
+</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: min(25% + 50px,150px);">min(25% + 50px,150px) - where 25% is 50px</div>
+</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: min(100px,100%);">min(100px,100%) - where 100% is 200px</div>
+</div>
+<div class="height-test" style="height: min(200px, 100px);">min(200px,100px)</div>
 
-<div class="width-test" style="width: -webkit-max(100px);">max(100px)</div>
-<div class="width-test" style="width: -webkit-max(50px,100px);">max(50px,100px)</div>
-<div class="width-test" style="width: -webkit-max(50px,100px,20px);">max(50px,100px,20px)</div>
-<div class="width-test" style="width: -webkit-max(120px - 50px,100px);">max(120px - 50px,100px)</div>
+<br/><br/>min() inside calc()
+<div class="width-test" style="width: calc(min(100px));">calc(min(100px))</div>
+<div class="width-test" style="width: calc(min(150px, 200px) - 50px);">calc(min(150px, 200px) - 50px)</div>
+<div class="width-test" style="width: calc(50px + min(150px, 50px));">calc(50 + min(150px, 50px))</div>
+<div class="width-test" style="width: calc(min(250px, 200px) / 2);">calc(min(250px, 200px) / 2)</div>
+<div class="width-test" style="width: calc(min( 100px ));">calc(min( 100px ))</div>
+<div class="width-test" style="width: calc(min((((100px)))));">calc(min((((100px)))))</div>
+<div class="width-test" style="width: calc(min(150px,100px));">calc(min(150px,100px))</div>
+<div class="width-test" style="width: calc(min(150px,100px,200px));">calc(min(150px,100px,200px))</div>
+<div class="width-test" style="width: calc(min(  150px ,  100px  ,200px));">calc(min(  150px  ,  100px  ,200px))</div>
+<div class="width-test" style="width: calc(min(90px + 50px ,100px));">calc(min(90px + 50px ,100px))</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(min(100%,100px));">calc(min(100%,100px)) - where 100% is 200px</div>
+</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(min(50%,150px));">calc(min(50%,150px)) - where 50% is 100px</div>
+</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(min(100% - 50px,100px));">calc(min(100% - 50px,100px)) - where 100% is 200px</div>
+</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(min(25% + 50px,150px));">calc(min(25% + 50px,150px)) - where 25% is 50px</div>
+</div>
+<div style="width: 200px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(min(100px,100%));">calc(min(100px,100%)) - where 100% is 200px</div>
+</div>
+<div class="height-test" style="height: calc(min(200px, 100px));">calc(min(200px,100px))</div>
+
+<br/><br/>Bare max()
+<div class="width-test" style="width: max(100px);">max(100px)</div>
+<div class="width-test" style="width: calc(max(150px, 100px) - 50px);">calc(max(150px, 100px) - 50px)</div>
+<div class="width-test" style="width: max(50px,100px);">max(50px,100px)</div>
+<div class="width-test" style="width: max(50px,100px,20px);">max(50px,100px,20px)</div>
+<div class="width-test" style="width: max(120px - 50px,100px);">max(120px - 50px,100px)</div>
 <div style="width: 50px; background-color: white;" class="wrapper">
-  <div class="width-test" style="width: -webkit-max(100%,100px);">max(100%,100px) - where 100% is 50px</div>
+  <div class="width-test" style="width: max(100%,100px);">max(100%,100px) - where 100% is 50px</div>
 </div>
 <div style="width: 50px; background-color: white;" class="wrapper">
-  <div class="width-test" style="width: -webkit-max(100px,100%);">max(100px,100%) - where 100% is 50px</div>
+  <div class="width-test" style="width: max(100px,100%);">max(100px,100%) - where 100% is 50px</div>
 </div>
 
-<div class="height-test" style="height: -webkit-min(200px, 100px);">min(200px,100px)</div>
+<br/><br/>max() inside calc()
+<div class="width-test" style="width: calc(max(100px));">calc(max(100px))</div>
+<div class="width-test" style="width: calc(max(50px,100px));">calc(max(50px,100px))</div>
+<div class="width-test" style="width: calc(max(50px,100px,20px));">calc(max(50px,100px,20px))</div>
+<div class="width-test" style="width: calc(max(120px - 50px,100px));">calc(max(120px - 50px,100px))</div>
+<div style="width: 50px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(max(100%,100px));">calc(max(100%,100px)) - where 100% is 50px</div>
+</div>
+<div style="width: 50px; background-color: white;" class="wrapper">
+  <div class="width-test" style="width: calc(max(100px,100%));">calc(max(100px,100%)) - where 100% is 50px</div>
+</div>
 
 </div>
 

Modified: trunk/Source/WebCore/ChangeLog (222189 => 222190)


--- trunk/Source/WebCore/ChangeLog	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/ChangeLog	2017-09-18 23:36:20 UTC (rev 222190)
@@ -1,3 +1,131 @@
+2017-09-18  Tim Horton  <[email protected]>
+
+        Support min() and max() in calc()
+        https://bugs.webkit.org/show_bug.cgi?id=167000
+        <rdar://problem/30153481>
+
+        Reviewed by David Hyatt.
+        Patch originally by Myles Maxfield.
+
+        Add two new toplevel functions to CSS, min() and max(), which take an
+        arbirary number of arguments and resolve to the minimum and maximum of
+        the resolved value of the arguments, respectively. It is also possible
+        to use min() and max() inside calc(), and to use calc()-like math
+        inside min() and max().
+
+        * css/CSSCalculationValue.cpp:
+        (WebCore::determineCategory):
+        min and max operators don't use determineCategory; we have a specific
+        implementation for them in createMinOrMax.
+
+        (WebCore::resolvedTypeForMinOrMax):
+        The spec says that min() and max() should be marked as invalid if they
+        have values of more than one type, but that percentages should resolve
+        against the destination type before making this determination. So,
+        if the destination type is length, percent turns into percent-length,
+        and similarly for number.
+
+        (WebCore::isIntegerResult):
+        Add an n-way implementation of isIntegerResult.
+
+        (WebCore::isSamePair):
+        (WebCore::CSSCalcOperation::createMinOrMax): Create a min() or max()
+        operation, as long as the types of arguments are all the same. Allow
+        lengths to upgrade the whole operation to percent-length, and numbers
+        to percent-number, which will cause us to use CalculationValue and friends
+        in order to do proper resolution of all of the parameters instead of
+        just comparing their numeric values.
+
+        (WebCore::CSSCalcOperation::createCalcExpression):
+        (WebCore::CSSCalcOperation::doubleValue):
+        (WebCore::CSSCalcOperation::computeLengthPx):
+        (WebCore::CSSCalcOperation::customCSSText):
+        (WebCore::CSSCalcOperation::primitiveType):
+        (WebCore::CSSCalcOperation::CSSCalcOperation):
+        (WebCore::CSSCalcOperation::evaluate):
+        (WebCore::CSSCalcOperation::evaluateOperator):
+        Adapt to child counts greater than two.
+
+        (WebCore::CSSCalcOperation::buildCssText):
+        Add support for min() and max().
+
+        (WebCore::CSSCalcExpressionNodeParser::parseCalc):
+        parseCalc now accepts a CSSValueID parameter indicating which calc function
+        it should parse (calc, webkit-calc, min, or max), and delegates to either
+        parseValueExpression or parseMinMaxExpression.
+
+        (WebCore::CSSCalcExpressionNodeParser::operatorValue):
+        (WebCore::CSSCalcExpressionNodeParser::parseValue):
+        If min() or max() are found while parsing a value (i.e. nested inside
+        either calc or themselves), use parseMinMaxExpression on that subtree.
+
+        (WebCore::CSSCalcExpressionNodeParser::parseValueTerm):
+        (WebCore::CSSCalcExpressionNodeParser::parseValueMultiplicativeExpression):
+        (WebCore::CSSCalcExpressionNodeParser::parseAdditiveValueExpression):
+        Adjust to the CSSCalcBinaryOperation->CSSCalcOperation rename.
+
+        (WebCore::CSSCalcExpressionNodeParser::parseMinMaxExpression):
+        Added. Parse an arbitrary number of comma-and-whitespace-separated children.
+
+        (WebCore::createBlendHalf):
+        Adjust to the CSSCalcBinaryOperation->CSSCalcOperation rename.
+
+        (WebCore::createCSS):
+        Build the CSSCalcOperation for the platform-independent min and max operations.
+
+        (WebCore::CSSCalcValue::create):
+        Pass the function being parsed and the destination calc category for the
+        property being parsed for into create, and then into the parser so that
+        it can know which function it is parsing for, and what kind of result it
+        needs (as previously mentioned above in resolvedTypeForMinOrMax).
+
+        * css/CSSCalculationValue.h:
+        * css/CSSValueKeywords.in:
+        Add min and max functions as CSS keywords.
+
+        * css/StyleBuilderConverter.h:
+        (WebCore::StyleBuilderConverter::convertLength):
+        (WebCore::StyleBuilderConverter::convertTo100PercentMinusLength):
+        * platform/Length.cpp:
+        (WebCore::convertTo100PercentMinusLength):
+        Adapt to the CalcExpressionOperation constructor taking a vector of
+        arguments instead of two.
+
+        * css/parser/CSSPropertyParserHelpers.cpp:
+        (WebCore::CSSPropertyParserHelpers::CalcParser::CalcParser):
+        Store and pass the specific function being parsed down into CSSCalcValue.
+
+        (WebCore::CSSPropertyParserHelpers::consumeInteger):
+        (WebCore::CSSPropertyParserHelpers::consumePositiveIntegerRaw):
+        (WebCore::CSSPropertyParserHelpers::consumeNumberRaw):
+        (WebCore::CSSPropertyParserHelpers::consumeNumber):
+        (WebCore::CSSPropertyParserHelpers::consumeFontWeightNumber):
+        (WebCore::CSSPropertyParserHelpers::consumeLength):
+        (WebCore::CSSPropertyParserHelpers::consumePercent):
+        (WebCore::CSSPropertyParserHelpers::consumeLengthOrPercent):
+        (WebCore::CSSPropertyParserHelpers::consumeAngle):
+        (WebCore::CSSPropertyParserHelpers::consumeTime):
+        Pass the destination type into each calc parser.
+
+        * platform/CalculationValue.cpp:
+        (WebCore::CalcExpressionOperation::evaluate const):
+        (WebCore::CalcExpressionOperation::operator== const):
+        (WebCore::CalcExpressionOperation::dump const):
+        (WebCore::operator<<):
+        (WebCore::CalcExpressionBinaryOperation::evaluate const): Deleted.
+        (WebCore::CalcExpressionBinaryOperation::operator== const): Deleted.
+        (WebCore::CalcExpressionBinaryOperation::dump const): Deleted.
+        * platform/CalculationValue.h:
+        (WebCore::CalcExpressionOperation::CalcExpressionOperation):
+        (WebCore::operator==):
+        (WebCore::toCalcExpressionOperation):
+        (WebCore::CalcExpressionBinaryOperation::CalcExpressionBinaryOperation): Deleted.
+        (WebCore::toCalcExpressionBinaryOperation): Deleted.
+        Adjust to the CSSCalcBinaryOperation->CSSCalcOperation rename.
+        Adjust to having n>2 children.
+        Support min() and max() operators in various places.
+        
+
 2017-09-18  Basuke Suzuki  <[email protected]>
 
         [Curl] Move error generation task into ResourceError

Modified: trunk/Source/WebCore/css/CSSCalculationValue.cpp (222189 => 222190)


--- trunk/Source/WebCore/css/CSSCalculationValue.cpp	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/css/CSSCalculationValue.cpp	2017-09-18 23:36:20 UTC (rev 222190)
@@ -325,6 +325,10 @@
         if (rightCategory != CalcNumber || rightSide.isZero())
             return CalcOther;
         return leftCategory;
+    case CalcMin:
+    case CalcMax:
+        ASSERT_NOT_REACHED();
+        return CalcOther;
     }
 
     ASSERT_NOT_REACHED();
@@ -331,6 +335,30 @@
     return CalcOther;
 }
 
+static CalculationCategory resolvedTypeForMinOrMax(CalculationCategory category, CalculationCategory destinationCategory)
+{
+    switch (category) {
+    case CalcNumber:
+    case CalcLength:
+    case CalcPercentNumber:
+    case CalcPercentLength:
+    case CalcAngle:
+    case CalcTime:
+    case CalcFrequency:
+    case CalcOther:
+        return category;
+
+    case CalcPercent:
+        if (destinationCategory == CalcLength)
+            return CalcPercentLength;
+        if (destinationCategory == CalcNumber)
+            return CalcPercentNumber;
+        return category;
+    }
+
+    return CalcOther;
+}
+
 static inline bool isIntegerResult(CalcOperator op, const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide)
 {
     // Performs W3C spec's type checking for calc integers.
@@ -338,10 +366,30 @@
     return op != CalcDivide && leftSide.isInteger() && rightSide.isInteger();
 }
 
-class CSSCalcBinaryOperation final : public CSSCalcExpressionNode {
+static inline bool isIntegerResult(CalcOperator op, const Vector<Ref<CSSCalcExpressionNode>>& nodes)
+{
+    // Performs W3C spec's type checking for calc integers.
+    // http://www.w3.org/TR/css3-values/#calc-type-checking
+    if (op == CalcDivide)
+        return false;
+
+    for (auto& node : nodes) {
+        if (!node->isInteger())
+            return false;
+    }
+
+    return true;
+}
+
+static bool isSamePair(CalculationCategory a, CalculationCategory b, CalculationCategory x, CalculationCategory y)
+{
+    return (a == x && b == y) || (a == y && b == x);
+}
+
+class CSSCalcOperation final : public CSSCalcExpressionNode {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static RefPtr<CSSCalcBinaryOperation> create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+    static RefPtr<CSSCalcOperation> create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
     {
         if (!leftSide || !rightSide)
             return nullptr;
@@ -352,9 +400,41 @@
         auto newCategory = determineCategory(*leftSide, *rightSide, op);
         if (newCategory == CalcOther)
             return nullptr;
-        return adoptRef(new CSSCalcBinaryOperation(newCategory, op, leftSide.releaseNonNull(), rightSide.releaseNonNull()));
+
+        return adoptRef(new CSSCalcOperation(newCategory, op, leftSide.releaseNonNull(), rightSide.releaseNonNull()));
     }
 
+    static RefPtr<CSSCalcOperation> createMinOrMax(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory)
+    {
+        ASSERT(op == CalcMin || op == CalcMax);
+
+        std::optional<CalculationCategory> category = std::nullopt;
+        for (auto& value : values) {
+            auto valueCategory = resolvedTypeForMinOrMax(value->category(), destinationCategory);
+
+            ASSERT(valueCategory < CalcOther);
+            if (!category) {
+                if (valueCategory == CalcOther)
+                    return nullptr;
+                category = valueCategory;
+            }
+
+            if (category != valueCategory) {
+                if (isSamePair(category.value(), valueCategory, CalcLength, CalcPercentLength)) {
+                    category = CalcPercentLength;
+                    continue;
+                }
+                if (isSamePair(category.value(), valueCategory, CalcNumber, CalcPercentNumber)) {
+                    category = CalcPercentNumber;
+                    continue;
+                }
+                return nullptr;
+            }
+        }
+
+        return adoptRef(new CSSCalcOperation(category.value(), op, WTFMove(values)));
+    }
+
     static RefPtr<CSSCalcExpressionNode> createSimplified(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
     {
         if (!leftSide || !rightSide)
@@ -370,7 +450,7 @@
         // Simplify numbers.
         if (leftCategory == CalcNumber && rightCategory == CalcNumber) {
             CSSPrimitiveValue::UnitType evaluationType = CSSPrimitiveValue::CSS_NUMBER;
-            return CSSCalcPrimitiveValue::create(evaluateOperator(op, leftSide->doubleValue(), rightSide->doubleValue()), evaluationType, isInteger);
+            return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), evaluationType, isInteger);
         }
 
         // Simplify addition and subtraction between same types.
@@ -380,7 +460,7 @@
                 if (hasDoubleValue(leftType)) {
                     CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType();
                     if (leftType == rightType)
-                        return CSSCalcPrimitiveValue::create(evaluateOperator(op, leftSide->doubleValue(), rightSide->doubleValue()), leftType, isInteger);
+                        return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), leftType, isInteger);
                     CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
                     if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
                         CSSPrimitiveValue::UnitType canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
@@ -387,7 +467,7 @@
                         if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) {
                             double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
                             double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
-                            return CSSCalcPrimitiveValue::create(evaluateOperator(op, leftValue, rightValue), canonicalType, isInteger);
+                            return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftValue, rightValue }), canonicalType, isInteger);
                         }
                     }
                 }
@@ -410,7 +490,7 @@
 
             auto otherType = otherSide.primitiveType();
             if (hasDoubleValue(otherType))
-                return CSSCalcPrimitiveValue::create(evaluateOperator(op, otherSide.doubleValue(), number), otherType, isInteger);
+                return CSSCalcPrimitiveValue::create(evaluateOperator(op, { otherSide.doubleValue(), number }), otherType, isInteger);
         }
 
         return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
@@ -424,36 +504,63 @@
 
     std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
     {
-        auto left = m_leftSide->createCalcExpression(conversionData);
-        if (!left)
-            return nullptr;
-        auto right = m_rightSide->createCalcExpression(conversionData);
-        if (!right)
-            return nullptr;
-        return std::make_unique<CalcExpressionBinaryOperation>(WTFMove(left), WTFMove(right), m_operator);
+        Vector<std::unique_ptr<CalcExpressionNode>> nodes;
+        nodes.reserveInitialCapacity(m_children.size());
+
+        for (auto& child : m_children) {
+            auto node = child->createCalcExpression(conversionData);
+            if (!node)
+                return nullptr;
+            nodes.uncheckedAppend(WTFMove(node));
+        }
+        return std::make_unique<CalcExpressionOperation>(WTFMove(nodes), m_operator);
     }
 
     double doubleValue() const final
     {
-        return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
+        Vector<double> doubleValues;
+        for (auto& child : m_children)
+            doubleValues.append(child->doubleValue());
+        return evaluate(doubleValues);
     }
 
     double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
     {
-        const double leftValue = m_leftSide->computeLengthPx(conversionData);
-        const double rightValue = m_rightSide->computeLengthPx(conversionData);
-        return evaluate(leftValue, rightValue);
+        Vector<double> doubleValues;
+        for (auto& child : m_children)
+            doubleValues.append(child->computeLengthPx(conversionData));
+        return evaluate(doubleValues);
     }
 
-    static String buildCssText(const String& leftExpression, const String& rightExpression, CalcOperator op)
+    static String buildCssText(Vector<String> childExpressions, CalcOperator op)
     {
         StringBuilder result;
         result.append('(');
-        result.append(leftExpression);
-        result.append(' ');
-        result.append(static_cast<char>(op));
-        result.append(' ');
-        result.append(rightExpression);
+        switch (op) {
+        case CalcAdd:
+        case CalcSubtract:
+        case CalcMultiply:
+        case CalcDivide:
+            ASSERT(childExpressions.size() == 2);
+            result.append(childExpressions[0]);
+            result.append(' ');
+            result.append(static_cast<char>(op));
+            result.append(' ');
+            result.append(childExpressions[1]);
+            break;
+        case CalcMin:
+        case CalcMax:
+            ASSERT(!childExpressions.isEmpty());
+            const char* functionName = op == CalcMin ? "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(')');
+        }
         result.append(')');
 
         return result.toString();
@@ -461,7 +568,10 @@
 
     String customCSSText() const final
     {
-        return buildCssText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator);
+        Vector<String> cssTexts;
+        for (auto& child : m_children)
+            cssTexts.append(child->customCSSText());
+        return buildCssText(cssTexts, m_operator);
     }
 
     bool equals(const CSSCalcExpressionNode& exp) const final
@@ -469,30 +579,45 @@
         if (type() != exp.type())
             return false;
 
-        const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
-        return compareCSSValuePtr(m_leftSide, other.m_leftSide)
-            && compareCSSValuePtr(m_rightSide, other.m_rightSide)
-            && m_operator == other.m_operator;
+        const CSSCalcOperation& other = static_cast<const CSSCalcOperation&>(exp);
+
+        if (m_children.size() != other.m_children.size() || m_operator != other.m_operator)
+            return false;
+
+        for (size_t i = 0; i < m_children.size(); ++i) {
+            if (!compareCSSValue(m_children[i], other.m_children[i]))
+                return false;
+        }
+        return true;
     }
 
-    Type type() const final { return CssCalcBinaryOperation; }
+    Type type() const final { return CssCalcOperation; }
 
     CSSPrimitiveValue::UnitType primitiveType() const final
     {
         switch (category()) {
         case CalcNumber:
-            ASSERT(m_leftSide->category() == CalcNumber && m_rightSide->category() == CalcNumber);
+#if !ASSERT_DISABLED
+            for (auto& child : m_children)
+                ASSERT(child->category() == CalcNumber);
+#endif
             return CSSPrimitiveValue::CSS_NUMBER;
         case CalcLength:
         case CalcPercent: {
-            if (m_leftSide->category() == CalcNumber)
-                return m_rightSide->primitiveType();
-            if (m_rightSide->category() == CalcNumber)
-                return m_leftSide->primitiveType();
-            CSSPrimitiveValue::UnitType leftType = m_leftSide->primitiveType();
-            if (leftType == m_rightSide->primitiveType())
-                return leftType;
-            return CSSPrimitiveValue::CSS_UNKNOWN;
+            if (m_children.isEmpty())
+                return CSSPrimitiveValue::CSS_UNKNOWN;
+            if (m_children.size() == 2) {
+                if (m_children[0]->category() == CalcNumber)
+                    return m_children[1]->primitiveType();
+                if (m_children[1]->category() == CalcNumber)
+                    return m_children[0]->primitiveType();
+            }
+            CSSPrimitiveValue::UnitType firstType = m_children[0]->primitiveType();
+            for (auto& child : m_children) {
+                if (firstType != child->primitiveType())
+                    return CSSPrimitiveValue::CSS_UNKNOWN;
+            }
+            return firstType;
         }
         case CalcAngle:
             return CSSPrimitiveValue::CSS_DEG;
@@ -509,14 +634,21 @@
         return CSSPrimitiveValue::CSS_UNKNOWN;
     }
 
-    CSSCalcBinaryOperation(CalculationCategory category, CalcOperator op, Ref<CSSCalcExpressionNode>&& leftSide, Ref<CSSCalcExpressionNode>&& rightSide)
+    CSSCalcOperation(CalculationCategory category, CalcOperator op, Ref<CSSCalcExpressionNode>&& leftSide, Ref<CSSCalcExpressionNode>&& rightSide)
         : CSSCalcExpressionNode(category, isIntegerResult(op, leftSide.get(), rightSide.get()))
-        , m_leftSide(WTFMove(leftSide))
-        , m_rightSide(WTFMove(rightSide))
         , m_operator(op)
     {
+        m_children.append(WTFMove(leftSide));
+        m_children.append(WTFMove(rightSide));
     }
 
+    CSSCalcOperation(CalculationCategory category, CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& children)
+        : CSSCalcExpressionNode(category, isIntegerResult(op, children))
+        , m_children(WTFMove(children))
+        , m_operator(op)
+    {
+    }
+
     static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode& leftSide, CSSCalcExpressionNode& rightSide)
     {
         if (leftSide.category() == CalcNumber)
@@ -526,31 +658,50 @@
         return nullptr;
     }
 
-    double evaluate(double leftSide, double rightSide) const
+    double evaluate(const Vector<double>& children) const
     {
-        return evaluateOperator(m_operator, leftSide, rightSide);
+        return evaluateOperator(m_operator, children);
     }
 
-    static double evaluateOperator(CalcOperator op, double leftValue, double rightValue)
+    static double evaluateOperator(CalcOperator op, const Vector<double>& children)
     {
         switch (op) {
         case CalcAdd:
-            return leftValue + rightValue;
+            ASSERT(children.size() == 2);
+            return children[0] + children[1];
         case CalcSubtract:
-            return leftValue - rightValue;
+            ASSERT(children.size() == 2);
+            return children[0] - children[1];
         case CalcMultiply:
-            return leftValue * rightValue;
+            ASSERT(children.size() == 2);
+            return children[0] * children[1];
         case CalcDivide:
-            if (rightValue)
-                return leftValue / rightValue;
-            return std::numeric_limits<double>::quiet_NaN();
+            ASSERT(children.size() == 1 || children.size() == 2);
+            if (children.size() == 1)
+                return std::numeric_limits<double>::quiet_NaN();
+            return children[0] / children[1];
+        case CalcMin: {
+            if (children.isEmpty())
+                return std::numeric_limits<double>::quiet_NaN();
+            double minimum = children[0];
+            for (auto child : children)
+                minimum = std::min(minimum, child);
+            return minimum;
         }
+        case CalcMax: {
+            if (children.isEmpty())
+                return std::numeric_limits<double>::quiet_NaN();
+            double maximum = children[0];
+            for (auto child : children)
+                maximum = std::max(maximum, child);
+            return maximum;
+        }
+        }
         ASSERT_NOT_REACHED();
         return 0;
     }
 
-    const RefPtr<CSSCalcExpressionNode> m_leftSide;
-    const RefPtr<CSSCalcExpressionNode> m_rightSide;
+    Vector<Ref<CSSCalcExpressionNode>> m_children;
     const CalcOperator m_operator;
 };
 
@@ -566,11 +717,19 @@
 
 class CSSCalcExpressionNodeParser {
 public:
-    RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens)
+    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
+        : m_destinationCategory(destinationCategory)
+    { }
+
+    RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens, CSSValueID function)
     {
         Value result;
         tokens.consumeWhitespace();
-        bool ok = parseValueExpression(tokens, 0, &result);
+        bool ok = false;
+        if (function == CSSValueCalc || function == CSSValueWebkitCalc)
+            ok = parseValueExpression(tokens, 0, &result);
+        else if (function == CSSValueMin || function == CSSValueMax)
+            ok = parseMinMaxExpression(tokens, function, 0, &result);
         if (!ok || !tokens.atEnd())
             return nullptr;
         return result.value;
@@ -608,13 +767,22 @@
     {
         if (checkDepthAndIndex(&depth, tokens) != OK)
             return false;
+
+        auto functionId = tokens.peek().functionId();
         
-        if (tokens.peek().type() == LeftParenthesisToken || tokens.peek().functionId() == CSSValueCalc) {
+        if (tokens.peek().type() == LeftParenthesisToken || functionId == CSSValueCalc) {
             CSSParserTokenRange innerRange = tokens.consumeBlock();
             tokens.consumeWhitespace();
             innerRange.consumeWhitespace();
             return parseValueExpression(innerRange, depth, result);
         }
+
+        if (functionId == CSSValueMax || functionId == CSSValueMin) {
+            CSSParserTokenRange innerRange = tokens.consumeBlock();
+            tokens.consumeWhitespace();
+            innerRange.consumeWhitespace();
+            return parseMinMaxExpression(innerRange, functionId, depth, result);
+        }
         
         return parseValue(tokens, result);
     }
@@ -637,7 +805,7 @@
             if (!parseValueTerm(tokens, depth, &rhs))
                 return false;
             
-            result->value = CSSCalcBinaryOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
+            result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
 
             if (!result->value)
                 return false;
@@ -669,7 +837,7 @@
             if (!parseValueMultiplicativeExpression(tokens, depth, &rhs))
                 return false;
             
-            result->value = CSSCalcBinaryOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
+            result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
             if (!result->value)
                 return false;
         }
@@ -676,16 +844,48 @@
         
         return true;
     }
-    
+
+    bool parseMinMaxExpression(CSSParserTokenRange& tokens, CSSValueID minMaxFunction, int depth, Value* result)
+    {
+        if (checkDepthAndIndex(&depth, tokens) != OK)
+            return false;
+
+        CalcOperator op = (minMaxFunction == CSSValueMin) ? CalcMin : CalcMax;
+
+        Value value;
+        if (!parseValueExpression(tokens, depth, &value))
+            return false;
+
+        Vector<Ref<CSSCalcExpressionNode>> nodes;
+        nodes.append(value.value.releaseNonNull());
+
+        while (!tokens.atEnd()) {
+            tokens.consumeWhitespace();
+            if (tokens.consume().type() != CommaToken)
+                return false;
+            tokens.consumeWhitespace();
+
+            if (!parseValueExpression(tokens, depth, &value))
+                return false;
+
+            nodes.append(value.value.releaseNonNull());
+        }
+
+        result->value = CSSCalcOperation::createMinOrMax(op, WTFMove(nodes), m_destinationCategory);
+        return result->value;
+    }
+
     bool parseValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
     {
         return parseAdditiveValueExpression(tokens, depth, result);
     }
+
+    CalculationCategory m_destinationCategory;
 };
 
-static inline RefPtr<CSSCalcBinaryOperation> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
+static inline RefPtr<CSSCalcOperation> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
 {
-    return CSSCalcBinaryOperation::create(CalcMultiply, createCSS(length, style),
+    return CSSCalcOperation::create(CalcMultiply, createCSS(length, style),
         CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER), !progress || progress == 1));
 }
 
@@ -698,15 +898,32 @@
     }
     case CalcExpressionNodeLength:
         return createCSS(toCalcExpressionLength(node).length(), style);
-    case CalcExpressionNodeBinaryOperation: {
-        auto& binaryNode = toCalcExpressionBinaryOperation(node);
-        return CSSCalcBinaryOperation::create(binaryNode.getOperator(), createCSS(binaryNode.leftSide(), style), createCSS(binaryNode.rightSide(), style));
+    case CalcExpressionNodeOperation: {
+        auto& operationNode = toCalcExpressionOperation(node);
+        auto& operationChildren = operationNode.children();
+        CalcOperator op = operationNode.getOperator();
+        if (op == CalcMin || op == CalcMax) {
+            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 CSSCalcOperation::createMinOrMax(operationNode.getOperator(), WTFMove(values), CalcOther);
+        }
+
+        if (operationChildren.size() == 2)
+            return CSSCalcOperation::create(operationNode.getOperator(), createCSS(*operationChildren[0], style), createCSS(*operationChildren[1], style));
+
+        return nullptr;
     }
     case CalcExpressionNodeBlendLength: {
         // FIXME: (http://webkit.org/b/122036) Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
         auto& blend = toCalcExpressionBlendLength(node);
         float progress = blend.progress();
-        return CSSCalcBinaryOperation::create(CalcAdd, createBlendHalf(blend.from(), style, 1 - progress), createBlendHalf(blend.to(), style, progress));
+        return CSSCalcOperation::create(CalcAdd, createBlendHalf(blend.from(), style, 1 - progress), createBlendHalf(blend.to(), style, progress));
     }
     case CalcExpressionNodeUndefined:
         ASSERT_NOT_REACHED();
@@ -736,10 +953,10 @@
     return nullptr;
 }
 
-RefPtr<CSSCalcValue> CSSCalcValue::create(const CSSParserTokenRange& tokens, ValueRange range)
+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
 {
-    CSSCalcExpressionNodeParser parser;
-    auto _expression_ = parser.parseCalc(tokens);
+    CSSCalcExpressionNodeParser parser(destinationCategory);
+    auto _expression_ = parser.parseCalc(tokens, function);
     if (!_expression_)
         return nullptr;
     return adoptRef(new CSSCalcValue(_expression_.releaseNonNull(), range != ValueRangeAll));

Modified: trunk/Source/WebCore/css/CSSCalculationValue.h (222189 => 222190)


--- trunk/Source/WebCore/css/CSSCalculationValue.h	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/css/CSSCalculationValue.h	2017-09-18 23:36:20 UTC (rev 222190)
@@ -55,7 +55,7 @@
 public:
     enum Type {
         CssCalcPrimitiveValue = 1,
-        CssCalcBinaryOperation
+        CssCalcOperation
     };
 
     virtual ~CSSCalcExpressionNode() { }
@@ -85,8 +85,8 @@
 
 class CSSCalcValue final : public CSSValue {
 public:
-    static RefPtr<CSSCalcValue> create(const CSSParserTokenRange&, ValueRange);
-    
+    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange);
+
     static RefPtr<CSSCalcValue> create(const CalculationValue&, const RenderStyle&);
 
     CalculationCategory category() const { return m_expression->category(); }

Modified: trunk/Source/WebCore/css/CSSValueKeywords.in (222189 => 222190)


--- trunk/Source/WebCore/css/CSSValueKeywords.in	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/css/CSSValueKeywords.in	2017-09-18 23:36:20 UTC (rev 222190)
@@ -1249,6 +1249,8 @@
 
 calc
 -webkit-calc
+min
+max
 
 #if defined(ENABLE_CSS_IMAGE_RESOLUTION) && ENABLE_CSS_IMAGE_RESOLUTION
 from-image

Modified: trunk/Source/WebCore/css/StyleBuilderConverter.h (222189 => 222190)


--- trunk/Source/WebCore/css/StyleBuilderConverter.h	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/css/StyleBuilderConverter.h	2017-09-18 23:36:20 UTC (rev 222190)
@@ -322,9 +322,11 @@
         return Length(100 - length.value(), Percent);
     
     // Turn this into a calc _expression_: calc(100% - length)
-    auto lhs = std::make_unique<CalcExpressionLength>(Length(100, Percent));
-    auto rhs = std::make_unique<CalcExpressionLength>(length);
-    auto op = std::make_unique<CalcExpressionBinaryOperation>(WTFMove(lhs), WTFMove(rhs), CalcSubtract);
+    Vector<std::unique_ptr<CalcExpressionNode>> lengths;
+    lengths.reserveInitialCapacity(2);
+    lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(Length(100, Percent)));
+    lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(length));
+    auto op = std::make_unique<CalcExpressionOperation>(WTFMove(lengths), CalcSubtract);
     return Length(CalculationValue::create(WTFMove(op), ValueRangeAll));
 }
 

Modified: trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp (222189 => 222190)


--- trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2017-09-18 23:36:20 UTC (rev 222190)
@@ -78,13 +78,14 @@
 class CalcParser {
 
 public:
-    explicit CalcParser(CSSParserTokenRange& range, ValueRange valueRange = ValueRangeAll)
+    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRangeAll)
         : m_sourceRange(range)
         , m_range(range)
     {
         const CSSParserToken& token = range.peek();
-        if (token.functionId() == CSSValueCalc || token.functionId() == CSSValueWebkitCalc)
-            m_calcValue = CSSCalcValue::create(consumeFunction(m_range), valueRange);
+        auto functionId = token.functionId();
+        if (functionId == CSSValueCalc || functionId == CSSValueWebkitCalc || functionId == CSSValueMin || functionId == CSSValueMax)
+            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
     }
 
     const CSSCalcValue* value() const { return m_calcValue.get(); }
@@ -137,7 +138,7 @@
             return nullptr;
         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_NUMBER);
     }
-    CalcParser calcParser(range);
+    CalcParser calcParser(range, CalcNumber);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         if (calculation->category() != CalcNumber || !calculation->isInt())
             return nullptr;
@@ -163,7 +164,7 @@
         result = range.consumeIncludingWhitespace().numericValue();
         return true;
     }
-    CalcParser calcParser(range);
+    CalcParser calcParser(range, CalcNumber);
     return calcParser.consumePositiveIntegerRaw(result);
 }
     
@@ -173,7 +174,7 @@
         result = range.consumeIncludingWhitespace().numericValue();
         return true;
     }
-    CalcParser calcParser(range, ValueRangeAll);
+    CalcParser calcParser(range, CalcNumber, ValueRangeAll);
     return calcParser.consumeNumberRaw(result);
 }
 
@@ -186,7 +187,7 @@
             return nullptr;
         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
     }
-    CalcParser calcParser(range, ValueRangeAll);
+    CalcParser calcParser(range, CalcNumber, ValueRangeAll);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         // FIXME: Calcs should not be subject to parse time range checks.
         // spec: https://drafts.csswg.org/css-values-3/#calc-range
@@ -216,7 +217,7 @@
         return consumeNumber(range, ValueRangeAll);
 
     // "[For calc()], the used value resulting from an _expression_ must be clamped to the range allowed in the target context."
-    CalcParser calcParser(range, ValueRangeAll);
+    CalcParser calcParser(range, CalcNumber, ValueRangeAll);
     double result;
     if (calcParser.consumeNumberRaw(result)
 #if !ENABLE(VARIATION_FONTS)
@@ -278,7 +279,7 @@
         CSSPrimitiveValue::UnitType unitType = CSSPrimitiveValue::UnitType::CSS_PX;
         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unitType);
     }
-    CalcParser calcParser(range, valueRange);
+    CalcParser calcParser(range, CalcLength, valueRange);
     if (calcParser.value() && calcParser.value()->category() == CalcLength)
         return calcParser.consumeValue();
     return nullptr;
@@ -292,7 +293,7 @@
             return nullptr;
         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_PERCENTAGE);
     }
-    CalcParser calcParser(range, valueRange);
+    CalcParser calcParser(range, CalcPercent, valueRange);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         if (calculation->category() == CalcPercent)
             return calcParser.consumeValue();
@@ -321,7 +322,7 @@
         return consumeLength(range, cssParserMode, valueRange, unitless);
     if (token.type() == PercentageToken)
         return consumePercent(range, valueRange);
-    CalcParser calcParser(range, valueRange);
+    CalcParser calcParser(range, CalcLength, valueRange);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         if (canConsumeCalcValue(calculation->category(), cssParserMode))
             return calcParser.consumeValue();
@@ -347,7 +348,7 @@
         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitType::CSS_DEG);
     }
 
-    CalcParser calcParser(range, ValueRangeAll);
+    CalcParser calcParser(range, CalcAngle, ValueRangeAll);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         if (calculation->category() == CalcAngle)
             return calcParser.consumeValue();
@@ -369,7 +370,7 @@
             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
         return nullptr;
     }
-    CalcParser calcParser(range, valueRange);
+    CalcParser calcParser(range, CalcTime, valueRange);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         if (calculation->category() == CalcTime)
             return calcParser.consumeValue();

Modified: trunk/Source/WebCore/platform/CalculationValue.cpp (222189 => 222190)


--- trunk/Source/WebCore/platform/CalculationValue.cpp	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/platform/CalculationValue.cpp	2017-09-18 23:36:20 UTC (rev 222190)
@@ -68,34 +68,74 @@
     return m_shouldClampToNonNegative && result < 0 ? 0 : result;
 }
 
-float CalcExpressionBinaryOperation::evaluate(float maxValue) const
+float CalcExpressionOperation::evaluate(float maxValue) const
 {
-    float left = m_leftSide->evaluate(maxValue);
-    float right = m_rightSide->evaluate(maxValue);
     switch (m_operator) {
-    case CalcAdd:
+    case CalcAdd: {
+        ASSERT(m_children.size() == 2);
+        float left = m_children[0]->evaluate(maxValue);
+        float right = m_children[1]->evaluate(maxValue);
         return left + right;
-    case CalcSubtract:
+    }
+    case CalcSubtract: {
+        ASSERT(m_children.size() == 2);
+        float left = m_children[0]->evaluate(maxValue);
+        float right = m_children[1]->evaluate(maxValue);
         return left - right;
-    case CalcMultiply:
+    }
+    case CalcMultiply: {
+        ASSERT(m_children.size() == 2);
+        float left = m_children[0]->evaluate(maxValue);
+        float right = m_children[1]->evaluate(maxValue);
         return left * right;
-    case CalcDivide:
-        if (!right)
+    }
+    case CalcDivide: {
+        ASSERT(m_children.size() == 1 || m_children.size() == 2);
+        if (m_children.size() == 1)
             return std::numeric_limits<float>::quiet_NaN();
+        float left = m_children[0]->evaluate(maxValue);
+        float right = m_children[1]->evaluate(maxValue);
         return left / right;
     }
+    case CalcMin: {
+        if (m_children.isEmpty())
+            return std::numeric_limits<float>::quiet_NaN();
+        float minimum = m_children[0]->evaluate(maxValue);
+        for (auto& child : m_children)
+            minimum = std::min(minimum, child->evaluate(maxValue));
+        return minimum;
+    }
+    case CalcMax: {
+        if (m_children.isEmpty())
+            return std::numeric_limits<float>::quiet_NaN();
+        float maximum = m_children[0]->evaluate(maxValue);
+        for (auto& child : m_children)
+            maximum = std::max(maximum, child->evaluate(maxValue));
+        return maximum;
+    }
+    }
     ASSERT_NOT_REACHED();
     return std::numeric_limits<float>::quiet_NaN();
 }
 
-bool CalcExpressionBinaryOperation::operator==(const CalcExpressionNode& other) const
+bool CalcExpressionOperation::operator==(const CalcExpressionNode& other) const
 {
-    return other.type() == CalcExpressionNodeBinaryOperation && *this == toCalcExpressionBinaryOperation(other);
+    return other.type() == CalcExpressionNodeOperation && *this == toCalcExpressionOperation(other);
 }
 
-void CalcExpressionBinaryOperation::dump(TextStream& ts) const
+void CalcExpressionOperation::dump(TextStream& ts) const
 {
-    ts << *m_leftSide << " " << m_operator << " " << *m_rightSide;
+    if (m_operator == CalcMin || m_operator == CalcMax) {
+        ts << m_operator << "(";
+        size_t childrenCount = m_children.size();
+        for (size_t i = 0; i < childrenCount; i++) {
+            ts << m_children[i].get();
+            if (i < childrenCount - 1)
+                ts << ", ";
+        }
+        ts << ")";
+    } else
+        ts << m_children[0].get() << " " << m_operator << " " << m_children[1].get();
 }
 
 float CalcExpressionLength::evaluate(float maxValue) const
@@ -135,6 +175,8 @@
     case CalcSubtract: ts << "-"; break;
     case CalcMultiply: ts << "*"; break;
     case CalcDivide: ts << "/"; break;
+    case CalcMin: ts << "max"; break;
+    case CalcMax: ts << "min"; break;
     }
     return ts;
 }

Modified: trunk/Source/WebCore/platform/CalculationValue.h (222189 => 222190)


--- trunk/Source/WebCore/platform/CalculationValue.h	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/platform/CalculationValue.h	2017-09-18 23:36:20 UTC (rev 222190)
@@ -35,6 +35,7 @@
 #include <memory>
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
 
 namespace WTF {
 class TextStream;
@@ -46,7 +47,9 @@
     CalcAdd = '+',
     CalcSubtract = '-',
     CalcMultiply = '*',
-    CalcDivide = '/'
+    CalcDivide = '/',
+    CalcMin = 0,
+    CalcMax = 1,
 };
 
 enum CalcExpressionNodeType {
@@ -53,7 +56,7 @@
     CalcExpressionNodeUndefined,
     CalcExpressionNodeNumber,
     CalcExpressionNodeLength,
-    CalcExpressionNodeBinaryOperation,
+    CalcExpressionNodeOperation,
     CalcExpressionNodeBlendLength,
 };
 
@@ -101,21 +104,20 @@
     Length m_length;
 };
 
-class CalcExpressionBinaryOperation final : public CalcExpressionNode {
+class CalcExpressionOperation final : public CalcExpressionNode {
 public:
-    CalcExpressionBinaryOperation(std::unique_ptr<CalcExpressionNode> leftSide, std::unique_ptr<CalcExpressionNode> rightSide, CalcOperator);
+    CalcExpressionOperation(Vector<std::unique_ptr<CalcExpressionNode>>&& children, CalcOperator);
 
-    const CalcExpressionNode& leftSide() const { return *m_leftSide; }
-    const CalcExpressionNode& rightSide() const { return *m_rightSide; }
     CalcOperator getOperator() const { return m_operator; }
 
+    const Vector<std::unique_ptr<CalcExpressionNode>>& children() const { return m_children; }
+
 private:
     float evaluate(float maxValue) const override;
     bool operator==(const CalcExpressionNode&) const override;
     void dump(WTF::TextStream&) const override;
 
-    std::unique_ptr<CalcExpressionNode> m_leftSide;
-    std::unique_ptr<CalcExpressionNode> m_rightSide;
+    Vector<std::unique_ptr<CalcExpressionNode>> m_children;
     CalcOperator m_operator;
 };
 
@@ -202,23 +204,22 @@
     return static_cast<const CalcExpressionLength&>(value);
 }
 
-inline CalcExpressionBinaryOperation::CalcExpressionBinaryOperation(std::unique_ptr<CalcExpressionNode> leftSide, std::unique_ptr<CalcExpressionNode> rightSide, CalcOperator op)
-    : CalcExpressionNode(CalcExpressionNodeBinaryOperation)
-    , m_leftSide(WTFMove(leftSide))
-    , m_rightSide(WTFMove(rightSide))
+inline CalcExpressionOperation::CalcExpressionOperation(Vector<std::unique_ptr<CalcExpressionNode>>&& children, CalcOperator op)
+    : CalcExpressionNode(CalcExpressionNodeOperation)
+    , m_children(WTFMove(children))
     , m_operator(op)
 {
 }
 
-inline bool operator==(const CalcExpressionBinaryOperation& a, const CalcExpressionBinaryOperation& b)
+inline bool operator==(const CalcExpressionOperation& a, const CalcExpressionOperation& b)
 {
-    return a.getOperator() == b.getOperator() && a.leftSide() == b.leftSide() && a.rightSide() == b.rightSide();
+    return a.getOperator() == b.getOperator() && a.children() == b.children();
 }
 
-inline const CalcExpressionBinaryOperation& toCalcExpressionBinaryOperation(const CalcExpressionNode& value)
+inline const CalcExpressionOperation& toCalcExpressionOperation(const CalcExpressionNode& value)
 {
-    ASSERT_WITH_SECURITY_IMPLICATION(value.type() == CalcExpressionNodeBinaryOperation);
-    return static_cast<const CalcExpressionBinaryOperation&>(value);
+    ASSERT_WITH_SECURITY_IMPLICATION(value.type() == CalcExpressionNodeOperation);
+    return static_cast<const CalcExpressionOperation&>(value);
 }
 
 inline CalcExpressionBlendLength::CalcExpressionBlendLength(Length from, Length to, float progress)

Modified: trunk/Source/WebCore/platform/Length.cpp (222189 => 222190)


--- trunk/Source/WebCore/platform/Length.cpp	2017-09-18 23:32:28 UTC (rev 222189)
+++ trunk/Source/WebCore/platform/Length.cpp	2017-09-18 23:36:20 UTC (rev 222190)
@@ -291,9 +291,11 @@
         return Length(100 - length.value(), Percent);
     
     // Turn this into a calc _expression_: calc(100% - length)
-    auto lhs = std::make_unique<CalcExpressionLength>(Length(100, Percent));
-    auto rhs = std::make_unique<CalcExpressionLength>(length);
-    auto op = std::make_unique<CalcExpressionBinaryOperation>(WTFMove(lhs), WTFMove(rhs), CalcSubtract);
+    Vector<std::unique_ptr<CalcExpressionNode>> lengths;
+    lengths.reserveInitialCapacity(2);
+    lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(Length(100, Percent)));
+    lengths.uncheckedAppend(std::make_unique<CalcExpressionLength>(length));
+    auto op = std::make_unique<CalcExpressionOperation>(WTFMove(lengths), CalcSubtract);
     return Length(CalculationValue::create(WTFMove(op), ValueRangeAll));
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to