blob: 2f323a9d17d7f881d605be595c6c5ec02b805ba6 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/css_math_expression_node.h"
#include "third_party/blink/renderer/core/css/css_math_function_value.h"
#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
namespace {
class CSSPrimitiveValueTest : public PageTestBase {
public:
const CSSPrimitiveValue* ParseValue(const char* text) {
const CSSPrimitiveValue* value = To<CSSPrimitiveValue>(
css_test_helpers::ParseValue(GetDocument(), "<length>", text));
DCHECK(value);
return value;
}
bool HasContainerRelativeUnits(const char* text) {
return ParseValue(text)->HasContainerRelativeUnits();
}
bool HasStaticViewportUnits(const char* text) {
const CSSPrimitiveValue* value = ParseValue(text);
CSSPrimitiveValue::LengthTypeFlags length_type_flags;
value->AccumulateLengthUnitTypes(length_type_flags);
return CSSPrimitiveValue::HasStaticViewportUnits(length_type_flags);
}
bool HasDynamicViewportUnits(const char* text) {
const CSSPrimitiveValue* value = ParseValue(text);
CSSPrimitiveValue::LengthTypeFlags length_type_flags;
value->AccumulateLengthUnitTypes(length_type_flags);
return CSSPrimitiveValue::HasDynamicViewportUnits(length_type_flags);
}
CSSPrimitiveValueTest() = default;
};
using UnitType = CSSPrimitiveValue::UnitType;
struct UnitValue {
double value;
UnitType unit_type;
};
CSSNumericLiteralValue* Create(UnitValue v) {
return CSSNumericLiteralValue::Create(v.value, v.unit_type);
}
CSSPrimitiveValue* CreateAddition(UnitValue a, UnitValue b) {
return CSSMathFunctionValue::Create(
CSSMathExpressionOperation::CreateArithmeticOperation(
CSSMathExpressionNumericLiteral::Create(Create(a)),
CSSMathExpressionNumericLiteral::Create(Create(b)),
CSSMathOperator::kAdd));
}
CSSPrimitiveValue* CreateNonNegativeSubtraction(UnitValue a, UnitValue b) {
return CSSMathFunctionValue::Create(
CSSMathExpressionOperation::CreateArithmeticOperation(
CSSMathExpressionNumericLiteral::Create(Create(a)),
CSSMathExpressionNumericLiteral::Create(Create(b)),
CSSMathOperator::kSubtract),
CSSPrimitiveValue::ValueRange::kNonNegative);
}
UnitType ToCanonicalUnit(CSSPrimitiveValue::UnitType unit) {
return CSSPrimitiveValue::CanonicalUnitTypeForCategory(
CSSPrimitiveValue::UnitTypeToUnitCategory(unit));
}
TEST_F(CSSPrimitiveValueTest, IsTime) {
EXPECT_FALSE(Create({5.0, UnitType::kNumber})->IsTime());
EXPECT_FALSE(Create({5.0, UnitType::kDegrees})->IsTime());
EXPECT_TRUE(Create({5.0, UnitType::kSeconds})->IsTime());
EXPECT_TRUE(Create({5.0, UnitType::kMilliseconds})->IsTime());
}
TEST_F(CSSPrimitiveValueTest, IsTimeCalc) {
{
UnitValue a = {1.0, UnitType::kSeconds};
UnitValue b = {1000.0, UnitType::kMilliseconds};
EXPECT_TRUE(CreateAddition(a, b)->IsTime());
}
{
UnitValue a = {1.0, UnitType::kDegrees};
UnitValue b = {1000.0, UnitType::kGradians};
EXPECT_FALSE(CreateAddition(a, b)->IsTime());
}
}
TEST_F(CSSPrimitiveValueTest, ClampTimeToNonNegative) {
UnitValue a = {4926, UnitType::kMilliseconds};
UnitValue b = {5, UnitType::kSeconds};
EXPECT_EQ(0.0, CreateNonNegativeSubtraction(a, b)->ComputeSeconds());
}
TEST_F(CSSPrimitiveValueTest, ClampAngleToNonNegative) {
UnitValue a = {89, UnitType::kDegrees};
UnitValue b = {0.25, UnitType::kTurns};
EXPECT_EQ(0.0, CreateNonNegativeSubtraction(a, b)->ComputeDegrees(
CSSToLengthConversionData()));
}
TEST_F(CSSPrimitiveValueTest, IsResolution) {
EXPECT_FALSE(Create({5.0, UnitType::kNumber})->IsResolution());
EXPECT_FALSE(Create({5.0, UnitType::kDegrees})->IsResolution());
EXPECT_TRUE(Create({5.0, UnitType::kDotsPerPixel})->IsResolution());
EXPECT_TRUE(Create({5.0, UnitType::kX})->IsResolution());
EXPECT_TRUE(Create({5.0, UnitType::kDotsPerInch})->IsResolution());
EXPECT_TRUE(Create({5.0, UnitType::kDotsPerCentimeter})->IsResolution());
}
// https://crbug.com/999875
TEST_F(CSSPrimitiveValueTest, Zooming) {
// Tests that the conversion CSSPrimitiveValue -> Length -> CSSPrimitiveValue
// yields the same value under zooming.
UnitValue a = {100, UnitType::kPixels};
UnitValue b = {10, UnitType::kPercentage};
CSSPrimitiveValue* original = CreateAddition(a, b);
CSSToLengthConversionData conversion_data;
conversion_data.SetZoom(0.5);
Length length = original->ConvertToLength(conversion_data);
EXPECT_TRUE(length.IsCalculated());
EXPECT_EQ(50.0, length.GetPixelsAndPercent().pixels);
EXPECT_EQ(10.0, length.GetPixelsAndPercent().percent);
CSSPrimitiveValue* converted =
CSSPrimitiveValue::CreateFromLength(length, conversion_data.Zoom());
EXPECT_TRUE(converted->IsMathFunctionValue());
EXPECT_EQ("calc(10% + 100px)", converted->CustomCSSText());
}
TEST_F(CSSPrimitiveValueTest, PositiveInfinityLengthClamp) {
UnitValue a = {std::numeric_limits<double>::infinity(), UnitType::kPixels};
UnitValue b = {1, UnitType::kPixels};
CSSPrimitiveValue* value = CreateAddition(a, b);
CSSToLengthConversionData conversion_data;
EXPECT_EQ(std::numeric_limits<double>::max(),
value->ComputeLength<double>(conversion_data));
}
TEST_F(CSSPrimitiveValueTest, NegativeInfinityLengthClamp) {
UnitValue a = {-std::numeric_limits<double>::infinity(), UnitType::kPixels};
UnitValue b = {1, UnitType::kPixels};
CSSPrimitiveValue* value = CreateAddition(a, b);
CSSToLengthConversionData conversion_data;
EXPECT_EQ(std::numeric_limits<double>::lowest(),
value->ComputeLength<double>(conversion_data));
}
TEST_F(CSSPrimitiveValueTest, NaNLengthClamp) {
UnitValue a = {-std::numeric_limits<double>::quiet_NaN(), UnitType::kPixels};
UnitValue b = {1, UnitType::kPixels};
CSSPrimitiveValue* value = CreateAddition(a, b);
CSSToLengthConversionData conversion_data;
EXPECT_EQ(0.0, value->ComputeLength<double>(conversion_data));
}
TEST_F(CSSPrimitiveValueTest, PositiveInfinityPercentLengthClamp) {
CSSPrimitiveValue* value =
Create({std::numeric_limits<double>::infinity(), UnitType::kPercentage});
CSSToLengthConversionData conversion_data;
Length length = value->ConvertToLength(conversion_data);
EXPECT_EQ(std::numeric_limits<float>::max(), length.Percent());
}
TEST_F(CSSPrimitiveValueTest, NegativeInfinityPercentLengthClamp) {
CSSPrimitiveValue* value =
Create({-std::numeric_limits<double>::infinity(), UnitType::kPercentage});
CSSToLengthConversionData conversion_data;
Length length = value->ConvertToLength(conversion_data);
EXPECT_EQ(std::numeric_limits<float>::lowest(), length.Percent());
}
TEST_F(CSSPrimitiveValueTest, NaNPercentLengthClamp) {
CSSPrimitiveValue* value = Create(
{-std::numeric_limits<double>::quiet_NaN(), UnitType::kPercentage});
CSSToLengthConversionData conversion_data;
Length length = value->ConvertToLength(conversion_data);
EXPECT_EQ(0.0, length.Percent());
}
TEST_F(CSSPrimitiveValueTest, GetDoubleValueWithoutClampingAllowNaN) {
CSSPrimitiveValue* value =
Create({std::numeric_limits<double>::quiet_NaN(), UnitType::kPixels});
EXPECT_TRUE(std::isnan(value->GetDoubleValueWithoutClamping()));
}
TEST_F(CSSPrimitiveValueTest,
GetDoubleValueWithoutClampingAllowPositveInfinity) {
CSSPrimitiveValue* value =
Create({std::numeric_limits<double>::infinity(), UnitType::kPixels});
EXPECT_TRUE(std::isinf(value->GetDoubleValueWithoutClamping()) &&
value->GetDoubleValueWithoutClamping() > 0);
}
TEST_F(CSSPrimitiveValueTest,
GetDoubleValueWithoutClampingAllowNegativeInfinity) {
CSSPrimitiveValue* value =
Create({-std::numeric_limits<double>::infinity(), UnitType::kPixels});
EXPECT_TRUE(std::isinf(value->GetDoubleValueWithoutClamping()) &&
value->GetDoubleValueWithoutClamping() < 0);
}
TEST_F(CSSPrimitiveValueTest, GetDoubleValueClampNaN) {
CSSPrimitiveValue* value =
Create({std::numeric_limits<double>::quiet_NaN(), UnitType::kPixels});
EXPECT_EQ(0.0, value->GetDoubleValue());
}
TEST_F(CSSPrimitiveValueTest, GetDoubleValueClampPositiveInfinity) {
CSSPrimitiveValue* value =
Create({std::numeric_limits<double>::infinity(), UnitType::kPixels});
EXPECT_EQ(std::numeric_limits<double>::max(), value->GetDoubleValue());
}
TEST_F(CSSPrimitiveValueTest, GetDoubleValueClampNegativeInfinity) {
CSSPrimitiveValue* value =
Create({-std::numeric_limits<double>::infinity(), UnitType::kPixels});
EXPECT_EQ(std::numeric_limits<double>::lowest(), value->GetDoubleValue());
}
TEST_F(CSSPrimitiveValueTest, TestCanonicalizingNumberUnitCategory) {
UnitType canonicalized_from_num = ToCanonicalUnit(UnitType::kNumber);
EXPECT_EQ(canonicalized_from_num, UnitType::kNumber);
UnitType canonicalized_from_int = ToCanonicalUnit(UnitType::kInteger);
EXPECT_EQ(canonicalized_from_int, UnitType::kNumber);
}
TEST_F(CSSPrimitiveValueTest, HasContainerRelativeUnits) {
EXPECT_TRUE(HasContainerRelativeUnits("1cqw"));
EXPECT_TRUE(HasContainerRelativeUnits("1cqh"));
EXPECT_TRUE(HasContainerRelativeUnits("1cqi"));
EXPECT_TRUE(HasContainerRelativeUnits("1cqb"));
EXPECT_TRUE(HasContainerRelativeUnits("1cqmin"));
EXPECT_TRUE(HasContainerRelativeUnits("1cqmax"));
EXPECT_TRUE(HasContainerRelativeUnits("calc(1px + 1cqw)"));
EXPECT_TRUE(HasContainerRelativeUnits("min(1px, 1cqw)"));
EXPECT_FALSE(HasContainerRelativeUnits("1px"));
EXPECT_FALSE(HasContainerRelativeUnits("1em"));
EXPECT_FALSE(HasContainerRelativeUnits("1vh"));
EXPECT_FALSE(HasContainerRelativeUnits("1svh"));
EXPECT_FALSE(HasContainerRelativeUnits("calc(1px + 1px)"));
EXPECT_FALSE(HasContainerRelativeUnits("calc(1px + 1em)"));
EXPECT_FALSE(HasContainerRelativeUnits("calc(1px + 1svh)"));
}
TEST_F(CSSPrimitiveValueTest, HasStaticViewportUnits) {
// v*
EXPECT_TRUE(HasStaticViewportUnits("1vw"));
EXPECT_TRUE(HasStaticViewportUnits("1vh"));
EXPECT_TRUE(HasStaticViewportUnits("1vi"));
EXPECT_TRUE(HasStaticViewportUnits("1vb"));
EXPECT_TRUE(HasStaticViewportUnits("1vmin"));
EXPECT_TRUE(HasStaticViewportUnits("1vmax"));
EXPECT_TRUE(HasStaticViewportUnits("calc(1px + 1vw)"));
EXPECT_TRUE(HasStaticViewportUnits("min(1px, 1vw)"));
EXPECT_FALSE(HasStaticViewportUnits("1px"));
EXPECT_FALSE(HasStaticViewportUnits("1em"));
EXPECT_FALSE(HasStaticViewportUnits("1dvh"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1px)"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1em)"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1dvh)"));
// sv*
EXPECT_TRUE(HasStaticViewportUnits("1svw"));
EXPECT_TRUE(HasStaticViewportUnits("1svh"));
EXPECT_TRUE(HasStaticViewportUnits("1svi"));
EXPECT_TRUE(HasStaticViewportUnits("1svb"));
EXPECT_TRUE(HasStaticViewportUnits("1svmin"));
EXPECT_TRUE(HasStaticViewportUnits("1svmax"));
EXPECT_TRUE(HasStaticViewportUnits("calc(1px + 1svw)"));
EXPECT_TRUE(HasStaticViewportUnits("min(1px, 1svw)"));
EXPECT_FALSE(HasStaticViewportUnits("1px"));
EXPECT_FALSE(HasStaticViewportUnits("1em"));
EXPECT_FALSE(HasStaticViewportUnits("1dvh"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1px)"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1em)"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1dvh)"));
// lv*
EXPECT_TRUE(HasStaticViewportUnits("1lvw"));
EXPECT_TRUE(HasStaticViewportUnits("1lvh"));
EXPECT_TRUE(HasStaticViewportUnits("1lvi"));
EXPECT_TRUE(HasStaticViewportUnits("1lvb"));
EXPECT_TRUE(HasStaticViewportUnits("1lvmin"));
EXPECT_TRUE(HasStaticViewportUnits("1lvmax"));
EXPECT_TRUE(HasStaticViewportUnits("calc(1px + 1lvw)"));
EXPECT_TRUE(HasStaticViewportUnits("min(1px, 1lvw)"));
EXPECT_FALSE(HasStaticViewportUnits("1px"));
EXPECT_FALSE(HasStaticViewportUnits("1em"));
EXPECT_FALSE(HasStaticViewportUnits("1dvh"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1px)"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1em)"));
EXPECT_FALSE(HasStaticViewportUnits("calc(1px + 1dvh)"));
}
TEST_F(CSSPrimitiveValueTest, HasDynamicViewportUnits) {
// dv*
EXPECT_TRUE(HasDynamicViewportUnits("1dvw"));
EXPECT_TRUE(HasDynamicViewportUnits("1dvh"));
EXPECT_TRUE(HasDynamicViewportUnits("1dvi"));
EXPECT_TRUE(HasDynamicViewportUnits("1dvb"));
EXPECT_TRUE(HasDynamicViewportUnits("1dvmin"));
EXPECT_TRUE(HasDynamicViewportUnits("1dvmax"));
EXPECT_TRUE(HasDynamicViewportUnits("calc(1px + 1dvw)"));
EXPECT_TRUE(HasDynamicViewportUnits("min(1px, 1dvw)"));
EXPECT_FALSE(HasDynamicViewportUnits("1px"));
EXPECT_FALSE(HasDynamicViewportUnits("1em"));
EXPECT_FALSE(HasDynamicViewportUnits("1svh"));
EXPECT_FALSE(HasDynamicViewportUnits("calc(1px + 1px)"));
EXPECT_FALSE(HasDynamicViewportUnits("calc(1px + 1em)"));
EXPECT_FALSE(HasDynamicViewportUnits("calc(1px + 1svh)"));
}
TEST_F(CSSPrimitiveValueTest, ComputeMethodsWithLengthResolver) {
{
auto* pxs = CSSMathExpressionNumericLiteral::Create(
12.0, CSSPrimitiveValue::UnitType::kPixels);
auto* ems = CSSMathExpressionNumericLiteral::Create(
1.0, CSSPrimitiveValue::UnitType::kEms);
auto* subtraction = CSSMathExpressionOperation::CreateArithmeticOperation(
pxs, ems, CSSMathOperator::kSubtract);
auto* sign = CSSMathExpressionOperation::CreateSignRelatedFunction(
{subtraction}, CSSValueID::kSign);
auto* degs = CSSMathExpressionNumericLiteral::Create(
10.0, CSSPrimitiveValue::UnitType::kDegrees);
auto* expression = CSSMathExpressionOperation::CreateArithmeticOperation(
sign, degs, CSSMathOperator::kMultiply);
CSSPrimitiveValue* value = CSSMathFunctionValue::Create(expression);
Font font;
CSSToLengthConversionData length_resolver = CSSToLengthConversionData();
length_resolver.SetFontSizes(
CSSToLengthConversionData::FontSizes(10.0f, 10.0f, &font, 1.0f));
EXPECT_EQ(10.0, value->ComputeDegrees(length_resolver));
EXPECT_EQ("calc(sign(-1em + 12px) * 10deg)", value->CustomCSSText());
}
}
TEST_F(CSSPrimitiveValueTest, ContainerProgressTreeScope) {
ScopedCSSProgressNotationForTest scoped_feature(true);
const CSSValue* value = css_test_helpers::ParseValue(
GetDocument(), "<number>",
"container-progress(width of my-container from 0px to 1px)");
ASSERT_TRUE(value);
const CSSValue& scoped_value = value->EnsureScopedValue(&GetDocument());
EXPECT_NE(value, &scoped_value);
EXPECT_TRUE(scoped_value.IsScopedValue());
// Don't crash:
const CSSValue& scoped_value2 =
scoped_value.EnsureScopedValue(&GetDocument());
EXPECT_TRUE(scoped_value2.IsScopedValue());
EXPECT_EQ(&scoped_value, &scoped_value2);
}
} // namespace
} // namespace blink