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));
}