blob: 55b1e9317cd72eb7e0bebcac95d9822fd2904eff [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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_syntax_string_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/css_syntax_component.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
class CSSSyntaxStringParserTest : public testing::Test {
public:
base::Optional<CSSSyntaxComponent> ParseSingleComponent(
const String& syntax) {
auto definition = CSSSyntaxStringParser(syntax).Parse();
if (!definition)
return base::nullopt;
if (definition->Components().size() != 1)
return base::nullopt;
return definition->Components()[0];
}
base::Optional<CSSSyntaxType> ParseSingleType(const String& syntax) {
auto component = ParseSingleComponent(syntax);
return component ? base::make_optional(component->GetType())
: base::nullopt;
}
String ParseSingleIdent(const String& syntax) {
auto component = ParseSingleComponent(syntax);
if (!component || component->GetType() != CSSSyntaxType::kIdent)
return g_empty_string;
return component->GetString();
}
size_t ParseNumberOfComponents(const String& syntax) {
auto definition = CSSSyntaxStringParser(syntax).Parse();
if (!definition)
return 0;
return definition->Components().size();
}
CSSSyntaxDefinition CreateUniversalDescriptor() {
return CSSSyntaxDefinition::CreateUniversal();
}
};
TEST_F(CSSSyntaxStringParserTest, UniversalDescriptor) {
auto universal = CreateUniversalDescriptor();
EXPECT_TRUE(universal.IsUniversal());
EXPECT_EQ(universal, *CSSSyntaxStringParser("*").Parse());
EXPECT_EQ(universal, *CSSSyntaxStringParser(" * ").Parse());
EXPECT_EQ(universal, *CSSSyntaxStringParser("\r*\r\n").Parse());
EXPECT_EQ(universal, *CSSSyntaxStringParser("\f*\f").Parse());
EXPECT_EQ(universal, *CSSSyntaxStringParser(" \n\t\r\f*").Parse());
}
TEST_F(CSSSyntaxStringParserTest, ValidDataType) {
EXPECT_EQ(CSSSyntaxType::kLength, *ParseSingleType("<length>"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType("<number>"));
EXPECT_EQ(CSSSyntaxType::kPercentage, *ParseSingleType("<percentage>"));
EXPECT_EQ(CSSSyntaxType::kLengthPercentage,
*ParseSingleType("<length-percentage>"));
EXPECT_EQ(CSSSyntaxType::kColor, *ParseSingleType("<color>"));
EXPECT_EQ(CSSSyntaxType::kImage, *ParseSingleType("<image>"));
EXPECT_EQ(CSSSyntaxType::kUrl, *ParseSingleType("<url>"));
EXPECT_EQ(CSSSyntaxType::kInteger, *ParseSingleType("<integer>"));
EXPECT_EQ(CSSSyntaxType::kAngle, *ParseSingleType("<angle>"));
EXPECT_EQ(CSSSyntaxType::kTime, *ParseSingleType("<time>"));
EXPECT_EQ(CSSSyntaxType::kResolution, *ParseSingleType("<resolution>"));
EXPECT_EQ(CSSSyntaxType::kTransformFunction,
*ParseSingleType("<transform-function>"));
EXPECT_EQ(CSSSyntaxType::kTransformList,
*ParseSingleType("<transform-list>"));
EXPECT_EQ(CSSSyntaxType::kCustomIdent, *ParseSingleType("<custom-ident>"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType(" <number>"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType("\r\n<number>"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType(" \t <number>"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType("<number> "));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType("<number>\n"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType("<number>\r\n"));
EXPECT_EQ(CSSSyntaxType::kNumber, *ParseSingleType("\f<number>\f"));
}
TEST_F(CSSSyntaxStringParserTest, InvalidDataType) {
EXPECT_FALSE(CSSSyntaxStringParser("< length>").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<length >").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<\tlength >").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<").Parse());
EXPECT_FALSE(CSSSyntaxStringParser(">").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<>").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("< >").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<length").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<\\61>").Parse());
EXPECT_FALSE(CSSSyntaxStringParser(" <\\61> ").Parse());
// Syntactically valid, but names unsupported data types.
EXPECT_FALSE(CSSSyntaxStringParser("<unsupported>").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<lengths>").Parse());
}
TEST_F(CSSSyntaxStringParserTest, Idents) {
EXPECT_EQ("foo", ParseSingleIdent("foo"));
EXPECT_EQ("foo", ParseSingleIdent(" foo"));
EXPECT_EQ("foo", ParseSingleIdent("foo "));
EXPECT_EQ("foo", ParseSingleIdent("foo "));
EXPECT_EQ("foo", ParseSingleIdent("\t\rfoo "));
EXPECT_EQ("_foo", ParseSingleIdent("_foo "));
EXPECT_EQ("foo-bar", ParseSingleIdent("foo-bar"));
EXPECT_EQ("abc", ParseSingleIdent("\\61 b\\63"));
EXPECT_EQ("azc", ParseSingleIdent("\\61z\\63"));
}
TEST_F(CSSSyntaxStringParserTest, InvalidIdents) {
EXPECT_FALSE(CSSSyntaxStringParser("-foo").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("007").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("initial").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("inherit").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("unset").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("default").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("revert").Parse());
}
TEST_F(CSSSyntaxStringParserTest, Combinator) {
{
auto desc = CSSSyntaxStringParser("<length> | <color>").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(2u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kLength, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxType::kColor, desc->Components()[1].GetType());
}
{
auto desc = CSSSyntaxStringParser("<integer> | foo | <color>").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(3u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kInteger, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[1].GetType());
EXPECT_EQ(CSSSyntaxType::kColor, desc->Components()[2].GetType());
EXPECT_EQ("foo", desc->Components()[1].GetString());
}
{
auto desc = CSSSyntaxStringParser("a|\\62|c").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(3u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[1].GetType());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[2].GetType());
EXPECT_EQ("a", desc->Components()[0].GetString());
EXPECT_EQ("b", desc->Components()[1].GetString());
EXPECT_EQ("c", desc->Components()[2].GetString());
}
}
TEST_F(CSSSyntaxStringParserTest, CombinatorWhitespace) {
EXPECT_EQ(2u, ParseNumberOfComponents("<length>|<color>"));
EXPECT_EQ(3u, ParseNumberOfComponents("a|<color>|b"));
EXPECT_EQ(3u, ParseNumberOfComponents("a\t\n| <color>\r\n | b "));
}
TEST_F(CSSSyntaxStringParserTest, InvalidCombinator) {
EXPECT_FALSE(CSSSyntaxStringParser("|<color>").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("\f| <color>").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("a||b").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("a| |b").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("a|\t|b").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("|").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("foo|").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("foo||").Parse());
}
TEST_F(CSSSyntaxStringParserTest, Multipliers) {
{
auto desc = CSSSyntaxStringParser("<length>").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(1u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kLength, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kNone, desc->Components()[0].GetRepeat());
}
{
auto desc = CSSSyntaxStringParser("foo").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(1u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kNone, desc->Components()[0].GetRepeat());
}
{
auto desc = CSSSyntaxStringParser("<length>+").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(1u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kLength, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kSpaceSeparated,
desc->Components()[0].GetRepeat());
}
{
auto desc = CSSSyntaxStringParser("<color>#").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(1u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kColor, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kCommaSeparated,
desc->Components()[0].GetRepeat());
}
{
auto desc = CSSSyntaxStringParser("foo#").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(1u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kCommaSeparated,
desc->Components()[0].GetRepeat());
}
}
TEST_F(CSSSyntaxStringParserTest, InvalidMultipliers) {
EXPECT_FALSE(CSSSyntaxStringParser("<length>*").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<length>?").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<length> +").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<color>\t#").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("foo #").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("foo{4}").Parse());
// Stacking multipliers may supported in the future, but it's currently
// not allowed by the spec.
EXPECT_FALSE(CSSSyntaxStringParser("<length>+#").Parse());
}
TEST_F(CSSSyntaxStringParserTest, CombinatorWithMultipliers) {
{
auto desc = CSSSyntaxStringParser("<length>+ | <color>#").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(2u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kLength, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kSpaceSeparated,
desc->Components()[0].GetRepeat());
EXPECT_EQ(CSSSyntaxType::kColor, desc->Components()[1].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kCommaSeparated,
desc->Components()[1].GetRepeat());
}
{
auto desc = CSSSyntaxStringParser("<length>+ | <color> | foo#").Parse();
ASSERT_TRUE(desc);
EXPECT_EQ(3u, desc->Components().size());
EXPECT_EQ(CSSSyntaxType::kLength, desc->Components()[0].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kSpaceSeparated,
desc->Components()[0].GetRepeat());
EXPECT_EQ(CSSSyntaxType::kColor, desc->Components()[1].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kNone, desc->Components()[1].GetRepeat());
EXPECT_EQ(CSSSyntaxType::kIdent, desc->Components()[2].GetType());
EXPECT_EQ(CSSSyntaxRepeat::kCommaSeparated,
desc->Components()[2].GetRepeat());
}
}
TEST_F(CSSSyntaxStringParserTest, PreMultiplied) {
// Multipliers may not be used on data type names that are pre-multiplied.
EXPECT_FALSE(CSSSyntaxStringParser("<transform-list>#").Parse());
EXPECT_FALSE(CSSSyntaxStringParser("<transform-list>+").Parse());
}
} // namespace blink