diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h b/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h index dc9acbb3a049..4a6e2c75c5c8 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h @@ -53,15 +53,15 @@ constexpr std::optional parseRgbFunction(CSSSyntaxParser& parser) { if (std::holds_alternative(firstValue)) { redNumber = std::get(firstValue).value; - auto green = parseNextCSSValue( - parser, CSSComponentValueDelimiter::CommaOrWhitespace); + auto green = + parseNextCSSValue(parser, CSSDelimiter::CommaOrWhitespace); if (!std::holds_alternative(green)) { return {}; } greenNumber = std::get(green).value; - auto blue = parseNextCSSValue( - parser, CSSComponentValueDelimiter::CommaOrWhitespace); + auto blue = + parseNextCSSValue(parser, CSSDelimiter::CommaOrWhitespace); if (!std::holds_alternative(blue)) { return {}; } @@ -70,31 +70,22 @@ constexpr std::optional parseRgbFunction(CSSSyntaxParser& parser) { redNumber = std::get(firstValue).value * 2.55f; auto green = parseNextCSSValue( - parser, CSSComponentValueDelimiter::CommaOrWhitespace); + parser, CSSDelimiter::CommaOrWhitespace); if (!std::holds_alternative(green)) { return {}; } greenNumber = std::get(green).value * 2.55f; auto blue = parseNextCSSValue( - parser, CSSComponentValueDelimiter::CommaOrWhitespace); + parser, CSSDelimiter::CommaOrWhitespace); if (!std::holds_alternative(blue)) { return {}; } blueNumber = std::get(blue).value * 2.55f; } - auto alphaValue = peekNextCSSValue( - parser, CSSComponentValueDelimiter::CommaOrWhitespace); - if (!std::holds_alternative(alphaValue)) { - parser.consumeComponentValue(CSSComponentValueDelimiter::CommaOrWhitespace); - } else { - alphaValue = peekNextCSSValue( - parser, CSSComponentValueDelimiter::Solidus); - if (!std::holds_alternative(alphaValue)) { - parser.consumeComponentValue(CSSComponentValueDelimiter::Solidus); - } - } + auto alphaValue = parseNextCSSValue( + parser, CSSDelimiter::CommaOrWhitespaceOrSolidus); float alphaNumber = std::holds_alternative(alphaValue) ? 1.0f : std::holds_alternative(alphaValue) diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h index 831c594380d7..29940a9ddaea 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h @@ -35,11 +35,11 @@ struct CSSDataTypeParser { if (isValidRatioPart(token.numericValue())) { float numerator = token.numericValue(); - auto denominator = peekNextCSSValue( - parser, CSSComponentValueDelimiter::Solidus); + auto denominator = + peekNextCSSValue(parser, CSSDelimiter::Solidus); if (std::holds_alternative(denominator) && isValidRatioPart(std::get(denominator).value)) { - parser.consumeComponentValue(CSSComponentValueDelimiter::Solidus); + parser.consumeComponentValue(CSSDelimiter::Solidus); return CSSRatio{numerator, std::get(denominator).value}; } diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h index b278c2683c95..3f4160a0c84f 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h @@ -89,11 +89,13 @@ concept CSSUniqueComponentValueVisitors = /** * Describes the delimeter to expect before the next component value. */ -enum class CSSComponentValueDelimiter { - Comma, +enum class CSSDelimiter { Whitespace, - CommaOrWhitespace, + OptionalWhitespace, Solidus, + Comma, + CommaOrWhitespace, + CommaOrWhitespaceOrSolidus, None, }; @@ -142,7 +144,7 @@ class CSSSyntaxParser { */ template constexpr ReturnT consumeComponentValue( - CSSComponentValueDelimiter delimiter, + CSSDelimiter delimiter, const CSSComponentValueVisitor auto&... visitors) requires(CSSUniqueComponentValueVisitors); @@ -172,7 +174,7 @@ class CSSSyntaxParser { */ template constexpr ReturnT peekComponentValue( - CSSComponentValueDelimiter delimiter, + CSSDelimiter delimiter, const CSSComponentValueVisitor auto&... visitors) requires(CSSUniqueComponentValueVisitors); @@ -229,38 +231,10 @@ struct CSSComponentValueVisitorDispatcher { CSSSyntaxParser& parser; constexpr ReturnT consumeComponentValue( - CSSComponentValueDelimiter delimiter, + CSSDelimiter delimiter, const VisitorsT&... visitors) { - switch (delimiter) { - case CSSComponentValueDelimiter::Comma: - parser.consumeWhitespace(); - if (parser.peek().type() != CSSTokenType::Comma) { - return ReturnT{}; - } - parser.consumeToken(); - parser.consumeWhitespace(); - break; - case CSSComponentValueDelimiter::Whitespace: - parser.consumeWhitespace(); - break; - case CSSComponentValueDelimiter::CommaOrWhitespace: - parser.consumeWhitespace(); - if (parser.peek().type() == CSSTokenType::Comma) { - parser.consumeToken(); - } - parser.consumeWhitespace(); - break; - case CSSComponentValueDelimiter::Solidus: - parser.consumeWhitespace(); - if (parser.peek().type() != CSSTokenType::Delim || - parser.peek().stringValue() != "/") { - return ReturnT{}; - } - parser.consumeToken(); - parser.consumeWhitespace(); - break; - case CSSComponentValueDelimiter::None: - break; + if (!consumeDelimiter(delimiter)) { + return {}; } if (parser.peek().type() == parser.terminator_) { @@ -301,8 +275,62 @@ struct CSSComponentValueVisitorDispatcher { return ReturnT{}; } + /** + * Consume a delimiter, returning false if a required delimiter is not found. + */ + constexpr bool consumeDelimiter(CSSDelimiter delimiter) { + if (delimiter == CSSDelimiter::None) { + return true; + } + + bool hasWhiteSpace = parser.peek().type() == CSSTokenType::WhiteSpace; + parser.consumeWhitespace(); + + switch (delimiter) { + case CSSDelimiter::Comma: + if (parser.peek().type() == CSSTokenType::Comma) { + parser.consumeToken(); + parser.consumeWhitespace(); + return true; + } + return false; + case CSSDelimiter::Whitespace: + return hasWhiteSpace; + case CSSDelimiter::OptionalWhitespace: + return true; + case CSSDelimiter::CommaOrWhitespace: + if (parser.peek().type() == CSSTokenType::Comma) { + parser.consumeToken(); + parser.consumeWhitespace(); + return true; + } + return hasWhiteSpace; + case CSSDelimiter::Solidus: + if (parser.peek().type() == CSSTokenType::Delim && + parser.peek().stringValue() == "/") { + parser.consumeToken(); + parser.consumeWhitespace(); + return true; + } + return false; + case CSSDelimiter::CommaOrWhitespaceOrSolidus: + if (parser.peek().type() == CSSTokenType::Comma || + (parser.peek().type() == CSSTokenType::Delim && + parser.peek().stringValue() == "/")) { + parser.consumeToken(); + parser.consumeWhitespace(); + return true; + } + return hasWhiteSpace; + case CSSDelimiter::None: + return true; + } + + return false; + } + constexpr ReturnT peekComponentValue( - CSSComponentValueDelimiter delimiter, + CSSDelimiter delimiter, const VisitorsT&... visitors) { auto originalParser = parser; auto ret = consumeComponentValue(delimiter, visitors...); @@ -393,7 +421,7 @@ struct CSSComponentValueVisitorDispatcher { template constexpr ReturnT CSSSyntaxParser::consumeComponentValue( - CSSComponentValueDelimiter delimiter, + CSSDelimiter delimiter, const CSSComponentValueVisitor auto&... visitors) requires(CSSUniqueComponentValueVisitors) { @@ -407,13 +435,12 @@ constexpr ReturnT CSSSyntaxParser::consumeComponentValue( const CSSComponentValueVisitor auto&... visitors) requires(CSSUniqueComponentValueVisitors) { - return consumeComponentValue( - CSSComponentValueDelimiter::None, visitors...); + return consumeComponentValue(CSSDelimiter::None, visitors...); } template constexpr ReturnT CSSSyntaxParser::peekComponentValue( - CSSComponentValueDelimiter delimiter, + CSSDelimiter delimiter, const CSSComponentValueVisitor auto&... visitors) requires(CSSUniqueComponentValueVisitors) { @@ -427,8 +454,7 @@ constexpr ReturnT CSSSyntaxParser::peekComponentValue( const CSSComponentValueVisitor auto&... visitors) requires(CSSUniqueComponentValueVisitors) { - return peekComponentValue( - CSSComponentValueDelimiter::None, visitors...); + return peekComponentValue(CSSDelimiter::None, visitors...); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h index fe45b378c31c..7ce6935e7c69 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h @@ -32,7 +32,7 @@ class CSSValueParser { */ template constexpr std::variant consumeValue( - CSSComponentValueDelimiter delimeter = CSSComponentValueDelimiter::None) { + CSSDelimiter delimeter = CSSDelimiter::None) { using ReturnT = std::variant; return parser_.consumeComponentValue( @@ -174,7 +174,7 @@ constexpr auto parseCSSProperty(std::string_view css) template constexpr auto parseNextCSSValue( CSSSyntaxParser& syntaxParser, - CSSComponentValueDelimiter delimeter = CSSComponentValueDelimiter::None) + CSSDelimiter delimeter = CSSDelimiter::None) -> std::variant { detail::CSSValueParser valueParser(syntaxParser); return valueParser.consumeValue(delimeter); @@ -187,7 +187,7 @@ constexpr auto parseNextCSSValue( template constexpr auto peekNextCSSValue( CSSSyntaxParser& syntaxParser, - CSSComponentValueDelimiter delimeter = CSSComponentValueDelimiter::None) + CSSDelimiter delimeter = CSSDelimiter::None) -> std::variant { auto savedParser = syntaxParser; detail::CSSValueParser valueParser(syntaxParser); diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp index 4bc7136bc2cb..dddccd1f09df 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSSyntaxParserTest.cpp @@ -14,8 +14,7 @@ TEST(CSSSyntaxParser, simple) { CSSSyntaxParser parser{"1px solid black"}; auto pxValue = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [](const CSSPreservedToken& token) { + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Dimension); EXPECT_EQ(token.numericValue(), 1.0f); EXPECT_EQ(token.unit(), "px"); @@ -24,9 +23,7 @@ TEST(CSSSyntaxParser, simple) { EXPECT_EQ(pxValue, 1.0f); auto identValue = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - - [](const CSSPreservedToken& token) { + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "solid"); return token.stringValue(); @@ -34,8 +31,7 @@ TEST(CSSSyntaxParser, simple) { EXPECT_EQ(identValue, "solid"); auto identValue2 = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [](const CSSPreservedToken& token) { + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "black"); return token.stringValue(); @@ -52,7 +48,6 @@ TEST(CSSSyntaxParser, single_function_no_args) { return function.name; auto hasMoreTokens = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(hasMoreTokens); @@ -70,7 +65,7 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) { std::vector args; args.emplace_back(blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); @@ -79,7 +74,7 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) { })); args.emplace_back(blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); @@ -88,7 +83,7 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) { })); args.emplace_back(blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); @@ -97,7 +92,7 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) { })); auto hasMoreTokens = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(hasMoreTokens); @@ -119,7 +114,7 @@ TEST(CSSSyntaxParser, single_function_with_comma_delimited_args) { std::array rgb{}; rgb[0] = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Number); EXPECT_EQ(token.numericValue(), 100); @@ -127,23 +122,21 @@ TEST(CSSSyntaxParser, single_function_with_comma_delimited_args) { }); rgb[1] = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Comma, - [](const CSSPreservedToken& token) { + CSSDelimiter::Comma, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Number); EXPECT_EQ(token.numericValue(), 200); return static_cast(token.numericValue()); }); rgb[2] = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Comma, - [](const CSSPreservedToken& token) { + CSSDelimiter::Comma, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Number); EXPECT_EQ(token.numericValue(), 50); return static_cast(token.numericValue()); }); auto hasMoreTokens = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(hasMoreTokens); @@ -165,15 +158,14 @@ TEST(CSSSyntaxParser, single_function_with_mixed_delimited_args) { std::array rgb{}; rgb[0] = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::None, - [](const CSSPreservedToken& token) { + CSSDelimiter::None, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Number); EXPECT_EQ(token.numericValue(), 100); return static_cast(token.numericValue()); }); rgb[1] = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::CommaOrWhitespace, + CSSDelimiter::CommaOrWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Number); EXPECT_EQ(token.numericValue(), 200); @@ -181,7 +173,7 @@ TEST(CSSSyntaxParser, single_function_with_mixed_delimited_args) { }); rgb[2] = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::CommaOrWhitespace, + CSSDelimiter::CommaOrWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Number); EXPECT_EQ(token.numericValue(), 50); @@ -189,7 +181,7 @@ TEST(CSSSyntaxParser, single_function_with_mixed_delimited_args) { }); auto hasMoreTokens = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(hasMoreTokens); @@ -208,7 +200,7 @@ TEST(CSSSyntaxParser, complex) { [&](const CSSFunctionBlock& function, CSSSyntaxParser& blockParser) { EXPECT_EQ(function.name, "foo"); auto identArg = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "a"); @@ -217,13 +209,13 @@ TEST(CSSSyntaxParser, complex) { EXPECT_EQ(identArg, "a"); auto barFunc = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [&](const CSSFunctionBlock& function, CSSSyntaxParser& nestedBlockParser) { EXPECT_EQ(function.name, "bar"); auto hasMoreTokens = nestedBlockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(hasMoreTokens); @@ -232,7 +224,7 @@ TEST(CSSSyntaxParser, complex) { EXPECT_EQ(barFunc, "bar"); auto hasMoreTokens = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, + CSSDelimiter::Whitespace, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(hasMoreTokens); @@ -248,8 +240,7 @@ TEST(CSSSyntaxParser, complex) { EXPECT_EQ(bazFunc, "baz"); auto pxValue = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [](const CSSPreservedToken& token) { + CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Dimension); EXPECT_EQ(token.numericValue(), 12.0f); EXPECT_EQ(token.unit(), "px"); @@ -366,8 +357,7 @@ TEST(CSSSyntaxParser, whitespace_surrounding_function_args) { EXPECT_EQ(function.name, "foo"); auto identArg = blockParser.consumeComponentValue( - CSSComponentValueDelimiter::None, - [](const CSSPreservedToken& token) { + CSSDelimiter::None, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "a"); return token.stringValue(); @@ -435,8 +425,7 @@ TEST(CSSSyntaxParser, preserved_token_without_visitor_consumed) { parser.consumeComponentValue(); auto identValue = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [](const CSSPreservedToken& token) { + CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "bar"); return token.stringValue(); @@ -451,8 +440,7 @@ TEST(CSSSyntaxParser, function_without_visitor_consumed) { parser.consumeComponentValue(); auto identValue = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [](const CSSPreservedToken& token) { + CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "bar"); return token.stringValue(); @@ -467,8 +455,7 @@ TEST(CSSSyntaxParser, simple_block_without_visitor_consumed) { parser.consumeComponentValue(); auto identValue = parser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [](const CSSPreservedToken& token) { + CSSDelimiter::Whitespace, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "bar"); return token.stringValue(); @@ -490,7 +477,7 @@ TEST(CSSSyntaxParser, solidus_delimiter) { EXPECT_EQ(identValue, "foo"); auto identValue2 = parser.consumeComponentValue( - CSSComponentValueDelimiter::Solidus, [](const CSSPreservedToken& token) { + CSSDelimiter::Solidus, [](const CSSPreservedToken& token) { EXPECT_EQ(token.type(), CSSTokenType::Ident); EXPECT_EQ(token.stringValue(), "bar"); return token.stringValue(); @@ -512,10 +499,87 @@ TEST(CSSSyntaxParser, solidus_delimiter_not_present) { EXPECT_EQ(identValue, "foo"); auto identValue2 = parser.consumeComponentValue( - CSSComponentValueDelimiter::Solidus, + CSSDelimiter::Solidus, [](const CSSPreservedToken& /*token*/) { return true; }); EXPECT_FALSE(identValue2); } +TEST(CSSSyntaxParser, required_whitespace_not_present) { + CSSSyntaxParser parser{"foo/"}; + + auto identValue = parser.consumeComponentValue( + [](const CSSPreservedToken& token) { + EXPECT_EQ(token.type(), CSSTokenType::Ident); + EXPECT_EQ(token.stringValue(), "foo"); + return token.stringValue(); + }); + + EXPECT_EQ(identValue, "foo"); + + auto delimValue1 = parser.consumeComponentValue( + CSSDelimiter::Whitespace, + [](const CSSPreservedToken& /*token*/) { return true; }); + + EXPECT_FALSE(delimValue1); + + auto delimValue2 = parser.consumeComponentValue( + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { + EXPECT_EQ(token.type(), CSSTokenType::Delim); + EXPECT_EQ(token.stringValue(), "/"); + return token.stringValue(); + }); + + EXPECT_EQ(delimValue2, "/"); +} + +TEST(CSSSyntaxParser, comma_or_whitespace_or_solidus) { + CSSSyntaxParser parser{"foo, bar / baz potato%"}; + + auto identValue1 = parser.consumeComponentValue( + CSSDelimiter::OptionalWhitespace, [](const CSSPreservedToken& token) { + EXPECT_EQ(token.type(), CSSTokenType::Ident); + EXPECT_EQ(token.stringValue(), "foo"); + return token.stringValue(); + }); + + EXPECT_EQ(identValue1, "foo"); + + auto identValue2 = parser.consumeComponentValue( + CSSDelimiter::CommaOrWhitespaceOrSolidus, + [](const CSSPreservedToken& token) { + EXPECT_EQ(token.type(), CSSTokenType::Ident); + EXPECT_EQ(token.stringValue(), "bar"); + return token.stringValue(); + }); + + EXPECT_EQ(identValue2, "bar"); + + auto identValue3 = parser.consumeComponentValue( + CSSDelimiter::CommaOrWhitespaceOrSolidus, + [](const CSSPreservedToken& token) { + EXPECT_EQ(token.type(), CSSTokenType::Ident); + EXPECT_EQ(token.stringValue(), "baz"); + return token.stringValue(); + }); + + EXPECT_EQ(identValue3, "baz"); + + auto identValue4 = parser.consumeComponentValue( + CSSDelimiter::CommaOrWhitespaceOrSolidus, + [](const CSSPreservedToken& token) { + EXPECT_EQ(token.type(), CSSTokenType::Ident); + EXPECT_EQ(token.stringValue(), "potato"); + return token.stringValue(); + }); + + EXPECT_EQ(identValue4, "potato"); + + auto delimValue1 = parser.consumeComponentValue( + CSSDelimiter::CommaOrWhitespaceOrSolidus, + [](const CSSPreservedToken& token) { return true; }); + + EXPECT_FALSE(delimValue1); +} + } // namespace facebook::react