Commit ebf624fb authored by Seokho Song's avatar Seokho Song Committed by Chromium LUCI CQ

CSS calc function should evaluate and parse infinity and NaN

According to the newest version of CSS standard [1],
calc function could return an infinity or NaN value
when the expression has an infinity keyword without NaN product
(like inf-inf) or the division by zero.

Therefore, this initial feature implementation evaluate the expression or keywords infinity value to std::numeric_limits<double>::infinity()
and NaN value to std::numeric_limits<double>::quiet_NaN().
Also, this patch contains the infinity and NaN value serialization.

Design docs: [2]
Feature Status: [3]
Intent to prototype: [4]

[1] https://drafts.csswg.org/css-values/#calc-type-checking
[2] https://bit.ly/349gXjq
[3] https://chromestatus.com/feature/5657825571241984
[4] https://groups.google.com/a/chromium.org/g/blink-dev/c/4cT9dMkzVXE/m/aCT8B6PDAwAJ

Bug: 1133390
Change-Id: Id543e9f9af446de623ecee00ba4a3e03100096b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2465414Reviewed-by: default avatarXiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: default avatarMason Freed <masonfreed@chromium.org>
Commit-Queue: Seokho Song <0xdevssh@gmail.com>
Cr-Commit-Position: refs/heads/master@{#845066}
parent 592fbab6
...@@ -44,6 +44,10 @@ def cpp_name(entry): ...@@ -44,6 +44,10 @@ def cpp_name(entry):
def enum_key_for_css_keyword(keyword): def enum_key_for_css_keyword(keyword):
# To make sure different enum keys are generated for infinity and -infinity.
# Design doc : https://bit.ly/349gXjq
if (not isinstance(keyword, str)) and keyword.original == '-infinity':
return 'kNegative' + _upper_camel_case(keyword)
return 'k' + _upper_camel_case(keyword) return 'k' + _upper_camel_case(keyword)
......
...@@ -138,7 +138,8 @@ CSSMathExpressionNumericLiteral* CSSMathExpressionNumericLiteral::Create( ...@@ -138,7 +138,8 @@ CSSMathExpressionNumericLiteral* CSSMathExpressionNumericLiteral::Create(
double value, double value,
CSSPrimitiveValue::UnitType type, CSSPrimitiveValue::UnitType type,
bool is_integer) { bool is_integer) {
if (std::isnan(value) || std::isinf(value)) if (!RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled() &&
(std::isnan(value) || std::isinf(value)))
return nullptr; return nullptr;
return MakeGarbageCollected<CSSMathExpressionNumericLiteral>( return MakeGarbageCollected<CSSMathExpressionNumericLiteral>(
CSSNumericLiteralValue::Create(value, type), is_integer); CSSNumericLiteralValue::Create(value, type), is_integer);
...@@ -434,10 +435,13 @@ CSSMathExpressionNode* CSSMathExpressionBinaryOperation::CreateSimplified( ...@@ -434,10 +435,13 @@ CSSMathExpressionNode* CSSMathExpressionBinaryOperation::CreateSimplified(
left_side == number_side ? right_side : left_side; left_side == number_side ? right_side : left_side;
double number = number_side->DoubleValue(); double number = number_side->DoubleValue();
if (std::isnan(number) || std::isinf(number))
return nullptr; if (!RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled()) {
if (op == CSSMathOperator::kDivide && !number) if (std::isnan(number) || std::isinf(number))
return nullptr; return nullptr;
if (op == CSSMathOperator::kDivide && !number)
return nullptr;
}
CSSPrimitiveValue::UnitType other_type = other_side->ResolvedUnitType(); CSSPrimitiveValue::UnitType other_type = other_side->ResolvedUnitType();
if (HasDoubleValue(other_type)) { if (HasDoubleValue(other_type)) {
...@@ -722,6 +726,15 @@ void CSSMathExpressionBinaryOperation::Trace(Visitor* visitor) const { ...@@ -722,6 +726,15 @@ void CSSMathExpressionBinaryOperation::Trace(Visitor* visitor) const {
CSSMathExpressionNode::Trace(visitor); CSSMathExpressionNode::Trace(visitor);
} }
// static
bool CheckSameSign(double left_value, double right_value) {
if (left_value >= 0 && right_value >= 0)
return true;
if (left_value <= 0 && right_value <= 0)
return true;
return false;
}
// static // static
const CSSMathExpressionNode* CSSMathExpressionBinaryOperation::GetNumberSide( const CSSMathExpressionNode* CSSMathExpressionBinaryOperation::GetNumberSide(
const CSSMathExpressionNode* left_side, const CSSMathExpressionNode* left_side,
...@@ -737,14 +750,23 @@ const CSSMathExpressionNode* CSSMathExpressionBinaryOperation::GetNumberSide( ...@@ -737,14 +750,23 @@ const CSSMathExpressionNode* CSSMathExpressionBinaryOperation::GetNumberSide(
double CSSMathExpressionBinaryOperation::EvaluateOperator(double left_value, double CSSMathExpressionBinaryOperation::EvaluateOperator(double left_value,
double right_value, double right_value,
CSSMathOperator op) { CSSMathOperator op) {
// Design doc for infinity and NaN: https://bit.ly/349gXjq
switch (op) { switch (op) {
case CSSMathOperator::kAdd: case CSSMathOperator::kAdd:
if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled())
return left_value + right_value;
return clampTo<double>(left_value + right_value); return clampTo<double>(left_value + right_value);
case CSSMathOperator::kSubtract: case CSSMathOperator::kSubtract:
if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled())
return left_value - right_value;
return clampTo<double>(left_value - right_value); return clampTo<double>(left_value - right_value);
case CSSMathOperator::kMultiply: case CSSMathOperator::kMultiply:
if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled())
return left_value * right_value;
return clampTo<double>(left_value * right_value); return clampTo<double>(left_value * right_value);
case CSSMathOperator::kDivide: case CSSMathOperator::kDivide:
if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled())
return left_value / right_value;
if (right_value) if (right_value)
return clampTo<double>(left_value / right_value); return clampTo<double>(left_value / right_value);
return std::numeric_limits<double>::quiet_NaN(); return std::numeric_limits<double>::quiet_NaN();
...@@ -1084,6 +1106,23 @@ class CSSMathExpressionNodeParser { ...@@ -1084,6 +1106,23 @@ class CSSMathExpressionNodeParser {
private: private:
CSSMathExpressionNode* ParseValue(CSSParserTokenRange& tokens) { CSSMathExpressionNode* ParseValue(CSSParserTokenRange& tokens) {
CSSParserToken token = tokens.ConsumeIncludingWhitespace(); CSSParserToken token = tokens.ConsumeIncludingWhitespace();
if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled()) {
if (token.Id() == CSSValueID::kInfinity) {
return CSSMathExpressionNumericLiteral::Create(
std::numeric_limits<double>::infinity(),
CSSPrimitiveValue::UnitType::kNumber, false);
}
if (token.Id() == CSSValueID::kNegativeInfinity) {
return CSSMathExpressionNumericLiteral::Create(
-std::numeric_limits<double>::infinity(),
CSSPrimitiveValue::UnitType::kNumber, false);
}
if (token.Id() == CSSValueID::kNan) {
return CSSMathExpressionNumericLiteral::Create(
std::numeric_limits<double>::quiet_NaN(),
CSSPrimitiveValue::UnitType::kNumber, false);
}
}
if (!(token.GetType() == kNumberToken || if (!(token.GetType() == kNumberToken ||
token.GetType() == kPercentageToken || token.GetType() == kPercentageToken ||
token.GetType() == kDimensionToken)) token.GetType() == kDimensionToken))
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "build/build_config.h" #include "build/build_config.h"
#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h" #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
#include "third_party/blink/renderer/core/css/css_value_pool.h" #include "third_party/blink/renderer/core/css/css_value_pool.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h" #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
...@@ -23,7 +24,8 @@ void CSSNumericLiteralValue::TraceAfterDispatch(blink::Visitor* visitor) const { ...@@ -23,7 +24,8 @@ void CSSNumericLiteralValue::TraceAfterDispatch(blink::Visitor* visitor) const {
CSSNumericLiteralValue::CSSNumericLiteralValue(double num, UnitType type) CSSNumericLiteralValue::CSSNumericLiteralValue(double num, UnitType type)
: CSSPrimitiveValue(kNumericLiteralClass), num_(num) { : CSSPrimitiveValue(kNumericLiteralClass), num_(num) {
DCHECK(std::isfinite(num)); DCHECK(RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled() ||
std::isfinite(num));
DCHECK_NE(UnitType::kUnknown, type); DCHECK_NE(UnitType::kUnknown, type);
numeric_literal_unit_type_ = static_cast<unsigned>(type); numeric_literal_unit_type_ = static_cast<unsigned>(type);
} }
...@@ -31,13 +33,19 @@ CSSNumericLiteralValue::CSSNumericLiteralValue(double num, UnitType type) ...@@ -31,13 +33,19 @@ CSSNumericLiteralValue::CSSNumericLiteralValue(double num, UnitType type)
// static // static
CSSNumericLiteralValue* CSSNumericLiteralValue::Create(double value, CSSNumericLiteralValue* CSSNumericLiteralValue::Create(double value,
UnitType type) { UnitType type) {
// TODO(timloh): This looks wrong.
if (std::isinf(value))
value = 0;
if (value < 0 || value > CSSValuePool::kMaximumCacheableIntegerValue) if (value < 0 || value > CSSValuePool::kMaximumCacheableIntegerValue)
return MakeGarbageCollected<CSSNumericLiteralValue>(value, type); return MakeGarbageCollected<CSSNumericLiteralValue>(value, type);
if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled()) {
// Value can be NaN.
if (std::isnan(value))
return MakeGarbageCollected<CSSNumericLiteralValue>(value, type);
} else {
// TODO(timloh): This looks wrong.
if (std::isinf(value))
value = 0;
}
int int_value = clampTo<int>(value); int int_value = clampTo<int>(value);
if (value != int_value) if (value != int_value)
return MakeGarbageCollected<CSSNumericLiteralValue>(value, type); return MakeGarbageCollected<CSSNumericLiteralValue>(value, type);
...@@ -155,6 +163,24 @@ static String FormatNumber(double number, const char* suffix) { ...@@ -155,6 +163,24 @@ static String FormatNumber(double number, const char* suffix) {
return result; return result;
} }
static String FormatInfinityOrNaN(double number, const char* suffix) {
String result;
if (std::isinf(number)) {
if (number > 0)
result = "infinity";
else
result = "-infinity";
} else {
DCHECK(std::isnan(number));
result = "NaN";
}
if (strlen(suffix) > 0)
result = result + String::Format(" * 1%s", suffix);
return result;
}
String CSSNumericLiteralValue::CustomCSSText() const { String CSSNumericLiteralValue::CustomCSSText() const {
String text; String text;
switch (GetType()) { switch (GetType()) {
...@@ -203,7 +229,13 @@ String CSSNumericLiteralValue::CustomCSSText() const { ...@@ -203,7 +229,13 @@ String CSSNumericLiteralValue::CustomCSSText() const {
// If the value is small integer, go the fast path. // If the value is small integer, go the fast path.
if (value < kMinInteger || value > kMaxInteger || if (value < kMinInteger || value > kMaxInteger ||
std::trunc(value) != value) { std::trunc(value) != value) {
text = FormatNumber(value, UnitTypeToString(GetType())); if (RuntimeEnabledFeatures::CSSCalcInfinityAndNaNEnabled() &&
(std::isinf(value) || std::isnan(value))) {
text = FormatInfinityOrNaN(value, UnitTypeToString(GetType()));
} else {
text = FormatNumber(value, UnitTypeToString(GetType()));
}
} else { } else {
StringBuilder builder; StringBuilder builder;
int int_value = value; int int_value = value;
......
...@@ -55,6 +55,9 @@ struct SameSizeAsCSSPrimitiveValue : CSSValue { ...@@ -55,6 +55,9 @@ struct SameSizeAsCSSPrimitiveValue : CSSValue {
ASSERT_SIZE(CSSPrimitiveValue, SameSizeAsCSSPrimitiveValue); ASSERT_SIZE(CSSPrimitiveValue, SameSizeAsCSSPrimitiveValue);
float CSSPrimitiveValue::ClampToCSSLengthRange(double value) { float CSSPrimitiveValue::ClampToCSSLengthRange(double value) {
// TODO(crbug.com/1133390): clampTo function could occur the DECHECK failure
// for NaN value. Therefore, infinity and NaN values should not be clamped
// here.
return clampTo<float>(value, kMinValueForCssLength, kMaxValueForCssLength); return clampTo<float>(value, kMinValueForCssLength, kMaxValueForCssLength);
} }
...@@ -261,6 +264,9 @@ uint8_t CSSPrimitiveValue::ComputeLength( ...@@ -261,6 +264,9 @@ uint8_t CSSPrimitiveValue::ComputeLength(
template <> template <>
float CSSPrimitiveValue::ComputeLength( float CSSPrimitiveValue::ComputeLength(
const CSSToLengthConversionData& conversion_data) const { const CSSToLengthConversionData& conversion_data) const {
// TODO(crbug.com/1133390): clampTo function could occur the DECHECK failure
// for NaN value. Therefore, infinity and NaN values should not be clamped
// here.
return clampTo<float>(ComputeLengthDouble(conversion_data)); return clampTo<float>(ComputeLengthDouble(conversion_data));
} }
......
...@@ -1329,6 +1329,12 @@ ...@@ -1329,6 +1329,12 @@
"max", "max",
"clamp", "clamp",
//<calc-constant>
//https://drafts.csswg.org/css-values/#calc-syntax
"infinity",
"-infinity",
"nan",
// scroll-snap-type // scroll-snap-type
// none // none
// x // x
......
...@@ -94,13 +94,16 @@ TEST(SizesMathFunctionParserTest, Basic) { ...@@ -94,13 +94,16 @@ TEST(SizesMathFunctionParserTest, Basic) {
{"calc(500px/0.5)", 1000, true, false}, {"calc(500px/0.5)", 1000, true, false},
{"calc(500px/.5)", 1000, true, false}, {"calc(500px/.5)", 1000, true, false},
{"calc(500/0)", 0, false, false}, {"calc(500/0)", 0, false, false},
{"calc(500px/0)", 0, false, false},
{"calc(-500px/10)", 0, true, {"calc(-500px/10)", 0, true,
true}, // CSSCalculationValue does not clamp negative values to 0. true}, // CSSCalculationValue does not clamp negative values to 0.
{"calc(((4) * ((10px))))", 40, true, false}, {"calc(((4) * ((10px))))", 40, true, false},
{"calc(50px / 0)", 0, false, false}, // TODO(crbug.com/1133390): These test cases failed with Infinity and NaN
// parsing implementation. Below tests will be reactivated when the
// sizes_math function supports the infinity and NaN.
//{"calc(500px/0)", 0, false, false},
//{"calc(50px / 0)", 0, false, false},
//{"calc(50px / (10 - 10))", 0, false, false},
{"calc(50px / (10 + 10))", 2.5, true, false}, {"calc(50px / (10 + 10))", 2.5, true, false},
{"calc(50px / (10 - 10))", 0, false, false},
{"calc(50px / (10 * 10))", 0.5, true, false}, {"calc(50px / (10 * 10))", 0.5, true, false},
{"calc(50px / (10 / 10))", 50, true, false}, {"calc(50px / (10 / 10))", 50, true, false},
{"calc(200px*)", 0, false, false}, {"calc(200px*)", 0, false, false},
......
...@@ -454,6 +454,11 @@ ...@@ -454,6 +454,11 @@
name: "CSSCalcAsInt", name: "CSSCalcAsInt",
status: "test", status: "test",
}, },
{
// https://github.com/DevSDK/calc-infinity-and-NaN/blob/master/explainer.md
name: "CSSCalcInfinityAndNaN",
status: "test",
},
{ {
// When the color-scheme is supported via the CSS color-scheme property // When the color-scheme is supported via the CSS color-scheme property
// (CSSColorScheme) or the meta tag (MetaColorScheme), the only UA // (CSSColorScheme) or the meta tag (MetaColorScheme), the only UA
......
This tests catching of divide by 0 in calc() at parse time
100px / 0 =>
100px / (0) =>
100px / (2 - 2) =>
100px / (2 - (-62 + 64)) =>
100px * (1 / 0) =>
100px * (1 / (0)) =>
100px * (1 / (2 - 2)) =>
100px * (1 / (2 - (-62 + 64))) =>
<!DOCTYPE HTML>
<div id="dummy"></div>
<div id="results">This tests catching of divide by 0 in calc() at parse time<br><br></div>
<script>
if (window.testRunner)
testRunner.dumpAsText();
var tests = [
"100px / 0",
"100px / (0)",
"100px / (2 - 2)",
"100px / (2 - (-62 + 64))",
"100px * (1 / 0)",
"100px * (1 / (0))",
"100px * (1 / (2 - 2))",
"100px * (1 / (2 - (-62 + 64)))",
];
var results = document.getElementById("results");
var dummy = document.getElementById("dummy");
for (var i = 0; i < tests.length; ++i) {
var expression = tests[i];
dummy.style.width = 'calc(' + expression + ')';
results.innerHTML += expression + " => " + dummy.style.width + "<br>";
}
</script>
\ No newline at end of file
This is a testharness.js-based test.
PASS 'calc(100px * 0 / 0)' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(100px / 0)' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px / (0))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px / (2 - 2))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px / (2 - (-62 + 64)))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px * (1 / 0))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px * (1 / (0)))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px * (1 / (2 - 2)))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(100px * (1 / (2 - (-62 + 64))))' as a specified value should serialize as 'calc(infinity * 1px)'.
FAIL 'calc(1px * max(1/0, 0))' as a specified value should serialize as 'calc(infinity * 1px)'. assert_equals: 'calc(1px * max(1/0, 0))' and 'calc(infinity * 1px)' should serialize the same in specified values. expected "calc(infinity * 1px)" but got "calc(1px * max(infinity, 0))"
FAIL 'calc(1px * min(1/0, 0))' as a specified value should serialize as 'calc(0px)'. assert_equals: 'calc(1px * min(1/0, 0))' and 'calc(0px)' should serialize the same in specified values. expected "calc(0px)" but got "calc(1px * min(infinity, 0))"
FAIL 'calc(1px * max(0/0, 0))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(0/0, 0))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(NaN, 0))"
FAIL 'calc(1px * min(0/0, 0))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * min(0/0, 0))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * min(NaN, 0))"
FAIL 'calc(1px * max(0/0, min(0,10)))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(0/0, min(0,10)))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(NaN, min(0, 10)))"
FAIL 'calc(1px * clamp(0/0, 0, 10))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * clamp(0/0, 0, 10))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * clamp(NaN, 0, 10))"
FAIL 'calc(1px * max(0, min(10, 0/0)))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(0, min(10, 0/0)))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(0, min(10, NaN)))"
FAIL 'calc(1px * clamp(0, 10, 0/0))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * clamp(0, 10, 0/0))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * clamp(0, 10, NaN))"
FAIL 'calc(1px * max(0, min(0/0, 10)))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(0, min(0/0, 10)))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(0, min(NaN, 10)))"
FAIL 'calc(1px * clamp(0, 0/0, 10))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * clamp(0, 0/0, 10))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * clamp(0, NaN, 10))"
FAIL 'calc(1px * clamp(-1/0, 0, 1/0))' as a specified value should serialize as 'calc(0px)'. assert_equals: 'calc(1px * clamp(-1/0, 0, 1/0))' and 'calc(0px)' should serialize the same in specified values. expected "calc(0px)" but got "calc(1px * clamp(-infinity, 0, infinity))"
FAIL 'calc(1px * clamp(-1/0, 1/0, 10))' as a specified value should serialize as 'calc(10px)'. assert_equals: 'calc(1px * clamp(-1/0, 1/0, 10))' and 'calc(10px)' should serialize the same in specified values. expected "calc(10px)" but got "calc(1px * clamp(-infinity, infinity, 10))"
Harness: the test ran to completion.
<!DOCTYPE HTML>
<title>Zero Division: calc() serialization.</title>
<link rel="author" title="Seokho Song" href="mailto:0xdevssh@gmail.com">
<link rel="help" href="https://drafts.csswg.org/css-values/#calc-type-checking">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/serialize-testcommon.js"></script>
<div id="target"></div>
<div id="log"></div>
<script>
function test_serialization(t,s, {prop="width"}={}) {
test_specified_serialization(prop, t, s)
}
//TEST CASE |EXPECTED
var test_map = {
"100px * 0 / 0" :"calc(NaN * 1px)",
"100px / 0" :"calc(infinity * 1px)",
"100px / (0)" :"calc(infinity * 1px)",
"100px / (2 - 2)" :"calc(infinity * 1px)",
"100px / (2 - (-62 + 64))" :"calc(infinity * 1px)",
"100px * (1 / 0)" :"calc(infinity * 1px)",
"100px * (1 / (0))" :"calc(infinity * 1px)",
"100px * (1 / (2 - 2))" :"calc(infinity * 1px)",
"100px * (1 / (2 - (-62 + 64)))" :"calc(infinity * 1px)",
"1px * max(1/0, 0)" :"calc(infinity * 1px)",
"1px * min(1/0, 0)" :"calc(0px)",
"1px * max(0/0, 0)" :"calc(NaN * 1px)",
"1px * min(0/0, 0)" :"calc(NaN * 1px)",
"1px * max(0/0, min(0,10))" :"calc(NaN * 1px)",
"1px * clamp(0/0, 0, 10)" :"calc(NaN * 1px)",
"1px * max(0, min(10, 0/0))" :"calc(NaN * 1px)",
"1px * clamp(0, 10, 0/0)" :"calc(NaN * 1px)",
"1px * max(0, min(0/0, 10))" :"calc(NaN * 1px)",
"1px * clamp(0, 0/0, 10)" :"calc(NaN * 1px)",
"1px * clamp(-1/0, 0, 1/0)" :"calc(0px)",
"1px * clamp(-1/0, 1/0, 10)" :"calc(10px)",
};
for (var exp in test_map) {
test_serialization("calc("+exp+")", test_map[exp])
}
</script>
\ No newline at end of file
This is a testharness.js-based test.
PASS 'calc(1px * NaN)' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * nan)' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * infinity / infinity)' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * 0 * infinity)' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * (infinity + -infinity))' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * (-infinity + infinity))' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * (infinity - infinity))' as a specified value should serialize as 'calc(NaN * 1px)'.
PASS 'calc(1px * infinity)' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(1px * -infinity)' as a specified value should serialize as 'calc(-infinity * 1px)'.
PASS 'calc(1px * iNFinIty)' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(1px * (infinity + infinity))' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(1px * (-infinity + -infinity))' as a specified value should serialize as 'calc(-infinity * 1px)'.
PASS 'calc(1px * 1/infinity)' as a specified value should serialize as 'calc(0px)'.
PASS 'calc(1px * infinity * infinity)' as a specified value should serialize as 'calc(infinity * 1px)'.
PASS 'calc(1px * -infinity * -infinity)' as a specified value should serialize as 'calc(infinity * 1px)'.
FAIL 'calc(1 * max(INFinity*3px, 0px))' as a specified value should serialize as 'calc(infinity * 1px)'. assert_equals: 'calc(1 * max(INFinity*3px, 0px))' and 'calc(infinity * 1px)' should serialize the same in specified values. expected "calc(infinity * 1px)" but got "calc(1 * max(infinity * 1px, 0px))"
FAIL 'calc(1 * min(inFInity*4px, 0px))' as a specified value should serialize as 'calc(0px)'. assert_equals: 'calc(1 * min(inFInity*4px, 0px))' and 'calc(0px)' should serialize the same in specified values. expected "calc(0px)" but got "calc(1 * min(infinity * 1px, 0px))"
FAIL 'calc(1 * max(nAn*2px, 0px))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1 * max(nAn*2px, 0px))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1 * max(NaN * 1px, 0px))"
FAIL 'calc(1 * min(nan*3px, 0px))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1 * min(nan*3px, 0px))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1 * min(NaN * 1px, 0px))"
FAIL 'calc(1 * clamp(-INFINITY*20px, 0px, infiniTY*10px))' as a specified value should serialize as 'calc(0px)'. assert_equals: 'calc(1 * clamp(-INFINITY*20px, 0px, infiniTY*10px))' and 'calc(0px)' should serialize the same in specified values. expected "calc(0px)" but got "calc(1 * clamp(-infinity * 1px, 0px, infinity * 1px))"
FAIL 'calc(1px * max(NaN, min(0,10)))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(NaN, min(0,10)))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(NaN, min(0, 10)))"
FAIL 'calc(1px * clamp(NaN, 0, 10))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * clamp(NaN, 0, 10))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * clamp(NaN, 0, 10))"
FAIL 'calc(1px * max(0, min(10, NaN)))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(0, min(10, NaN)))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(0, min(10, NaN)))"
FAIL 'calc(1px * clamp(0, 10, NaN))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * clamp(0, 10, NaN))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * clamp(0, 10, NaN))"
FAIL 'calc(1px * max(0, min(NaN, 10)))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * max(0, min(NaN, 10)))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * max(0, min(NaN, 10)))"
FAIL 'calc(1px * clamp(0, NaN, 10))' as a specified value should serialize as 'calc(NaN * 1px)'. assert_equals: 'calc(1px * clamp(0, NaN, 10))' and 'calc(NaN * 1px)' should serialize the same in specified values. expected "calc(NaN * 1px)" but got "calc(1px * clamp(0, NaN, 10))"
FAIL 'calc(1px * clamp(-Infinity, 0, infinity))' as a specified value should serialize as 'calc(0px)'. assert_equals: 'calc(1px * clamp(-Infinity, 0, infinity))' and 'calc(0px)' should serialize the same in specified values. expected "calc(0px)" but got "calc(1px * clamp(-infinity, 0, infinity))"
FAIL 'calc(1px * clamp(-inFinity, infinity, 10))' as a specified value should serialize as 'calc(10px)'. assert_equals: 'calc(1px * clamp(-inFinity, infinity, 10))' and 'calc(10px)' should serialize the same in specified values. expected "calc(10px)" but got "calc(1px * clamp(-infinity, infinity, 10))"
Harness: the test ran to completion.
<!DOCTYPE HTML>
<title>Infinity and NaN: calc() serialization.</title>
<link rel="author" title="Seokho Song" href="mailto:0xdevssh@gmail.com">
<link rel="help" href="https://drafts.csswg.org/css-values/#calc-type-checking">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/serialize-testcommon.js"></script>
<div id="target"></div>
<div id="log"></div>
<script>
function test_serialization(t,s, {prop="width"}={}) {
test_specified_serialization(prop, t, s)
}
//TEST CASE | EXPECTED
var test_map = {
"1px * NaN" :"calc(NaN * 1px)",
"1px * nan" :"calc(NaN * 1px)",
"1px * infinity / infinity" :"calc(NaN * 1px)",
"1px * 0 * infinity" :"calc(NaN * 1px)",
"1px * (infinity + -infinity)" :"calc(NaN * 1px)",
"1px * (-infinity + infinity)" :"calc(NaN * 1px)",
"1px * (infinity - infinity)" :"calc(NaN * 1px)",
"1px * infinity" :"calc(infinity * 1px)",
"1px * -infinity" :"calc(-infinity * 1px)",
"1px * iNFinIty" :"calc(infinity * 1px)",
"1px * (infinity + infinity)" :"calc(infinity * 1px)",
"1px * (-infinity + -infinity)" :"calc(-infinity * 1px)",
"1px * 1/infinity" :"calc(0px)",
"1px * infinity * infinity" :"calc(infinity * 1px)",
"1px * -infinity * -infinity" :"calc(infinity * 1px)",
"1 * max(INFinity*3px, 0px)" :"calc(infinity * 1px)",
"1 * min(inFInity*4px, 0px)" :"calc(0px)",
"1 * max(nAn*2px, 0px)" :"calc(NaN * 1px)",
"1 * min(nan*3px, 0px)" :"calc(NaN * 1px)",
"1 * clamp(-INFINITY*20px, 0px, infiniTY*10px)" :"calc(0px)",
"1px * max(NaN, min(0,10))" :"calc(NaN * 1px)",
"1px * clamp(NaN, 0, 10)" :"calc(NaN * 1px)",
"1px * max(0, min(10, NaN))" :"calc(NaN * 1px)",
"1px * clamp(0, 10, NaN)" :"calc(NaN * 1px)",
"1px * max(0, min(NaN, 10))" :"calc(NaN * 1px)",
"1px * clamp(0, NaN, 10)" :"calc(NaN * 1px)",
"1px * clamp(-Infinity, 0, infinity)" :"calc(0px)",
"1px * clamp(-inFinity, infinity, 10)" :"calc(10px)",
};
for (var exp in test_map) {
test_serialization("calc("+exp+")", test_map[exp]);
}
</script>
\ No newline at end of file
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