calc expressions should support time, angle and frequency values.

According to the CSS3 standard calc() expressions can contain time,
angle and frequency values. (http://www.w3.org/TR/css3-values/#calc).
The patch implements this feature.

The patch is partially based on http://trac.webkit.org/changeset/168685
and http://trac.webkit.org/changeset/170544 (Patch by Martin Hodovan,
reviewers were: Darin Adler and Simon Fraser)

R=mikelawther@chromium.org,alancutter@chromium.org
BUG=390566

Review URL: https://codereview.chromium.org/345903005

git-svn-id: svn://svn.chromium.org/blink/trunk@179101 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent f9790474
All boxes below should be 100px * 100px and green.
unclosed calc
unclosed calc with garbage
garbage
garbage
dpi
dpi / number
dpi + dpi
fr
zero division
mod10
1mod
70px+40px no whitespace around +
70px +40px no whitespace on right of +
70px+ 40px no whitespace on left of +
70px+-40px no whitespace around +
70px-40px no whitespace around -
70px -40px no whitespace on right of -
70px- 40px no whitespace on left of -
70px-+40px no whitespace around -
too many nests
end with operator
start with operator
no expressions
too many pluses
no binary operator
two binary operators
invalid operator '@'
invalid operator 'flim'
invalid operator '@'
invalid operator 'flim'
invalid operator 'flim' with parens
non length
number + length
length + number
percent + number
number + percent
angle + number
number + angle
angle + length
length + angle
angle + percent
percent + angle
angle + time
time + angle
angle + frequency
frequency + angle
time + number
number + time
time + length
length + time
time + percent
percent + time
time + angle
angle + time
time + frequency
frequency + time
length - number
number - length
percent - number
number - percent
angle - number
number - angle
angle - length
length - angle
angle - percent
percent - angle
angle - time
time - angle
angle - frequency
frequency - angle
time - number
number - time
time - length
length - time
time - percent
percent - time
time - angle
angle - time
time - frequency
frequency - time
length * length
length * percent
percent * length
percent * percent
angle * length
length * angle
angle * percent
percent * angle
angle * time
time * angle
angle * frequency
frequency * angle
time * length
length * time
time * percent
percent * time
time * angle
angle * time
time * frequency
frequency * time
number / length
number / percent
length / length
length / percent
percent / length
percent / percent
number / angle
angle / length
length / angle
angle / percent
percent / angle
angle / time
time / angle
angle / frequency
frequency / angle
number / time
time / length
length / time
time / percent
percent / time
time / angle
angle / time
time / frequency
frequency / time
This is a testharness.js-based test.
PASS Tests invalid calc() expression handling.
Harness: the test ran to completion.
unclosed calc => PASS
unclosed calc with garbage => PASS
garbage => PASS
dpi => PASS
dpi / number => PASS
dpi + dpi => PASS
zero division => PASS
non length => PASS
number + length => PASS
length + number => PASS
percent + number => PASS
number + percent => PASS
length - number => PASS
number - length => PASS
percent - number => PASS
number - percent => PASS
length * length => PASS
length * percent => PASS
percent * length => PASS
percent * percent => PASS
number / length => PASS
number / percent => PASS
length / length => PASS
length / percent => PASS
percent / length => PASS
percent / percent => PASS
number mod length => PASS
number mod percent => PASS
length mod length => PASS
length mod percent => PASS
percent mod length => PASS
percent mod percent => PASS
mod10 => PASS
1mod => PASS
70px+40px no whitespace around + => PASS
70px +40px no whitespace on right of + => PASS
70px+ 40px no whitespace on left of + => PASS
70px+-40px no whitespace around + => PASS
70px-40px no whitespace around - => PASS
70px -40px no whitespace on right of - => PASS
70px- 40px no whitespace on left of - => PASS
70px-+40px no whitespace around - => PASS
too many nests => PASS
end with operator => PASS
start with operator => PASS
no expressions => PASS
too many pluses => PASS
no binary operator => PASS
two binary operators => PASS
invalid operator '@' => PASS
invalid operator 'flim' => PASS
invalid operator '@' => PASS
invalid operator 'flim' => PASS
invalid operator 'flim' with parens => PASS
<!DOCTYPE HTML>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#test div {
div {
height: 100px;
background-color: red;
width: 100px;
transform: matrix(1, 0, 0, 1, 0, 0);
animation-delay: 100s;
}
</style>
<p>
All boxes below should be 100px * 100px and green.
</p>
<div id="test">
<!-- just plain bad -->
<div style="width: 100px; width: calc(;">unclosed calc</div>
<div style="width: 100px; width: calc( flim;">unclosed calc with garbage</div>
<div style="width: 100px; width: calc( flim );">garbage</div>
<div style="width: calc(;">unclosed calc</div>
<div style="width: calc( flim;">unclosed calc with garbage</div>
<div style="width: calc( flim );">garbage</div>
<div style="width: calc( auto );">garbage</div>
<!-- unsupported units -->
<div style="width: 100px; width: calc(10dpi);">dpi</div>
<div style="width: 100px; width: calc(10dpi / 4);">dpi / number</div>
<div style="width: 100px; width: calc(5dpi + 5dpi);">dpi + dpi</div>
<div style="width: calc(10dpi);">dpi</div>
<div style="width: calc(10dpi / 4);">dpi / number</div>
<div style="width: calc(5dpi + 5dpi);">dpi + dpi</div>
<div style="width: calc(42fr);">fr</div>
<!-- zero division -->
<div style="width: 100px; width: calc(1ex / 0);">zero division</div>
<!-- wrong combination -->
<div style="width: 100px; width: calc(200);">non length</div>
<div style="width: 100px; width: calc(10 + 10px);">number + length</div>
<div style="width: 100px; width: calc(10px + 10);">length + number</div>
<div style="width: 100px; width: calc(10% + 100);">percent + number</div>
<div style="width: 100px; width: calc(100 + 10%);">number + percent</div>
<div style="width: 100px; width: calc(300px - 100);">length - number</div>
<div style="width: 100px; width: calc(300 - 100px);">number - length</div>
<div style="width: 100px; width: calc(100% - 10);">percent - number</div>
<div style="width: 100px; width: calc(100 - 10%);">number - percent</div>
<div style="width: 100px; width: calc(10px*100px);">length * length</div>
<div style="width: 100px; width: calc(10px*100%);">length * percent</div>
<div style="width: 100px; width: calc(10%*100px);">percent * length</div>
<div style="width: 100px; width: calc(10%*100%);">percent * percent</div>
<div style="width: 100px; width: calc(100/10px);">number / length</div>
<div style="width: 100px; width: calc(100/10%);">number / percent</div>
<div style="width: 100px; width: calc(100px/10px);">length / length</div>
<div style="width: 100px; width: calc(100px/10%);">length / percent</div>
<div style="width: 100px; width: calc(100%/10px);">percent / length</div>
<div style="width: 100px; width: calc(100%/10%);">percent / percent</div>
<div style="width: 100px; width: calc(100 mod 10px);">number mod length</div>
<div style="width: 100px; width: calc(100 mod 10%);">number mod percent</div>
<div style="width: 100px; width: calc(100px mod 10px);">length mod length</div>
<div style="width: 100px; width: calc(100px mod 10%);">length mod percent</div>
<div style="width: 100px; width: calc(100% mod 10px);">percent mod length</div>
<div style="width: 100px; width: calc(100% mod 10%);">percent mod percent</div>
<div style="width: calc(1ex / 0);">zero division</div>
<!-- mod, +, - require whitespaces around the operator -->
<div style="width: 100px; width: calc(1 mod10 * 200px);">mod10 </div>
<div style="width: 100px; width: calc(1mod 10 * 200px);">1mod</div>
<div style="width: 100px; width: calc(70px+40px);">70px+40px no whitespace around + </div>
<div style="width: 100px; width: calc(70px +40px);">70px +40px no whitespace on right of +</div>
<div style="width: 100px; width: calc(70px+ 40px);">70px+ 40px no whitespace on left of +</div>
<div style="width: 100px; width: calc(70px+-40px);">70px+-40px no whitespace around + </div>
<div style="width: 100px; width: calc(70px-40px);">70px-40px no whitespace around - </div>
<div style="width: 100px; width: calc(70px -40px);">70px -40px no whitespace on right of -</div>
<div style="width: 100px; width: calc(70px- 40px);">70px- 40px no whitespace on left of -</div>
<div style="width: 100px; width: calc(70px-+40px);">70px-+40px no whitespace around - </div>
<div style="width: calc(1 mod10 * 200px);">mod10 </div>
<div style="width: calc(1mod 10 * 200px);">1mod</div>
<div style="width: calc(70px+40px);">70px+40px no whitespace around + </div>
<div style="width: calc(70px +40px);">70px +40px no whitespace on right of +</div>
<div style="width: calc(70px+ 40px);">70px+ 40px no whitespace on left of +</div>
<div style="width: calc(70px+-40px);">70px+-40px no whitespace around + </div>
<div style="width: calc(70px-40px);">70px-40px no whitespace around - </div>
<div style="width: calc(70px -40px);">70px -40px no whitespace on right of -</div>
<div style="width: calc(70px- 40px);">70px- 40px no whitespace on left of -</div>
<div style="width: calc(70px-+40px);">70px-+40px no whitespace around - </div>
<!-- too many nests should be rejected to avoid stack overflow -->
<div style="width: 100px; width: calc(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((200px)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))));">too many nests</div>
<div style="width: calc(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((200px)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))));">too many nests</div>
<!-- invalid formulas -->
<div style="width: 100px; width: calc(200px*);">end with operator</div>
<div style="width: 100px; width: calc(+ +200px);">start with operator</div>
<div style="width: 100px; width: calc();">no expressions</div>
<div style="width: 100px; width: calc(100px + + +100px);">too many pluses</div>
<div style="width: 100px; width: calc(200px 200px);">no binary operator</div>
<div style="width: 100px; width: calc(100px * * 2);">two binary operators</div>
<div style="width: 100px; width: calc(100px @ 2);">invalid operator '@'</div>
<div style="width: 100px; width: calc(1 flim 2);">invalid operator 'flim'</div>
<div style="width: 100px; width: calc(100px @ 2);">invalid operator '@'</div>
<div style="width: 100px; width: calc(1 flim 2);">invalid operator 'flim'</div>
<div style="width: 100px; width: calc(1 flim (2));">invalid operator 'flim' with parens</div>
</div>
<div style="width: calc(200px*);">end with operator</div>
<div style="width: calc(+ +200px);">start with operator</div>
<div style="width: calc();">no expressions</div>
<div style="width: calc(100px + + +100px);">too many pluses</div>
<div style="width: calc(200px 200px);">no binary operator</div>
<div style="width: calc(100px * * 2);">two binary operators</div>
<div style="width: calc(100px @ 2);">invalid operator '@'</div>
<div style="width: calc(1 flim 2);">invalid operator 'flim'</div>
<div style="width: calc(100px @ 2);">invalid operator '@'</div>
<div style="width: calc(1 flim 2);">invalid operator 'flim'</div>
<div style="width: calc(1 flim (2));">invalid operator 'flim' with parens</div>
<!-- Single unitless non length value -->
<div style="width: 100px; width: calc(200);">non length</div>
<!-- Wrong combinations -->
<div style="width: calc(10 + 10px);">number + length</div>
<div style="width: calc(10px + 10);">length + number</div>
<div style="width: calc(10% + 100);">percent + number</div>
<div style="width: calc(100 + 10%);">number + percent</div>
<div style="transform: rotate(calc(42deg + 10))">angle + number</div>
<div style="transform: rotate(calc(10 + 42rad))">number + angle</div>
<div style="transform: rotate(calc(42deg + 10px))">angle + length</div>
<div style="transform: rotate(calc(10px + 42rad))">length + angle</div>
<div style="transform: rotate(calc(42rad + 10%))">angle + percent</div>
<div style="transform: rotate(calc(10% + 42rad))">percent + angle</div>
<div style="transform: rotate(calc(42grad + 10ms))">angle + time</div>
<div style="transform: rotate(calc(10s + 42grad))">time + angle</div>
<div style="transform: rotate(calc(42turn + 10Hz))">angle + frequency</div>
<div style="transform: rotate(calc(10kHz + 42turn))">frequency + angle</div>
<div style="animation-delay: rotate(calc(10ms + 10)">time + number</div>
<div style="animation-delay: rotate(calc(10 + 10s))">number + time</div>
<div style="animation-delay: rotate(calc(10ms + 10px)">time + length</div>
<div style="animation-delay: rotate(calc(10px + 10s))">length + time</div>
<div style="animation-delay: rotate(calc(10ms + 10%))">time + percent</div>
<div style="animation-delay: rotate(calc(10% + 10s))">percent + time</div>
<div style="animation-delay: rotate(calc(10ms + 10rad))">time + angle</div>
<div style="animation-delay: rotate(calc(10deg + 10s))">angle + time</div>
<div style="animation-delay: rotate(calc(10ms + 10Hz))">time + frequency</div>
<div style="animation-delay: rotate(calc(10kHz + 10s))">frequency + time</div>
<div style="width: calc(300px - 100);">length - number</div>
<div style="width: calc(300 - 100px);">number - length</div>
<div style="width: calc(100% - 10);">percent - number</div>
<div style="width: calc(100 - 10%);">number - percent</div>
<div style="transform: rotate(calc(42deg - 10))">angle - number</div>
<div style="transform: rotate(calc(10 - 42rad))">number - angle</div>
<div style="transform: rotate(calc(42deg - 10px))">angle - length</div>
<div style="transform: rotate(calc(10px - 42rad))">length - angle</div>
<div style="transform: rotate(calc(42rad - 10%))">angle - percent</div>
<div style="transform: rotate(calc(10% - 42rad))">percent - angle</div>
<div style="transform: rotate(calc(42grad - 10ms))">angle - time</div>
<div style="transform: rotate(calc(10s - 42grad))">time - angle</div>
<div style="transform: rotate(calc(42turn - 10Hz))">angle - frequency</div>
<div style="transform: rotate(calc(10kHz - 42turn))">frequency - angle</div>
<div style="animation-delay: rotate(calc(10ms - 10)">time - number</div>
<div style="animation-delay: rotate(calc(10 - 10s))">number - time</div>
<div style="animation-delay: rotate(calc(10ms - 10px))">time - length</div>
<div style="animation-delay: rotate(calc(10px - 10s))">length - time</div>
<div style="animation-delay: rotate(calc(10ms - 10%))">time - percent</div>
<div style="animation-delay: rotate(calc(10% - 10s))">percent - time</div>
<div style="animation-delay: rotate(calc(10ms - 10turn))">time - angle</div>
<div style="animation-delay: rotate(calc(10rad - 10s))">angle - time</div>
<div style="animation-delay: rotate(calc(10ms - 10Hz))">time - frequency</div>
<div style="animation-delay: rotate(calc(10kHz - 10s))">frequency - time</div>
<div style="width: calc(10px * 100px);">length * length</div>
<div style="width: calc(10px * 100%);">length * percent</div>
<div style="width: calc(10% * 100px);">percent * length</div>
<div style="width: calc(10% * 100%);">percent * percent</div>
<div style="transform: rotate(calc(42deg * 10px))">angle * length</div>
<div style="transform: rotate(calc(10px * 42rad))">length * angle</div>
<div style="transform: rotate(calc(42rad * 10%))">angle * percent</div>
<div style="transform: rotate(calc(10% * 42rad))">percent * angle</div>
<div style="transform: rotate(calc(42grad * 10ms))">angle * time</div>
<div style="transform: rotate(calc(10s * 42grad))">time * angle</div>
<div style="transform: rotate(calc(42turn * 10Hz))">angle * frequency</div>
<div style="transform: rotate(calc(10kHz * 42turn))">frequency * angle</div>
<div style="animation-delay: rotate(calc(10ms * 10px))">time * length</div>
<div style="animation-delay: rotate(calc(10px * 10s))">length * time</div>
<div style="animation-delay: rotate(calc(10ms * 10%))">time * percent</div>
<div style="animation-delay: rotate(calc(10% * 10s))">percent * time</div>
<div style="animation-delay: rotate(calc(10ms * 10deg))">time * angle</div>
<div style="animation-delay: rotate(calc(10turn * 10s))">angle * time</div>
<div style="animation-delay: rotate(calc(10ms * 10Hz))">time * frequency</div>
<div style="animation-delay: rotate(calc(10kHz * 10s))">frequency * time</div>
<div style="width: calc(100 / 10px);">number / length</div>
<div style="width: calc(100 / 10%);">number / percent</div>
<div style="width: calc(100px / 10px);">length / length</div>
<div style="width: calc(100px / 10%);">length / percent</div>
<div style="width: calc(100% / 10px);">percent / length</div>
<div style="width: calc(100% / 10%);">percent / percent</div>
<div style="transform: rotate(calc(10 / 42rad))">number / angle</div>
<div style="transform: rotate(calc(42deg / 10px))">angle / length</div>
<div style="transform: rotate(calc(10px / 42rad))">length / angle</div>
<div style="transform: rotate(calc(42rad / 10%))">angle / percent</div>
<div style="transform: rotate(calc(10% / 42rad))">percent / angle</div>
<div style="transform: rotate(calc(42grad / 10ms))">angle / time</div>
<div style="transform: rotate(calc(10s / 42grad))">time / angle</div>
<div style="transform: rotate(calc(42turn / 10Hz))">angle / frequency</div>
<div style="transform: rotate(calc(10kHz / 42turn))">frequency / angle</div>
<div style="animation-delay: rotate(calc(10 / 10s))">number / time</div>
<div style="animation-delay: rotate(calc(10ms / 10px))">time / length</div>
<div style="animation-delay: rotate(calc(10px / 10s))">length / time</div>
<div style="animation-delay: rotate(calc(10ms / 10%))">time / percent</div>
<div style="animation-delay: rotate(calc(10% / 10s))">percent / time</div>
<div style="animation-delay: rotate(calc(10ms / 10grad))">time / angle</div>
<div style="animation-delay: rotate(calc(10rad / 10s))">angle / time</div>
<div style="animation-delay: rotate(calc(10ms / 10Hz))">time / frequency</div>
<div style="animation-delay: rotate(calc(10kHz / 10s))">frequency / time</div>
<script>
if (window.testRunner)
testRunner.dumpAsText();
var test = document.getElementById("test");
for (var element = test.firstChild; element; element = element.nextSibling) {
var width = element.offsetWidth;
var error = [];
if (width != 100)
error.push("expected width of 100, but was " + width);
var height = element.offsetHeight;
if (height != 100)
error.push("expected height of 100, but was " + width);
if (error == "") {
element.style.backgroundColor = "green";
element.innerHTML += " => PASS";
} else {
element.innerHTML += " => FAIL: " + error.join(", ");
test(function() {
var divs = document.getElementsByTagName("div");
for (var i = 0, element = divs[i]; i < divs.length; ++i) {
assert_equals(getComputedStyle(element).getPropertyValue("width"), "100px");
assert_equals(getComputedStyle(element).getPropertyValue("transform"), "matrix(1, 0, 0, 1, 0, 0)");
assert_equals(getComputedStyle(element).getPropertyValue("animation-delay"), "100s");
}
}
}, "Tests invalid calc() expression handling.");
</script>
This is a testharness.js-based test.
PASS Tests calc() with time units.
PASS Tests calc() with angle units.
PASS Tests calc() with frequency units.
PASS Tests unit conversion of single values in calc().
Harness: the test ran to completion.
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<div id="target"></div>
<script>
function compareValue(property, calcValue, expectedValue) {
target.style[property] = '';
target.style[property] = calcValue;
var computedCalcValue = getComputedStyle(target)[property];
target.style[property] = expectedValue;
var computedExpectedValue = getComputedStyle(target)[property];
assert_equals(computedCalcValue, computedExpectedValue, calcValue + ' should equal to ' + expectedValue);
}
test(function() {
compareValue("transition-delay", "calc(4s + 1s)", "5s");
compareValue("transition-delay", "calc(4s + 1ms)", "4.001s");
compareValue("transition-delay", "calc(4ms + 1ms)", "0.005s");
compareValue("transition-delay", "calc(4s - 1s)", "3s");
compareValue("transition-delay", "calc(4s - 1ms)", "3.999s");
compareValue("transition-delay", "calc(4 * 1s)", "4s");
compareValue("transition-delay", "calc(4 * 1ms)", "0.004s");
compareValue("transition-delay", "calc(4s / 2)", "2s");
compareValue("transition-delay", "calc(4ms / 2)", "0.002s");
}, "Tests calc() with time units.");
test(function() {
compareValue("transform", "rotate(calc(45deg + 45deg))", "rotate(90deg)");
compareValue("transform", "rotate(calc(45deg + 1rad))", "rotate(102.3deg)");
compareValue("transform", "rotate(calc(20deg + 200grad))", "rotate(200deg)");
compareValue("transform", "rotate(calc(200deg + 0.5turn))", "rotate(200deg)");
compareValue("transform", "rotate(calc(45rad + 45rad))", "rotate(90rad)");
compareValue("transform", "rotate(calc(1rad + 40grad))", "matrix(-0.0574904875548093, 0.998346054151921, -0.998346054151921, -0.0574904875548093, 0, 0)");
compareValue("transform", "rotate(calc(1rad + 0.5turn))", "matrix(-0.54030230586814, -0.841470984807896, 0.841470984807896, -0.54030230586814, 0, 0)");
compareValue("transform", "rotate(calc(45grad + 45grad))", "rotate(90grad)");
compareValue("transform", "rotate(calc(10grad + 0.5turn))", "rotate(189deg)");
compareValue("transform", "rotate(calc(45deg - 15deg))", "rotate(30deg)");
compareValue("transform", "rotate(calc(90deg - 1rad))", "matrix(0.841470984807897, 0.54030230586814, -0.54030230586814, 0.841470984807897, 0, 0)");
compareValue("transform", "rotate(calc(38deg - 20grad))", "rotate(20deg)");
compareValue("transform", "rotate(calc(360deg - 0.5turn))", "rotate(180deg)");
compareValue("transform", "rotate(calc(45rad - 15rad))", "rotate(30rad)");
compareValue("transform", "rotate(calc(30rad - 10grad))", "matrix(-0.955728013201613, 0.294251533184956, -0.294251533184956, -0.955728013201613, 0, 0)");
compareValue("transform", "rotate(calc(4rad - 0.1turn))", "matrix(-0.973646143183581, -0.228063999490797, 0.228063999490797, -0.973646143183581, 0, 0)");
compareValue("transform", "rotate(calc(45grad - 15grad))", "rotate(30grad)");
compareValue("transform", "rotate(calc(100grad - 0.25turn))", "rotate(0deg)");
compareValue("transform", "rotate(calc(45deg * 2))", "rotate(90deg)");
compareValue("transform", "rotate(calc(2 * 45rad))", "rotate(90rad)");
compareValue("transform", "rotate(calc(45grad * 2))", "rotate(90grad)");
compareValue("transform", "rotate(calc(2 * 45turn))", "rotate(90turn)");
compareValue("transform", "rotate(calc(90deg / 2))", "rotate(45deg)");
compareValue("transform", "rotate(calc(90rad / 2))", "rotate(45rad)");
compareValue("transform", "rotate(calc(90grad / 2))", "rotate(45grad)");
compareValue("transform", "rotate(calc(90turn / 2))", "rotate(45turn)");
}, "Tests calc() with angle units.");
test(function() {
// NOTE: Since there is no CSS property that uses frequency at the moment we only test the parsing.
compareValue("pitch", "calc(10Hz + 20Hz)", null);
compareValue("pitch", "calc(10kHz + 20kHz)", null);
compareValue("pitch", "calc(10kHz + 20Hz)", null);
compareValue("pitch", "calc(20Hz - 10Hz)", null);
compareValue("pitch", "calc(20kHz - 10kHz)", null);
compareValue("pitch", "calc(20kHz - 10Hz)", null);
compareValue("pitch", "calc(10Hz * 2)", null);
compareValue("pitch", "calc(10kHz * 2)", null);
compareValue("pitch", "calc(10Hz / 2)", null);
compareValue("pitch", "calc(10kHz / 2)", null);
}, "Tests calc() with frequency units.");
test(function() {
compareValue("transition-delay", "calc(4000ms)", "4s");
compareValue("transform", "rotate(calc(50grad)", "rotate(45deg)");
}, "Tests unit conversion of single values in calc().");
</script>
......@@ -70,8 +70,17 @@ static CalculationCategory unitCategory(CSSPrimitiveValue::UnitType type)
case CSSPrimitiveValue::CSS_VMIN:
case CSSPrimitiveValue::CSS_VMAX:
return CalcLength;
// FIXME: Support angle, time and frequency units.
// http://www.w3.org/TR/css3-values/#calc-notation
case CSSPrimitiveValue::CSS_DEG:
case CSSPrimitiveValue::CSS_GRAD:
case CSSPrimitiveValue::CSS_RAD:
case CSSPrimitiveValue::CSS_TURN:
return CalcAngle;
case CSSPrimitiveValue::CSS_MS:
case CSSPrimitiveValue::CSS_S:
return CalcTime;
case CSSPrimitiveValue::CSS_HZ:
case CSSPrimitiveValue::CSS_KHZ:
return CalcFrequency;
default:
return CalcOther;
}
......@@ -96,6 +105,7 @@ static bool hasDoubleValue(CSSPrimitiveValue::UnitType type)
case CSSPrimitiveValue::CSS_DEG:
case CSSPrimitiveValue::CSS_RAD:
case CSSPrimitiveValue::CSS_GRAD:
case CSSPrimitiveValue::CSS_TURN:
case CSSPrimitiveValue::CSS_MS:
case CSSPrimitiveValue::CSS_S:
case CSSPrimitiveValue::CSS_HZ:
......@@ -123,7 +133,6 @@ static bool hasDoubleValue(CSSPrimitiveValue::UnitType type)
case CSSPrimitiveValue::CSS_PARSER_OPERATOR:
case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR:
case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER:
case CSSPrimitiveValue::CSS_TURN:
case CSSPrimitiveValue::CSS_COUNTER_NAME:
case CSSPrimitiveValue::CSS_SHAPE:
case CSSPrimitiveValue::CSS_QUAD:
......@@ -232,11 +241,14 @@ public:
switch (m_category) {
case CalcLength:
return m_value->computeLength<double>(conversionData);
case CalcPercent:
case CalcNumber:
case CalcPercent:
return m_value->getDoubleValue();
case CalcAngle:
case CalcFrequency:
case CalcPercentLength:
case CalcPercentNumber:
case CalcTime:
case CalcOther:
ASSERT_NOT_REACHED();
break;
......@@ -283,12 +295,15 @@ private:
};
static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
// CalcNumber CalcLength CalcPercent CalcPercentNumber CalcPercentLength
/* CalcNumber */ { CalcNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther },
/* CalcLength */ { CalcOther, CalcLength, CalcPercentLength, CalcOther, CalcPercentLength },
/* CalcPercent */ { CalcPercentNumber, CalcPercentLength, CalcPercent, CalcPercentNumber, CalcPercentLength },
/* CalcPercentNumber */ { CalcPercentNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther },
/* CalcPercentLength */ { CalcOther, CalcPercentLength, CalcPercentLength, CalcOther, CalcPercentLength },
// CalcNumber CalcLength CalcPercent CalcPercentNumber CalcPercentLength CalcAngle CalcTime CalcFrequency
/* CalcNumber */ { CalcNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther, CalcOther, CalcOther, CalcOther },
/* CalcLength */ { CalcOther, CalcLength, CalcPercentLength, CalcOther, CalcPercentLength, CalcOther, CalcOther, CalcOther },
/* CalcPercent */ { CalcPercentNumber, CalcPercentLength, CalcPercent, CalcPercentNumber, CalcPercentLength, CalcOther, CalcOther, CalcOther },
/* CalcPercentNumber */ { CalcPercentNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther, CalcOther, CalcOther, CalcOther },
/* CalcPercentLength */ { CalcOther, CalcPercentLength, CalcPercentLength, CalcOther, CalcPercentLength, CalcOther, CalcOther, CalcOther },
/* CalcAngle */ { CalcOther, CalcOther, CalcOther, CalcOther, CalcOther, CalcAngle, CalcOther, CalcOther },
/* CalcTime */ { CalcOther, CalcOther, CalcOther, CalcOther, CalcOther, CalcOther, CalcTime, CalcOther },
/* CalcFrequency */ { CalcOther, CalcOther, CalcOther, CalcOther, CalcOther, CalcOther, CalcOther, CalcFrequency }
};
static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
......@@ -517,6 +532,12 @@ public:
return leftType;
return CSSPrimitiveValue::CSS_UNKNOWN;
}
case CalcAngle:
return CSSPrimitiveValue::CSS_DEG;
case CalcTime:
return CSSPrimitiveValue::CSS_MS;
case CalcFrequency:
return CSSPrimitiveValue::CSS_HZ;
case CalcPercentLength:
case CalcPercentNumber:
case CalcOther:
......
......@@ -54,12 +54,17 @@ enum CalcOperator {
CalcDivide = '/'
};
// The order of this enum should not change since its elements are used as indices
// in the addSubtractResult matrix.
enum CalculationCategory {
CalcNumber = 0,
CalcLength,
CalcPercent,
CalcPercentNumber,
CalcPercentLength,
CalcAngle,
CalcTime,
CalcFrequency,
CalcOther
};
......
......@@ -143,7 +143,6 @@ StringToUnitTable createStringToUnitTable()
return table;
}
CSSPrimitiveValue::UnitType CSSPrimitiveValue::fromName(const String& unit)
{
DEFINE_STATIC_LOCAL(StringToUnitTable, unitTable, (createStringToUnitTable()));
......@@ -216,6 +215,10 @@ CSSPrimitiveValue::UnitType CSSPrimitiveValue::primitiveType() const
return static_cast<UnitType>(m_primitiveUnitType);
switch (m_value.calc->category()) {
case CalcAngle:
return CSS_DEG;
case CalcFrequency:
return CSS_HZ;
case CalcNumber:
return CSS_NUMBER;
case CalcPercent:
......@@ -226,6 +229,8 @@ CSSPrimitiveValue::UnitType CSSPrimitiveValue::primitiveType() const
return CSS_CALC_PERCENTAGE_WITH_NUMBER;
case CalcPercentLength:
return CSS_CALC_PERCENTAGE_WITH_LENGTH;
case CalcTime:
return CSS_MS;
case CalcOther:
return CSS_UNKNOWN;
}
......@@ -535,9 +540,23 @@ void CSSPrimitiveValue::cleanup()
}
}
double CSSPrimitiveValue::computeSeconds()
{
ASSERT(isTime() || (isCalculated() && cssCalcValue()->category() == CalcTime));
UnitType currentType = isCalculated() ? cssCalcValue()->expressionNode()->primitiveType() : static_cast<UnitType>(m_primitiveUnitType);
if (currentType == CSS_S)
return getDoubleValue();
if (currentType == CSS_MS)
return getDoubleValue() / 1000;
ASSERT_NOT_REACHED();
return 0;
}
double CSSPrimitiveValue::computeDegrees()
{
switch (m_primitiveUnitType) {
ASSERT(isAngle() || (isCalculated() && cssCalcValue()->category() == CalcAngle));
UnitType currentType = isCalculated() ? cssCalcValue()->expressionNode()->primitiveType() : static_cast<UnitType>(m_primitiveUnitType);
switch (currentType) {
case CSS_DEG:
return getDoubleValue();
case CSS_RAD:
......
......@@ -273,21 +273,7 @@ public:
UnitType primitiveType() const;
double computeDegrees();
enum TimeUnit { Seconds, Milliseconds };
template <typename T, TimeUnit timeUnit> T computeTime()
{
if (timeUnit == Seconds && m_primitiveUnitType == CSS_S)
return getValue<T>();
if (timeUnit == Seconds && m_primitiveUnitType == CSS_MS)
return getValue<T>() / 1000;
if (timeUnit == Milliseconds && m_primitiveUnitType == CSS_MS)
return getValue<T>();
if (timeUnit == Milliseconds && m_primitiveUnitType == CSS_S)
return getValue<T>() * 1000;
ASSERT_NOT_REACHED();
return 0;
}
double computeSeconds();
/*
* Computes a length in pixels out of the given CSSValue
......
......@@ -225,6 +225,15 @@ bool CSSPropertyParser::validCalculationUnit(CSSParserValue* value, Units unitfl
case CalcPercentNumber:
b = (unitflags & FPercent) && (unitflags & FNumber);
break;
case CalcAngle:
b = (unitflags & FAngle);
break;
case CalcTime:
b = (unitflags & FTime);
break;
case CalcFrequency:
b = (unitflags & FFrequency);
break;
case CalcOther:
break;
}
......
......@@ -291,7 +291,7 @@ double CSSToStyleMap::mapAnimationDelay(CSSValue* value)
{
if (value->isInitialValue())
return CSSTimingData::initialDelay();
return toCSSPrimitiveValue(value)->computeTime<double, CSSPrimitiveValue::Seconds>();
return toCSSPrimitiveValue(value)->computeSeconds();
}
Timing::PlaybackDirection CSSToStyleMap::mapAnimationDirection(CSSValue* value)
......@@ -318,7 +318,7 @@ double CSSToStyleMap::mapAnimationDuration(CSSValue* value)
{
if (value->isInitialValue())
return CSSTimingData::initialDuration();
return toCSSPrimitiveValue(value)->computeTime<double, CSSPrimitiveValue::Seconds>();
return toCSSPrimitiveValue(value)->computeSeconds();
}
Timing::FillMode CSSToStyleMap::mapAnimationFillMode(CSSValue* value)
......
......@@ -1034,7 +1034,7 @@ void StyleBuilderFunctions::applyValueCSSPropertyInternalMarqueeSpeed(StyleResol
break;
}
} else if (primitiveValue->isTime()) {
state.style()->setMarqueeSpeed(primitiveValue->computeTime<int, CSSPrimitiveValue::Milliseconds>());
state.style()->setMarqueeSpeed(static_cast<int>(primitiveValue->computeSeconds()) * 1000);
} else if (primitiveValue->isNumber()) { // For scrollamount support.
state.style()->setMarqueeSpeed(primitiveValue->getIntValue());
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment