From ffb95bedaa445d8dffac0f6b3680f0f957ba64c1 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Wed, 25 Feb 2026 02:23:05 -0800 Subject: [PATCH 1/2] Unify CSSCompoundDataType with its variant Summary: Previously, CSS compound data types used two separate types: `CSSCompoundDataType` as an empty marker struct for template parameter deduction, and `CSSVariantWithTypes>` which resolved to `std::variant` for actual storage. This resulted in redundant type aliases like `CSSTransformFunctionVariant`. This change makes `CSSCompoundDataType` a `using` alias for `std::variant` directly, so one type serves both roles: template parameter deduction for parsing AND storage. - Change `CSSCompoundDataType` from a marker struct to a `using` alias for `std::variant` - Replace `CSSValidCompoundDataType` concept with `is_variant_of_data_types` trait that pattern-matches `std::variant` where all types satisfy `CSSDataType` - Update `merge_data_types` and `merge_variant` specializations to use `std::variant` directly - Store `AllowedTypesT` directly in `CSSList` compound type specialization (since it IS now the variant) - Remove redundant `CSSTransformFunctionVariant`, `CSSFilterFunctionVariant`, and `CSSBackgroundImageVariant` aliases - Update all downstream references in conversions Changelog: [Internal] Differential Revision: D94347088 --- .../view/BackgroundImagePropsConversions.cpp | 2 +- .../components/view/FilterPropsConversions.h | 4 +- .../renderer/components/view/conversions.h | 2 +- .../react/renderer/css/CSSBackgroundImage.h | 5 --- .../react/renderer/css/CSSCompoundDataType.h | 40 +++++++++++-------- .../react/renderer/css/CSSFilter.h | 5 --- .../ReactCommon/react/renderer/css/CSSList.h | 2 +- .../react/renderer/css/CSSTransform.h | 5 --- .../react/renderer/css/CSSValueParser.h | 2 +- 9 files changed, 29 insertions(+), 38 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BackgroundImagePropsConversions.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/BackgroundImagePropsConversions.cpp index 6861ab759ba6..0875fd04e969 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BackgroundImagePropsConversions.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BackgroundImagePropsConversions.cpp @@ -477,7 +477,7 @@ void fromCSSColorStop( } std::optional fromCSSBackgroundImage( - const CSSBackgroundImageVariant& cssBackgroundImage) { + const CSSBackgroundImage& cssBackgroundImage) { if (std::holds_alternative(cssBackgroundImage)) { const auto& gradient = std::get(cssBackgroundImage); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/FilterPropsConversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/FilterPropsConversions.h index 264ea7d0c45f..c24851104535 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/FilterPropsConversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/FilterPropsConversions.h @@ -108,7 +108,7 @@ parseProcessedFilter(const PropsParserContext &context, const RawValue &value, s result = filter; } -inline FilterType filterTypeFromVariant(const CSSFilterFunctionVariant &filter) +inline FilterType filterTypeFromVariant(const CSSFilterFunction &filter) { return std::visit( [](auto &&filter) -> FilterType { @@ -148,7 +148,7 @@ inline FilterType filterTypeFromVariant(const CSSFilterFunctionVariant &filter) filter); } -inline std::optional fromCSSFilter(const CSSFilterFunctionVariant &cssFilter) +inline std::optional fromCSSFilter(const CSSFilterFunction &cssFilter) { return std::visit( [&](auto &&filter) -> std::optional { diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index 69b92b00b92b..929fe68cd58c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -550,7 +550,7 @@ inline ValueUnit cssLengthPercentageToValueUnit(const std::variant fromCSSTransformFunction(const CSSTransformFunctionVariant &cssTransform) +inline std::optional fromCSSTransformFunction(const CSSTransformFunction &cssTransform) { constexpr auto Zero = ValueUnit(0, UnitType::Point); constexpr auto One = ValueUnit(1, UnitType::Point); diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h b/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h index 6df7eee43dfc..3376d1759c8f 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h @@ -845,11 +845,6 @@ static_assert(CSSDataType); */ using CSSBackgroundImage = CSSCompoundDataType; -/** - * Variant of possible CSS background image types - */ -using CSSBackgroundImageVariant = CSSVariantWithTypes; - /** * Representation of */ diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSCompoundDataType.h b/packages/react-native/ReactCommon/react/renderer/css/CSSCompoundDataType.h index a97296e02a01..72fa717ca1e1 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSCompoundDataType.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSCompoundDataType.h @@ -11,18 +11,24 @@ namespace facebook::react { -namespace detail { -struct CSSCompoundDataTypeMarker {}; -} // namespace detail - /** * Allows grouping together multiple possible CSSDataType to parse. + * CSSCompoundDataType is a std::variant of the allowed types, serving both + * as storage and for template parameter deduction during parsing. */ template -struct CSSCompoundDataType : public detail::CSSCompoundDataTypeMarker {}; +using CSSCompoundDataType = std::variant; + +namespace detail { +template +struct is_variant_of_data_types : std::false_type {}; + +template +struct is_variant_of_data_types> : std::true_type {}; +} // namespace detail template -concept CSSValidCompoundDataType = std::is_base_of_v; +concept CSSValidCompoundDataType = detail::is_variant_of_data_types::value; /** * A concrete data type, or a compound data type which represents multiple other @@ -38,40 +44,40 @@ template struct merge_data_types; template -struct merge_data_types, CSSCompoundDataType, RestT...> { - using type = typename merge_data_types, RestT...>::type; +struct merge_data_types, std::variant, RestT...> { + using type = typename merge_data_types, RestT...>::type; }; template -struct merge_data_types, RestT...> { - using type = typename merge_data_types, RestT...>::type; +struct merge_data_types, RestT...> { + using type = typename merge_data_types, RestT...>::type; }; template -struct merge_data_types, AlllowedType2T, RestT...> { - using type = typename merge_data_types, RestT...>::type; +struct merge_data_types, AlllowedType2T, RestT...> { + using type = typename merge_data_types, RestT...>::type; }; template struct merge_data_types { - using type = typename merge_data_types, RestT...>::type; + using type = typename merge_data_types, RestT...>::type; }; template -struct merge_data_types> { - using type = CSSCompoundDataType; +struct merge_data_types> { + using type = std::variant; }; template struct merge_data_types { - using type = CSSCompoundDataType; + using type = std::variant; }; template struct merge_variant; template -struct merge_variant, RestT...> { +struct merge_variant, RestT...> { using type = std::variant; }; } // namespace detail diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h b/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h index f1ff35eda5de..697537d0d552 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h @@ -307,11 +307,6 @@ using CSSFilterFunction = CSSCompoundDataType< CSSSaturateFilter, CSSSepiaFilter>; -/** - * Variant of possible CSS filter function types - */ -using CSSFilterFunctionVariant = CSSVariantWithTypes; - /** * Representation of * https://www.w3.org/TR/filter-effects-1/#typedef-filter-value-list diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSList.h b/packages/react-native/ReactCommon/react/renderer/css/CSSList.h index 1bccd52d7aa5..3fe63a799f37 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSList.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSList.h @@ -24,7 +24,7 @@ template struct CSSList : public std::vector {}; template -struct CSSList : public std::vector> {}; +struct CSSList : public std::vector {}; template struct CSSDataTypeParser> { diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h b/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h index 367080c9bebb..2f66f8914101 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h @@ -452,11 +452,6 @@ using CSSTransformFunction = CSSCompoundDataType< CSSSkewY, CSSPerspective>; -/** - * Variant of possible CSS transform function types - */ -using CSSTransformFunctionVariant = CSSVariantWithTypes; - /** * Represents the type. * https://drafts.csswg.org/css-transforms-1/#typedef-transform-list diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h index 144c04821d71..e88850cd92e1 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h @@ -33,7 +33,7 @@ class CSSValueParser { template constexpr std::variant consumeValue( CSSDelimiter delimeter, - CSSCompoundDataType /*unused*/) + std::variant /*unused*/) { using ReturnT = std::variant; From c3236d7b3758ba807a15c16ee1872d96ec683085 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Wed, 25 Feb 2026 11:27:30 -0800 Subject: [PATCH 2/2] Replace CSSSyntaxParser with CSSValueParser in data type sinks Summary: Previously, CSSDataTypeParser sinks received a raw CSSSyntaxParser& reference, which exposed low-level tokenization APIs (consumeComponentValue with lambdas, consumeToken, etc.) that data type parsers should not use directly. This made it unclear what operations are appropriate when implementing a new CSS data type. This change promotes detail::CSSValueParser to a public CSSValueParser class that wraps CSSSyntaxParser and exposes only value-level parsing operations: parseNextValue, peekNextValue, consumeWhitespace, consumeDelimiter, isFinished, peekNextTokenIs, and saveState/restoreState for lookahead patterns. All CSSDataTypeParser sink methods now receive CSSValueParser& instead of CSSSyntaxParser&, and use parser.parseNextValue() instead of the free function parseNextCSSValue(parser). Direct consumeComponentValue calls in gradient parsers (CSSBackgroundImage.h) are replaced with keyword set parsing via parseNextValue, with new To/At keywords added to CSSKeyword. The free functions parseCSSProperty, parseNextCSSValue, and peekNextCSSValue remain as convenience wrappers for backward compatibility. Changelog: [Internal] Reviewed By: jorge-cab Differential Revision: D94357104 --- .../react/renderer/css/CSSBackgroundImage.h | 237 ++++++++---------- .../ReactCommon/react/renderer/css/CSSColor.h | 2 +- .../react/renderer/css/CSSColorFunction.h | 77 +++--- .../react/renderer/css/CSSDataType.h | 25 +- .../react/renderer/css/CSSFilter.h | 24 +- .../react/renderer/css/CSSKeyword.h | 6 + .../ReactCommon/react/renderer/css/CSSList.h | 6 +- .../ReactCommon/react/renderer/css/CSSRatio.h | 8 +- .../react/renderer/css/CSSShadow.h | 14 +- .../react/renderer/css/CSSSyntaxParser.h | 14 +- .../react/renderer/css/CSSTransform.h | 40 +-- .../react/renderer/css/CSSTransformOrigin.h | 9 +- .../react/renderer/css/CSSValueParser.h | 136 +++++----- .../renderer/css/tests/CSSValueParserTest.cpp | 21 +- .../intersection/IntersectionObserver.cpp | 3 +- 15 files changed, 312 insertions(+), 310 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h b/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h index 3376d1759c8f..75daa6853d85 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSBackgroundImage.h @@ -25,6 +25,37 @@ namespace facebook::react { +enum class CSSGradientToKeyword : std::underlying_type_t { + To = to_underlying(CSSKeyword::To), +}; + +static_assert(CSSDataType); + +enum class CSSGradientDirectionKeyword : std::underlying_type_t { + Top = to_underlying(CSSKeyword::Top), + Bottom = to_underlying(CSSKeyword::Bottom), + Left = to_underlying(CSSKeyword::Left), + Right = to_underlying(CSSKeyword::Right), +}; + +static_assert(CSSDataType); + +enum class CSSGradientAtKeyword : std::underlying_type_t { + At = to_underlying(CSSKeyword::At), +}; + +static_assert(CSSDataType); + +enum class CSSGradientPositionKeyword : std::underlying_type_t { + Top = to_underlying(CSSKeyword::Top), + Bottom = to_underlying(CSSKeyword::Bottom), + Left = to_underlying(CSSKeyword::Left), + Right = to_underlying(CSSKeyword::Right), + Center = to_underlying(CSSKeyword::Center), +}; + +static_assert(CSSDataType); + enum class CSSLinearGradientDirectionKeyword : uint8_t { ToTopLeft, ToTopRight, @@ -41,115 +72,81 @@ struct CSSLinearGradientDirection { template <> struct CSSDataTypeParser { - static constexpr auto consume(CSSSyntaxParser &parser) -> std::optional + static constexpr auto consume(CSSValueParser &parser) -> std::optional { return parseLinearGradientDirection(parser); } private: - static constexpr std::optional parseLinearGradientDirection(CSSSyntaxParser &parser) + static constexpr std::optional parseLinearGradientDirection(CSSValueParser &parser) { - auto angle = parseNextCSSValue(parser); + auto angle = parser.parseNextValue(); if (std::holds_alternative(angle)) { return CSSLinearGradientDirection{std::get(angle)}; } - auto toResult = parser.consumeComponentValue([](const CSSPreservedToken &token) -> bool { - return token.type() == CSSTokenType::Ident && fnv1aLowercase(token.stringValue()) == fnv1a("to"); - }); - - if (!toResult) { + auto toResult = parser.parseNextValue(); + if (!std::holds_alternative(toResult)) { // no direction found, default to 180 degrees (to bottom) return CSSLinearGradientDirection{CSSAngle{180.0f}}; } - parser.consumeWhitespace(); - - std::optional primaryDir; - auto primaryResult = parser.consumeComponentValue>( - [](const CSSPreservedToken &token) -> std::optional { - if (token.type() == CSSTokenType::Ident) { - switch (fnv1aLowercase(token.stringValue())) { - case fnv1a("top"): - return CSSKeyword::Top; - case fnv1a("bottom"): - return CSSKeyword::Bottom; - case fnv1a("left"): - return CSSKeyword::Left; - case fnv1a("right"): - return CSSKeyword::Right; - } - } - return {}; - }); + parser.syntaxParser().consumeWhitespace(); - if (!primaryResult) { + auto primaryResult = parser.parseNextValue(); + if (std::holds_alternative(primaryResult)) { return {}; } - - primaryDir = primaryResult; - parser.consumeWhitespace(); - - std::optional secondaryDir; - auto secondaryResult = parser.consumeComponentValue>( - [&](const CSSPreservedToken &token) -> std::optional { - if (token.type() == CSSTokenType::Ident) { - auto hash = fnv1aLowercase(token.stringValue()); - // validate compatible combinations - if (primaryDir == CSSKeyword::Top || primaryDir == CSSKeyword::Bottom) { - if (hash == fnv1a("left")) { - return CSSKeyword::Left; - } - if (hash == fnv1a("right")) { - return CSSKeyword::Right; - } - } - if (primaryDir == CSSKeyword::Left || primaryDir == CSSKeyword::Right) { - if (hash == fnv1a("top")) { - return CSSKeyword::Top; - } - if (hash == fnv1a("bottom")) { - return CSSKeyword::Bottom; - } - } - } - return {}; - }); - - if (secondaryResult) { - secondaryDir = secondaryResult; + auto primaryDir = std::get(primaryResult); + + parser.syntaxParser().consumeWhitespace(); + + auto secondaryPeek = parser.peekNextValue(); + std::optional secondaryDir; + if (std::holds_alternative(secondaryPeek)) { + auto kw = std::get(secondaryPeek); + bool isCompatible = false; + if (primaryDir == CSSGradientDirectionKeyword::Top || primaryDir == CSSGradientDirectionKeyword::Bottom) { + isCompatible = (kw == CSSGradientDirectionKeyword::Left || kw == CSSGradientDirectionKeyword::Right); + } else if (primaryDir == CSSGradientDirectionKeyword::Left || primaryDir == CSSGradientDirectionKeyword::Right) { + isCompatible = (kw == CSSGradientDirectionKeyword::Top || kw == CSSGradientDirectionKeyword::Bottom); + } + if (isCompatible) { + parser.parseNextValue(); + secondaryDir = kw; + } } - if (primaryDir == CSSKeyword::Top) { - if (secondaryDir == CSSKeyword::Left) { + if (primaryDir == CSSGradientDirectionKeyword::Top) { + if (secondaryDir == CSSGradientDirectionKeyword::Left) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToTopLeft}; - } else if (secondaryDir == CSSKeyword::Right) { + } else if (secondaryDir == CSSGradientDirectionKeyword::Right) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToTopRight}; } else { // "to top" = 0 degrees return CSSLinearGradientDirection{CSSAngle{0.0f}}; } - } else if (primaryDir == CSSKeyword::Bottom) { - if (secondaryDir == CSSKeyword::Left) { + } else if (primaryDir == CSSGradientDirectionKeyword::Bottom) { + if (secondaryDir == CSSGradientDirectionKeyword::Left) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToBottomLeft}; - } else if (secondaryDir == CSSKeyword::Right) { + } else if (secondaryDir == CSSGradientDirectionKeyword::Right) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToBottomRight}; } else { // "to bottom" = 180 degrees return CSSLinearGradientDirection{CSSAngle{180.0f}}; } - } else if (primaryDir == CSSKeyword::Left) { - if (secondaryDir == CSSKeyword::Top) { + } else if (primaryDir == CSSGradientDirectionKeyword::Left) { + if (secondaryDir == CSSGradientDirectionKeyword::Top) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToTopLeft}; - } else if (secondaryDir == CSSKeyword::Bottom) { + } else if (secondaryDir == CSSGradientDirectionKeyword::Bottom) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToBottomLeft}; } else { // "to left" = 270 degrees return CSSLinearGradientDirection{CSSAngle{270.0f}}; } - } else if (primaryDir == CSSKeyword::Right) { - if (secondaryDir == CSSKeyword::Top) { + } else if (primaryDir == CSSGradientDirectionKeyword::Right) { + if (secondaryDir == CSSGradientDirectionKeyword::Top) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToTopRight}; - } else if (secondaryDir == CSSKeyword::Bottom) { + } else if (secondaryDir == CSSGradientDirectionKeyword::Bottom) { return CSSLinearGradientDirection{CSSLinearGradientDirectionKeyword::ToBottomRight}; } else { // "to right" = 90 degrees @@ -174,15 +171,15 @@ struct CSSColorHint { template <> struct CSSDataTypeParser { - static auto consume(CSSSyntaxParser &parser) -> std::optional + static auto consume(CSSValueParser &parser) -> std::optional { return parseCSSColorHint(parser); } private: - static std::optional parseCSSColorHint(CSSSyntaxParser &parser) + static std::optional parseCSSColorHint(CSSValueParser &parser) { - auto position = parseNextCSSValue(parser); + auto position = parser.parseNextValue(); if (std::holds_alternative(position)) { return CSSColorHint{std::get(position)}; } else if (std::holds_alternative(position)) { @@ -235,15 +232,15 @@ struct CSSColorStop { template <> struct CSSDataTypeParser { - static constexpr auto consume(CSSSyntaxParser &parser) -> std::optional + static constexpr auto consume(CSSValueParser &parser) -> std::optional { return parseCSSColorStop(parser); } private: - static constexpr std::optional parseCSSColorStop(CSSSyntaxParser &parser) + static constexpr std::optional parseCSSColorStop(CSSValueParser &parser) { - auto color = parseNextCSSValue(parser); + auto color = parser.parseNextValue(); if (!std::holds_alternative(color)) { return {}; } @@ -251,7 +248,7 @@ struct CSSDataTypeParser { CSSColorStop colorStop; colorStop.color = std::get(color); - auto startPosition = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto startPosition = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(startPosition)) { colorStop.startPosition = std::get(startPosition); } else if (std::holds_alternative(startPosition)) { @@ -261,7 +258,7 @@ struct CSSDataTypeParser { if (colorStop.startPosition) { // Try to parse second optional position (supports both lengths and // percentages) - auto endPosition = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto endPosition = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(endPosition)) { colorStop.endPosition = std::get(endPosition); } else if (std::holds_alternative(endPosition)) { @@ -281,27 +278,27 @@ struct CSSLinearGradientFunction { bool operator==(const CSSLinearGradientFunction &rhs) const = default; static std::pair>, int> parseGradientColorStopsAndHints( - CSSSyntaxParser &parser) + CSSValueParser &parser) { std::vector> items; int colorStopCount = 0; std::optional prevColorStop = std::nullopt; do { - auto colorStop = parseNextCSSValue(parser); + auto colorStop = parser.parseNextValue(); if (std::holds_alternative(colorStop)) { auto parsedColorStop = std::get(colorStop); items.emplace_back(parsedColorStop); prevColorStop = parsedColorStop; colorStopCount++; } else { - auto colorHint = parseNextCSSValue(parser); + auto colorHint = parser.parseNextValue(); if (std::holds_alternative(colorHint)) { // color hint must be between two color stops if (!prevColorStop) { return {}; } - auto nextColorStop = peekNextCSSValue(parser, CSSDelimiter::Comma); + auto nextColorStop = parser.peekNextValue(CSSDelimiter::Comma); if (!std::holds_alternative(nextColorStop)) { return {}; } @@ -310,7 +307,7 @@ struct CSSLinearGradientFunction { break; // No more valid items } } - } while (parser.consumeDelimiter(CSSDelimiter::Comma)); + } while (parser.syntaxParser().consumeDelimiter(CSSDelimiter::Comma)); return {items, colorStopCount}; } @@ -378,16 +375,16 @@ struct CSSRadialGradientExplicitSize { template <> struct CSSDataTypeParser { - static auto consume(CSSSyntaxParser &syntaxParser) -> std::optional + static auto consume(CSSValueParser &syntaxParser) -> std::optional { - auto sizeX = parseNextCSSValue(syntaxParser); + auto sizeX = syntaxParser.parseNextValue(); if (std::holds_alternative(sizeX)) { return {}; } - syntaxParser.consumeWhitespace(); + syntaxParser.syntaxParser().consumeWhitespace(); - auto sizeY = parseNextCSSValue(syntaxParser); + auto sizeY = syntaxParser.parseNextValue(); CSSRadialGradientExplicitSize result; if (std::holds_alternative(sizeX)) { @@ -434,7 +431,7 @@ struct CSSRadialGradientFunction { template <> struct CSSDataTypeParser { - static auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "radial-gradient")) { @@ -445,20 +442,20 @@ struct CSSDataTypeParser { auto hasExplicitShape = false; auto hasExplicitSingleSize = false; - auto shapeResult = parseNextCSSValue(parser); + auto shapeResult = parser.parseNextValue(); if (std::holds_alternative(shapeResult)) { - parser.consumeWhitespace(); + parser.syntaxParser().consumeWhitespace(); } std::optional sizeResult; - auto sizeKeywordResult = parseNextCSSValue(parser); + auto sizeKeywordResult = parser.parseNextValue(); if (std::holds_alternative(sizeKeywordResult)) { sizeResult = CSSRadialGradientSize{std::get(sizeKeywordResult)}; - parser.consumeWhitespace(); + parser.syntaxParser().consumeWhitespace(); } else { - auto explicitSizeResult = parseNextCSSValue(parser); + auto explicitSizeResult = parser.parseNextValue(); if (std::holds_alternative(explicitSizeResult)) { auto explicitSize = std::get(explicitSizeResult); // negative value validation @@ -492,7 +489,7 @@ struct CSSDataTypeParser { } sizeResult = CSSRadialGradientSize{explicitSize}; - parser.consumeWhitespace(); + parser.syntaxParser().consumeWhitespace(); } } @@ -521,38 +518,24 @@ struct CSSDataTypeParser { return {}; } - auto atResult = parser.consumeComponentValue([](const CSSPreservedToken &token) -> bool { - return token.type() == CSSTokenType::Ident && fnv1aLowercase(token.stringValue()) == fnv1a("at"); - }); + auto atResult = parser.parseNextValue(); + bool hasAtKeyword = std::holds_alternative(atResult); CSSRadialGradientPosition position; - if (atResult) { - parser.consumeWhitespace(); + if (hasAtKeyword) { + parser.syntaxParser().consumeWhitespace(); std::vector> positionKeywordValues; for (int i = 0; i < 2; i++) { auto keywordFound = false; auto valueFound = false; - auto positionKeyword = parser.consumeComponentValue>( - [](const CSSPreservedToken &token) -> std::optional { - if (token.type() == CSSTokenType::Ident) { - auto keyword = std::string(token.stringValue()); - auto hash = fnv1aLowercase(keyword); - if (hash == fnv1a("top")) { - return CSSKeyword::Top; - } else if (hash == fnv1a("bottom")) { - return CSSKeyword::Bottom; - } else if (hash == fnv1a("left")) { - return CSSKeyword::Left; - } else if (hash == fnv1a("right")) { - return CSSKeyword::Right; - } else if (hash == fnv1a("center")) { - return CSSKeyword::Center; - } - } - return {}; - }); + auto positionKeywordResult = parser.parseNextValue(); + std::optional positionKeyword; + if (std::holds_alternative(positionKeywordResult)) { + positionKeyword = + static_cast(to_underlying(std::get(positionKeywordResult))); + } if (positionKeyword) { // invalid position declaration of same keyword "at top 10% top 20%" @@ -567,9 +550,9 @@ struct CSSDataTypeParser { keywordFound = true; } - parser.consumeWhitespace(); + parser.syntaxParser().consumeWhitespace(); - auto lengthPercentageValue = parseNextCSSValue(parser); + auto lengthPercentageValue = parser.parseNextValue(); std::optional value; if (std::holds_alternative(lengthPercentageValue)) { @@ -582,7 +565,7 @@ struct CSSDataTypeParser { valueFound = true; } - parser.consumeWhitespace(); + parser.syntaxParser().consumeWhitespace(); if (!keywordFound && !valueFound) { break; @@ -791,7 +774,7 @@ struct CSSDataTypeParser { gradient.position = position; } - parser.consumeDelimiter(CSSDelimiter::Comma); + parser.syntaxParser().consumeDelimiter(CSSDelimiter::Comma); auto [items, colorStopCount] = CSSLinearGradientFunction::parseGradientColorStopsAndHints(parser); if (items.empty() || colorStopCount < 2) { @@ -807,7 +790,7 @@ static_assert(CSSDataType); template <> struct CSSDataTypeParser { - static auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "linear-gradient")) { @@ -816,12 +799,12 @@ struct CSSDataTypeParser { CSSLinearGradientFunction gradient; - auto parsedDirection = parseNextCSSValue(parser); + auto parsedDirection = parser.parseNextValue(); if (!std::holds_alternative(parsedDirection)) { return {}; } - parser.consumeDelimiter(CSSDelimiter::Comma); + parser.syntaxParser().consumeDelimiter(CSSDelimiter::Comma); gradient.direction = std::get(parsedDirection); diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h b/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h index 0f7121ae7ca1..91924cf389d6 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h @@ -47,7 +47,7 @@ struct CSSDataTypeParser { return {}; } - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { return parseCSSColorFunction(func.name, parser); diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h b/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h index 0c82af922f97..76c7da2b5292 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSColorFunction.h @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -154,16 +153,19 @@ constexpr std::optional normalizeComponent( } template -constexpr bool isLegacyColorFunction(CSSSyntaxParser &parser) +constexpr bool isLegacyColorFunction(CSSValueParser &parser) { - auto lookahead = parser; - auto next = parseNextCSSValue(lookahead); + auto saved = parser.syntaxParser(); + auto next = parser.parseNextValue(); if (std::holds_alternative(next)) { + parser.syntaxParser() = saved; return false; } - return lookahead.consumeComponentValue( - CSSDelimiter::OptionalWhitespace, [](CSSPreservedToken token) { return token.type() == CSSTokenType::Comma; }); + parser.syntaxParser().consumeWhitespace(); + bool isLegacy = parser.syntaxParser().peek().type() == CSSTokenType::Comma; + parser.syntaxParser() = saved; + return isLegacy; } /** @@ -172,9 +174,9 @@ constexpr bool isLegacyColorFunction(CSSSyntaxParser &parser) * https://www.w3.org/TR/css-color-4/#typedef-legacy-rgb-syntax */ template -constexpr std::optional parseLegacyRgbFunction(CSSSyntaxParser &parser) +constexpr std::optional parseLegacyRgbFunction(CSSValueParser &parser) { - auto rawRed = parseNextCSSValue(parser); + auto rawRed = parser.parseNextValue(); bool usesNumber = std::holds_alternative(rawRed); auto red = normalizeComponent(rawRed, 255.0f); @@ -182,19 +184,19 @@ constexpr std::optional parseLegacyRgbFunction(CSSSyntaxParser &parser return {}; } - auto green = usesNumber ? normalizeNumberComponent(parseNextCSSValue(parser, CSSDelimiter::Comma)) - : normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Comma), 255.0f); + auto green = usesNumber ? normalizeNumberComponent(parser.parseNextValue(CSSDelimiter::Comma)) + : normalizeComponent(parser.parseNextValue(CSSDelimiter::Comma), 255.0f); if (!green.has_value()) { return {}; } - auto blue = usesNumber ? normalizeNumberComponent(parseNextCSSValue(parser, CSSDelimiter::Comma)) - : normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Comma), 255.0f); + auto blue = usesNumber ? normalizeNumberComponent(parser.parseNextValue(CSSDelimiter::Comma)) + : normalizeComponent(parser.parseNextValue(CSSDelimiter::Comma), 255.0f); if (!blue.has_value()) { return {}; } - auto alpha = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Comma), 1.0f); + auto alpha = normalizeComponent(parser.parseNextValue(CSSDelimiter::Comma), 1.0f); return CSSColor{ .r = clamp255Component(*red), @@ -210,26 +212,25 @@ constexpr std::optional parseLegacyRgbFunction(CSSSyntaxParser &parser * https://www.w3.org/TR/css-color-4/#typedef-modern-rgb-syntax */ template -constexpr std::optional parseModernRgbFunction(CSSSyntaxParser &parser) +constexpr std::optional parseModernRgbFunction(CSSValueParser &parser) { - auto red = normalizeComponent(parseNextCSSValue(parser), 255.0f); + auto red = normalizeComponent(parser.parseNextValue(), 255.0f); if (!red.has_value()) { return {}; } - auto green = - normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Whitespace), 255.0f); + auto green = normalizeComponent(parser.parseNextValue(CSSDelimiter::Whitespace), 255.0f); if (!green.has_value()) { return {}; } - auto blue = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Whitespace), 255.0f); + auto blue = normalizeComponent(parser.parseNextValue(CSSDelimiter::Whitespace), 255.0f); if (!blue.has_value()) { return {}; } auto alpha = - normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::SolidusOrWhitespace), 1.0f); + normalizeComponent(parser.parseNextValue(CSSDelimiter::SolidusOrWhitespace), 1.0f); return CSSColor{ .r = clamp255Component(*red), @@ -244,7 +245,7 @@ constexpr std::optional parseModernRgbFunction(CSSSyntaxParser &parser * https://www.w3.org/TR/css-color-4/#funcdef-rgb */ template -constexpr std::optional parseRgbFunction(CSSSyntaxParser &parser) +constexpr std::optional parseRgbFunction(CSSValueParser &parser) { if (isLegacyColorFunction(parser)) { return parseLegacyRgbFunction(parser); @@ -259,24 +260,24 @@ constexpr std::optional parseRgbFunction(CSSSyntaxParser &parser) * https://www.w3.org/TR/css-color-4/#typedef-legacy-hsl-syntax */ template -inline std::optional parseLegacyHslFunction(CSSSyntaxParser &parser) +inline std::optional parseLegacyHslFunction(CSSValueParser &parser) { - auto h = normalizeHueComponent(parseNextCSSValue(parser)); + auto h = normalizeHueComponent(parser.parseNextValue()); if (!h.has_value()) { return {}; } - auto s = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Comma), 100.0f); + auto s = normalizeComponent(parser.parseNextValue(CSSDelimiter::Comma), 100.0f); if (!s.has_value()) { return {}; } - auto l = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Comma), 100.0f); + auto l = normalizeComponent(parser.parseNextValue(CSSDelimiter::Comma), 100.0f); if (!l.has_value()) { return {}; } - auto a = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Comma), 1.0f); + auto a = normalizeComponent(parser.parseNextValue(CSSDelimiter::Comma), 1.0f); auto [r, g, b] = hslToRgb(*h, *s, *l); @@ -293,25 +294,24 @@ inline std::optional parseLegacyHslFunction(CSSSyntaxParser &parser) * it is valid. https://www.w3.org/TR/css-color-4/#typedef-modern-hsl-syntax */ template -inline std::optional parseModernHslFunction(CSSSyntaxParser &parser) +inline std::optional parseModernHslFunction(CSSValueParser &parser) { - auto h = normalizeHueComponent(parseNextCSSValue(parser)); + auto h = normalizeHueComponent(parser.parseNextValue()); if (!h.has_value()) { return {}; } - auto s = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Whitespace), 100.0f); + auto s = normalizeComponent(parser.parseNextValue(CSSDelimiter::Whitespace), 100.0f); if (!s.has_value()) { return {}; } - auto l = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Whitespace), 100.0f); + auto l = normalizeComponent(parser.parseNextValue(CSSDelimiter::Whitespace), 100.0f); if (!l.has_value()) { return {}; } - auto a = - normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::SolidusOrWhitespace), 1.0f); + auto a = normalizeComponent(parser.parseNextValue(CSSDelimiter::SolidusOrWhitespace), 1.0f); auto [r, g, b] = hslToRgb(*h, *s, *l); @@ -328,7 +328,7 @@ inline std::optional parseModernHslFunction(CSSSyntaxParser &parser) * https://www.w3.org/TR/css-color-4/#funcdef-hsl */ template -inline std::optional parseHslFunction(CSSSyntaxParser &parser) +inline std::optional parseHslFunction(CSSValueParser &parser) { if (isLegacyColorFunction(parser)) { return parseLegacyHslFunction(parser); @@ -342,25 +342,24 @@ inline std::optional parseHslFunction(CSSSyntaxParser &parser) * https://www.w3.org/TR/css-color-4/#funcdef-hwb */ template -inline std::optional parseHwbFunction(CSSSyntaxParser &parser) +inline std::optional parseHwbFunction(CSSValueParser &parser) { - auto h = normalizeHueComponent(parseNextCSSValue(parser)); + auto h = normalizeHueComponent(parser.parseNextValue()); if (!h.has_value()) { return {}; } - auto w = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Whitespace), 100.0f); + auto w = normalizeComponent(parser.parseNextValue(CSSDelimiter::Whitespace), 100.0f); if (!w.has_value()) { return {}; } - auto b = normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::Whitespace), 100.0f); + auto b = normalizeComponent(parser.parseNextValue(CSSDelimiter::Whitespace), 100.0f); if (!b.has_value()) { return {}; } - auto a = - normalizeComponent(parseNextCSSValue(parser, CSSDelimiter::SolidusOrWhitespace), 1.0f); + auto a = normalizeComponent(parser.parseNextValue(CSSDelimiter::SolidusOrWhitespace), 1.0f); auto [red, green, blue] = hwbToRgb(*h, *w, *b); @@ -380,7 +379,7 @@ inline std::optional parseHwbFunction(CSSSyntaxParser &parser) * https://www.w3.org/TR/css-color-4/#typedef-color-function */ template -constexpr std::optional parseCSSColorFunction(std::string_view colorFunction, CSSSyntaxParser &parser) +constexpr std::optional parseCSSColorFunction(std::string_view colorFunction, CSSValueParser &parser) { switch (fnv1aLowercase(colorFunction)) { // CSS Color Module Level 4 treats the alpha variants of functions as the diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h b/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h index 840fdffa7ba7..99437d08ba3f 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h @@ -16,6 +16,8 @@ namespace facebook::react { +class CSSValueParser; + /** * May be specialized to instruct the CSS value parser how to parse a given data * type, according to CSSValidDataTypeParser. @@ -24,26 +26,28 @@ template struct CSSDataTypeParser {}; /** - * Accepts a CSS function block and may parse it (and future syntax) into a - * concrete representation. + * Accepts a CSS function block and may parse it into a concrete representation. + * The CSSValueParser provides methods for parsing sub-values within the + * function block scope. */ template -concept CSSFunctionBlockSink = requires(const CSSFunctionBlock &func, CSSSyntaxParser &parser) { +concept CSSFunctionBlockSink = requires(const CSSFunctionBlock &func, CSSValueParser &parser) { { T::consumeFunctionBlock(func, parser) } -> std::convertible_to; }; /** - * Accepts a CSS simple block and may parse it (and future syntax) into a - * concrete representation. + * Accepts a CSS simple block and may parse it into a concrete representation. + * The CSSValueParser provides methods for parsing sub-values within the + * block scope. */ template -concept CSSSimpleBlockSink = requires(const CSSSimpleBlock &block, CSSSyntaxParser &parser) { +concept CSSSimpleBlockSink = requires(const CSSSimpleBlock &block, CSSValueParser &parser) { { T::consumeSimpleBlock(block, parser) } -> std::convertible_to; }; /** - * Accepts a CSS preserved token and may parse it (and future syntax) into a - * concrete representation. + * Accepts a CSS preserved token and may parse it into a concrete + * representation. */ template concept CSSPreservedTokenSink = requires(const CSSPreservedToken &token) { @@ -51,10 +55,11 @@ concept CSSPreservedTokenSink = requires(const CSSPreservedToken &token) { }; /** - * Accepts a raw syntax parser, to be able to parse compounded values + * Accepts a CSSValueParser to be able to parse compounded values spanning + * multiple component values. */ template -concept CSSParserSink = requires(CSSSyntaxParser &parser) { +concept CSSParserSink = requires(CSSValueParser &parser) { { T::consume(parser) } -> std::convertible_to; }; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h b/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h index 697537d0d552..cfc0fd519185 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSFilter.h @@ -28,14 +28,14 @@ namespace detail { template requires(std::is_same_v) struct CSSFilterSimpleAmountParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, Name)) { return {}; } - auto amount = parseNextCSSValue(parser); + auto amount = parser.parseNextValue(); if (std::holds_alternative(amount)) { if (std::get(amount).value < 0.0f) { return {}; @@ -65,14 +65,14 @@ struct CSSBlurFilter { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "blur")) { return {}; } - auto len = parseNextCSSValue(parser); + auto len = parser.parseNextValue(); return CSSBlurFilter{std::holds_alternative(len) ? std::get(len) : CSSLength{}}; } }; @@ -123,7 +123,7 @@ struct CSSDropShadowFilter { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "drop-shadow")) { @@ -133,7 +133,7 @@ struct CSSDataTypeParser { std::optional color{}; std::optional> lengths{}; - auto firstVal = parseNextCSSValue(parser); + auto firstVal = parser.parseNextValue(); if (std::holds_alternative(firstVal)) { return {}; } @@ -147,7 +147,7 @@ struct CSSDataTypeParser { } } - auto secondVal = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto secondVal = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(secondVal)) { if (color.has_value()) { return {}; @@ -173,14 +173,14 @@ struct CSSDataTypeParser { } private: - static constexpr std::optional> parseLengths(CSSLength offsetX, CSSSyntaxParser &parser) + static constexpr std::optional> parseLengths(CSSLength offsetX, CSSValueParser &parser) { - auto offsetY = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto offsetY = parser.parseNextValue(CSSDelimiter::Whitespace); if (!std::holds_alternative(offsetY)) { return {}; } - auto standardDeviation = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto standardDeviation = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(standardDeviation) && std::get(standardDeviation).value < 0.0f) { return {}; } @@ -220,14 +220,14 @@ struct CSSHueRotateFilter { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "hue-rotate")) { return {}; } - auto angle = parseNextCSSValue(parser); + auto angle = parser.parseNextValue(); return CSSHueRotateFilter{std::holds_alternative(angle) ? std::get(angle).degrees : 0.0f}; } }; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h b/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h index b173a7b97b04..f75901853c86 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h @@ -23,6 +23,7 @@ namespace facebook::react { */ enum class CSSKeyword : uint8_t { Absolute, + At, Auto, Baseline, Block, @@ -110,6 +111,7 @@ enum class CSSKeyword : uint8_t { TabularNums, Thick, Thin, + To, Top, Unset, Visible, @@ -143,6 +145,7 @@ enum class CSSWideKeyword : std::underlying_type_t { } CSS_DEFINE_KEYWORD(Absolute, "absolute") +CSS_DEFINE_KEYWORD(At, "at") CSS_DEFINE_KEYWORD(Auto, "auto") CSS_DEFINE_KEYWORD(Baseline, "baseline") CSS_DEFINE_KEYWORD(Block, "block") @@ -230,6 +233,7 @@ CSS_DEFINE_KEYWORD(StylisticTwo, "stylistic-two") CSS_DEFINE_KEYWORD(TabularNums, "tabular-nums") CSS_DEFINE_KEYWORD(Thick, "thick") CSS_DEFINE_KEYWORD(Thin, "thin") +CSS_DEFINE_KEYWORD(To, "to") CSS_DEFINE_KEYWORD(Top, "top") CSS_DEFINE_KEYWORD(Unset, "unset") CSS_DEFINE_KEYWORD(Visible, "visible") @@ -254,6 +258,7 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { switch (fnv1aLowercase(ident)) { CSS_HANDLE_KEYWORD(Absolute) + CSS_HANDLE_KEYWORD(At) CSS_HANDLE_KEYWORD(Auto) CSS_HANDLE_KEYWORD(Baseline) CSS_HANDLE_KEYWORD(Block) @@ -341,6 +346,7 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) CSS_HANDLE_KEYWORD(TabularNums) CSS_HANDLE_KEYWORD(Thick) CSS_HANDLE_KEYWORD(Thin) + CSS_HANDLE_KEYWORD(To) CSS_HANDLE_KEYWORD(Top) CSS_HANDLE_KEYWORD(Unset) CSS_HANDLE_KEYWORD(Visible) diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSList.h b/packages/react-native/ReactCommon/react/renderer/css/CSSList.h index 3fe63a799f37..acb33ae3403d 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSList.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSList.h @@ -28,11 +28,11 @@ struct CSSList : public std::vector {}; template struct CSSDataTypeParser> { - static inline auto consume(CSSSyntaxParser &parser) -> std::optional> + static inline auto consume(CSSValueParser &parser) -> std::optional> { CSSList result; - for (auto nextValue = parseNextCSSValue(parser); !std::holds_alternative(nextValue); - nextValue = parseNextCSSValue(parser, Delim)) { + for (auto nextValue = parser.parseNextValue(); !std::holds_alternative(nextValue); + nextValue = parser.parseNextValue(Delim)) { // Copy from the variant of possible values to the element (either the // concrete type, or a variant of compound types which exlcudes the // possibility of std::monostate for parse error) diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h index 821a9dba8d76..6440f101b195 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h @@ -38,20 +38,20 @@ struct CSSRatio { template <> struct CSSDataTypeParser { - static constexpr auto consume(CSSSyntaxParser &parser) -> std::optional + static constexpr auto consume(CSSValueParser &parser) -> std::optional { // = [ / ]? // https://www.w3.org/TR/css-values-4/#ratio - auto numerator = parseNextCSSValue(parser); + auto numerator = parser.parseNextValue(); if (!std::holds_alternative(numerator)) { return {}; } auto numeratorValue = std::get(numerator).value; if (numeratorValue >= 0) { - auto denominator = peekNextCSSValue(parser, CSSDelimiter::Solidus); + auto denominator = parser.peekNextValue(CSSDelimiter::Solidus); if (std::holds_alternative(denominator) && std::get(denominator).value >= 0) { - parseNextCSSValue(parser, CSSDelimiter::Solidus); + parser.parseNextValue(CSSDelimiter::Solidus); return CSSRatio{.numerator = numeratorValue, .denominator = std::get(denominator).value}; } diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSShadow.h b/packages/react-native/ReactCommon/react/renderer/css/CSSShadow.h index 30f15bcdea9f..3e0788fc9ad9 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSShadow.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSShadow.h @@ -46,15 +46,15 @@ static_assert(CSSDataType); template <> struct CSSDataTypeParser { - static constexpr auto consume(CSSSyntaxParser &parser) -> std::optional + static constexpr auto consume(CSSValueParser &parser) -> std::optional { std::optional color{}; bool inset{false}; std::optional> lengths{}; - for (auto nextValue = parseNextCSSValue(parser); + for (auto nextValue = parser.parseNextValue(); !std::holds_alternative(nextValue); - nextValue = parseNextCSSValue(parser, CSSDelimiter::Whitespace)) { + nextValue = parser.parseNextValue(CSSDelimiter::Whitespace)) { if (std::holds_alternative(nextValue)) { if (lengths.has_value()) { return {}; @@ -98,15 +98,15 @@ struct CSSDataTypeParser { } private: - static constexpr auto parseRestLengths(CSSLength offsetX, CSSSyntaxParser &parser) + static constexpr auto parseRestLengths(CSSLength offsetX, CSSValueParser &parser) -> std::optional> { - auto offsetY = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto offsetY = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(offsetY)) { return {}; } - auto blurRadius = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto blurRadius = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(blurRadius)) { return std::make_tuple(offsetX, std::get(offsetY), CSSLength{}, CSSLength{}); } @@ -114,7 +114,7 @@ struct CSSDataTypeParser { return {}; } - auto spreadDistance = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto spreadDistance = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(spreadDistance)) { return std::make_tuple(offsetX, std::get(offsetY), std::get(blurRadius), CSSLength{}); } diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h index 45579398578a..248c9378395b 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h @@ -113,6 +113,7 @@ enum class CSSDelimiter { class CSSSyntaxParser { template ... VisitorsT> friend struct CSSComponentValueVisitorDispatcher; + friend class CSSValueParser; public: /** @@ -231,15 +232,18 @@ class CSSSyntaxParser { } } - private: - constexpr CSSSyntaxParser(CSSSyntaxParser &parser, CSSTokenType terminator) - : tokenizer_{parser.tokenizer_}, currentToken_{parser.currentToken_}, terminator_{terminator} + /** + * Returns the current token without consuming it. + */ + constexpr const CSSToken &peek() const { + return currentToken_; } - constexpr const CSSToken &peek() const + private: + constexpr CSSSyntaxParser(CSSSyntaxParser &parser, CSSTokenType terminator) + : tokenizer_{parser.tokenizer_}, currentToken_{parser.currentToken_}, terminator_{terminator} { - return currentToken_; } constexpr CSSToken consumeToken() diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h b/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h index 2f66f8914101..d287ce5327e1 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSTransform.h @@ -29,14 +29,14 @@ namespace detail { template requires(std::is_same_v>) struct CSSVariantComponentTransformParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, Name)) { return {}; } - auto val = parseNextCSSValue(parser); + auto val = parser.parseNextValue(); return std::visit( [&](auto &&v) -> std::optional { @@ -53,14 +53,14 @@ struct CSSVariantComponentTransformParser { template requires(std::is_same_v) struct CSSNumberPercentTransformParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, Name)) { return {}; } - auto val = parseNextCSSValue(parser); + auto val = parser.parseNextValue(); if (std::holds_alternative(val)) { return {}; } @@ -74,14 +74,14 @@ struct CSSNumberPercentTransformParser { template requires(std::is_same_v) struct CSSAngleTransformParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, Name)) { return {}; } - auto value = parseNextCSSValue(parser); + auto value = parser.parseNextValue(); if (std::holds_alternative(value)) { return {}; } @@ -105,7 +105,7 @@ struct CSSMatrix { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "matrix")) { @@ -114,7 +114,7 @@ struct CSSDataTypeParser { CSSMatrix matrix{}; for (int i = 0; i < 6; i++) { - auto value = parseNextCSSValue(parser, i == 0 ? CSSDelimiter::None : CSSDelimiter::Comma); + auto value = parser.parseNextValue(i == 0 ? CSSDelimiter::None : CSSDelimiter::Comma); if (std::holds_alternative(value)) { return {}; } @@ -139,19 +139,19 @@ struct CSSTranslate { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "translate")) { return {}; } - auto x = parseNextCSSValue(parser); + auto x = parser.parseNextValue(); if (std::holds_alternative(x)) { return {}; } - auto y = parseNextCSSValue(parser, CSSDelimiter::Comma); + auto y = parser.parseNextValue(CSSDelimiter::Comma); CSSTranslate translate{}; translate.x = std::holds_alternative(x) @@ -183,24 +183,24 @@ struct CSSTranslate3D { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "translate3d")) { return {}; } - auto x = parseNextCSSValue(parser); + auto x = parser.parseNextValue(); if (std::holds_alternative(x)) { return {}; } - auto y = parseNextCSSValue(parser, CSSDelimiter::Comma); + auto y = parser.parseNextValue(CSSDelimiter::Comma); if (std::holds_alternative(y)) { return {}; } - auto z = parseNextCSSValue(parser, CSSDelimiter::Comma); + auto z = parser.parseNextValue(CSSDelimiter::Comma); if (std::holds_alternative(z)) { return {}; } @@ -259,7 +259,7 @@ struct CSSScale { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "scale")) { @@ -268,12 +268,12 @@ struct CSSDataTypeParser { // Transforms module level 2 allows percentage syntax // https://drafts.csswg.org/css-transforms-2/#transform-functions - auto x = parseNextCSSValue(parser); + auto x = parser.parseNextValue(); if (std::holds_alternative(x)) { return {}; } - auto y = parseNextCSSValue(parser, CSSDelimiter::Comma); + auto y = parser.parseNextValue(CSSDelimiter::Comma); auto normX = std::holds_alternative(x) ? std::get(x).value : std::get(x).value / 100.0f; @@ -411,14 +411,14 @@ struct CSSPerspective { template <> struct CSSDataTypeParser { - static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &parser) + static constexpr auto consumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &parser) -> std::optional { if (!iequals(func.name, "perspective")) { return {}; } - auto value = parseNextCSSValue(parser); + auto value = parser.parseNextValue(); if (std::holds_alternative(value) || std::get(value).value < 0) { return {}; } diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSTransformOrigin.h b/packages/react-native/ReactCommon/react/renderer/css/CSSTransformOrigin.h index ee5d06792dc1..f2ed5baa8b2d 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSTransformOrigin.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSTransformOrigin.h @@ -45,7 +45,7 @@ struct CSSTransformOrigin { template <> struct CSSDataTypeParser { - static constexpr auto consume(CSSSyntaxParser &parser) -> std::optional + static constexpr auto consume(CSSValueParser &parser) -> std::optional { // [ left | center | right | top | bottom | ] // | @@ -54,19 +54,18 @@ struct CSSDataTypeParser { // | // [ [ center | left | right ] && [ center | top | bottom ] ] ? - auto firstValue = parseNextCSSValue(parser); + auto firstValue = parser.parseNextValue(); if (std::holds_alternative(firstValue)) { return {}; } - auto secondValue = - parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto secondValue = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(secondValue)) { return singleValue(firstValue); } - auto thirdValue = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + auto thirdValue = parser.parseNextValue(CSSDelimiter::Whitespace); if (std::holds_alternative(firstValue) || std::holds_alternative(firstValue) || std::holds_alternative(secondValue) || std::holds_alternative(secondValue)) { diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h index e88850cd92e1..a0cc05d027b2 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h @@ -18,12 +18,54 @@ namespace facebook::react { -namespace detail { - class CSSValueParser { public: - explicit constexpr CSSValueParser(CSSSyntaxParser &parser) : parser_{parser} {} + explicit constexpr CSSValueParser(CSSSyntaxParser &parser) : parser_{&parser} {} + + constexpr CSSValueParser(const CSSValueParser &) = default; + constexpr CSSValueParser &operator=(const CSSValueParser &) = default; + + /** + * Attempts to parse the next CSS value of a given set of data types, at the + * current location of the parser, advancing the parser. + */ + template + constexpr auto parseNextValue(CSSDelimiter delimiter = CSSDelimiter::None) + -> CSSVariantWithTypes, std::monostate> + { + return consumeValue(delimiter, CSSMergedDataTypes{}); + } + + /** + * Attempts to parse the next CSS value of a given set of data types, at the + * current location of the parser, without advancing the parser. + */ + template + constexpr auto peekNextValue(CSSDelimiter delimiter = CSSDelimiter::None) + -> CSSVariantWithTypes, std::monostate> + { + auto savedParser = *parser_; + auto ret = consumeValue(delimiter, CSSMergedDataTypes{}); + *parser_ = savedParser; + return ret; + } + + /** + * Returns a reference to the underlying CSSSyntaxParser. Use this for + * syntax-level operations like consumeWhitespace(), consumeDelimiter(), + * and isFinished(). + */ + constexpr CSSSyntaxParser &syntaxParser() + { + return *parser_; + } + constexpr const CSSSyntaxParser &syntaxParser() const + { + return *parser_; + } + + private: /* * Attempts to parse the characters starting at the current component value * into one of the given data types. Types are attempted in order, which the @@ -32,41 +74,32 @@ class CSSValueParser { */ template constexpr std::variant consumeValue( - CSSDelimiter delimeter, + CSSDelimiter delimiter, std::variant /*unused*/) { using ReturnT = std::variant; - auto consumedValue = tryConsumeParser...>(delimeter); + auto consumedValue = tryConsumeParser...>(delimiter); if (!std::holds_alternative(consumedValue)) { return consumedValue; } - return parser_.consumeComponentValue( - delimeter, + return parser_->consumeComponentValue( + delimiter, [&](const CSSPreservedToken &token) { return tryConsumePreservedToken...>(token); }, [&](const CSSSimpleBlock &block, CSSSyntaxParser &blockParser) { - return tryConsumeSimpleBlock...>(block, blockParser); + CSSValueParser valueParser(blockParser); + return tryConsumeSimpleBlock...>(block, valueParser); }, [&](const CSSFunctionBlock &func, CSSSyntaxParser &blockParser) { - return tryConsumeFunctionBlock...>(func, blockParser); + CSSValueParser valueParser(blockParser); + return tryConsumeFunctionBlock...>(func, valueParser); }); } - constexpr bool isFinished() const - { - return parser_.isFinished(); - } - - constexpr void consumeWhitespace() - { - parser_.consumeWhitespace(); - } - - private: template constexpr ReturnT tryConsumePreservedToken(const CSSPreservedToken & /*token*/) { @@ -86,13 +119,13 @@ class CSSValueParser { } template - constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock & /*token*/, CSSSyntaxParser & /*blockParser*/) + constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock & /*token*/, CSSValueParser & /*blockParser*/) { return {}; } template - constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock &block, CSSSyntaxParser &blockParser) + constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock &block, CSSValueParser &blockParser) { if constexpr (CSSSimpleBlockSink) { auto currentParser = blockParser; @@ -106,13 +139,13 @@ class CSSValueParser { } template - constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock & /*func*/, CSSSyntaxParser & /*blockParser*/) + constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock & /*func*/, CSSValueParser & /*blockParser*/) { return {}; } template - constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock &func, CSSSyntaxParser &blockParser) + constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock &func, CSSValueParser &blockParser) { if constexpr (CSSFunctionBlockSink) { auto currentParser = blockParser; @@ -126,32 +159,30 @@ class CSSValueParser { } template - constexpr ReturnT tryConsumeParser(CSSDelimiter /*delimeter*/) + constexpr ReturnT tryConsumeParser(CSSDelimiter /*delimiter*/) { return {}; } template - constexpr ReturnT tryConsumeParser(CSSDelimiter delimeter) + constexpr ReturnT tryConsumeParser(CSSDelimiter delimiter) { if constexpr (CSSParserSink) { - auto originalParser = parser_; - if (parser_.consumeDelimiter(delimeter)) { - if (auto ret = ParserT::consume(parser_)) { + auto originalParser = *parser_; + if (parser_->consumeDelimiter(delimiter)) { + if (auto ret = ParserT::consume(*this)) { return *ret; } } - parser_ = originalParser; + *parser_ = originalParser; } - return tryConsumeParser(delimeter); + return tryConsumeParser(delimiter); } - CSSSyntaxParser &parser_; + CSSSyntaxParser *parser_; }; -} // namespace detail - /** * Parse a single CSS property value. Returns a variant holding std::monostate * on syntax error. @@ -161,44 +192,17 @@ constexpr auto parseCSSProperty(std::string_view css) -> CSSVariantWithTypes, std::monostate> { CSSSyntaxParser syntaxParser(css); - detail::CSSValueParser parser(syntaxParser); + CSSValueParser parser(syntaxParser); - parser.consumeWhitespace(); - auto value = parser.consumeValue(CSSDelimiter::None, CSSMergedDataTypes{}); - parser.consumeWhitespace(); + syntaxParser.consumeWhitespace(); + auto value = parser.parseNextValue(); + syntaxParser.consumeWhitespace(); - if (parser.isFinished()) { + if (syntaxParser.isFinished()) { return value; } return {}; }; -/** - * Attempts to parse the next CSS value of a given set of data types, at the - * current location of the syntax parser, advancing the syntax parser - */ -template -constexpr auto parseNextCSSValue(CSSSyntaxParser &syntaxParser, CSSDelimiter delimeter = CSSDelimiter::None) - -> CSSVariantWithTypes, std::monostate> -{ - detail::CSSValueParser valueParser(syntaxParser); - return valueParser.consumeValue(delimeter, CSSMergedDataTypes{}); -} - -/** - * Attempts to parse the next CSS value of a given set of data types, at the - * current location of the syntax parser, without advancing the syntax parser - */ -template -constexpr auto peekNextCSSValue(CSSSyntaxParser &syntaxParser, CSSDelimiter delimeter = CSSDelimiter::None) - -> CSSVariantWithTypes, std::monostate> -{ - auto savedParser = syntaxParser; - detail::CSSValueParser valueParser(syntaxParser); - auto ret = valueParser.consumeValue(delimeter, CSSMergedDataTypes{}); - syntaxParser = savedParser; - return ret; -} - } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp index b2384abbb94c..bedf04baa6f1 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp @@ -21,8 +21,8 @@ struct ConsumeDataType { template <> struct CSSDataTypeParser { constexpr static std::optional consume( - CSSSyntaxParser& parser) { - auto val = parseNextCSSValue(parser); + CSSValueParser& parser) { + auto val = parser.parseNextValue(); if (std::holds_alternative(val)) { return ConsumeDataType{std::get(val).value}; } @@ -34,32 +34,33 @@ struct CSSDataTypeParser { static_assert(CSSDataType); TEST(CSSValueParser, consume_multiple_with_delimeter) { - CSSSyntaxParser parser{"1 2, 3, 4 / 5"}; + CSSSyntaxParser syntaxParser{"1 2, 3, 4 / 5"}; + CSSValueParser parser{syntaxParser}; - auto next = parseNextCSSValue(parser); + auto next = parser.parseNextValue(); EXPECT_TRUE(std::holds_alternative(next)); EXPECT_EQ(std::get(next).number, 1); - next = parseNextCSSValue(parser, CSSDelimiter::None); + next = parser.parseNextValue(CSSDelimiter::None); EXPECT_FALSE(std::holds_alternative(next)); - next = parseNextCSSValue(parser, CSSDelimiter::Whitespace); + next = parser.parseNextValue(CSSDelimiter::Whitespace); EXPECT_TRUE(std::holds_alternative(next)); EXPECT_EQ(std::get(next).number, 2); - next = parseNextCSSValue(parser, CSSDelimiter::Comma); + next = parser.parseNextValue(CSSDelimiter::Comma); EXPECT_TRUE(std::holds_alternative(next)); EXPECT_EQ(std::get(next).number, 3); - next = parseNextCSSValue(parser, CSSDelimiter::Comma); + next = parser.parseNextValue(CSSDelimiter::Comma); EXPECT_TRUE(std::holds_alternative(next)); EXPECT_EQ(std::get(next).number, 4); - next = parseNextCSSValue(parser, CSSDelimiter::Solidus); + next = parser.parseNextValue(CSSDelimiter::Solidus); EXPECT_TRUE(std::holds_alternative(next)); EXPECT_EQ(std::get(next).number, 5); - next = parseNextCSSValue(parser); + next = parser.parseNextValue(); EXPECT_FALSE(std::holds_alternative(next)); } diff --git a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp index 8a852b90f00b..fec2944ba21b 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/intersection/IntersectionObserver.cpp @@ -32,6 +32,7 @@ std::vector parseNormalizedRootMargin( std::vector values; CSSSyntaxParser syntaxParser(marginStr); + CSSValueParser valueParser(syntaxParser); // Parse exactly 4 space-separated values while (!syntaxParser.isFinished() && values.size() < 4) { @@ -40,7 +41,7 @@ std::vector parseNormalizedRootMargin( break; } - auto parsed = parseNextCSSValue(syntaxParser); + auto parsed = valueParser.parseNextValue(); if (std::holds_alternative(parsed)) { auto length = std::get(parsed);