From c37fc74212beac79ddcf63b49b350b2dea43f371 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Tue, 13 Feb 2024 20:13:22 -0800 Subject: [PATCH] Add CSSProp and CSSPropDefinition (#42913) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42913 This structures properties into a `CSSProp` enum (so that we can have a runtime-key per style prop), associated with a `CSSPropDefinition` structure which groups the supported types and keywords. This has some niceness of removing the macro bits, but more importantly, means we can query parse related information without a field of the value yet existing (need for sparse storage of CSS values). In the future, it will serve as where we define "initial" values, and likely, the processes for interpolation and inheritance. We restructure `CSSValueVariant` to not always support keywords, as it may not be a valid possibility for computed values (which do not have CSS wide keywords). Computed values themselves may also reduce more keywords than the global ones (e.g. border width computed value absolutizes keywords). We also flesh out more of the prop definitions, and parsing. All the properties here relay back to YogaStylableProps of today, but I intentionally filled out the prop definitions a bit more than we do anything with right now (will design higher level to ignore unknown props). Changelog: [Internal] Reviewed By: rozele Differential Revision: D53518450 fbshipit-source-id: 1b48ae2513a258d15c5e7fd16ef06f1b6be8dab2 --- .../react/renderer/css/CSSKeywords.h | 280 ++++--- .../react/renderer/css/CSSParser.h | 63 +- .../react/renderer/css/CSSProperties.h | 712 ++++++++++++++++++ .../ReactCommon/react/renderer/css/CSSValue.h | 81 +- .../renderer/css/tests/CSSParserTest.cpp | 313 ++++---- 5 files changed, 1127 insertions(+), 322 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h b/packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h index d7606b51f20..15e5037c684 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h @@ -25,9 +25,15 @@ enum class CSSKeyword : uint8_t { Absolute, Auto, Baseline, + Block, Center, + Clip, Column, ColumnReverse, + Content, + Contents, + End, + Fixed, Flex, FlexEnd, FlexStart, @@ -35,8 +41,16 @@ enum class CSSKeyword : uint8_t { Inherit, Initial, Inline, + InlineBlock, + InlineFlex, + InlineGrid, Ltr, + Grid, + MaxContent, + Medium, + MinContent, None, + Normal, NoWrap, Relative, Row, @@ -46,8 +60,12 @@ enum class CSSKeyword : uint8_t { SpaceAround, SpaceBetween, SpaceEvenly, + Start, Static, + Sticky, Stretch, + Thick, + Thin, Unset, Visible, Wrap, @@ -55,159 +73,21 @@ enum class CSSKeyword : uint8_t { }; /** - * Represents a set of CSS keywords, including CSS-wide keywords. + * Represents a contrained set of CSS keywords. */ template -concept CSSKeywordSet = std::is_enum_v && requires { - { T::Inherit } -> std::same_as; - { T::Initial } -> std::same_as; - { T::Unset } -> std::same_as; -}; - -/** - * Defines a new set of CSS keywords - */ -#define CSS_DEFINE_KEYWORD_SET(name, ...) \ - enum class name : uint8_t { \ - Inherit = to_underlying(CSSKeyword::Inherit), \ - Initial = to_underlying(CSSKeyword::Initial), \ - Unset = to_underlying(CSSKeyword::Unset), \ - __VA_ARGS__ \ - }; +concept CSSKeywordSet = std::is_enum_v && std:: + is_same_v, std::underlying_type_t>; /** * CSS-wide keywords. * https://www.w3.org/TR/css-values-4/#common-keywords */ -CSS_DEFINE_KEYWORD_SET(CSSWideKeyword) - -/** - * CSS-wide keywords along with a context-dependent "auto" keyword. - */ -CSS_DEFINE_KEYWORD_SET(CSSAutoKeyword, Auto = to_underlying(CSSKeyword::Auto)) - -/** - * Keywords for the CSS "align-content" property. - * https://www.w3.org/TR/css-flexbox-1/#align-content-property - * https://www.w3.org/TR/css-align-3/#align-justify-content - */ -CSS_DEFINE_KEYWORD_SET( - CSSAlignContent, - Center = to_underlying(CSSKeyword::Center), - FlexEnd = to_underlying(CSSKeyword::FlexEnd), - FlexStart = to_underlying(CSSKeyword::FlexStart), - SpaceAround = to_underlying(CSSKeyword::SpaceAround), - SpaceBetween = to_underlying(CSSKeyword::SpaceBetween), - SpaceEvenly = to_underlying(CSSKeyword::SpaceEvenly), - Stretch = to_underlying(CSSKeyword::Stretch)) - -/** - * Keywords for the CSS "align-items" property. - * https://www.w3.org/TR/css-flexbox-1/#align-items-property - * https://www.w3.org/TR/css-align-3/#align-items-property - */ -CSS_DEFINE_KEYWORD_SET( - CSSAlignItems, - Baseline = to_underlying(CSSKeyword::Baseline), - Center = to_underlying(CSSKeyword::Center), - FlexEnd = to_underlying(CSSKeyword::FlexEnd), - FlexStart = to_underlying(CSSKeyword::FlexStart), - Stretch = to_underlying(CSSKeyword::Stretch)) - -/** - * Keywords for the CSS "align-items" property. - * https://www.w3.org/TR/css-flexbox-1/#align-self-property - * https://www.w3.org/TR/css-align-3/#align-self-property - */ -CSS_DEFINE_KEYWORD_SET( - CSSAlignSelf, - Auto = to_underlying(CSSKeyword::Auto), - Baseline = to_underlying(CSSKeyword::Baseline), - Center = to_underlying(CSSKeyword::Center), - FlexEnd = to_underlying(CSSKeyword::FlexEnd), - FlexStart = to_underlying(CSSKeyword::FlexStart), - Stretch = to_underlying(CSSKeyword::Stretch)) - -/** - * Keywords for the CSS "direction" property. - * https://www.w3.org/TR/css-writing-modes-3/#direction - */ -CSS_DEFINE_KEYWORD_SET( - CSSDirection, - Ltr = to_underlying(CSSKeyword::Ltr), - Rtl = to_underlying(CSSKeyword::Rtl)) - -/** - * Keywords for the CSS "display" property. - * https://www.w3.org/TR/css-display-3/#display-type - */ -CSS_DEFINE_KEYWORD_SET( - CSSDisplay, - Flex = to_underlying(CSSKeyword::Flex), - Inline = to_underlying(CSSKeyword::Inline), - None = to_underlying(CSSKeyword::None)) - -/** - * Keywords for the CSS "flex-direction" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-direction-property - */ -CSS_DEFINE_KEYWORD_SET( - CSSFlexDirection, - Column = to_underlying(CSSKeyword::Column), - ColumnReverse = to_underlying(CSSKeyword::ColumnReverse), - Row = to_underlying(CSSKeyword::Row), - RowReverse = to_underlying(CSSKeyword::RowReverse)) - -/** - * Keywords for the CSS "flex-wrap" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property - */ -CSS_DEFINE_KEYWORD_SET( - CSSFlexWrap, - NoWrap = to_underlying(CSSKeyword::NoWrap), - Wrap = to_underlying(CSSKeyword::Wrap), - WrapReverse = to_underlying(CSSKeyword::WrapReverse)) - -/** - * Keywords for the CSS "justify-content" property. - * https://www.w3.org/TR/css-flexbox-1/#justify-content-property - * https://www.w3.org/TR/css-align-3/#align-justify-content - */ -CSS_DEFINE_KEYWORD_SET( - CSSJustifyContent, - Center = to_underlying(CSSKeyword::Center), - FlexEnd = to_underlying(CSSKeyword::FlexEnd), - FlexStart = to_underlying(CSSKeyword::FlexStart), - SpaceAround = to_underlying(CSSKeyword::SpaceAround), - SpaceBetween = to_underlying(CSSKeyword::SpaceBetween), - SpaceEvenly = to_underlying(CSSKeyword::SpaceEvenly)) - -/** - * Keywords for the CSS "overflow" property. - * https://www.w3.org/TR/css-overflow-3/#overflow-control - */ -CSS_DEFINE_KEYWORD_SET( - CSSOverflow, - Hidden = to_underlying(CSSKeyword::Hidden), - Scroll = to_underlying(CSSKeyword::Scroll), - Visible = to_underlying(CSSKeyword::Visible)) - -/** - * Keywords for the CSS "position" property. - * https://www.w3.org/TR/css-position-3/#position-property - */ -CSS_DEFINE_KEYWORD_SET( - CSSPosition, - Absolute = to_underlying(CSSKeyword::Absolute), - Relative = to_underlying(CSSKeyword::Relative), - Static = to_underlying(CSSKeyword::Static)) - -/** - * Compare two keywords of any representation - */ -constexpr bool operator==(CSSKeywordSet auto lhs, CSSKeywordSet auto rhs) { - return to_underlying(lhs) == to_underlying(rhs); -} +enum class CSSWideKeyword : std::underlying_type_t { + Inherit = to_underlying(CSSKeyword::Inherit), + Initial = to_underlying(CSSKeyword::Initial), + Unset = to_underlying(CSSKeyword::Unset), +}; /** * Defines a concept for whether an enum has a given member. @@ -221,18 +101,32 @@ constexpr bool operator==(CSSKeywordSet auto lhs, CSSKeywordSet auto rhs) { CSS_DEFINE_KEYWORD_CONEPTS(Absolute) CSS_DEFINE_KEYWORD_CONEPTS(Auto) CSS_DEFINE_KEYWORD_CONEPTS(Baseline) +CSS_DEFINE_KEYWORD_CONEPTS(Block) CSS_DEFINE_KEYWORD_CONEPTS(Center) +CSS_DEFINE_KEYWORD_CONEPTS(Clip) CSS_DEFINE_KEYWORD_CONEPTS(Column) CSS_DEFINE_KEYWORD_CONEPTS(ColumnReverse) +CSS_DEFINE_KEYWORD_CONEPTS(Content) +CSS_DEFINE_KEYWORD_CONEPTS(Contents) +CSS_DEFINE_KEYWORD_CONEPTS(End) +CSS_DEFINE_KEYWORD_CONEPTS(Fixed) CSS_DEFINE_KEYWORD_CONEPTS(Flex) CSS_DEFINE_KEYWORD_CONEPTS(FlexEnd) CSS_DEFINE_KEYWORD_CONEPTS(FlexStart) +CSS_DEFINE_KEYWORD_CONEPTS(Grid) CSS_DEFINE_KEYWORD_CONEPTS(Hidden) CSS_DEFINE_KEYWORD_CONEPTS(Inherit) CSS_DEFINE_KEYWORD_CONEPTS(Initial) CSS_DEFINE_KEYWORD_CONEPTS(Inline) +CSS_DEFINE_KEYWORD_CONEPTS(InlineBlock) +CSS_DEFINE_KEYWORD_CONEPTS(InlineFlex) +CSS_DEFINE_KEYWORD_CONEPTS(InlineGrid) CSS_DEFINE_KEYWORD_CONEPTS(Ltr) +CSS_DEFINE_KEYWORD_CONEPTS(MaxContent) +CSS_DEFINE_KEYWORD_CONEPTS(Medium) +CSS_DEFINE_KEYWORD_CONEPTS(MinContent) CSS_DEFINE_KEYWORD_CONEPTS(None) +CSS_DEFINE_KEYWORD_CONEPTS(Normal) CSS_DEFINE_KEYWORD_CONEPTS(NoWrap) CSS_DEFINE_KEYWORD_CONEPTS(Relative) CSS_DEFINE_KEYWORD_CONEPTS(Row) @@ -242,8 +136,12 @@ CSS_DEFINE_KEYWORD_CONEPTS(Scroll) CSS_DEFINE_KEYWORD_CONEPTS(SpaceAround) CSS_DEFINE_KEYWORD_CONEPTS(SpaceBetween) CSS_DEFINE_KEYWORD_CONEPTS(SpaceEvenly) +CSS_DEFINE_KEYWORD_CONEPTS(Start) CSS_DEFINE_KEYWORD_CONEPTS(Static) +CSS_DEFINE_KEYWORD_CONEPTS(Sticky) CSS_DEFINE_KEYWORD_CONEPTS(Stretch) +CSS_DEFINE_KEYWORD_CONEPTS(Thick) +CSS_DEFINE_KEYWORD_CONEPTS(Thin) CSS_DEFINE_KEYWORD_CONEPTS(Unset) CSS_DEFINE_KEYWORD_CONEPTS(Visible) CSS_DEFINE_KEYWORD_CONEPTS(Wrap) @@ -279,11 +177,21 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { return KeywordT::Baseline; } break; + case fnv1a("block"): + if constexpr (detail::hasBlock) { + return KeywordT::Block; + } + break; case fnv1a("center"): if constexpr (detail::hasCenter) { return KeywordT::Center; } break; + case fnv1a("clip"): + if constexpr (detail::hasClip) { + return KeywordT::Clip; + } + break; case fnv1a("column"): if constexpr (detail::hasColumn) { return KeywordT::Column; @@ -294,6 +202,25 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { return KeywordT::ColumnReverse; } break; + case fnv1a("content"): + if constexpr (detail::hasContent) { + return KeywordT::Content; + } + break; + case fnv1a("contents"): + if constexpr (detail::hasContents) { + return KeywordT::Contents; + } + break; + case fnv1a("end"): + if constexpr (detail::hasEnd) { + return KeywordT::End; + } + break; + case fnv1a("fixed"): + if constexpr (detail::hasFixed) { + return KeywordT::Fixed; + } case fnv1a("flex"): if constexpr (detail::hasFlex) { return KeywordT::Flex; @@ -309,6 +236,11 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { return KeywordT::FlexStart; } break; + case fnv1a("grid"): + if constexpr (detail::hasGrid) { + return KeywordT::Grid; + } + break; case fnv1a("hidden"): if constexpr (detail::hasHidden) { return KeywordT::Hidden; @@ -324,17 +256,52 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { return KeywordT::Inline; } break; + case fnv1a("inline-block"): + if constexpr (detail::hasInlineBlock) { + return KeywordT::InlineBlock; + } + break; + case fnv1a("inline-flex"): + if constexpr (detail::hasInlineFlex) { + return KeywordT::InlineFlex; + } + break; + case fnv1a("inline-grid"): + if constexpr (detail::hasInlineGrid) { + return KeywordT::InlineGrid; + } + break; case fnv1a("ltr"): if constexpr (detail::hasLtr) { return KeywordT::Ltr; } break; + case fnv1a("max-content"): + if constexpr (detail::hasMaxContent) { + return KeywordT::MaxContent; + } + break; + case fnv1a("medium"): + if constexpr (detail::hasMedium) { + return KeywordT::Medium; + } + break; + case fnv1a("min-content"): + if constexpr (detail::hasMinContent) { + return KeywordT::MinContent; + } + break; case fnv1a("none"): if constexpr (detail::hasNone) { return KeywordT::None; } break; - case fnv1a("no-wrap"): + case fnv1a("normal"): + if constexpr (detail::hasNormal) { + return KeywordT::Normal; + } + break; + case fnv1a("nowrap"): if constexpr (detail::hasNoWrap) { return KeywordT::NoWrap; } @@ -379,16 +346,35 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { return KeywordT::Scroll; } break; + case fnv1a("start"): + if constexpr (detail::hasStart) { + return KeywordT::Start; + } case fnv1a("static"): if constexpr (detail::hasStatic) { return KeywordT::Static; } break; + case fnv1a("sticky"): + if constexpr (detail::hasSticky) { + return KeywordT::Sticky; + } + break; case fnv1a("stretch"): if constexpr (detail::hasStretch) { return KeywordT::Stretch; } break; + case fnv1a("thick"): + if constexpr (detail::hasThick) { + return KeywordT::Thick; + } + break; + case fnv1a("thin"): + if constexpr (detail::hasThin) { + return KeywordT::Thin; + } + break; case fnv1a("unset"): if constexpr (detail::hasUnset) { return KeywordT::Unset; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h index 6c31b259ab1..f9ad61c4846 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSParser.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -23,13 +24,13 @@ class CSSParser { explicit constexpr CSSParser(std::string_view css) : tokenizer_{css}, currentToken_(tokenizer_.next()) {} - template - constexpr CSSValueVariant - consumeComponentValue() { - using CSSValueT = CSSValueVariant; + template + constexpr CSSValueVariant consumeComponentValue() { + using CSSValueT = CSSValueVariant; switch (peek().type()) { case CSSTokenType::Ident: - if (auto keywordValue = consumeIdentToken()) { + if (auto keywordValue = + consumeIdentToken()) { return *keywordValue; } break; @@ -80,16 +81,26 @@ class CSSParser { return prevToken; } - template + template constexpr std::optional consumeIdentToken() { - if (auto keyword = - parseCSSKeyword(consumeToken().stringValue())) { - return CSSValueT::keyword(*keyword); + if constexpr (!std::is_same_v) { + if (auto keyword = parseCSSKeyword( + peek().stringValue())) { + consumeToken(); + return CSSValueT::keyword(*keyword); + } + } + if constexpr (traits::containsType()) { + if (auto keyword = + parseCSSKeyword(peek().stringValue())) { + consumeToken(); + return CSSValueT::cssWideKeyword(*keyword); + } } return {}; } - template + template constexpr std::optional consumeDimensionToken() { if constexpr (traits::containsType()) { if (auto unit = parseCSSLengthUnit(peek().unit())) { @@ -99,7 +110,7 @@ class CSSParser { return {}; } - template + template constexpr std::optional consumePercentageToken() { if constexpr (traits::containsType()) { return CSSValueT::percentage(consumeToken().numericValue()); @@ -107,7 +118,7 @@ class CSSParser { return {}; } - template + template constexpr std::optional consumeNumberToken() { // = [ / ]? // https://www.w3.org/TR/css-values-4/#ratio @@ -174,20 +185,36 @@ class CSSParser { * * https://www.w3.org/TR/css-syntax-3/#parse-component-value */ -template -constexpr CSSValueVariant parseCSSComponentValue( - std::string_view css) { +template +constexpr void parseCSSComponentValue( + std::string_view css, + CSSValueVariant& value) { detail::CSSParser parser(css); parser.consumeWhitespace(); - auto value = parser.consumeComponentValue(); + auto componentValue = parser.consumeComponentValue(); parser.consumeWhitespace(); if (parser.hasMoreTokens()) { - return {}; + value = {}; } else { - return value; + value = componentValue; } +}; + +template +CSSValueVariant parseCSSComponentValue(std::string_view css) { + CSSValueVariant value; + parseCSSComponentValue(css, value); + return value; +}; + +template +constexpr auto parseCSSProp(std::string_view css) { + // For now we only allow parsing props composed of a single component value. + CSSSpecifiedValue value; + parseCSSComponentValue(css, value); + return value; } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h b/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h new file mode 100644 index 00000000000..ad982552bcf --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h @@ -0,0 +1,712 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react { + +/** + * All CSS properties,including CSS,and React-Native specific shorthands + * https://www.w3.org/TR/css-cascade-4/#css-property + */ +enum class CSSProp { + AlignContent, + AlignItems, + AlignSelf, + AspectRatio, + BorderBlockEndWidth, + BorderBlockStartWidth, + BorderBlockWidth, + BorderBottomWidth, + BorderEndWidth, + BorderHorizontalWidth, + BorderInlineEndWidth, + BorderInlineStartWidth, + BorderInlineWidth, + BorderLeftWidth, + BorderRightWidth, + BorderStartWidth, + BorderTopWidth, + BorderVerticalWidth, + BorderWidth, + Bottom, + ColumnGap, + Direction, + Display, + End, + Flex, + FlexBasis, + FlexDirection, + FlexGrow, + FlexShrink, + FlexWrap, + Gap, + Height, + Inset, + InsetBlock, + InsetBlockEnd, + InsetBlockStart, + InsetInline, + InsetInlineEnd, + InsetInlineStart, + JustifyContent, + Left, + Margin, + MarginBlock, + MarginBlockEnd, + MarginBlockStart, + MarginBottom, + MarginEnd, + MarginHorizontal, + MarginInline, + MarginInlineEnd, + MarginInlineStart, + MarginLeft, + MarginRight, + MarginStart, + MarginTop, + MarginVertical, + MaxHeight, + MaxWidth, + MinHeight, + MinWidth, + Overflow, + Padding, + PaddingBlock, + PaddingBlockEnd, + PaddingBlockStart, + PaddingBottom, + PaddingEnd, + PaddingHorizontal, + PaddingInline, + PaddingInlineEnd, + PaddingInlineStart, + PaddingLeft, + PaddingRight, + PaddingStart, + PaddingTop, + PaddingVertical, + Position, + Right, + RowGap, + Start, + Top, + Width, + // Please update "kCSSPropCount" if adding a new prop to the end +}; + +/** + * The total number of CSS properties. + */ +constexpr auto kCSSPropCount = to_underlying(CSSProp::Width) + 1; + +/** + * CSSPropDefinition associates a CSSProp to its + * supported data types, Keyword, and other behaviors. + */ +template +struct CSSPropDefinition {}; + +template +using CSSAllowedKeywords = typename CSSPropDefinition

::Keyword; + +template +using CSSSpecifiedValue = typename CSSPropDefinition

::SpecifiedValue; + +template +using CSSComputedValue = typename CSSPropDefinition

::ComputedValue; + +/** + * CSS "align-content" property. + * https://www.w3.org/TR/css-flexbox-1/#align-content-property + * https://www.w3.org/TR/css-align-3/#align-justify-content + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Center = to_underlying(CSSKeyword::Center), + FlexEnd = to_underlying(CSSKeyword::FlexEnd), + FlexStart = to_underlying(CSSKeyword::FlexStart), + SpaceAround = to_underlying(CSSKeyword::SpaceAround), + SpaceBetween = to_underlying(CSSKeyword::SpaceBetween), + SpaceEvenly = to_underlying(CSSKeyword::SpaceEvenly), + Stretch = to_underlying(CSSKeyword::Stretch), + Start = to_underlying(CSSKeyword::Start), + End = to_underlying(CSSKeyword::End), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "align-items" property. + * https://www.w3.org/TR/css-flexbox-1/#align-items-property + * https://www.w3.org/TR/css-align-3/#align-items-property + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Baseline = to_underlying(CSSKeyword::Baseline), + Center = to_underlying(CSSKeyword::Center), + FlexEnd = to_underlying(CSSKeyword::FlexEnd), + FlexStart = to_underlying(CSSKeyword::FlexStart), + Stretch = to_underlying(CSSKeyword::Stretch), + Start = to_underlying(CSSKeyword::Start), + End = to_underlying(CSSKeyword::End), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "align-items" property. + * https://www.w3.org/TR/css-flexbox-1/#propdef-align-self + * https://www.w3.org/TR/css-align-3/#align-self-property + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + Baseline = to_underlying(CSSKeyword::Baseline), + Center = to_underlying(CSSKeyword::Center), + FlexEnd = to_underlying(CSSKeyword::FlexEnd), + FlexStart = to_underlying(CSSKeyword::FlexStart), + Stretch = to_underlying(CSSKeyword::Stretch), + Start = to_underlying(CSSKeyword::Start), + End = to_underlying(CSSKeyword::End), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "aspect-ratio" property. + * https://www.w3.org/TR/css-sizing-4/#aspect-ratio + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "border-width" properties + * https://www.w3.org/TR/css-backgrounds-3/#border-width + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Thin = to_underlying(CSSKeyword::Thin), + Medium = to_underlying(CSSKeyword::Medium), + Thick = to_underlying(CSSKeyword::Thick), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +/** + * CSS "direction" property. + * https://www.w3.org/TR/css-writing-modes-3/#direction + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Ltr = to_underlying(CSSKeyword::Ltr), + Rtl = to_underlying(CSSKeyword::Rtl), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "display" property. + * https://www.w3.org/TR/css-display-3/#display-type + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + None = to_underlying(CSSKeyword::None), + Contents = to_underlying(CSSKeyword::Contents), + Inline = to_underlying(CSSKeyword::Inline), + Block = to_underlying(CSSKeyword::Block), + InlineBlock = to_underlying(CSSKeyword::InlineBlock), + Flex = to_underlying(CSSKeyword::Flex), + InlineFlex = to_underlying(CSSKeyword::InlineFlex), + Grid = to_underlying(CSSKeyword::Grid), + InlineGrid = to_underlying(CSSKeyword::InlineGrid), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "flex" shorthand property. + * https://www.w3.org/TR/css-flexbox-1/#flex-property + * + * React Native's interpretation of this prop is currently different than in CSS + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + None = to_underlying(CSSKeyword::None), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "flex-basis" property. + * https://www.w3.org/TR/css-flexbox-1/#flex-basis-property + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + Content = to_underlying(CSSKeyword::Content), + }; + + using SpecifiedValue = + CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "flex-direction" property. + * https://www.w3.org/TR/css-flexbox-1/#flex-direction-property + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Row = to_underlying(CSSKeyword::Row), + RowReverse = to_underlying(CSSKeyword::RowReverse), + Column = to_underlying(CSSKeyword::Column), + ColumnReverse = to_underlying(CSSKeyword::ColumnReverse), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "flex-grow" property. + * https://www.w3.org/TR/css-flexbox-1/#flex-grow-property + */ +template <> +struct CSSPropDefinition { + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "flex-shrink" property. + * https://www.w3.org/TR/css-flexbox-1/#flex-shrink-property + */ +template <> +struct CSSPropDefinition { + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "flex-wrap" property. + * https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + NoWrap = to_underlying(CSSKeyword::NoWrap), + Wrap = to_underlying(CSSKeyword::Wrap), + WrapReverse = to_underlying(CSSKeyword::WrapReverse), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS gutter properties. + * https://www.w3.org/TR/css-align-3/#column-row-gap + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Normal = to_underlying(CSSKeyword::Normal), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +template <> +struct CSSPropDefinition : CSSPropDefinition { +}; + +template <> +struct CSSPropDefinition : CSSPropDefinition {}; + +/** + * CSS sizing properties + * https://www.w3.org/TR/css-sizing-3/#sizing-properties + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + MaxContent = to_underlying(CSSKeyword::MaxContent), + MinContent = to_underlying(CSSKeyword::MinContent), + }; + + using SpecifiedValue = + CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +template <> +struct CSSPropDefinition : CSSPropDefinition { +}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +/** + * CSS box inset properties + * https://drafts.csswg.org/css-position-3/#insets + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + }; + + using SpecifiedValue = + CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +template <> +struct CSSPropDefinition : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition : CSSPropDefinition { +}; + +template <> +struct CSSPropDefinition : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +/** + * CSS "justify-content" property. + * https://www.w3.org/TR/css-flexbox-1/#justify-content-property + * https://www.w3.org/TR/css-align-3/#align-justify-content + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Center = to_underlying(CSSKeyword::Center), + FlexEnd = to_underlying(CSSKeyword::FlexEnd), + FlexStart = to_underlying(CSSKeyword::FlexStart), + SpaceAround = to_underlying(CSSKeyword::SpaceAround), + SpaceBetween = to_underlying(CSSKeyword::SpaceBetween), + SpaceEvenly = to_underlying(CSSKeyword::SpaceEvenly), + Start = to_underlying(CSSKeyword::Start), + End = to_underlying(CSSKeyword::End), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS "margin" properties + * https://www.w3.org/TR/css-box-4/#margins + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + }; + + using SpecifiedValue = + CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +/** + * CSS "overflow" property. + * https://www.w3.org/TR/css-overflow-3/#overflow-control + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Auto = to_underlying(CSSKeyword::Auto), + Clip = to_underlying(CSSKeyword::Clip), + Hidden = to_underlying(CSSKeyword::Hidden), + Scroll = to_underlying(CSSKeyword::Scroll), + Visible = to_underlying(CSSKeyword::Visible), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +/** + * CSS padding properties + * https://www.w3.org/TR/css-box-4/#paddings + */ +template <> +struct CSSPropDefinition { + using SpecifiedValue = + CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +template <> +struct CSSPropDefinition + : CSSPropDefinition {}; + +/** + * CSS "position" property. + * https://www.w3.org/TR/css-position-3/#position-property + */ +template <> +struct CSSPropDefinition { + enum class Keyword : std::underlying_type_t { + Static = to_underlying(CSSKeyword::Static), + Relative = to_underlying(CSSKeyword::Relative), + Absolute = to_underlying(CSSKeyword::Absolute), + Fixed = to_underlying(CSSKeyword::Fixed), + Sticky = to_underlying(CSSKeyword::Sticky), + }; + + using SpecifiedValue = CSSValueVariant; + using ComputedValue = CSSValueVariant; +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h index 255db87185a..4663dd9e818 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h @@ -23,6 +23,7 @@ namespace facebook::react { * https://www.w3.org/TR/css-values-4/#component-types */ enum class CSSValueType : uint8_t { + CSSWideKeyword, Keyword, Length, Number, @@ -31,11 +32,11 @@ enum class CSSValueType : uint8_t { }; /** - * Concrete representation for a CSS basic data type. + * Concrete representation for a CSS basic data type, or keywords * https://www.w3.org/TR/css-values-4/#component-types */ template -concept CSSBasicDataType = std::is_trivially_destructible_v && +concept CSSDataType = std::is_trivially_destructible_v && std::is_default_constructible_v && requires() { sizeof(T); }; @@ -88,9 +89,9 @@ struct CSSRatio { * set of values. */ #pragma pack(push, 1) -template +template class CSSValueVariant { - template + template constexpr ValueT getIf() const { if (type_ == Type) { return *std::launder(reinterpret_cast(data_.data())); @@ -99,16 +100,49 @@ class CSSValueVariant { } } - template + template static constexpr bool canRepresent() { - return traits::containsType(); + return traits::containsType(); } - public: - constexpr CSSValueVariant() - : CSSValueVariant(CSSValueType::Keyword, KeywordT::Unset) {} + template + static constexpr bool hasKeywordSet() { + if constexpr (CSSKeywordSet && !std::is_same_v) { + return true; + } else if constexpr (sizeof...(Rest) == 0) { + return false; + } else { + return hasKeywordSet(); + } + } - static constexpr CSSValueVariant keyword(KeywordT keyword) { + template + struct PackedKeywordSet { + using Type = void; + }; + + template + struct PackedKeywordSet { + using Type = std::conditional_t< + hasKeywordSet(), + T, + typename PackedKeywordSet::Type>; + }; + + public: + using Keyword = typename PackedKeywordSet::Type; + + constexpr CSSValueVariant() requires(canRepresent()) + : CSSValueVariant(CSSValueType::CSSWideKeyword, CSSWideKeyword::Unset) {} + + static constexpr CSSValueVariant cssWideKeyword(CSSWideKeyword keyword) { + return CSSValueVariant( + CSSValueType::CSSWideKeyword, CSSWideKeyword{keyword}); + } + + template + static constexpr CSSValueVariant keyword(KeywordT keyword) requires( + canRepresent()) { return CSSValueVariant(CSSValueType::Keyword, KeywordT{keyword}); } @@ -139,8 +173,14 @@ class CSSValueVariant { return type_; } - constexpr KeywordT getKeyword() const { - return getIf(); + constexpr CSSWideKeyword getCSSWideKeyword() const + requires(canRepresent()) { + return getIf(); + } + + constexpr Keyword getKeyword() const + requires(hasKeywordSet()) { + return getIf(); } constexpr CSSLength getLength() const requires(canRepresent()) { @@ -160,30 +200,29 @@ class CSSValueVariant { return getIf(); } - constexpr operator bool() const { + constexpr operator bool() const requires(canRepresent()) { return *this != CSSValueVariant{}; } constexpr bool operator==(const CSSValueVariant& rhs) const = default; private: - constexpr CSSValueVariant(CSSValueType type, CSSBasicDataType auto&& value) + constexpr CSSValueVariant(CSSValueType type, CSSDataType auto&& value) : type_(type) { new (data_.data()) std::remove_cvref_t{ std::forward(value)}; } CSSValueType type_; - std::array()> data_; + std::array()> data_; }; #pragma pack(pop) -static_assert(sizeof(CSSValueVariant) == 2); -static_assert(sizeof(CSSValueVariant) == 6); +static_assert(sizeof(CSSValueVariant) == 2); +static_assert(sizeof(CSSValueVariant) == 6); static_assert( - sizeof(CSSValueVariant) == 6); -static_assert(sizeof(CSSValueVariant) == 5); -static_assert( - sizeof(CSSValueVariant) == 9); + sizeof(CSSValueVariant) == 6); +static_assert(sizeof(CSSValueVariant) == 5); +static_assert(sizeof(CSSValueVariant) == 9); } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp index a940fcc922c..280dc43e8be 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSParserTest.cpp @@ -11,247 +11,288 @@ namespace facebook::react { TEST(CSSParser, keyword_values) { - auto emptyValue = parseCSSComponentValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::Keyword); - EXPECT_EQ(emptyValue.getKeyword(), CSSKeyword::Unset); + auto emptyValue = parseCSSComponentValue(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto autoValue = parseCSSComponentValue("auto"); + auto autoValue = parseCSSComponentValue("auto"); EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); - auto autoCapsValue = parseCSSComponentValue("AuTO"); + auto autoCapsValue = + parseCSSComponentValue("AuTO"); EXPECT_EQ(autoCapsValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoCapsValue.getKeyword(), CSSKeyword::Auto); - auto autoDisallowedValue = parseCSSComponentValue("auto"); - EXPECT_EQ(autoDisallowedValue.type(), CSSValueType::Keyword); - EXPECT_EQ(autoDisallowedValue.getKeyword(), CSSKeyword::Unset); + auto autoDisallowedValue = parseCSSComponentValue("auto"); + EXPECT_EQ(autoDisallowedValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(autoDisallowedValue.getCSSWideKeyword(), CSSWideKeyword::Unset); auto whitespaceValue = - parseCSSComponentValue(" flex-start "); + parseCSSComponentValue(" flex-start "); EXPECT_EQ(whitespaceValue.type(), CSSValueType::Keyword); - EXPECT_EQ(whitespaceValue.getKeyword(), CSSAlignItems::FlexStart); + EXPECT_EQ(whitespaceValue.getKeyword(), CSSKeyword::FlexStart); - auto badIdentValue = parseCSSComponentValue("bad"); - EXPECT_EQ(badIdentValue.type(), CSSValueType::Keyword); - EXPECT_EQ(badIdentValue.getKeyword(), CSSKeyword::Unset); + auto badIdentValue = + parseCSSComponentValue("bad"); + EXPECT_EQ(badIdentValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(badIdentValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto pxValue = parseCSSComponentValue("20px"); - EXPECT_EQ(pxValue.type(), CSSValueType::Keyword); - EXPECT_EQ(pxValue.getKeyword(), CSSKeyword::Unset); + auto pxValue = parseCSSComponentValue("20px"); + EXPECT_EQ(pxValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(pxValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto multiValue = parseCSSComponentValue("auto flex-start"); - EXPECT_EQ(multiValue.type(), CSSValueType::Keyword); - EXPECT_EQ(multiValue.getKeyword(), CSSKeyword::Unset); + auto multiValue = parseCSSComponentValue("auto flex-start"); + EXPECT_EQ(multiValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(multiValue.getCSSWideKeyword(), CSSWideKeyword::Unset); } TEST(CSSParser, length_values) { - auto emptyValue = parseCSSComponentValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::Keyword); - EXPECT_EQ(emptyValue.getKeyword(), CSSKeyword::Unset); + auto emptyValue = parseCSSComponentValue(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto autoValue = parseCSSComponentValue("auto"); + auto autoValue = + parseCSSComponentValue("auto"); EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); - auto pxValue = parseCSSComponentValue("20px"); + auto pxValue = parseCSSComponentValue("20px"); EXPECT_EQ(pxValue.type(), CSSValueType::Length); EXPECT_EQ(pxValue.getLength().value, 20.0f); EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); - auto cmValue = parseCSSComponentValue("453cm"); + auto cmValue = parseCSSComponentValue("453cm"); EXPECT_EQ(cmValue.type(), CSSValueType::Length); EXPECT_EQ(cmValue.getLength().value, 453.0f); EXPECT_EQ(cmValue.getLength().unit, CSSLengthUnit::Cm); auto unitlessZeroValue = - parseCSSComponentValue("0"); + parseCSSComponentValue("0"); EXPECT_EQ(unitlessZeroValue.type(), CSSValueType::Length); EXPECT_EQ(unitlessZeroValue.getLength().value, 0.0f); EXPECT_EQ(unitlessZeroValue.getLength().unit, CSSLengthUnit::Px); auto unitlessNonzeroValue = - parseCSSComponentValue("123"); - EXPECT_EQ(unitlessNonzeroValue.type(), CSSValueType::Keyword); - EXPECT_EQ(unitlessNonzeroValue.getKeyword(), CSSKeyword::Unset); + parseCSSComponentValue("123"); + EXPECT_EQ(unitlessNonzeroValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(unitlessNonzeroValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto pctValue = parseCSSComponentValue("-40%"); - EXPECT_EQ(pctValue.type(), CSSValueType::Keyword); - EXPECT_EQ(pctValue.getKeyword(), CSSKeyword::Unset); + auto pctValue = parseCSSComponentValue("-40%"); + EXPECT_EQ(pctValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(pctValue.getCSSWideKeyword(), CSSWideKeyword::Unset); } TEST(CSSParser, length_percentage_values) { auto emptyValue = - parseCSSComponentValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::Keyword); - EXPECT_EQ(emptyValue.getKeyword(), CSSKeyword::Unset); + parseCSSComponentValue(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto autoValue = - parseCSSComponentValue("auto"); + auto autoValue = parseCSSComponentValue< + CSSWideKeyword, + CSSKeyword, + CSSLength, + CSSPercentage>("auto"); EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); auto pxValue = - parseCSSComponentValue("20px"); + parseCSSComponentValue("20px"); EXPECT_EQ(pxValue.type(), CSSValueType::Length); EXPECT_EQ(pxValue.getLength().value, 20.0f); EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); auto pctValue = - parseCSSComponentValue("-40%"); + parseCSSComponentValue("-40%"); EXPECT_EQ(pctValue.type(), CSSValueType::Percentage); EXPECT_EQ(pctValue.getPercentage().value, -40.0f); } TEST(CSSParser, number_values) { - auto emptyValue = parseCSSComponentValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::Keyword); - EXPECT_EQ(emptyValue.getKeyword(), CSSKeyword::Unset); + auto emptyValue = parseCSSComponentValue(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto inheritValue = parseCSSComponentValue("inherit"); - EXPECT_EQ(inheritValue.type(), CSSValueType::Keyword); - EXPECT_EQ(inheritValue.getKeyword(), CSSKeyword::Inherit); + auto inheritValue = + parseCSSComponentValue("inherit"); + EXPECT_EQ(inheritValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(inheritValue.getCSSWideKeyword(), CSSWideKeyword::Inherit); - auto pxValue = parseCSSComponentValue("20px"); - EXPECT_EQ(pxValue.type(), CSSValueType::Keyword); - EXPECT_EQ(pxValue.getKeyword(), CSSKeyword::Unset); + auto pxValue = + parseCSSComponentValue("20px"); + EXPECT_EQ(pxValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(pxValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto numberValue = parseCSSComponentValue("123.456"); + auto numberValue = + parseCSSComponentValue("123.456"); EXPECT_EQ(numberValue.type(), CSSValueType::Number); EXPECT_EQ(numberValue.getNumber().value, 123.456f); auto unitlessZeroValue = - parseCSSComponentValue("0"); + parseCSSComponentValue("0"); EXPECT_EQ(unitlessZeroValue.type(), CSSValueType::Number); EXPECT_EQ(unitlessZeroValue.getNumber().value, 0.0f); } TEST(CSSParser, ratio_values) { - auto emptyValue = parseCSSComponentValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::Keyword); - EXPECT_EQ(emptyValue.getKeyword(), CSSKeyword::Unset); + auto emptyValue = parseCSSComponentValue(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - auto validRatio = parseCSSComponentValue("16/9"); + auto validRatio = parseCSSComponentValue("16/9"); EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); auto validRatioWithWhitespace = - parseCSSComponentValue("16 / 9"); - EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); - EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); - EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); - - auto singleNumberRatio = parseCSSComponentValue("16"); - EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); - - auto fractionalNumber = parseCSSComponentValue("16.5"); - EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); - EXPECT_EQ(fractionalNumber.getRatio().denominator, 1.0f); - - auto negativeNumber = parseCSSComponentValue("-16"); - EXPECT_EQ(negativeNumber.type(), CSSValueType::Keyword); - EXPECT_EQ(negativeNumber.getKeyword(), CSSKeyword::Unset); - - auto missingDenominator = parseCSSComponentValue("16/"); - EXPECT_EQ(missingDenominator.type(), CSSValueType::Keyword); - EXPECT_EQ(missingDenominator.getKeyword(), CSSKeyword::Unset); - - auto negativeNumerator = - parseCSSComponentValue("-16/9"); - EXPECT_EQ(negativeNumerator.type(), CSSValueType::Keyword); - EXPECT_EQ(negativeNumerator.getKeyword(), CSSKeyword::Unset); - - auto negativeDenominator = - parseCSSComponentValue("16/-9"); - EXPECT_EQ(negativeDenominator.type(), CSSValueType::Keyword); - EXPECT_EQ(negativeDenominator.getKeyword(), CSSKeyword::Unset); - - auto fractionalNumerator = - parseCSSComponentValue("16.5/9"); - EXPECT_EQ(fractionalNumerator.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalNumerator.getRatio().numerator, 16.5f); - EXPECT_EQ(fractionalNumerator.getRatio().denominator, 9.0f); - - auto fractionalDenominator = - parseCSSComponentValue("16/9.5"); - EXPECT_EQ(fractionalDenominator.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalDenominator.getRatio().numerator, 16.0f); - EXPECT_EQ(fractionalDenominator.getRatio().denominator, 9.5f); - - auto degenerateRatio = parseCSSComponentValue("0"); - EXPECT_EQ(degenerateRatio.type(), CSSValueType::Keyword); - EXPECT_EQ(degenerateRatio.getKeyword(), CSSKeyword::Unset); -} - -TEST(CSSParser, number_ratio_values) { - auto emptyValue = parseCSSComponentValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::Keyword); - EXPECT_EQ(emptyValue.getKeyword(), CSSKeyword::Unset); - - auto validRatio = - parseCSSComponentValue("16/9"); - EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); - - auto validRatioWithWhitespace = - parseCSSComponentValue("16 / 9"); + parseCSSComponentValue("16 / 9"); EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); auto singleNumberRatio = - parseCSSComponentValue("16"); + parseCSSComponentValue("16"); EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); auto fractionalNumber = - parseCSSComponentValue("16.5"); + parseCSSComponentValue("16.5"); EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); - EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); + EXPECT_EQ(fractionalNumber.getRatio().denominator, 1.0f); - auto negativeNumber = - parseCSSComponentValue("-16"); - EXPECT_EQ(negativeNumber.type(), CSSValueType::Number); - EXPECT_EQ(negativeNumber.getNumber().value, -16.0f); + auto negativeNumber = parseCSSComponentValue("-16"); + EXPECT_EQ(negativeNumber.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(negativeNumber.getCSSWideKeyword(), CSSWideKeyword::Unset); auto missingDenominator = - parseCSSComponentValue("16/"); - EXPECT_EQ(missingDenominator.type(), CSSValueType::Keyword); - EXPECT_EQ(missingDenominator.getKeyword(), CSSKeyword::Unset); + parseCSSComponentValue("16/"); + EXPECT_EQ(missingDenominator.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(missingDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); auto negativeNumerator = - parseCSSComponentValue("-16/9"); - EXPECT_EQ(negativeNumerator.type(), CSSValueType::Keyword); - EXPECT_EQ(negativeNumerator.getKeyword(), CSSKeyword::Unset); + parseCSSComponentValue("-16/9"); + EXPECT_EQ(negativeNumerator.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(negativeNumerator.getCSSWideKeyword(), CSSWideKeyword::Unset); auto negativeDenominator = - parseCSSComponentValue("16/-9"); - EXPECT_EQ(negativeDenominator.type(), CSSValueType::Keyword); - EXPECT_EQ(negativeDenominator.getKeyword(), CSSKeyword::Unset); + parseCSSComponentValue("16/-9"); + EXPECT_EQ(negativeDenominator.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(negativeDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); auto fractionalNumerator = - parseCSSComponentValue("16.5/9"); + parseCSSComponentValue("16.5/9"); EXPECT_EQ(fractionalNumerator.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalNumerator.getRatio().numerator, 16.5f); EXPECT_EQ(fractionalNumerator.getRatio().denominator, 9.0f); auto fractionalDenominator = - parseCSSComponentValue("16/9.5"); + parseCSSComponentValue("16/9.5"); + EXPECT_EQ(fractionalDenominator.type(), CSSValueType::Ratio); + EXPECT_EQ(fractionalDenominator.getRatio().numerator, 16.0f); + EXPECT_EQ(fractionalDenominator.getRatio().denominator, 9.5f); + + auto degenerateRatio = parseCSSComponentValue("0"); + EXPECT_EQ(degenerateRatio.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(degenerateRatio.getCSSWideKeyword(), CSSWideKeyword::Unset); +} + +TEST(CSSParser, number_ratio_values) { + auto emptyValue = + parseCSSComponentValue(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto validRatio = + parseCSSComponentValue("16/9"); + EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); + EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); + EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); + + auto validRatioWithWhitespace = + parseCSSComponentValue("16 / 9"); + EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); + EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); + EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); + + auto singleNumberRatio = + parseCSSComponentValue("16"); + EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); + EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); + EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); + + auto fractionalNumber = + parseCSSComponentValue("16.5"); + EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); + EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); + EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); + + auto negativeNumber = + parseCSSComponentValue("-16"); + EXPECT_EQ(negativeNumber.type(), CSSValueType::Number); + EXPECT_EQ(negativeNumber.getNumber().value, -16.0f); + + auto missingDenominator = + parseCSSComponentValue("16/"); + EXPECT_EQ(missingDenominator.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(missingDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto negativeNumerator = + parseCSSComponentValue("-16/9"); + EXPECT_EQ(negativeNumerator.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(negativeNumerator.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto negativeDenominator = + parseCSSComponentValue("16/-9"); + EXPECT_EQ(negativeDenominator.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(negativeDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto fractionalNumerator = + parseCSSComponentValue("16.5/9"); + EXPECT_EQ(fractionalNumerator.type(), CSSValueType::Ratio); + EXPECT_EQ(fractionalNumerator.getRatio().numerator, 16.5f); + EXPECT_EQ(fractionalNumerator.getRatio().denominator, 9.0f); + + auto fractionalDenominator = + parseCSSComponentValue("16/9.5"); EXPECT_EQ(fractionalDenominator.type(), CSSValueType::Ratio); EXPECT_EQ(fractionalDenominator.getRatio().numerator, 16.0f); EXPECT_EQ(fractionalDenominator.getRatio().denominator, 9.5f); auto degenerateRatio = - parseCSSComponentValue("0"); + parseCSSComponentValue("0"); EXPECT_EQ(degenerateRatio.type(), CSSValueType::Number); EXPECT_EQ(degenerateRatio.getNumber().value, 0.0f); } +TEST(CSSParser, parse_prop) { + auto emptyValue = parseCSSProp(""); + EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto numberWidthValue = parseCSSProp("50px"); + EXPECT_EQ(numberWidthValue.type(), CSSValueType::Length); + EXPECT_EQ(numberWidthValue.getLength().value, 50.0f); + EXPECT_EQ(numberWidthValue.getLength().unit, CSSLengthUnit::Px); + + auto percentWidthValue = parseCSSProp("50%"); + EXPECT_EQ(percentWidthValue.type(), CSSValueType::Percentage); + EXPECT_EQ(percentWidthValue.getPercentage().value, 50.0f); + + auto autoWidthValue = parseCSSProp("auto"); + EXPECT_EQ(autoWidthValue.type(), CSSValueType::Keyword); + EXPECT_EQ( + autoWidthValue.getKeyword(), CSSAllowedKeywords::Auto); + + auto invalidWidthValue = parseCSSProp("50"); + EXPECT_EQ(invalidWidthValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(invalidWidthValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + + auto invalidKeywordValue = parseCSSProp("flex-start"); + EXPECT_EQ(invalidKeywordValue.type(), CSSValueType::CSSWideKeyword); + EXPECT_EQ(invalidKeywordValue.getCSSWideKeyword(), CSSWideKeyword::Unset); +} + } // namespace facebook::react