<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[283073] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/283073">283073</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2021-09-24 22:03:08 -0700 (Fri, 24 Sep 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>Implement round,mod,rem functions for calc
https://bugs.webkit.org/show_bug.cgi?id=230073

Patch by Nikos Mouchtaris <nmouchtaris@apple.com> on 2021-09-24
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/css/css-values/round-function-expected.txt:
* web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt: Added.
* web-platform-tests/css/css-values/round-mod-rem-computed.html: Added.
* web-platform-tests/css/css-values/round-mod-rem-invalid-expected.txt: Added.
* web-platform-tests/css/css-values/round-mod-rem-invalid.html: Added.
* web-platform-tests/css/css-values/round-mod-rem-serialize-expected.txt: Added.
* web-platform-tests/css/css-values/round-mod-rem-serialize.html: Added.
* web-platform-tests/css/support/numeric-testcommon.js:

Source/WebCore:

Implemented round, mod and rem calc functions. Involved adding css keywords for the functions
and the keywords associated with the round function, as well as implementing the parsing and
computation of the expression. The spec for these functions:
https://drafts.csswg.org/css-values-4/#round-func.

Tests: imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed.html
       imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid.html
       imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize.html

* css/CSSValueKeywords.in:
* css/calc/CSSCalcExpressionNodeParser.cpp:
(WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
(WebCore::checkRoundKeyword):
Addition of functionID to parsing so the round keywords are only parsed within round
function.
(WebCore::CSSCalcExpressionNodeParser::parseValue):
(WebCore::CSSCalcExpressionNodeParser::parseCalcValue):
(WebCore::CSSCalcExpressionNodeParser::parseCalcProduct):
(WebCore::CSSCalcExpressionNodeParser::parseCalcSum):
* css/calc/CSSCalcExpressionNodeParser.h:
* css/calc/CSSCalcOperationNode.cpp:
(WebCore::determineCategory):
(WebCore::functionFromOperator):
(WebCore::CSSCalcOperationNode::createStep):
(WebCore::validateRoundChildren):
(WebCore::CSSCalcOperationNode::createRound):
(WebCore::CSSCalcOperationNode::createRoundConstant):
(WebCore::CSSCalcOperationNode::combineChildren):
(WebCore::CSSCalcOperationNode::simplifyNode):
(WebCore::functionPrefixForOperator):
(WebCore::getNearestMultiples):
(WebCore::CSSCalcOperationNode::evaluateOperator):
* css/calc/CSSCalcOperationNode.h:
* css/calc/CSSCalcValue.cpp:
(WebCore::createCSS):
(WebCore::CSSCalcValue::isCalcFunction):
* platform/calc/CalcExpressionOperation.cpp:
(WebCore::getNearestMultiples):
(WebCore::CalcExpressionOperation::evaluate const):
* platform/calc/CalcOperator.cpp:
(WebCore::operator<<):
* platform/calc/CalcOperator.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsimportedw3cChangeLog">trunk/LayoutTests/imported/w3c/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundfunctionexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-function-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorecssCSSValueKeywordsin">trunk/Source/WebCore/css/CSSValueKeywords.in</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcExpressionNodeParsercpp">trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcExpressionNodeParserh">trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcOperationNodecpp">trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcOperationNodeh">trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcValuecpp">trunk/Source/WebCore/css/calc/CSSCalcValue.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformcalcCalcExpressionOperationcpp">trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformcalcCalcOperatorcpp">trunk/Source/WebCore/platform/calc/CalcOperator.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformcalcCalcOperatorh">trunk/Source/WebCore/platform/calc/CalcOperator.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremcomputedexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremcomputedhtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodreminvalidexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodreminvalidhtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremserializeexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremserializehtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/LayoutTests/imported/w3c/ChangeLog    2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -1,5 +1,21 @@
</span><span class="cx"> 2021-09-24  Nikos Mouchtaris  <nmouchtaris@apple.com>
</span><span class="cx"> 
</span><ins>+        Implement round,mod,rem functions for calc
+        https://bugs.webkit.org/show_bug.cgi?id=230073
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/css/css-values/round-function-expected.txt:
+        * web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt: Added.
+        * web-platform-tests/css/css-values/round-mod-rem-computed.html: Added.
+        * web-platform-tests/css/css-values/round-mod-rem-invalid-expected.txt: Added.
+        * web-platform-tests/css/css-values/round-mod-rem-invalid.html: Added.
+        * web-platform-tests/css/css-values/round-mod-rem-serialize-expected.txt: Added.
+        * web-platform-tests/css/css-values/round-mod-rem-serialize.html: Added.
+        * web-platform-tests/css/support/numeric-testcommon.js:
+
+2021-09-24  Nikos Mouchtaris  <nmouchtaris@apple.com>
+
</ins><span class="cx">         Implement abs,sign calc functions
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=229786
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundfunctionexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-function-expected.txt (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-function-expected.txt     2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-function-expected.txt        2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -1,40 +1,40 @@
</span><span class="cx"> 
</span><del>-FAIL round(23px, 10px) should be used-value-equivalent to 20px assert_equals: round(23px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(18px, 10px) should be used-value-equivalent to 20px assert_equals: round(18px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(15px, 10px) should be used-value-equivalent to 20px assert_equals: round(15px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(13px, 10px) should be used-value-equivalent to 10px assert_equals: round(13px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(-13px, 10px) should be used-value-equivalent to -10px assert_equals: round(-13px, 10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(-18px, 10px) should be used-value-equivalent to -20px assert_equals: round(-18px, 10px) and -20px serialize to the same thing in used values. expected "-20px" but got "0px"
-FAIL round(nearest, 23px, 10px) should be used-value-equivalent to 20px assert_equals: round(nearest, 23px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(nearest, 18px, 10px) should be used-value-equivalent to 20px assert_equals: round(nearest, 18px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(nearest, 15px, 10px) should be used-value-equivalent to 20px assert_equals: round(nearest, 15px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(nearest, 13px, 10px) should be used-value-equivalent to 10px assert_equals: round(nearest, 13px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(nearest, -13px, 10px) should be used-value-equivalent to -10px assert_equals: round(nearest, -13px, 10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(nearest, -18px, 10px) should be used-value-equivalent to -20px assert_equals: round(nearest, -18px, 10px) and -20px serialize to the same thing in used values. expected "-20px" but got "0px"
-FAIL round(down, 23px, 10px) should be used-value-equivalent to 20px assert_equals: round(down, 23px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(down, 18px, 10px) should be used-value-equivalent to 10px assert_equals: round(down, 18px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(down, 15px, 10px) should be used-value-equivalent to 10px assert_equals: round(down, 15px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(down, 13px, 10px) should be used-value-equivalent to 10px assert_equals: round(down, 13px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(down, -13px, 10px) should be used-value-equivalent to -20px assert_equals: round(down, -13px, 10px) and -20px serialize to the same thing in used values. expected "-20px" but got "0px"
-FAIL round(down, -18px, 10px) should be used-value-equivalent to -20px assert_equals: round(down, -18px, 10px) and -20px serialize to the same thing in used values. expected "-20px" but got "0px"
-FAIL round(up, 23px, 10px) should be used-value-equivalent to 30px assert_equals: round(up, 23px, 10px) and 30px serialize to the same thing in used values. expected "30px" but got "0px"
-FAIL round(up, 18px, 10px) should be used-value-equivalent to 20px assert_equals: round(up, 18px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(up, 15px, 10px) should be used-value-equivalent to 20px assert_equals: round(up, 15px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(up, 13px, 10px) should be used-value-equivalent to 20px assert_equals: round(up, 13px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(up, -13px, 10px) should be used-value-equivalent to -10px assert_equals: round(up, -13px, 10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(up, -18px, 10px) should be used-value-equivalent to -10px assert_equals: round(up, -18px, 10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(to-zero, 23px, 10px) should be used-value-equivalent to 20px assert_equals: round(to-zero, 23px, 10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(to-zero, 18px, 10px) should be used-value-equivalent to 10px assert_equals: round(to-zero, 18px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(to-zero, 15px, 10px) should be used-value-equivalent to 10px assert_equals: round(to-zero, 15px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(to-zero, 13px, 10px) should be used-value-equivalent to 10px assert_equals: round(to-zero, 13px, 10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(to-zero, -13px, 10px) should be used-value-equivalent to -10px assert_equals: round(to-zero, -13px, 10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(to-zero, -18px, 10px) should be used-value-equivalent to -10px assert_equals: round(to-zero, -18px, 10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(23px, -10px) should be used-value-equivalent to 20px assert_equals: round(23px, -10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(18px, -10px) should be used-value-equivalent to 20px assert_equals: round(18px, -10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(15px, -10px) should be used-value-equivalent to 20px assert_equals: round(15px, -10px) and 20px serialize to the same thing in used values. expected "20px" but got "0px"
-FAIL round(13px, -10px) should be used-value-equivalent to 10px assert_equals: round(13px, -10px) and 10px serialize to the same thing in used values. expected "10px" but got "0px"
-FAIL round(-13px, -10px) should be used-value-equivalent to -10px assert_equals: round(-13px, -10px) and -10px serialize to the same thing in used values. expected "-10px" but got "0px"
-FAIL round(-18px, -10px) should be used-value-equivalent to -20px assert_equals: round(-18px, -10px) and -20px serialize to the same thing in used values. expected "-20px" but got "0px"
</del><ins>+PASS round(23px, 10px) should be used-value-equivalent to 20px
+PASS round(18px, 10px) should be used-value-equivalent to 20px
+PASS round(15px, 10px) should be used-value-equivalent to 20px
+PASS round(13px, 10px) should be used-value-equivalent to 10px
+PASS round(-13px, 10px) should be used-value-equivalent to -10px
+PASS round(-18px, 10px) should be used-value-equivalent to -20px
+PASS round(nearest, 23px, 10px) should be used-value-equivalent to 20px
+PASS round(nearest, 18px, 10px) should be used-value-equivalent to 20px
+PASS round(nearest, 15px, 10px) should be used-value-equivalent to 20px
+PASS round(nearest, 13px, 10px) should be used-value-equivalent to 10px
+PASS round(nearest, -13px, 10px) should be used-value-equivalent to -10px
+PASS round(nearest, -18px, 10px) should be used-value-equivalent to -20px
+PASS round(down, 23px, 10px) should be used-value-equivalent to 20px
+PASS round(down, 18px, 10px) should be used-value-equivalent to 10px
+PASS round(down, 15px, 10px) should be used-value-equivalent to 10px
+PASS round(down, 13px, 10px) should be used-value-equivalent to 10px
+PASS round(down, -13px, 10px) should be used-value-equivalent to -20px
+PASS round(down, -18px, 10px) should be used-value-equivalent to -20px
+PASS round(up, 23px, 10px) should be used-value-equivalent to 30px
+PASS round(up, 18px, 10px) should be used-value-equivalent to 20px
+PASS round(up, 15px, 10px) should be used-value-equivalent to 20px
+PASS round(up, 13px, 10px) should be used-value-equivalent to 20px
+PASS round(up, -13px, 10px) should be used-value-equivalent to -10px
+PASS round(up, -18px, 10px) should be used-value-equivalent to -10px
+PASS round(to-zero, 23px, 10px) should be used-value-equivalent to 20px
+PASS round(to-zero, 18px, 10px) should be used-value-equivalent to 10px
+PASS round(to-zero, 15px, 10px) should be used-value-equivalent to 10px
+PASS round(to-zero, 13px, 10px) should be used-value-equivalent to 10px
+PASS round(to-zero, -13px, 10px) should be used-value-equivalent to -10px
+PASS round(to-zero, -18px, 10px) should be used-value-equivalent to -10px
+PASS round(23px, -10px) should be used-value-equivalent to 20px
+PASS round(18px, -10px) should be used-value-equivalent to 20px
+PASS round(15px, -10px) should be used-value-equivalent to 20px
+PASS round(13px, -10px) should be used-value-equivalent to 10px
+PASS round(-13px, -10px) should be used-value-equivalent to -10px
+PASS round(-18px, -10px) should be used-value-equivalent to -20px
</ins><span class="cx"> PASS round(5, 0) should be used-value-equivalent to calc(NaN)
</span><span class="cx"> PASS calc(-1 * round(5, 0)) should be used-value-equivalent to calc(NaN)
</span><span class="cx"> PASS round(infinity, infinity) should be used-value-equivalent to calc(NaN)
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremcomputedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt (0 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt                             (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed-expected.txt        2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -0,0 +1,91 @@
</span><ins>+
+PASS round(10,10) should be used-value-equivalent to 10
+PASS mod(1,1) should be used-value-equivalent to 0
+PASS rem(1,1) should be used-value-equivalent to 0
+PASS calc(round(100,10)) should be used-value-equivalent to 100
+PASS calc(round(up, 101,10)) should be used-value-equivalent to 110
+PASS calc(round(down, 106,10)) should be used-value-equivalent to 100
+PASS calc(round(to-zero,105, 10)) should be used-value-equivalent to 100
+PASS calc(round(to-zero,-105, 10)) should be used-value-equivalent to -100
+PASS calc(round(-100,10)) should be used-value-equivalent to -100
+PASS calc(round(up, -103,10)) should be used-value-equivalent to -100
+PASS mod(18,5) should be used-value-equivalent to 3
+PASS rem(18,5) should be used-value-equivalent to 3
+PASS mod(-140,-90) should be used-value-equivalent to -50
+PASS mod(-18,5) should be used-value-equivalent to 2
+PASS rem(-18,5) should be used-value-equivalent to -3
+PASS mod(140,-90) should be used-value-equivalent to -40
+PASS rem(140,-90) should be used-value-equivalent to 50
+PASS calc(round(round(100,10), 10)) should be used-value-equivalent to 100
+PASS calc(round(up, round(100,10) + 1,10)) should be used-value-equivalent to 110
+PASS calc(round(down, round(100,10) + 2 * 3,10)) should be used-value-equivalent to 100
+PASS calc(round(to-zero,round(100,10) * 2 - 95, 10)) should be used-value-equivalent to 100
+PASS calc(round(round(100,10)* -1,10)) should be used-value-equivalent to -100
+PASS calc(round(up, -103 + -103 / -103 - 1,10)) should be used-value-equivalent to -100
+PASS calc(mod(18,5) * 2 + mod(17,5)) should be used-value-equivalent to 8
+PASS calc(rem(mod(18,5),5)) should be used-value-equivalent to 3
+PASS calc(rem(mod(18,5),mod(17,5))) should be used-value-equivalent to 1
+PASS calc(mod(-140,-90)) should be used-value-equivalent to -50
+PASS calc(mod(rem(1,18)* -1,5)) should be used-value-equivalent to -1
+PASS round(10px,6px) should be used-value-equivalent to 12px
+PASS round(10cm,6cm) should be used-value-equivalent to 12cm
+PASS round(10mm,6mm) should be used-value-equivalent to 12mm
+PASS round(10Q, 6Q) should be used-value-equivalent to 12Q
+PASS round(10in,6in) should be used-value-equivalent to 12in
+PASS round(10pc,6pc) should be used-value-equivalent to 12pc
+PASS round(10pt,6pt) should be used-value-equivalent to 12pt
+PASS round(10em,6em) should be used-value-equivalent to 12em
+PASS round(10ex,6ex) should be used-value-equivalent to 12ex
+PASS round(10ch,6ch) should be used-value-equivalent to 12ch
+PASS round(10rem,6rem) should be used-value-equivalent to 12rem
+PASS round(10vh,6vh) should be used-value-equivalent to 12vh
+PASS round(10vw,6vw) should be used-value-equivalent to 12vw
+PASS round(10vmin,6vmin) should be used-value-equivalent to 12vmin
+PASS round(10vmax,6vmax) should be used-value-equivalent to 12vmax
+PASS round(10s,6s) should be used-value-equivalent to 12s
+PASS round(10ms,6ms) should be used-value-equivalent to 12ms
+PASS round(10deg,6deg) should be used-value-equivalent to 12deg
+PASS round(10grad,6grad) should be used-value-equivalent to 12grad
+PASS round(10rad,6rad) should be used-value-equivalent to 12rad
+PASS round(10turn,6turn) should be used-value-equivalent to 12turn
+PASS mod(10px,6px) should be used-value-equivalent to 4px
+PASS mod(10cm,6cm) should be used-value-equivalent to 4cm
+PASS mod(10mm,6mm) should be used-value-equivalent to 4mm
+PASS mod(10Q, 6Q) should be used-value-equivalent to 4Q
+PASS mod(10in,6in) should be used-value-equivalent to 4in
+PASS mod(10pc,6pc) should be used-value-equivalent to 4pc
+PASS mod(10em,6em) should be used-value-equivalent to 4em
+PASS mod(10ex,6ex) should be used-value-equivalent to 4ex
+PASS mod(10ch,6ch) should be used-value-equivalent to 4ch
+PASS mod(10rem,6rem) should be used-value-equivalent to 4rem
+PASS mod(10vh,6vh) should be used-value-equivalent to 4vh
+PASS mod(10vw,6vw) should be used-value-equivalent to 4vw
+PASS mod(10vmin,6vmin) should be used-value-equivalent to 4vmin
+PASS mod(10vmax,6vmax) should be used-value-equivalent to 4vmax
+PASS mod(10s,6s) should be used-value-equivalent to 4s
+PASS mod(10ms,6ms) should be used-value-equivalent to 4ms
+PASS mod(10deg,6deg) should be used-value-equivalent to 4deg
+PASS mod(10grad,6grad) should be used-value-equivalent to 4grad
+PASS mod(10rad,6rad) should be used-value-equivalent to 4rad
+PASS mod(10turn,6turn) should be used-value-equivalent to 4turn
+PASS rem(10px,6px) should be used-value-equivalent to 4px
+PASS rem(10cm,6cm) should be used-value-equivalent to 4cm
+PASS rem(10mm,6mm) should be used-value-equivalent to 4mm
+PASS rem(10Q, 6Q) should be used-value-equivalent to 4Q
+PASS rem(10in,6in) should be used-value-equivalent to 4in
+PASS rem(10pc,6pc) should be used-value-equivalent to 4pc
+PASS rem(10em,6em) should be used-value-equivalent to 4em
+PASS rem(10ex,6ex) should be used-value-equivalent to 4ex
+PASS rem(10ch,6ch) should be used-value-equivalent to 4ch
+PASS rem(10rem,6rem) should be used-value-equivalent to 4rem
+PASS rem(10vh,6vh) should be used-value-equivalent to 4vh
+PASS rem(10vw,6vw) should be used-value-equivalent to 4vw
+PASS rem(10vmin,6vmin) should be used-value-equivalent to 4vmin
+PASS rem(10vmax,6vmax) should be used-value-equivalent to 4vmax
+PASS rem(10s,6s) should be used-value-equivalent to 4s
+PASS rem(10ms,6ms) should be used-value-equivalent to 4ms
+PASS rem(10deg,6deg) should be used-value-equivalent to 4deg
+PASS rem(10grad,6grad) should be used-value-equivalent to 4grad
+PASS rem(10rad,6rad) should be used-value-equivalent to 4rad
+PASS rem(10turn,6turn) should be used-value-equivalent to 4turn
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremcomputedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed.html (0 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed.html                             (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed.html        2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -0,0 +1,111 @@
</span><ins>+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#numbers">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-type-checking">
+<link rel="author" title="Apple Inc">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/numeric-testcommon.js"></script>
+<div id="target"></div>
+<script>
+// Simple tests
+test_math_used('round(10,10)', '10', {type:'number'});
+test_math_used('mod(1,1)', '0', {type:'number'});
+test_math_used('rem(1,1)', '0', {type:'number'});
+
+//Test basic round
+test_math_used('calc(round(100,10))', '100', {type:'number'});
+test_math_used('calc(round(up, 101,10))', '110', {type:'number'});
+test_math_used('calc(round(down, 106,10))', '100', {type:'number'});
+test_math_used('calc(round(to-zero,105, 10))', '100', {type:'number'});
+test_math_used('calc(round(to-zero,-105, 10))', '-100', {type:'number'});
+test_math_used('calc(round(-100,10))', '-100', {type:'number'});
+test_math_used('calc(round(up, -103,10))', '-100', {type:'number'});
+
+//Test basic mod/rem
+test_math_used('mod(18,5)', '3', {type:'number'});
+test_math_used('rem(18,5)', '3', {type:'number'});
+test_math_used('mod(-140,-90)', '-50', {type:'number'});
+test_math_used('mod(-18,5)', '2', {type:'number'});
+test_math_used('rem(-18,5)', '-3', {type:'number'});
+test_math_used('mod(140,-90)', '-40', {type:'number'});
+test_math_used('rem(140,-90)', '50', {type:'number'});
+
+//Test basic calculations
+test_math_used('calc(round(round(100,10), 10))', '100', {type:'number'});
+test_math_used('calc(round(up, round(100,10) + 1,10))', '110', {type:'number'});
+test_math_used('calc(round(down, round(100,10) + 2 * 3,10))', '100', {type:'number'});
+test_math_used('calc(round(to-zero,round(100,10) * 2 - 95, 10))', '100', {type:'number'});
+test_math_used('calc(round(round(100,10)* -1,10))', '-100', {type:'number'});
+test_math_used('calc(round(up, -103 + -103 / -103 - 1,10))', '-100', {type:'number'});
+test_math_used('calc(mod(18,5) * 2 + mod(17,5))', '8', {type:'number'});
+test_math_used('calc(rem(mod(18,5),5))', '3', {type:'number'});
+test_math_used('calc(rem(mod(18,5),mod(17,5)))', '1', {type:'number'});
+test_math_used('calc(mod(-140,-90))', '-50', {type:'number'});
+test_math_used('calc(mod(rem(1,18)* -1,5))', '-1', {type:'number'});
+
+// Type check
+test_math_used('round(10px,6px)', '12px');
+test_math_used('round(10cm,6cm)', '12cm');
+test_math_used('round(10mm,6mm)', '12mm');
+test_math_used('round(10Q, 6Q)', '12Q');
+test_math_used('round(10in,6in)', '12in');
+test_math_used('round(10pc,6pc)', '12pc');
+test_math_used('round(10pt,6pt)', '12pt');
+test_math_used('round(10em,6em)', '12em');
+test_math_used('round(10ex,6ex)', '12ex');
+test_math_used('round(10ch,6ch)', '12ch');
+test_math_used('round(10rem,6rem)', '12rem');
+test_math_used('round(10vh,6vh)', '12vh');
+test_math_used('round(10vw,6vw)', '12vw');
+test_math_used('round(10vmin,6vmin)', '12vmin');
+test_math_used('round(10vmax,6vmax)', '12vmax');
+test_math_used('round(10s,6s)', '12s');
+test_math_used('round(10ms,6ms)', '12ms');
+test_math_used('round(10deg,6deg)', '12deg', {type:'angle', approx:0.1});
+test_math_used('round(10grad,6grad)', '12grad', {type:'angle', approx:0.1});
+test_math_used('round(10rad,6rad)', '12rad',{type:'angle', approx:0.1});
+test_math_used('round(10turn,6turn)', '12turn',{type:'angle', approx:0.1});
+
+test_math_used('mod(10px,6px)', '4px');
+test_math_used('mod(10cm,6cm)', '4cm');
+test_math_used('mod(10mm,6mm)', '4mm');
+test_math_used('mod(10Q, 6Q)', '4Q');
+test_math_used('mod(10in,6in)', '4in');
+test_math_used('mod(10pc,6pc)', '4pc');
+test_math_used('mod(10em,6em)', '4em');
+test_math_used('mod(10ex,6ex)', '4ex');
+test_math_used('mod(10ch,6ch)', '4ch');
+test_math_used('mod(10rem,6rem)', '4rem');
+test_math_used('mod(10vh,6vh)', '4vh');
+test_math_used('mod(10vw,6vw)', '4vw');
+test_math_used('mod(10vmin,6vmin)', '4vmin');
+test_math_used('mod(10vmax,6vmax)', '4vmax');
+test_math_used('mod(10s,6s)', '4s');
+test_math_used('mod(10ms,6ms)', '4ms');
+test_math_used('mod(10deg,6deg)', '4deg', {type:'angle', approx:0.1});
+test_math_used('mod(10grad,6grad)', '4grad', {type:'angle', approx:0.1});
+test_math_used('mod(10rad,6rad)', '4rad',{type:'angle', approx:0.1});
+test_math_used('mod(10turn,6turn)', '4turn',{type:'angle', approx:0.1});
+
+test_math_used('rem(10px,6px)', '4px');
+test_math_used('rem(10cm,6cm)', '4cm');
+test_math_used('rem(10mm,6mm)', '4mm');
+test_math_used('rem(10Q, 6Q)', '4Q');
+test_math_used('rem(10in,6in)', '4in');
+test_math_used('rem(10pc,6pc)', '4pc');
+test_math_used('rem(10em,6em)', '4em');
+test_math_used('rem(10ex,6ex)', '4ex');
+test_math_used('rem(10ch,6ch)', '4ch');
+test_math_used('rem(10rem,6rem)', '4rem');
+test_math_used('rem(10vh,6vh)', '4vh');
+test_math_used('rem(10vw,6vw)', '4vw');
+test_math_used('rem(10vmin,6vmin)', '4vmin');
+test_math_used('rem(10vmax,6vmax)', '4vmax');
+test_math_used('rem(10s,6s)', '4s');
+test_math_used('rem(10ms,6ms)', '4ms');
+test_math_used('rem(10deg,6deg)', '4deg', {type:'angle', approx:0.1});
+test_math_used('rem(10grad,6grad)', '4grad', {type:'angle', approx:0.1});
+test_math_used('rem(10rad,6rad)', '4rad',{type:'angle', approx:0.1});
+test_math_used('rem(10turn,6turn)', '4turn',{type:'angle', approx:0.1});
+</script>
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodreminvalidexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid-expected.txt (0 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid-expected.txt                              (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid-expected.txt 2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -0,0 +1,79 @@
</span><ins>+
+PASS e.style['opacity'] = "round()" should not set the property value
+PASS e.style['opacity'] = "round( )" should not set the property value
+PASS e.style['opacity'] = "round(,)" should not set the property value
+PASS e.style['opacity'] = "round(1, )" should not set the property value
+PASS e.style['opacity'] = "round(, 1)" should not set the property value
+PASS e.style['opacity'] = "round(1 + )" should not set the property value
+PASS e.style['opacity'] = "round(1 - )" should not set the property value
+PASS e.style['opacity'] = "round(1 * )" should not set the property value
+PASS e.style['opacity'] = "round(1 / )" should not set the property value
+PASS e.style['opacity'] = "round(1 2)" should not set the property value
+PASS e.style['opacity'] = "round(nearest, 1 2)" should not set the property value
+PASS e.style['opacity'] = "round(1, nearest, 12)" should not set the property value
+PASS e.style['opacity'] = "round(1, nearest)" should not set the property value
+PASS e.style['opacity'] = "round(nearest, 1, nearest)" should not set the property value
+PASS e.style['opacity'] = "round(nearest, 1)" should not set the property value
+PASS e.style['opacity'] = "round(1, , 2)" should not set the property value
+PASS e.style['opacity'] = "mod()" should not set the property value
+PASS e.style['opacity'] = "mod( )" should not set the property value
+PASS e.style['opacity'] = "mod(,)" should not set the property value
+PASS e.style['opacity'] = "mod(1, )" should not set the property value
+PASS e.style['opacity'] = "mod(, 1)" should not set the property value
+PASS e.style['opacity'] = "mod(1 + )" should not set the property value
+PASS e.style['opacity'] = "mod(1 - )" should not set the property value
+PASS e.style['opacity'] = "mod(1 * )" should not set the property value
+PASS e.style['opacity'] = "mod(1 / )" should not set the property value
+PASS e.style['opacity'] = "mod(1 2)" should not set the property value
+PASS e.style['opacity'] = "mod(1, , 2)" should not set the property value
+PASS e.style['opacity'] = "rem()" should not set the property value
+PASS e.style['opacity'] = "rem( )" should not set the property value
+PASS e.style['opacity'] = "rem(,)" should not set the property value
+PASS e.style['opacity'] = "rem(1, )" should not set the property value
+PASS e.style['opacity'] = "rem(, 1)" should not set the property value
+PASS e.style['opacity'] = "rem(1 + )" should not set the property value
+PASS e.style['opacity'] = "rem(1 - )" should not set the property value
+PASS e.style['opacity'] = "rem(1 * )" should not set the property value
+PASS e.style['opacity'] = "rem(1 / )" should not set the property value
+PASS e.style['opacity'] = "rem(1 2)" should not set the property value
+PASS e.style['opacity'] = "rem(1, , 2)" should not set the property value
+PASS e.style['opacity'] = "round(0px)" should not set the property value
+PASS e.style['opacity'] = "round(0s)" should not set the property value
+PASS e.style['opacity'] = "round(0deg)" should not set the property value
+PASS e.style['opacity'] = "round(0Hz)" should not set the property value
+PASS e.style['opacity'] = "round(0dpi)" should not set the property value
+PASS e.style['opacity'] = "round(0fr)" should not set the property value
+PASS e.style['opacity'] = "round(1, 1%)" should not set the property value
+PASS e.style['opacity'] = "round(1, 0px)" should not set the property value
+PASS e.style['opacity'] = "round(1, 0s)" should not set the property value
+PASS e.style['opacity'] = "round(1, 0deg)" should not set the property value
+PASS e.style['opacity'] = "round(1, 0Hz)" should not set the property value
+PASS e.style['opacity'] = "round(1, 0dpi)" should not set the property value
+PASS e.style['opacity'] = "round(1, 0fr)" should not set the property value
+PASS e.style['opacity'] = "mod(0px)" should not set the property value
+PASS e.style['opacity'] = "mod(0s)" should not set the property value
+PASS e.style['opacity'] = "mod(0deg)" should not set the property value
+PASS e.style['opacity'] = "mod(0Hz)" should not set the property value
+PASS e.style['opacity'] = "mod(0dpi)" should not set the property value
+PASS e.style['opacity'] = "mod(0fr)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 1%)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 0px)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 0s)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 0deg)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 0Hz)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 0dpi)" should not set the property value
+PASS e.style['opacity'] = "mod(1, 0fr)" should not set the property value
+PASS e.style['opacity'] = "rem(0px)" should not set the property value
+PASS e.style['opacity'] = "rem(0s)" should not set the property value
+PASS e.style['opacity'] = "rem(0deg)" should not set the property value
+PASS e.style['opacity'] = "rem(0Hz)" should not set the property value
+PASS e.style['opacity'] = "rem(0dpi)" should not set the property value
+PASS e.style['opacity'] = "rem(0fr)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 1%)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 0px)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 0s)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 0deg)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 0Hz)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 0dpi)" should not set the property value
+PASS e.style['opacity'] = "rem(1, 0fr)" should not set the property value
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodreminvalidhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid.html (0 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid.html                              (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid.html 2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -0,0 +1,94 @@
</span><ins>+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#numbers">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-type-checking">
+<link rel="author" title="Apple Inc">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+function test_invalid_number(value) {
+  test_invalid_value('opacity', value);
+}
+
+// Syntax checking
+test_invalid_number('round()');
+test_invalid_number('round( )');
+test_invalid_number('round(,)');
+test_invalid_number('round(1, )');
+test_invalid_number('round(, 1)');
+test_invalid_number('round(1 + )');
+test_invalid_number('round(1 - )');
+test_invalid_number('round(1 * )');
+test_invalid_number('round(1 / )');
+test_invalid_number('round(1 2)');
+test_invalid_number('round(nearest, 1 2)');
+test_invalid_number('round(1, nearest, 12)');
+test_invalid_number('round(1, nearest)');
+test_invalid_number('round(nearest, 1, nearest)');
+test_invalid_number('round(nearest, 1)');
+test_invalid_number('round(1, , 2)');
+test_invalid_number('mod()');
+test_invalid_number('mod( )');
+test_invalid_number('mod(,)');
+test_invalid_number('mod(1, )');
+test_invalid_number('mod(, 1)');
+test_invalid_number('mod(1 + )');
+test_invalid_number('mod(1 - )');
+test_invalid_number('mod(1 * )');
+test_invalid_number('mod(1 / )');
+test_invalid_number('mod(1 2)');
+test_invalid_number('mod(1, , 2)');
+test_invalid_number('rem()');
+test_invalid_number('rem( )');
+test_invalid_number('rem(,)');
+test_invalid_number('rem(1, )');
+test_invalid_number('rem(, 1)');
+test_invalid_number('rem(1 + )');
+test_invalid_number('rem(1 - )');
+test_invalid_number('rem(1 * )');
+test_invalid_number('rem(1 / )');
+test_invalid_number('rem(1 2)');
+test_invalid_number('rem(1, , 2)');
+
+// Type checking
+test_invalid_number('round(0px)');
+test_invalid_number('round(0s)');
+test_invalid_number('round(0deg)');
+test_invalid_number('round(0Hz)');
+test_invalid_number('round(0dpi)');
+test_invalid_number('round(0fr)');
+test_invalid_number('round(1, 1%)');
+test_invalid_number('round(1, 0px)');
+test_invalid_number('round(1, 0s)');
+test_invalid_number('round(1, 0deg)');
+test_invalid_number('round(1, 0Hz)');
+test_invalid_number('round(1, 0dpi)');
+test_invalid_number('round(1, 0fr)');
+test_invalid_number('mod(0px)');
+test_invalid_number('mod(0s)');
+test_invalid_number('mod(0deg)');
+test_invalid_number('mod(0Hz)');
+test_invalid_number('mod(0dpi)');
+test_invalid_number('mod(0fr)');
+test_invalid_number('mod(1, 1%)');
+test_invalid_number('mod(1, 0px)');
+test_invalid_number('mod(1, 0s)');
+test_invalid_number('mod(1, 0deg)');
+test_invalid_number('mod(1, 0Hz)');
+test_invalid_number('mod(1, 0dpi)');
+test_invalid_number('mod(1, 0fr)');
+test_invalid_number('rem(0px)');
+test_invalid_number('rem(0s)');
+test_invalid_number('rem(0deg)');
+test_invalid_number('rem(0Hz)');
+test_invalid_number('rem(0dpi)');
+test_invalid_number('rem(0fr)');
+test_invalid_number('rem(1, 1%)');
+test_invalid_number('rem(1, 0px)');
+test_invalid_number('rem(1, 0s)');
+test_invalid_number('rem(1, 0deg)');
+test_invalid_number('rem(1, 0Hz)');
+test_invalid_number('rem(1, 0dpi)');
+test_invalid_number('rem(1, 0fr)');
+</script>
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremserializeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize-expected.txt (0 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize-expected.txt                            (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize-expected.txt       2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -0,0 +1,14 @@
</span><ins>+
+FAIL 'round(1.1,1)' as a specified value should serialize as 'calc(1)'. assert_equals: 'round(1.1,1)' and 'calc(1)' should serialize the same in specified values. expected "calc(1)" but got "round(nearest, 1.1, 1)"
+FAIL 'scale(round(1.1,1))' as a specified value should serialize as 'scale(calc(1))'. assert_equals: 'scale(round(1.1,1))' and 'scale(calc(1))' should serialize the same in specified values. expected "scale(calc(1))" but got "scale(round(nearest, 1.1, 1))"
+PASS 'round(1.1,1)' as a computed value should serialize as '1'.
+PASS 'scale(round(1.1,1))' as a computed value should serialize as 'matrix(1, 0, 0, 1, 0, 0)'.
+FAIL 'mod(1,1)' as a specified value should serialize as 'calc(0)'. assert_equals: 'mod(1,1)' and 'calc(0)' should serialize the same in specified values. expected "calc(0)" but got "mod(1, 1)"
+FAIL 'scale(mod(1,1))' as a specified value should serialize as 'scale(calc(0))'. assert_equals: 'scale(mod(1,1))' and 'scale(calc(0))' should serialize the same in specified values. expected "scale(calc(0))" but got "scale(mod(1, 1))"
+PASS 'mod(1,1)' as a computed value should serialize as '0'.
+PASS 'scale(mod(1,1))' as a computed value should serialize as 'matrix(0, 0, 0, 0, 0, 0)'.
+FAIL 'rem(1,1)' as a specified value should serialize as 'calc(0)'. assert_equals: 'rem(1,1)' and 'calc(0)' should serialize the same in specified values. expected "calc(0)" but got "rem(1, 1)"
+FAIL 'scale(rem(1,1))' as a specified value should serialize as 'scale(calc(0))'. assert_equals: 'scale(rem(1,1))' and 'scale(calc(0))' should serialize the same in specified values. expected "scale(calc(0))" but got "scale(rem(1, 1))"
+PASS 'rem(1,1)' as a computed value should serialize as '0'.
+PASS 'scale(rem(1,1))' as a computed value should serialize as 'matrix(0, 0, 0, 0, 0, 0)'.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscsscssvaluesroundmodremserializehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize.html (0 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize.html                            (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize.html       2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#numbers">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<link rel="author" title="Apple Inc">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/serialize-testcommon.js"></script>
+<div id=target></div>
+<script>
+function test_serialization(t,s,c) {
+    test_specified_serialization('opacity', t, s);
+    test_specified_serialization('transform', `scale(${t})`, `scale(calc(${c}))`);
+    test_computed_serialization('opacity', t, c);
+    test_computed_serialization('transform', `scale(${t})`, `matrix(${c}, 0, 0, ${c}, 0, 0)`);
+}
+
+test_serialization(
+    'round(1.1,1)',
+    'calc(1)',
+    '1');
+test_serialization(
+    'mod(1,1)',
+    'calc(0)',
+    '0');
+test_serialization(
+    'rem(1,1)',
+    'calc(0)',
+    '0');
+</script>
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/ChangeLog      2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -1,3 +1,53 @@
</span><ins>+2021-09-24  Nikos Mouchtaris  <nmouchtaris@apple.com>
+
+        Implement round,mod,rem functions for calc
+        https://bugs.webkit.org/show_bug.cgi?id=230073
+
+        Reviewed by Simon Fraser.
+
+        Implemented round, mod and rem calc functions. Involved adding css keywords for the functions
+        and the keywords associated with the round function, as well as implementing the parsing and 
+        computation of the expression. The spec for these functions: 
+        https://drafts.csswg.org/css-values-4/#round-func.
+
+        Tests: imported/w3c/web-platform-tests/css/css-values/round-mod-rem-computed.html
+               imported/w3c/web-platform-tests/css/css-values/round-mod-rem-invalid.html
+               imported/w3c/web-platform-tests/css/css-values/round-mod-rem-serialize.html
+
+        * css/CSSValueKeywords.in:
+        * css/calc/CSSCalcExpressionNodeParser.cpp:
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcFunction):
+        (WebCore::checkRoundKeyword):
+        Addition of functionID to parsing so the round keywords are only parsed within round 
+        function.
+        (WebCore::CSSCalcExpressionNodeParser::parseValue):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcValue):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcProduct):
+        (WebCore::CSSCalcExpressionNodeParser::parseCalcSum):
+        * css/calc/CSSCalcExpressionNodeParser.h:
+        * css/calc/CSSCalcOperationNode.cpp:
+        (WebCore::determineCategory):
+        (WebCore::functionFromOperator):
+        (WebCore::CSSCalcOperationNode::createStep):
+        (WebCore::validateRoundChildren):
+        (WebCore::CSSCalcOperationNode::createRound):
+        (WebCore::CSSCalcOperationNode::createRoundConstant):
+        (WebCore::CSSCalcOperationNode::combineChildren):
+        (WebCore::CSSCalcOperationNode::simplifyNode):
+        (WebCore::functionPrefixForOperator):
+        (WebCore::getNearestMultiples):
+        (WebCore::CSSCalcOperationNode::evaluateOperator):
+        * css/calc/CSSCalcOperationNode.h:
+        * css/calc/CSSCalcValue.cpp:
+        (WebCore::createCSS):
+        (WebCore::CSSCalcValue::isCalcFunction):
+        * platform/calc/CalcExpressionOperation.cpp:
+        (WebCore::getNearestMultiples):
+        (WebCore::CalcExpressionOperation::evaluate const):
+        * platform/calc/CalcOperator.cpp:
+        (WebCore::operator<<):
+        * platform/calc/CalcOperator.h:
+
</ins><span class="cx"> 2021-09-24  Simon Fraser  <simon.fraser@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Add a ScrollAnimations log channel
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSValueKeywordsin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSValueKeywords.in (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/CSSValueKeywords.in     2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/css/CSSValueKeywords.in        2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -1359,6 +1359,10 @@
</span><span class="cx"> atan2
</span><span class="cx"> abs
</span><span class="cx"> sign
</span><ins>+mod
+rem
+to-zero
+nearest
</ins><span class="cx"> 
</span><span class="cx"> from-image
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcExpressionNodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp    2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp       2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -129,10 +129,18 @@
</span><span class="cx">         minArgumentCount = 3;
</span><span class="cx">         maxArgumentCount = 3;
</span><span class="cx">         break;
</span><del>-
</del><span class="cx">     case CSSValueLog:
</span><span class="cx">         maxArgumentCount = 2;
</span><span class="cx">         break;
</span><ins>+    case CSSValueRound:
+        minArgumentCount = 2;
+        maxArgumentCount = 3;
+        break;
+    case CSSValueMod:
+    case CSSValueRem:
+        minArgumentCount = 2;
+        maxArgumentCount = 2;
+        break;
</ins><span class="cx">     case CSSValueExp:
</span><span class="cx">     case CSSValueSin:
</span><span class="cx">     case CSSValueCos:
</span><span class="lines">@@ -163,7 +171,7 @@
</span><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         RefPtr<CSSCalcExpressionNode> node;
</span><del>-        if (!parseCalcSum(tokens, depth, node))
</del><ins>+        if (!parseCalcSum(tokens, functionID, depth, node))
</ins><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         ++argumentCount;
</span><span class="lines">@@ -196,6 +204,15 @@
</span><span class="cx">     case CSSValueTan:
</span><span class="cx">         result = CSSCalcOperationNode::createTrig(CalcOperator::Tan, WTFMove(nodes));
</span><span class="cx">         break;
</span><ins>+    case CSSValueRound:
+        result = CSSCalcOperationNode::createRound(WTFMove(nodes));
+        break;
+    case CSSValueMod:
+        result = CSSCalcOperationNode::createStep(CalcOperator::Mod, WTFMove(nodes));
+        break;
+    case CSSValueRem:
+        result = CSSCalcOperationNode::createStep(CalcOperator::Rem, WTFMove(nodes));
+        break;
</ins><span class="cx">     case CSSValueWebkitCalc:
</span><span class="cx">     case CSSValueCalc:
</span><span class="cx">         result = CSSCalcOperationNode::createSum(WTFMove(nodes));
</span><span class="lines">@@ -232,8 +249,30 @@
</span><span class="cx">     return !!result;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
</del><ins>+static bool checkRoundKeyword(CSSValueID functionID, RefPtr<CSSCalcExpressionNode>& result, CSSValueID constantID)
</ins><span class="cx"> {
</span><ins>+    if (functionID != CSSValueRound)
+        return false;
+    switch (constantID) {
+    case CSSValueNearest:
+        result = CSSCalcOperationNode::createRoundConstant(CalcOperator::Nearest);
+        return true;
+    case CSSValueToZero:
+        result = CSSCalcOperationNode::createRoundConstant(CalcOperator::ToZero);
+        return true;
+    case CSSValueUp:
+        result = CSSCalcOperationNode::createRoundConstant(CalcOperator::Up);
+        return true;
+    case CSSValueDown:
+        result = CSSCalcOperationNode::createRoundConstant(CalcOperator::Down);
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, CSSValueID functionID, RefPtr<CSSCalcExpressionNode>& result)
+{
</ins><span class="cx">     auto makeCSSCalcPrimitiveValueNode = [&] (CSSUnitType type, double value) -> bool {
</span><span class="cx">         if (calcUnitCategory(type) == CalculationCategory::Other)
</span><span class="cx">             return false;
</span><span class="lines">@@ -246,6 +285,8 @@
</span><span class="cx"> 
</span><span class="cx">     switch (token.type()) {
</span><span class="cx">     case IdentToken: {
</span><ins>+        if (checkRoundKeyword(functionID, result, token.id()))
+            return true;
</ins><span class="cx">         auto value = m_symbolTable.get(token.id());
</span><span class="cx">         value = value ? value : getConstantTable().get(token.id());
</span><span class="cx">         if (!value)
</span><span class="lines">@@ -266,7 +307,7 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool CSSCalcExpressionNodeParser::parseCalcValue(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
</del><ins>+bool CSSCalcExpressionNodeParser::parseCalcValue(CSSParserTokenRange& tokens, CSSValueID functionID, int depth, RefPtr<CSSCalcExpressionNode>& result)
</ins><span class="cx"> {
</span><span class="cx">     if (checkDepthAndIndex(depth, tokens) != OK)
</span><span class="cx">         return false;
</span><span class="lines">@@ -289,16 +330,16 @@
</span><span class="cx">         return parseCalcFunction(innerRange, functionId, depth + 1, result);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return parseValue(tokens, result);
</del><ins>+    return parseValue(tokens, functionID, result);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool CSSCalcExpressionNodeParser::parseCalcProduct(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
</del><ins>+bool CSSCalcExpressionNodeParser::parseCalcProduct(CSSParserTokenRange& tokens, CSSValueID functionID, int depth, RefPtr<CSSCalcExpressionNode>& result)
</ins><span class="cx"> {
</span><span class="cx">     if (checkDepthAndIndex(depth, tokens) != OK)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     RefPtr<CSSCalcExpressionNode> firstValue;
</span><del>-    if (!parseCalcValue(tokens, depth, firstValue))
</del><ins>+    if (!parseCalcValue(tokens, functionID, depth, firstValue))
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     Vector<Ref<CSSCalcExpressionNode>> nodes;
</span><span class="lines">@@ -310,7 +351,7 @@
</span><span class="cx">         tokens.consumeIncludingWhitespace();
</span><span class="cx">         
</span><span class="cx">         RefPtr<CSSCalcExpressionNode> nextValue;
</span><del>-        if (!parseCalcValue(tokens, depth, nextValue) || !nextValue)
</del><ins>+        if (!parseCalcValue(tokens, functionID, depth, nextValue) || !nextValue)
</ins><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         if (operatorCharacter == static_cast<char>(CalcOperator::Divide))
</span><span class="lines">@@ -331,13 +372,13 @@
</span><span class="cx">     return !!result;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool CSSCalcExpressionNodeParser::parseCalcSum(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
</del><ins>+bool CSSCalcExpressionNodeParser::parseCalcSum(CSSParserTokenRange& tokens, CSSValueID functionID, int depth, RefPtr<CSSCalcExpressionNode>& result)
</ins><span class="cx"> {
</span><span class="cx">     if (checkDepthAndIndex(depth, tokens) != OK)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     RefPtr<CSSCalcExpressionNode> firstValue;
</span><del>-    if (!parseCalcProduct(tokens, depth, firstValue))
</del><ins>+    if (!parseCalcProduct(tokens, functionID, depth, firstValue))
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     Vector<Ref<CSSCalcExpressionNode>> nodes;
</span><span class="lines">@@ -357,7 +398,7 @@
</span><span class="cx">         tokens.consumeIncludingWhitespace();
</span><span class="cx"> 
</span><span class="cx">         RefPtr<CSSCalcExpressionNode> nextValue;
</span><del>-        if (!parseCalcProduct(tokens, depth, nextValue) || !nextValue)
</del><ins>+        if (!parseCalcProduct(tokens, functionID, depth, nextValue) || !nextValue)
</ins><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         if (operatorCharacter == static_cast<char>(CalcOperator::Subtract))
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcExpressionNodeParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h      2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h 2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -49,12 +49,12 @@
</span><span class="cx"> private:
</span><span class="cx">     char operatorValue(const CSSParserToken&);
</span><span class="cx"> 
</span><del>-    bool parseValue(CSSParserTokenRange&, RefPtr<CSSCalcExpressionNode>&);
-    bool parseValueTerm(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
</del><ins>+    bool parseValue(CSSParserTokenRange&, CSSValueID, RefPtr<CSSCalcExpressionNode>&);
+    bool parseValueTerm(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
</ins><span class="cx">     bool parseCalcFunction(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
</span><del>-    bool parseCalcSum(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
-    bool parseCalcProduct(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
-    bool parseCalcValue(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
</del><ins>+    bool parseCalcSum(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
+    bool parseCalcProduct(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
+    bool parseCalcValue(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
</ins><span class="cx"> 
</span><span class="cx">     CalculationCategory m_destinationCategory;
</span><span class="cx">     const CSSCalcSymbolTable& m_symbolTable;
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcOperationNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp   2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/css/calc/CSSCalcOperationNode.cpp      2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -87,6 +87,13 @@
</span><span class="cx">     case CalcOperator::Atan2:
</span><span class="cx">     case CalcOperator::Abs:
</span><span class="cx">     case CalcOperator::Sign:
</span><ins>+    case CalcOperator::Mod:
+    case CalcOperator::Rem:
+    case CalcOperator::Round:
+    case CalcOperator::Up:
+    case CalcOperator::Down:
+    case CalcOperator::Nearest:
+    case CalcOperator::ToZero:
</ins><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return CalculationCategory::Other;
</span><span class="cx">     }
</span><span class="lines">@@ -166,6 +173,13 @@
</span><span class="cx">         case CalcOperator::Acos:
</span><span class="cx">         case CalcOperator::Atan:
</span><span class="cx">         case CalcOperator::Atan2:
</span><ins>+        case CalcOperator::Mod:
+        case CalcOperator::Rem:
+        case CalcOperator::Round:
+        case CalcOperator::Up:
+        case CalcOperator::Down:
+        case CalcOperator::Nearest:
+        case CalcOperator::ToZero:
</ins><span class="cx">             // The type of a min(), max(), or clamp() expression is the result of adding the types of its comma-separated calculations
</span><span class="cx">             return CalculationCategory::Other;
</span><span class="cx">         }
</span><span class="lines">@@ -299,6 +313,20 @@
</span><span class="cx">         return CSSValueAbs;
</span><span class="cx">     case CalcOperator::Sign:
</span><span class="cx">         return CSSValueSign;
</span><ins>+    case CalcOperator::Mod:
+        return CSSValueMod;
+    case CalcOperator::Rem:
+        return CSSValueRem;
+    case CalcOperator::Round:
+        return CSSValueRound;
+    case CalcOperator::Up:
+        return CSSValueUp;
+    case CalcOperator::Down:
+        return CSSValueDown;
+    case CalcOperator::Nearest:
+        return CSSValueNearest;
+    case CalcOperator::ToZero:
+        return CSSValueToZero;
</ins><span class="cx">     }
</span><span class="cx">     return CSSValueCalc;
</span><span class="cx"> }
</span><span class="lines">@@ -476,6 +504,68 @@
</span><span class="cx">     return adoptRef(new CSSCalcOperationNode(newCategory, op, WTFMove(values)));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createStep(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values)
+{
+    if (values.size() != 2)
+        return nullptr;
+
+    if (values[0]->category() != values[1]->category()) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create stepped value node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+    
+    if (!values[1]->doubleValue(values[1]->primitiveType())) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create stepped value node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+
+    return adoptRef(new CSSCalcOperationNode(values[0]->category(), op, WTFMove(values)));
+}
+
+static bool validateRoundChildren(Vector<Ref<CSSCalcExpressionNode>>& values)
+{
+    // for 3 children 1st node must be round constant
+    if (values.size() == 3) {
+        if (!is<CSSCalcOperationNode>(values[0]) || !(downcast<CSSCalcOperationNode>(values[0].get()).isRoundOperation()))
+            return false;
+    }
+    // for 2 children should not have round constant anywhere but first node of 3
+    for (size_t i = values.size() == 2 ? 0 : 1; i < values.size(); i++) {
+        if (is<CSSCalcOperationNode>(values[i])) {
+            if (downcast<CSSCalcOperationNode>(values[i].get()).isRoundConstant())
+                return false;
+        }
+    }
+    // check that two categories of numerical values are the same
+    return values.rbegin()[1]->category() == values.rbegin()[0]->category();
+    
+}
+
+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createRound(Vector<Ref<CSSCalcExpressionNode>>&& values)
+{
+    if (values.size() != 2 && values.size() != 3)
+        return nullptr;
+    
+    if (!validateRoundChildren(values)) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create round node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+    
+    CalcOperator roundType = values.size() == 2 ?  CalcOperator::Nearest : downcast<CSSCalcOperationNode>(values[0].get()).calcOperator();
+    if (values.size() == 3)
+        values.remove(0);
+    if (!values[1]->doubleValue(values[1]->primitiveType())) {
+        LOG_WITH_STREAM(Calc, stream << "Failed to create round node because unable to determine category from " << prettyPrintNodes(values));
+        return nullptr;
+    }
+    return adoptRef(new CSSCalcOperationNode(values.rbegin()[0]->category(), roundType, WTFMove(values)));
+}
+
+RefPtr<CSSCalcOperationNode> CSSCalcOperationNode::createRoundConstant(CalcOperator op)
+{
+    return adoptRef(new CSSCalcOperationNode(CalculationCategory::Number, op, { }));
+}
+
</ins><span class="cx"> void CSSCalcOperationNode::hoistChildrenWithOperator(CalcOperator op)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(op == CalcOperator::Add || op == CalcOperator::Multiply);
</span><span class="lines">@@ -710,6 +800,20 @@
</span><span class="cx">         m_children.clear();
</span><span class="cx">         m_children.append(WTFMove(newChild));
</span><span class="cx">     }
</span><ins>+    if (isSteppedNode()) {
+        auto combinedUnitType = m_children[0]->primitiveType();
+        double resolvedValue = doubleValue(combinedUnitType);
+        auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, combinedUnitType));
+        m_children.clear();
+        m_children.append(WTFMove(newChild));
+    }
+    if (isRoundOperation()) {
+        auto combinedUnitType = m_children[0]->primitiveType();
+        double resolvedValue = doubleValue(combinedUnitType);
+        auto newChild = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(resolvedValue, combinedUnitType));
+        m_children.clear();
+        m_children.append(WTFMove(newChild));
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // https://drafts.csswg.org/css-values-4/#simplify-a-calculation-tree
</span><span class="lines">@@ -798,6 +902,11 @@
</span><span class="cx">         if (calcOperationNode.isSignNode() && depth)
</span><span class="cx">             calcOperationNode.combineChildren();
</span><span class="cx"> 
</span><ins>+        if (calcOperationNode.isSteppedNode() && depth)
+            calcOperationNode.combineChildren();
+        
+        if (calcOperationNode.isRoundOperation() && depth)
+            calcOperationNode.combineChildren();
</ins><span class="cx">         // If only one child remains, return the child (except at the root).
</span><span class="cx">         auto shouldCombineParentWithOnlyChild = [](const CSSCalcOperationNode& parent, int depth)
</span><span class="cx">         {
</span><span class="lines">@@ -1003,6 +1112,13 @@
</span><span class="cx">     case CalcOperator::Atan2: return "atan2(";
</span><span class="cx">     case CalcOperator::Abs: return "abs(";
</span><span class="cx">     case CalcOperator::Sign: return "sign(";
</span><ins>+    case CalcOperator::Mod: return "mod(";
+    case CalcOperator::Rem: return "rem(";
+    case CalcOperator::Round: return "round(";
+    case CalcOperator::Up: return "round(up, ";
+    case CalcOperator::Down: return "round(down, ";
+    case CalcOperator::Nearest: return "round(nearest, ";
+    case CalcOperator::ToZero: return "round(to-zero, ";
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     return "";
</span><span class="lines">@@ -1157,6 +1273,13 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static std::pair<double, double> getNearestMultiples(double a, double b)
+{
+    double lowerB = std::floor(a / std::abs(b))*std::abs(b);
+    double upperB = lowerB + std::abs(b);
+    return std::make_pair(lowerB, upperB);
+}
+
</ins><span class="cx"> double CSSCalcOperationNode::evaluateOperator(CalcOperator op, const Vector<double>& children)
</span><span class="cx"> {
</span><span class="cx">     switch (op) {
</span><span class="lines">@@ -1265,7 +1388,57 @@
</span><span class="cx">             return -1;
</span><span class="cx">         return children[0];
</span><span class="cx">     }
</span><ins>+    case CalcOperator::Mod: {
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        float left = children[0];
+        float right = children[1];
+        if (!right)
+            return std::numeric_limits<double>::quiet_NaN();
+        if ((left < 0) == (right < 0))
+            return std::fmod(left, right);
+        return std::remainder(left, right);
</ins><span class="cx">     }
</span><ins>+    case CalcOperator::Rem: {
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        float left = children[0];
+        float right = children[1];
+        if (!right)
+            return std::numeric_limits<double>::quiet_NaN();
+        return std::fmod(left, right);
+    }
+    case CalcOperator::Round:
+        return std::numeric_limits<double>::quiet_NaN();
+    case CalcOperator::Up: {
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(children[0], children[1]);
+        return ret.second;
+    }
+    case CalcOperator::Down: {
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(children[0], children[1]);
+        return ret.first;
+    }
+    case CalcOperator::Nearest: {
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(children[0], children[1]);
+        auto upperB = ret.second;
+        auto lowerB = ret.first;
+        return std::abs(upperB - children[0]) <= std::abs(children[1]) / 2 ? upperB : lowerB;
+    }
+    case CalcOperator::ToZero: {
+        if (children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(children[0], children[1]);
+        auto upperB = ret.second;
+        auto lowerB = ret.first;
+        return std::abs(upperB) < std::abs(lowerB) ? upperB : lowerB;
+    }
+    }
</ins><span class="cx">     ASSERT_NOT_REACHED();
</span><span class="cx">     return 0;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcOperationNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h     2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/css/calc/CSSCalcOperationNode.h        2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -43,6 +43,9 @@
</span><span class="cx">     static RefPtr<CSSCalcOperationNode> createInverseTrig(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
</span><span class="cx">     static RefPtr<CSSCalcOperationNode> createAtan2(Vector<Ref<CSSCalcExpressionNode>>&& values);
</span><span class="cx">     static RefPtr<CSSCalcOperationNode> createSign(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
</span><ins>+    static RefPtr<CSSCalcOperationNode> createStep(CalcOperator, Vector<Ref<CSSCalcExpressionNode>>&& values);
+    static RefPtr<CSSCalcOperationNode> createRound(Vector<Ref<CSSCalcExpressionNode>>&& values);
+    static RefPtr<CSSCalcOperationNode> createRoundConstant(CalcOperator);
</ins><span class="cx">     static Ref<CSSCalcExpressionNode> simplify(Ref<CSSCalcExpressionNode>&&);
</span><span class="cx"> 
</span><span class="cx">     static void buildCSSText(const CSSCalcExpressionNode&, StringBuilder&);
</span><span class="lines">@@ -57,6 +60,9 @@
</span><span class="cx">     bool isAtan2Node() const { return m_operator == CalcOperator::Atan2; }
</span><span class="cx">     bool isSignNode() const { return m_operator == CalcOperator::Abs || m_operator == CalcOperator::Sign; }
</span><span class="cx">     bool shouldSortChildren() const { return isCalcSumNode() || isCalcProductNode(); }
</span><ins>+    bool isSteppedNode() const { return m_operator == CalcOperator::Mod || m_operator == CalcOperator::Rem || m_operator == CalcOperator::Round; }
+    bool isRoundOperation() const { return m_operator == CalcOperator::Down || m_operator == CalcOperator::Up || m_operator == CalcOperator::ToZero || m_operator == CalcOperator::Nearest; }
+    bool isRoundConstant() const { return (isRoundOperation()) && !m_children.size(); }
</ins><span class="cx"> 
</span><span class="cx">     void hoistChildrenWithOperator(CalcOperator);
</span><span class="cx">     void combineChildren();
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcValuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcValue.cpp (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcValue.cpp   2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/css/calc/CSSCalcValue.cpp      2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -213,7 +213,21 @@
</span><span class="cx">                 return nullptr;
</span><span class="cx">             return CSSCalcOperationNode::createSign(op, WTFMove(children));
</span><span class="cx">         }
</span><ins>+        case CalcOperator::Mod:
+        case CalcOperator::Rem:
+        case CalcOperator::Round: {
+            auto children = createCSS(operationChildren, style);
+            if (children.size() != 2)
+                return nullptr;
+            return CSSCalcOperationNode::createStep(op, WTFMove(children));
</ins><span class="cx">         }
</span><ins>+        case CalcOperator::Nearest:
+        case CalcOperator::ToZero:
+        case CalcOperator::Up:
+        case CalcOperator::Down: {
+            return CSSCalcOperationNode::createRoundConstant(op);
+        }
+        }
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx">     case CalcExpressionNodeType::BlendLength: {
</span><span class="lines">@@ -335,6 +349,9 @@
</span><span class="cx">     case CSSValueAtan2:
</span><span class="cx">     case CSSValueAbs:
</span><span class="cx">     case CSSValueSign:
</span><ins>+    case CSSValueRound:
+    case CSSValueMod:
+    case CSSValueRem:
</ins><span class="cx">         return true;
</span><span class="cx">     default:
</span><span class="cx">         return false;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcalcCalcExpressionOperationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp   2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/platform/calc/CalcExpressionOperation.cpp      2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -31,6 +31,14 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+static std::pair<double, double> getNearestMultiples(double a, double b)
+{
+    auto absB = std::abs(b);
+    double lowerB = std::floor(a / absB) * absB;
+    double upperB = lowerB + absB;
+    return std::make_pair(lowerB, upperB);
+}
+
</ins><span class="cx"> float CalcExpressionOperation::evaluate(float maxValue) const
</span><span class="cx"> {
</span><span class="cx">     switch (m_operator) {
</span><span class="lines">@@ -148,7 +156,59 @@
</span><span class="cx">             return -1;
</span><span class="cx">         return m_children[0]->evaluate(maxValue);
</span><span class="cx">     }
</span><ins>+    case CalcOperator::Mod: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        float left = m_children[0]->evaluate(maxValue);
+        float right = m_children[1]->evaluate(maxValue);
+        if (!right)
+            return std::numeric_limits<double>::quiet_NaN();
+        if ((left < 0) == (right < 0))
+            return std::fmod(left, right);
+        return std::remainder(left, right);
</ins><span class="cx">     }
</span><ins>+    case CalcOperator::Rem: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        float left = m_children[0]->evaluate(maxValue);
+        float right = m_children[1]->evaluate(maxValue);
+        if (!right)
+            return std::numeric_limits<double>::quiet_NaN();
+        return std::fmod(left, right);
+    }
+    case CalcOperator::Round:
+        return std::numeric_limits<double>::quiet_NaN();
+    case CalcOperator::Up: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue));
+        return ret.second;
+    }
+    case CalcOperator::Down: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue));
+        return ret.first;
+    }
+    case CalcOperator::Nearest: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto a = m_children[0]->evaluate(maxValue);
+        auto b = m_children[1]->evaluate(maxValue);
+        auto ret = getNearestMultiples(a, b);
+        auto upperB = ret.second;
+        auto lowerB = ret.first;
+        return std::abs(upperB - a) <= std::abs(b) / 2 ? upperB : lowerB;
+    }
+    case CalcOperator::ToZero: {
+        if (m_children.size() != 2)
+            return std::numeric_limits<double>::quiet_NaN();
+        auto ret = getNearestMultiples(m_children[0]->evaluate(maxValue), m_children[1]->evaluate(maxValue));
+        auto upperB = ret.second;
+        auto lowerB = ret.first;
+        return std::abs(upperB) < std::abs(lowerB) ? upperB : lowerB;
+    }
+    }
</ins><span class="cx">     ASSERT_NOT_REACHED();
</span><span class="cx">     return std::numeric_limits<float>::quiet_NaN();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcalcCalcOperatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/calc/CalcOperator.cpp (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/calc/CalcOperator.cpp      2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/platform/calc/CalcOperator.cpp 2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -51,6 +51,13 @@
</span><span class="cx">     case CalcOperator::Atan2: ts << "atan2"; break;
</span><span class="cx">     case CalcOperator::Abs: ts << "abs"; break;
</span><span class="cx">     case CalcOperator::Sign: ts << "sign"; break;
</span><ins>+    case CalcOperator::Mod: ts << "mod"; break;
+    case CalcOperator::Rem: ts << "rem"; break;
+    case CalcOperator::Round: ts << "round"; break;
+    case CalcOperator::Up: ts << "up"; break;
+    case CalcOperator::Down: ts << "down"; break;
+    case CalcOperator::ToZero: ts << "to-zero"; break;
+    case CalcOperator::Nearest: ts << "nearest"; break;
</ins><span class="cx">     }
</span><span class="cx">     return ts;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcalcCalcOperatorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/calc/CalcOperator.h (283072 => 283073)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/calc/CalcOperator.h        2021-09-25 03:39:35 UTC (rev 283072)
+++ trunk/Source/WebCore/platform/calc/CalcOperator.h   2021-09-25 05:03:08 UTC (rev 283073)
</span><span class="lines">@@ -49,6 +49,13 @@
</span><span class="cx">     Atan2,
</span><span class="cx">     Abs,
</span><span class="cx">     Sign,
</span><ins>+    Mod,
+    Rem,
+    Round,
+    Nearest,
+    Up,
+    Down,
+    ToZero,
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> TextStream& operator<<(TextStream&, CalcOperator);
</span></span></pre>
</div>
</div>

</body>
</html>