From 69f761f1ace121cc3cba9904b2eecc2221db6543 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 16 Jan 2025 11:42:34 -0800 Subject: [PATCH] Allow parser to support generic data types (#48718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48718 This diff looks a bit scary, but it's mostly just structural changes of existing code and some deletion, on a well tested path πŸ˜…. The current parser implementation tries to special case "basic" data types. When I was looking at how to add in support for more complex values, such as lists, function notations, and more compounded types, this distinction ends up not making much sense. Instead of treating some types as basic, this diff instead moves to a model where a user can declare any structure as a `CSSDataType`, so long as they also supply a parser, which may be visited when iterating through CSS syntax blocks (preserved tokens, function blocks, or simple blocks, which probably won't be used). The user then specifies a list of supported CSS data types to parse, which invokes said parser, calling any defined methods for specific syntax. E.g. ```cpp struct CSSNumber { float value{}; }; template <> struct CSSDataTypeParser { static constexpr auto consumePreservedToken(const CSSPreservedToken& token) -> std::optional { if (token.type() == CSSTokenType::Number) { return CSSNumber{token.numericValue()}; } return {}; } // Could also accept function block here as well (e.g. for future math // expressions) }; static_assert(CSSDataType); ``` ```cpp // Can be one of std::monostate (variant null-type), CSSWideKeyword, // CSSNumber, CSSLength, or CSSPercentage. In this case, a CSSLength. auto value = parseCSSProperty("5px"); ``` This breaks a whole lot of assumptions I made a year ago, especially around `CSSValueVariant` which must now be able to handle arbitrary values. For now, for the sake of simplicity, I threw this out, and migrated parser code to use plain-old `std::variant`, which has a downside of being a bit less optimized in terms of storage. I also ended up completely throwing out `CSSDeclaredStyle`, since it would majorly need to change, and we're not going to be migrating style storage quite yet. This change also broke the `CSSProperties.h` property definitions and parsing shorthand a bit, which we will need for value processing later. I also opted to delete this for now (a big centralized list is the wrong structure anyways), but will likely copy bits from its source history later. Another particular hairy bit, that likely won't bite us in practice, is that some strings may be parseable under different data types. This just adds caller requirement to order the types correctly, instead of precedence being implemented as part of the parser. Changelog: [Internal] Reviewed By: lenaic Differential Revision: D68245734 fbshipit-source-id: 132b11053cf41f57483c89176a9a6dceebb69fad --- .../ReactCommon/react/renderer/css/CSSAngle.h | 40 + .../ReactCommon/react/renderer/css/CSSColor.h | 46 + .../react/renderer/css/CSSColorUtils.h | 376 ----- .../react/renderer/css/CSSDataType.h | 82 ++ .../react/renderer/css/CSSDeclaredStyle.h | 138 -- .../react/renderer/css/CSSHexColor.h | 109 ++ .../css/{CSSKeywords.h => CSSKeyword.h} | 15 + .../react/renderer/css/CSSLength.h | 56 + .../react/renderer/css/CSSNamedColor.h | 328 +++++ .../react/renderer/css/CSSNumber.h | 38 + .../react/renderer/css/CSSPercentage.h | 38 + .../react/renderer/css/CSSProperties.h | 1287 ----------------- .../ReactCommon/react/renderer/css/CSSRatio.h | 77 + .../react/renderer/css/CSSSyntaxParser.h | 71 +- .../ReactCommon/react/renderer/css/CSSValue.h | 96 -- .../react/renderer/css/CSSValueParser.h | 359 ++--- .../react/renderer/css/CSSValueVariant.h | 267 ---- .../css/tests/CSSDeclaredStyleTest.cpp | 164 --- .../renderer/css/tests/CSSValueParserTest.cpp | 547 +++---- 19 files changed, 1185 insertions(+), 2949 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSAngle.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSColor.h delete mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSColorUtils.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h delete mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSHexColor.h rename packages/react-native/ReactCommon/react/renderer/css/{CSSKeywords.h => CSSKeyword.h} (96%) create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSLength.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSNamedColor.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSNumber.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSPercentage.h delete mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h create mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h delete mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSValue.h delete mode 100644 packages/react-native/ReactCommon/react/renderer/css/CSSValueVariant.h delete mode 100644 packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSAngle.h b/packages/react-native/ReactCommon/react/renderer/css/CSSAngle.h new file mode 100644 index 00000000000..8f97e351699 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSAngle.h @@ -0,0 +1,40 @@ +/* + * 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 + +#include +#include + +namespace facebook::react { + +/** + * Representation of CSS data type + * https://www.w3.org/TR/css-values-4/#angles + */ +struct CSSAngle { + float degrees{}; +}; + +template <> +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken(const CSSPreservedToken& token) + -> std::optional { + if (token.type() == CSSTokenType::Dimension) { + if (auto unit = parseCSSAngleUnit(token.unit())) { + return CSSAngle{canonicalize(token.numericValue(), *unit)}; + } + } + return {}; + } +}; + +static_assert(CSSDataType); + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h b/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h new file mode 100644 index 00000000000..5d8324f6da2 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSColor.h @@ -0,0 +1,46 @@ +/* + * 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 + +#include +#include +#include + +namespace facebook::react { + +/** + * Representation of CSS data type + * https://www.w3.org/TR/css-color-5/#typedef-color + */ +struct CSSColor { + uint8_t r{}; + uint8_t g{}; + uint8_t b{}; + uint8_t a{}; +}; + +template <> +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken(const CSSPreservedToken& token) + -> std::optional { + switch (token.type()) { + case CSSTokenType::Ident: + return parseCSSNamedColor(token.stringValue()); + case CSSTokenType::Hash: + return parseCSSHexColor(token.stringValue()); + default: + return {}; + } + } +}; + +static_assert(CSSDataType); + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSColorUtils.h b/packages/react-native/ReactCommon/react/renderer/css/CSSColorUtils.h deleted file mode 100644 index ca384138779..00000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSColorUtils.h +++ /dev/null @@ -1,376 +0,0 @@ -/* - * 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 -#include -#include - -namespace facebook::react { - -// https://www.w3.org/TR/css-color-4/#named-colors -template -constexpr std::optional parseCSSNamedColor(std::string_view name) { - switch (fnv1aLowercase(name)) { - case fnv1a("aliceblue"): - return CSSValueT::color(240, 248, 255, 255); - case fnv1a("antiquewhite"): - return CSSValueT::color(250, 235, 215, 255); - case fnv1a("aqua"): - return CSSValueT::color(0, 255, 255, 255); - case fnv1a("aquamarine"): - return CSSValueT::color(127, 255, 212, 255); - case fnv1a("azure"): - return CSSValueT::color(240, 255, 255, 255); - case fnv1a("beige"): - return CSSValueT::color(245, 245, 220, 255); - case fnv1a("bisque"): - return CSSValueT::color(255, 228, 196, 255); - case fnv1a("black"): - return CSSValueT::color(0, 0, 0, 255); - case fnv1a("blanchedalmond"): - return CSSValueT::color(255, 235, 205, 255); - case fnv1a("blue"): - return CSSValueT::color(0, 0, 255, 255); - case fnv1a("blueviolet"): - return CSSValueT::color(138, 43, 226, 255); - case fnv1a("brown"): - return CSSValueT::color(165, 42, 42, 255); - case fnv1a("burlywood"): - return CSSValueT::color(222, 184, 135, 255); - case fnv1a("cadetblue"): - return CSSValueT::color(95, 158, 160, 255); - case fnv1a("chartreuse"): - return CSSValueT::color(127, 255, 0, 255); - case fnv1a("chocolate"): - return CSSValueT::color(210, 105, 30, 255); - case fnv1a("coral"): - return CSSValueT::color(255, 127, 80, 255); - case fnv1a("cornflowerblue"): - return CSSValueT::color(100, 149, 237, 255); - case fnv1a("cornsilk"): - return CSSValueT::color(255, 248, 220, 255); - case fnv1a("crimson"): - return CSSValueT::color(220, 20, 60, 255); - case fnv1a("cyan"): - return CSSValueT::color(0, 255, 255, 255); - case fnv1a("darkblue"): - return CSSValueT::color(0, 0, 139, 255); - case fnv1a("darkcyan"): - return CSSValueT::color(0, 139, 139, 255); - case fnv1a("darkgoldenrod"): - return CSSValueT::color(184, 134, 11, 255); - case fnv1a("darkgray"): - return CSSValueT::color(169, 169, 169, 255); - case fnv1a("darkgreen"): - return CSSValueT::color(0, 100, 0, 255); - case fnv1a("darkgrey"): - return CSSValueT::color(169, 169, 169, 255); - case fnv1a("darkkhaki"): - return CSSValueT::color(189, 183, 107, 255); - case fnv1a("darkmagenta"): - return CSSValueT::color(139, 0, 139, 255); - case fnv1a("darkolivegreen"): - return CSSValueT::color(85, 107, 47, 255); - case fnv1a("darkorange"): - return CSSValueT::color(255, 140, 0, 255); - case fnv1a("darkorchid"): - return CSSValueT::color(153, 50, 204, 255); - case fnv1a("darkred"): - return CSSValueT::color(139, 0, 0, 255); - case fnv1a("darksalmon"): - return CSSValueT::color(233, 150, 122, 255); - case fnv1a("darkseagreen"): - return CSSValueT::color(143, 188, 143, 255); - case fnv1a("darkslateblue"): - return CSSValueT::color(72, 61, 139, 255); - case fnv1a("darkslategray"): - return CSSValueT::color(47, 79, 79, 255); - case fnv1a("darkslategrey"): - return CSSValueT::color(47, 79, 79, 255); - case fnv1a("darkturquoise"): - return CSSValueT::color(0, 206, 209, 255); - case fnv1a("darkviolet"): - return CSSValueT::color(148, 0, 211, 255); - case fnv1a("deeppink"): - return CSSValueT::color(255, 20, 147, 255); - case fnv1a("deepskyblue"): - return CSSValueT::color(0, 191, 255, 255); - case fnv1a("dimgray"): - return CSSValueT::color(105, 105, 105, 255); - case fnv1a("dimgrey"): - return CSSValueT::color(105, 105, 105, 255); - case fnv1a("dodgerblue"): - return CSSValueT::color(30, 144, 255, 255); - case fnv1a("firebrick"): - return CSSValueT::color(178, 34, 34, 255); - case fnv1a("floralwhite"): - return CSSValueT::color(255, 250, 240, 255); - case fnv1a("forestgreen"): - return CSSValueT::color(34, 139, 34, 255); - case fnv1a("fuchsia"): - return CSSValueT::color(255, 0, 255, 255); - case fnv1a("gainsboro"): - return CSSValueT::color(220, 220, 220, 255); - case fnv1a("ghostwhite"): - return CSSValueT::color(248, 248, 255, 255); - case fnv1a("gold"): - return CSSValueT::color(255, 215, 0, 255); - case fnv1a("goldenrod"): - return CSSValueT::color(218, 165, 32, 255); - case fnv1a("gray"): - return CSSValueT::color(128, 128, 128, 255); - case fnv1a("green"): - return CSSValueT::color(0, 128, 0, 255); - case fnv1a("greenyellow"): - return CSSValueT::color(173, 255, 47, 255); - case fnv1a("grey"): - return CSSValueT::color(128, 128, 128, 255); - case fnv1a("honeydew"): - return CSSValueT::color(240, 255, 240, 255); - case fnv1a("hotpink"): - return CSSValueT::color(255, 105, 180, 255); - case fnv1a("indianred"): - return CSSValueT::color(205, 92, 92, 255); - case fnv1a("indigo"): - return CSSValueT::color(75, 0, 130, 255); - case fnv1a("ivory"): - return CSSValueT::color(255, 255, 240, 255); - case fnv1a("khaki"): - return CSSValueT::color(240, 230, 140, 255); - case fnv1a("lavender"): - return CSSValueT::color(230, 230, 250, 255); - case fnv1a("lavenderblush"): - return CSSValueT::color(255, 240, 245, 255); - case fnv1a("lawngreen"): - return CSSValueT::color(124, 252, 0, 255); - case fnv1a("lemonchiffon"): - return CSSValueT::color(255, 250, 205, 255); - case fnv1a("lightblue"): - return CSSValueT::color(173, 216, 230, 255); - case fnv1a("lightcoral"): - return CSSValueT::color(240, 128, 128, 255); - case fnv1a("lightcyan"): - return CSSValueT::color(224, 255, 255, 255); - case fnv1a("lightgoldenrodyellow"): - return CSSValueT::color(250, 250, 210, 255); - case fnv1a("lightgray"): - return CSSValueT::color(211, 211, 211, 255); - case fnv1a("lightgreen"): - return CSSValueT::color(144, 238, 144, 255); - case fnv1a("lightgrey"): - return CSSValueT::color(211, 211, 211, 255); - case fnv1a("lightpink"): - return CSSValueT::color(255, 182, 193, 255); - case fnv1a("lightsalmon"): - return CSSValueT::color(255, 160, 122, 255); - case fnv1a("lightseagreen"): - return CSSValueT::color(32, 178, 170, 255); - case fnv1a("lightskyblue"): - return CSSValueT::color(135, 206, 250, 255); - case fnv1a("lightslategray"): - return CSSValueT::color(119, 136, 153, 255); - case fnv1a("lightslategrey"): - return CSSValueT::color(119, 136, 153, 255); - case fnv1a("lightsteelblue"): - return CSSValueT::color(176, 196, 222, 255); - case fnv1a("lightyellow"): - return CSSValueT::color(255, 255, 224, 255); - case fnv1a("lime"): - return CSSValueT::color(0, 255, 0, 255); - case fnv1a("limegreen"): - return CSSValueT::color(50, 205, 50, 255); - case fnv1a("linen"): - return CSSValueT::color(250, 240, 230, 255); - case fnv1a("magenta"): - return CSSValueT::color(255, 0, 255, 255); - case fnv1a("maroon"): - return CSSValueT::color(128, 0, 0, 255); - case fnv1a("mediumaquamarine"): - return CSSValueT::color(102, 205, 170, 255); - case fnv1a("mediumblue"): - return CSSValueT::color(0, 0, 205, 255); - case fnv1a("mediumorchid"): - return CSSValueT::color(186, 85, 211, 255); - case fnv1a("mediumpurple"): - return CSSValueT::color(147, 112, 219, 255); - case fnv1a("mediumseagreen"): - return CSSValueT::color(60, 179, 113, 255); - case fnv1a("mediumslateblue"): - return CSSValueT::color(123, 104, 238, 255); - case fnv1a("mediumspringgreen"): - return CSSValueT::color(0, 250, 154, 255); - case fnv1a("mediumturquoise"): - return CSSValueT::color(72, 209, 204, 255); - case fnv1a("mediumvioletred"): - return CSSValueT::color(199, 21, 133, 255); - case fnv1a("midnightblue"): - return CSSValueT::color(25, 25, 112, 255); - case fnv1a("mintcream"): - return CSSValueT::color(245, 255, 250, 255); - case fnv1a("mistyrose"): - return CSSValueT::color(255, 228, 225, 255); - case fnv1a("moccasin"): - return CSSValueT::color(255, 228, 181, 255); - case fnv1a("navajowhite"): - return CSSValueT::color(255, 222, 173, 255); - case fnv1a("navy"): - return CSSValueT::color(0, 0, 128, 255); - case fnv1a("oldlace"): - return CSSValueT::color(253, 245, 230, 255); - case fnv1a("olive"): - return CSSValueT::color(128, 128, 0, 255); - case fnv1a("olivedrab"): - return CSSValueT::color(107, 142, 35, 255); - case fnv1a("orange"): - return CSSValueT::color(255, 165, 0, 255); - case fnv1a("orangered"): - return CSSValueT::color(255, 69, 0, 255); - case fnv1a("orchid"): - return CSSValueT::color(218, 112, 214, 255); - case fnv1a("palegoldenrod"): - return CSSValueT::color(238, 232, 170, 255); - case fnv1a("palegreen"): - return CSSValueT::color(152, 251, 152, 255); - case fnv1a("paleturquoise"): - return CSSValueT::color(175, 238, 238, 255); - case fnv1a("palevioletred"): - return CSSValueT::color(219, 112, 147, 255); - case fnv1a("papayawhip"): - return CSSValueT::color(255, 239, 213, 255); - case fnv1a("peachpuff"): - return CSSValueT::color(255, 218, 185, 255); - case fnv1a("peru"): - return CSSValueT::color(205, 133, 63, 255); - case fnv1a("pink"): - return CSSValueT::color(255, 192, 203, 255); - case fnv1a("plum"): - return CSSValueT::color(221, 160, 221, 255); - case fnv1a("powderblue"): - return CSSValueT::color(176, 224, 230, 255); - case fnv1a("purple"): - return CSSValueT::color(128, 0, 128, 255); - case fnv1a("rebeccapurple"): - return CSSValueT::color(102, 51, 153, 255); - case fnv1a("red"): - return CSSValueT::color(255, 0, 0, 255); - case fnv1a("rosybrown"): - return CSSValueT::color(188, 143, 143, 255); - case fnv1a("royalblue"): - return CSSValueT::color(65, 105, 225, 255); - case fnv1a("saddlebrown"): - return CSSValueT::color(139, 69, 19, 255); - case fnv1a("salmon"): - return CSSValueT::color(250, 128, 114, 255); - case fnv1a("sandybrown"): - return CSSValueT::color(244, 164, 96, 255); - case fnv1a("seagreen"): - return CSSValueT::color(46, 139, 87, 255); - case fnv1a("seashell"): - return CSSValueT::color(255, 245, 238, 255); - case fnv1a("sienna"): - return CSSValueT::color(160, 82, 45, 255); - case fnv1a("silver"): - return CSSValueT::color(192, 192, 192, 255); - case fnv1a("skyblue"): - return CSSValueT::color(135, 206, 235, 255); - case fnv1a("slateblue"): - return CSSValueT::color(106, 90, 205, 255); - case fnv1a("slategray"): - return CSSValueT::color(112, 128, 144, 255); - case fnv1a("slategrey"): - return CSSValueT::color(112, 128, 144, 255); - case fnv1a("snow"): - return CSSValueT::color(255, 250, 250, 255); - case fnv1a("springgreen"): - return CSSValueT::color(0, 255, 127, 255); - case fnv1a("steelblue"): - return CSSValueT::color(70, 130, 180, 255); - case fnv1a("tan"): - return CSSValueT::color(210, 180, 140, 255); - case fnv1a("teal"): - return CSSValueT::color(0, 128, 128, 255); - case fnv1a("thistle"): - return CSSValueT::color(216, 191, 216, 255); - case fnv1a("tomato"): - return CSSValueT::color(255, 99, 71, 255); - case fnv1a("transparent"): - return CSSValueT::color(0, 0, 0, 0); - case fnv1a("turquoise"): - return CSSValueT::color(64, 224, 208, 255); - case fnv1a("violet"): - return CSSValueT::color(238, 130, 238, 255); - case fnv1a("wheat"): - return CSSValueT::color(245, 222, 179, 255); - case fnv1a("white"): - return CSSValueT::color(255, 255, 255, 255); - case fnv1a("whitesmoke"): - return CSSValueT::color(245, 245, 245, 255); - case fnv1a("yellow"): - return CSSValueT::color(255, 255, 0, 255); - case fnv1a("yellowgreen"): - return CSSValueT::color(154, 205, 50, 255); - default: - return std::nullopt; - } -} - -enum class HexColorType { - Long, - Short, -}; - -constexpr char toLower(char c) { - if (c >= 'A' && c <= 'Z') { - return static_cast(c + 32); - } - return c; -} - -constexpr uint8_t hexToNumeric(std::string_view hex, HexColorType hexType) { - int result = 0; - for (char c : hex) { - int value = 0; - if (c >= '0' && c <= '9') { - value = c - '0'; - } else { - value = toLower(c) - 'a' + 10; - } - result *= 16; - result += value; - } - - if (hexType == HexColorType::Short) { - return result * 16 + result; - } else { - return result; - } -} - -constexpr bool isHexDigit(char c) { - return (c >= '0' && c <= '9') || (toLower(c) >= 'a' && toLower(c) <= 'f'); -} - -constexpr bool isValidHexColor(std::string_view hex) { - // The syntax of a is a token whose value consists - // of 3, 4, 6, or 8 hexadecimal digits. - if (hex.size() != 3 && hex.size() != 4 && hex.size() != 6 && - hex.size() != 8) { - return false; - } - - for (auto c : hex) { - if (!isHexDigit(c)) { - return false; - } - } - - return true; -} - -}; // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h b/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h new file mode 100644 index 00000000000..111b208bc5d --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSDataType.h @@ -0,0 +1,82 @@ +/* + * 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 +#include +#include +#include + +#include + +namespace facebook::react { + +/** + * May be specialized to instruct the CSS value parser how to parse a given data + * type, according to CSSValidDataTypeParser. + */ +template +struct CSSDataTypeParser {}; + +/** + * Accepts a CSS function block and may parse it (and future syntax) into a + * concrete representation. + */ +template +concept CSSFunctionBlockSink = + requires(const CSSFunctionBlock& func, CSSSyntaxParser& parser) { + { T::consumeFunctionBlock(func, parser) } -> std::convertible_to; + }; + +/** + * Accepts a CSS simple block and may parse it (and future syntax) into a + * concrete representation. + */ +template +concept CSSSimpleBlockSink = + requires(const CSSSimpleBlock& block, CSSSyntaxParser& parser) { + { T::consumeSimpleBlock(block, parser) } -> std::convertible_to; + }; + +/** + * Accepts a CSS preserved token and may parse it (and future syntax) into a + * concrete representation. + */ +template +concept CSSPreservedTokenSink = + requires(const CSSPreservedToken& token, CSSSyntaxParser& parser) { + { + T::consumePreservedToken(token, parser) + } -> std::convertible_to; + }; + +/** + * Accepts a CSS preserved token and may parse it into a concrete + * representation. + */ +template +concept CSSSimplePreservedTokenSink = requires(const CSSPreservedToken& token) { + { T::consumePreservedToken(token) } -> std::convertible_to; +}; + +/** + * Represents a valid specialization of CSSDataTypeParser + */ +template +concept CSSValidDataTypeParser = CSSFunctionBlockSink || + CSSSimpleBlockSink || CSSPreservedTokenSink || + CSSSimplePreservedTokenSink; + +/** + * Concrete representation for a CSS data type, or keywords + */ +template +concept CSSDataType = + CSSValidDataTypeParser, std::optional>; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h b/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h deleted file mode 100644 index 92179583c56..00000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSDeclaredStyle.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#include -#include - -namespace facebook::react { - -namespace detail { -constexpr CSSProp kFirstCSSProp = static_cast(0); - -template -constexpr size_t maxSizeofDeclaredValue() { - if constexpr (to_underlying(Prop) < kCSSPropCount - 1) { - return std::max( - sizeof(CSSDeclaredValue), - maxSizeofDeclaredValue( - to_underlying(Prop) + 1)>()); - } else { - return sizeof(CSSDeclaredValue); - } -} -} // namespace detail - -/** - * CSSDeclaredStyle represents the set of style declarations on an element set - * by the user. Users should generally not read from CSSDeclaredStyle directly, - * and should instead use the computed style calculated on ShadowTree commit. - */ -class CSSDeclaredStyle { - public: - template - void set(const CSSDeclaredValue& value) { - using DeclaredValueT = std::remove_cvref_t>; - static_assert(sizeof(value) <= sizeof(PropMapping::value)); - static_assert(std::is_trivially_destructible_v); - - if (specifiedProperties_.test(to_underlying(Prop))) { - auto it = std::lower_bound( - properties_.begin(), properties_.end(), PropMapping{Prop, {}}); - react_native_assert(it->prop == Prop); - std::construct_at( - reinterpret_cast(it->value.data()), value); - } else { - auto it = std::upper_bound( - properties_.begin(), properties_.end(), PropMapping{Prop, {}}); - it = properties_.insert(it, {Prop, {}}); - std::construct_at( - reinterpret_cast(it->value.data()), value); - specifiedProperties_.set(to_underlying(Prop)); - } - } - - template - bool set(std::string_view value) { - auto cssProp = parseCSSProp(value); - set(cssProp); - return cssProp.hasValue(); - } - - bool set(std::string_view prop, std::string_view value) { - return setPropIfHashMatches(fnv1a(prop), value); - } - - /** - * Returns the declared value, represented as the "unset" keyword if never - * specified. Additional shorthands can be provided in order - * of precedence if Prop is unset. - */ - template - CSSDeclaredValue get() const { - if (specifiedProperties_.test(to_underlying(Prop))) { - auto it = std::lower_bound( - properties_.begin(), properties_.end(), PropMapping{Prop, {}}); - react_native_assert(it->prop == Prop); - - CSSDeclaredValue value{*std::launder( - reinterpret_cast*>(it->value.data()))}; - - if (value) { - return value; - } - } - - if constexpr (sizeof...(ShorthandsT) == 0) { - return {}; - } else { - return get(); - } - } - - bool operator==(const CSSDeclaredStyle& rhs) const = default; - - private: - struct PropMapping { - CSSProp prop; - std::array value; - - constexpr bool operator<(const PropMapping& rhs) const { - return to_underlying(prop) < to_underlying(rhs.prop); - } - }; - - template - constexpr bool setPropIfHashMatches( - size_t propNameHash, - std::string_view value) { - constexpr std::string_view currentPropName = - CSSPropDefinition::kName; - constexpr size_t currentHash = fnv1a(currentPropName); - if (currentHash == propNameHash) { - return set(value); - } else if constexpr (to_underlying(CurrentProp) < kCSSPropCount - 1) { - return setPropIfHashMatches( - to_underlying(CurrentProp) + 1)>(propNameHash, value); - } else { - return false; - } - } - - std::vector properties_; - std::bitset specifiedProperties_; -}; - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSHexColor.h b/packages/react-native/ReactCommon/react/renderer/css/CSSHexColor.h new file mode 100644 index 00000000000..ca53b90e83a --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSHexColor.h @@ -0,0 +1,109 @@ +/* + * 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 +#include + +namespace facebook::react { + +namespace detail { +enum class HexColorType { + Long, + Short, +}; + +constexpr char toLower(char c) { + if (c >= 'A' && c <= 'Z') { + return static_cast(c + 32); + } + return c; +} + +constexpr uint8_t hexToNumeric(std::string_view hex, HexColorType hexType) { + int result = 0; + for (char c : hex) { + int value = 0; + if (c >= '0' && c <= '9') { + value = c - '0'; + } else { + value = toLower(c) - 'a' + 10; + } + result *= 16; + result += value; + } + + if (hexType == HexColorType::Short) { + return result * 16 + result; + } else { + return result; + } +} + +constexpr bool isHexDigit(char c) { + return (c >= '0' && c <= '9') || (toLower(c) >= 'a' && toLower(c) <= 'f'); +} + +constexpr bool isValidHexColor(std::string_view hex) { + // The syntax of a is a token whose value consists + // of 3, 4, 6, or 8 hexadecimal digits. + if (hex.size() != 3 && hex.size() != 4 && hex.size() != 6 && + hex.size() != 8) { + return false; + } + + for (auto c : hex) { + if (!isHexDigit(c)) { + return false; + } + } + + return true; +} +} // namespace detail + +/** + * Parses a CSS value from hash stoken string value and returns a + * CSSColor if it is valid. + * https://www.w3.org/TR/css-color-4/#hex-color + */ +template +constexpr std::optional parseCSSHexColor( + std::string_view hexColorValue) { + if (detail::isValidHexColor(hexColorValue)) { + if (hexColorValue.length() == 3) { + return CSSColor{ + hexToNumeric(hexColorValue.substr(0, 1), detail::HexColorType::Short), + hexToNumeric(hexColorValue.substr(1, 1), detail::HexColorType::Short), + hexToNumeric(hexColorValue.substr(2, 1), detail::HexColorType::Short), + 255u}; + } else if (hexColorValue.length() == 4) { + return CSSColor{ + hexToNumeric(hexColorValue.substr(0, 1), detail::HexColorType::Short), + hexToNumeric(hexColorValue.substr(1, 1), detail::HexColorType::Short), + hexToNumeric(hexColorValue.substr(2, 1), detail::HexColorType::Short), + hexToNumeric( + hexColorValue.substr(3, 1), detail::HexColorType::Short)}; + } else if (hexColorValue.length() == 6) { + return CSSColor{ + hexToNumeric(hexColorValue.substr(0, 2), detail::HexColorType::Long), + hexToNumeric(hexColorValue.substr(2, 2), detail::HexColorType::Long), + hexToNumeric(hexColorValue.substr(4, 2), detail::HexColorType::Long), + 255u}; + } else if (hexColorValue.length() == 8) { + return CSSColor{ + hexToNumeric(hexColorValue.substr(0, 2), detail::HexColorType::Long), + hexToNumeric(hexColorValue.substr(2, 2), detail::HexColorType::Long), + hexToNumeric(hexColorValue.substr(4, 2), detail::HexColorType::Long), + hexToNumeric(hexColorValue.substr(6, 2), detail::HexColorType::Long)}; + } + } + return {}; +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h b/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h similarity index 96% rename from packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h rename to packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h index 0a92769bbd7..07bca3f5e99 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSKeywords.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -453,4 +454,18 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) { return std::nullopt; } +template +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken(const CSSPreservedToken& token) + -> std::optional { + if (token.type() == CSSTokenType::Ident) { + return parseCSSKeyword(token.stringValue()); + } + + return {}; + } +}; + +static_assert(CSSDataType); + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSLength.h b/packages/react-native/ReactCommon/react/renderer/css/CSSLength.h new file mode 100644 index 00000000000..8b4de0213ba --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSLength.h @@ -0,0 +1,56 @@ +/* + * 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 + +#include +#include + +namespace facebook::react { + +/** + * Representation of CSS data type + * https://www.w3.org/TR/css-values-4/#lengths + */ +struct CSSLength { + float value{}; + CSSLengthUnit unit{CSSLengthUnit::Px}; +}; + +template <> +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken(const CSSPreservedToken& token) + -> std::optional { + switch (token.type()) { + case CSSTokenType::Dimension: + if (auto unit = parseCSSLengthUnit(token.unit())) { + return CSSLength{token.numericValue(), *unit}; + } + break; + case CSSTokenType::Number: + // For zero lengths the unit identifier is optional (i.e. can be + // syntactically represented as the 0). However, if a 0 + // could be parsed as either a or a in a + // property (such as line-height), it must parse as a . + // https://www.w3.org/TR/css-values-4/#lengths + if (token.numericValue() == 0) { + return CSSLength{token.numericValue(), CSSLengthUnit::Px}; + } + break; + default: + break; + } + + return {}; + } +}; + +static_assert(CSSDataType); + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSNamedColor.h b/packages/react-native/ReactCommon/react/renderer/css/CSSNamedColor.h new file mode 100644 index 00000000000..4ed3065c96b --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSNamedColor.h @@ -0,0 +1,328 @@ +/* + * 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 +#include + +#include + +namespace facebook::react { + +/** + * Parse one of the given , including the "transparent" special + * keyword. + * https://www.w3.org/TR/css-color-4/#named-colors + */ +template +constexpr std::optional parseCSSNamedColor(std::string_view name) { + switch (fnv1aLowercase(name)) { + case fnv1a("aliceblue"): + return CSSColor{240, 248, 255, 255}; + case fnv1a("antiquewhite"): + return CSSColor{250, 235, 215, 255}; + case fnv1a("aqua"): + return CSSColor{0, 255, 255, 255}; + case fnv1a("aquamarine"): + return CSSColor{127, 255, 212, 255}; + case fnv1a("azure"): + return CSSColor{240, 255, 255, 255}; + case fnv1a("beige"): + return CSSColor{245, 245, 220, 255}; + case fnv1a("bisque"): + return CSSColor{255, 228, 196, 255}; + case fnv1a("black"): + return CSSColor{0, 0, 0, 255}; + case fnv1a("blanchedalmond"): + return CSSColor{255, 235, 205, 255}; + case fnv1a("blue"): + return CSSColor{0, 0, 255, 255}; + case fnv1a("blueviolet"): + return CSSColor{138, 43, 226, 255}; + case fnv1a("brown"): + return CSSColor{165, 42, 42, 255}; + case fnv1a("burlywood"): + return CSSColor{222, 184, 135, 255}; + case fnv1a("cadetblue"): + return CSSColor{95, 158, 160, 255}; + case fnv1a("chartreuse"): + return CSSColor{127, 255, 0, 255}; + case fnv1a("chocolate"): + return CSSColor{210, 105, 30, 255}; + case fnv1a("coral"): + return CSSColor{255, 127, 80, 255}; + case fnv1a("cornflowerblue"): + return CSSColor{100, 149, 237, 255}; + case fnv1a("cornsilk"): + return CSSColor{255, 248, 220, 255}; + case fnv1a("crimson"): + return CSSColor{220, 20, 60, 255}; + case fnv1a("cyan"): + return CSSColor{0, 255, 255, 255}; + case fnv1a("darkblue"): + return CSSColor{0, 0, 139, 255}; + case fnv1a("darkcyan"): + return CSSColor{0, 139, 139, 255}; + case fnv1a("darkgoldenrod"): + return CSSColor{184, 134, 11, 255}; + case fnv1a("darkgray"): + return CSSColor{169, 169, 169, 255}; + case fnv1a("darkgreen"): + return CSSColor{0, 100, 0, 255}; + case fnv1a("darkgrey"): + return CSSColor{169, 169, 169, 255}; + case fnv1a("darkkhaki"): + return CSSColor{189, 183, 107, 255}; + case fnv1a("darkmagenta"): + return CSSColor{139, 0, 139, 255}; + case fnv1a("darkolivegreen"): + return CSSColor{85, 107, 47, 255}; + case fnv1a("darkorange"): + return CSSColor{255, 140, 0, 255}; + case fnv1a("darkorchid"): + return CSSColor{153, 50, 204, 255}; + case fnv1a("darkred"): + return CSSColor{139, 0, 0, 255}; + case fnv1a("darksalmon"): + return CSSColor{233, 150, 122, 255}; + case fnv1a("darkseagreen"): + return CSSColor{143, 188, 143, 255}; + case fnv1a("darkslateblue"): + return CSSColor{72, 61, 139, 255}; + case fnv1a("darkslategray"): + return CSSColor{47, 79, 79, 255}; + case fnv1a("darkslategrey"): + return CSSColor{47, 79, 79, 255}; + case fnv1a("darkturquoise"): + return CSSColor{0, 206, 209, 255}; + case fnv1a("darkviolet"): + return CSSColor{148, 0, 211, 255}; + case fnv1a("deeppink"): + return CSSColor{255, 20, 147, 255}; + case fnv1a("deepskyblue"): + return CSSColor{0, 191, 255, 255}; + case fnv1a("dimgray"): + return CSSColor{105, 105, 105, 255}; + case fnv1a("dimgrey"): + return CSSColor{105, 105, 105, 255}; + case fnv1a("dodgerblue"): + return CSSColor{30, 144, 255, 255}; + case fnv1a("firebrick"): + return CSSColor{178, 34, 34, 255}; + case fnv1a("floralwhite"): + return CSSColor{255, 250, 240, 255}; + case fnv1a("forestgreen"): + return CSSColor{34, 139, 34, 255}; + case fnv1a("fuchsia"): + return CSSColor{255, 0, 255, 255}; + case fnv1a("gainsboro"): + return CSSColor{220, 220, 220, 255}; + case fnv1a("ghostwhite"): + return CSSColor{248, 248, 255, 255}; + case fnv1a("gold"): + return CSSColor{255, 215, 0, 255}; + case fnv1a("goldenrod"): + return CSSColor{218, 165, 32, 255}; + case fnv1a("gray"): + return CSSColor{128, 128, 128, 255}; + case fnv1a("green"): + return CSSColor{0, 128, 0, 255}; + case fnv1a("greenyellow"): + return CSSColor{173, 255, 47, 255}; + case fnv1a("grey"): + return CSSColor{128, 128, 128, 255}; + case fnv1a("honeydew"): + return CSSColor{240, 255, 240, 255}; + case fnv1a("hotpink"): + return CSSColor{255, 105, 180, 255}; + case fnv1a("indianred"): + return CSSColor{205, 92, 92, 255}; + case fnv1a("indigo"): + return CSSColor{75, 0, 130, 255}; + case fnv1a("ivory"): + return CSSColor{255, 255, 240, 255}; + case fnv1a("khaki"): + return CSSColor{240, 230, 140, 255}; + case fnv1a("lavender"): + return CSSColor{230, 230, 250, 255}; + case fnv1a("lavenderblush"): + return CSSColor{255, 240, 245, 255}; + case fnv1a("lawngreen"): + return CSSColor{124, 252, 0, 255}; + case fnv1a("lemonchiffon"): + return CSSColor{255, 250, 205, 255}; + case fnv1a("lightblue"): + return CSSColor{173, 216, 230, 255}; + case fnv1a("lightcoral"): + return CSSColor{240, 128, 128, 255}; + case fnv1a("lightcyan"): + return CSSColor{224, 255, 255, 255}; + case fnv1a("lightgoldenrodyellow"): + return CSSColor{250, 250, 210, 255}; + case fnv1a("lightgray"): + return CSSColor{211, 211, 211, 255}; + case fnv1a("lightgreen"): + return CSSColor{144, 238, 144, 255}; + case fnv1a("lightgrey"): + return CSSColor{211, 211, 211, 255}; + case fnv1a("lightpink"): + return CSSColor{255, 182, 193, 255}; + case fnv1a("lightsalmon"): + return CSSColor{255, 160, 122, 255}; + case fnv1a("lightseagreen"): + return CSSColor{32, 178, 170, 255}; + case fnv1a("lightskyblue"): + return CSSColor{135, 206, 250, 255}; + case fnv1a("lightslategray"): + return CSSColor{119, 136, 153, 255}; + case fnv1a("lightslategrey"): + return CSSColor{119, 136, 153, 255}; + case fnv1a("lightsteelblue"): + return CSSColor{176, 196, 222, 255}; + case fnv1a("lightyellow"): + return CSSColor{255, 255, 224, 255}; + case fnv1a("lime"): + return CSSColor{0, 255, 0, 255}; + case fnv1a("limegreen"): + return CSSColor{50, 205, 50, 255}; + case fnv1a("linen"): + return CSSColor{250, 240, 230, 255}; + case fnv1a("magenta"): + return CSSColor{255, 0, 255, 255}; + case fnv1a("maroon"): + return CSSColor{128, 0, 0, 255}; + case fnv1a("mediumaquamarine"): + return CSSColor{102, 205, 170, 255}; + case fnv1a("mediumblue"): + return CSSColor{0, 0, 205, 255}; + case fnv1a("mediumorchid"): + return CSSColor{186, 85, 211, 255}; + case fnv1a("mediumpurple"): + return CSSColor{147, 112, 219, 255}; + case fnv1a("mediumseagreen"): + return CSSColor{60, 179, 113, 255}; + case fnv1a("mediumslateblue"): + return CSSColor{123, 104, 238, 255}; + case fnv1a("mediumspringgreen"): + return CSSColor{0, 250, 154, 255}; + case fnv1a("mediumturquoise"): + return CSSColor{72, 209, 204, 255}; + case fnv1a("mediumvioletred"): + return CSSColor{199, 21, 133, 255}; + case fnv1a("midnightblue"): + return CSSColor{25, 25, 112, 255}; + case fnv1a("mintcream"): + return CSSColor{245, 255, 250, 255}; + case fnv1a("mistyrose"): + return CSSColor{255, 228, 225, 255}; + case fnv1a("moccasin"): + return CSSColor{255, 228, 181, 255}; + case fnv1a("navajowhite"): + return CSSColor{255, 222, 173, 255}; + case fnv1a("navy"): + return CSSColor{0, 0, 128, 255}; + case fnv1a("oldlace"): + return CSSColor{253, 245, 230, 255}; + case fnv1a("olive"): + return CSSColor{128, 128, 0, 255}; + case fnv1a("olivedrab"): + return CSSColor{107, 142, 35, 255}; + case fnv1a("orange"): + return CSSColor{255, 165, 0, 255}; + case fnv1a("orangered"): + return CSSColor{255, 69, 0, 255}; + case fnv1a("orchid"): + return CSSColor{218, 112, 214, 255}; + case fnv1a("palegoldenrod"): + return CSSColor{238, 232, 170, 255}; + case fnv1a("palegreen"): + return CSSColor{152, 251, 152, 255}; + case fnv1a("paleturquoise"): + return CSSColor{175, 238, 238, 255}; + case fnv1a("palevioletred"): + return CSSColor{219, 112, 147, 255}; + case fnv1a("papayawhip"): + return CSSColor{255, 239, 213, 255}; + case fnv1a("peachpuff"): + return CSSColor{255, 218, 185, 255}; + case fnv1a("peru"): + return CSSColor{205, 133, 63, 255}; + case fnv1a("pink"): + return CSSColor{255, 192, 203, 255}; + case fnv1a("plum"): + return CSSColor{221, 160, 221, 255}; + case fnv1a("powderblue"): + return CSSColor{176, 224, 230, 255}; + case fnv1a("purple"): + return CSSColor{128, 0, 128, 255}; + case fnv1a("rebeccapurple"): + return CSSColor{102, 51, 153, 255}; + case fnv1a("red"): + return CSSColor{255, 0, 0, 255}; + case fnv1a("rosybrown"): + return CSSColor{188, 143, 143, 255}; + case fnv1a("royalblue"): + return CSSColor{65, 105, 225, 255}; + case fnv1a("saddlebrown"): + return CSSColor{139, 69, 19, 255}; + case fnv1a("salmon"): + return CSSColor{250, 128, 114, 255}; + case fnv1a("sandybrown"): + return CSSColor{244, 164, 96, 255}; + case fnv1a("seagreen"): + return CSSColor{46, 139, 87, 255}; + case fnv1a("seashell"): + return CSSColor{255, 245, 238, 255}; + case fnv1a("sienna"): + return CSSColor{160, 82, 45, 255}; + case fnv1a("silver"): + return CSSColor{192, 192, 192, 255}; + case fnv1a("skyblue"): + return CSSColor{135, 206, 235, 255}; + case fnv1a("slateblue"): + return CSSColor{106, 90, 205, 255}; + case fnv1a("slategray"): + return CSSColor{112, 128, 144, 255}; + case fnv1a("slategrey"): + return CSSColor{112, 128, 144, 255}; + case fnv1a("snow"): + return CSSColor{255, 250, 250, 255}; + case fnv1a("springgreen"): + return CSSColor{0, 255, 127, 255}; + case fnv1a("steelblue"): + return CSSColor{70, 130, 180, 255}; + case fnv1a("tan"): + return CSSColor{210, 180, 140, 255}; + case fnv1a("teal"): + return CSSColor{0, 128, 128, 255}; + case fnv1a("thistle"): + return CSSColor{216, 191, 216, 255}; + case fnv1a("tomato"): + return CSSColor{255, 99, 71, 255}; + case fnv1a("transparent"): + return CSSColor{0, 0, 0, 0}; + case fnv1a("turquoise"): + return CSSColor{64, 224, 208, 255}; + case fnv1a("violet"): + return CSSColor{238, 130, 238, 255}; + case fnv1a("wheat"): + return CSSColor{245, 222, 179, 255}; + case fnv1a("white"): + return CSSColor{255, 255, 255, 255}; + case fnv1a("whitesmoke"): + return CSSColor{245, 245, 245, 255}; + case fnv1a("yellow"): + return CSSColor{255, 255, 0, 255}; + case fnv1a("yellowgreen"): + return CSSColor{154, 205, 50, 255}; + default: + return std::nullopt; + } +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSNumber.h b/packages/react-native/ReactCommon/react/renderer/css/CSSNumber.h new file mode 100644 index 00000000000..328ace3d2b0 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSNumber.h @@ -0,0 +1,38 @@ +/* + * 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 + +#include + +namespace facebook::react { + +/** + * Representation of CSS data type + * https://www.w3.org/TR/css-values-4/#numbers + */ +struct CSSNumber { + float value{}; +}; + +template <> +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken(const CSSPreservedToken& token) + -> std::optional { + if (token.type() == CSSTokenType::Number) { + return CSSNumber{token.numericValue()}; + } + + return {}; + } +}; + +static_assert(CSSDataType); + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSPercentage.h b/packages/react-native/ReactCommon/react/renderer/css/CSSPercentage.h new file mode 100644 index 00000000000..329c92c7154 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSPercentage.h @@ -0,0 +1,38 @@ +/* + * 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 + +#include + +namespace facebook::react { + +/** + * Representation of CSS data type + * https://www.w3.org/TR/css-values-4/#percentages + */ +struct CSSPercentage { + float value{}; +}; + +template <> +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken(const CSSPreservedToken& token) + -> std::optional { + if (token.type() == CSSTokenType::Percentage) { + return CSSPercentage{token.numericValue()}; + } + + return {}; + } +}; + +static_assert(CSSDataType); + +} // 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 deleted file mode 100644 index 637bab85bbb..00000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSProperties.h +++ /dev/null @@ -1,1287 +0,0 @@ -/* - * 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, - BorderBlockEndStyle, - BorderBlockEndWidth, - BorderBlockStartStyle, - BorderBlockStartWidth, - BorderBlockStyle, - BorderBlockWidth, - BorderBottomLeftRadius, - BorderBottomRightRadius, - BorderBottomStyle, - BorderBottomWidth, - BorderEndEndRadius, - BorderEndStartRadius, - BorderEndWidth, - BorderHorizontalWidth, - BorderInlineEndStyle, - BorderInlineEndWidth, - BorderInlineStartStyle, - BorderInlineStartWidth, - BorderInlineStyle, - BorderInlineWidth, - BorderLeftStyle, - BorderLeftWidth, - BorderRadius, - BorderRightStyle, - BorderRightWidth, - BorderStartEndRadius, - BorderStartStartRadius, - BorderStartWidth, - BorderStyle, - BorderTopLeftRadius, - BorderTopRightRadius, - BorderTopStyle, - 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, - Opacity, - 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 CSSDeclaredValue = typename CSSPropDefinition

::DeclaredValue; - -template -using CSSSpecifiedValue = typename CSSPropDefinition

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

::ComputedValue; - -/** - * Whether to behave in accordance with W3C specs, or to incorporate React - * Native specific tweaks to defaults and computation. - */ -enum class CSSFlavor { - W3C, - ReactNative, -}; - -/** - * 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 { - constexpr static std::string_view kName = "alignContent"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C - ? DeclaredValue::keyword(Keyword::Stretch) - : DeclaredValue::keyword(Keyword::FlexStart); - } -}; - -/** - * 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 { - constexpr static std::string_view kName = "alignItems"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Stretch); - } -}; - -/** - * CSS "align-self" 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 { - constexpr static std::string_view kName = "alignSelf"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Auto); - } -}; - -/** - * CSS "aspect-ratio" property. - * https://www.w3.org/TR/css-sizing-4/#aspect-ratio - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "aspectRatio"; - - enum class Keyword : std::underlying_type_t { - Auto = to_underlying(CSSKeyword::Auto), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Auto); - } -}; - -/** - * CSS "border-radius" properties - * https://www.w3.org/TR/css-backgrounds-3/#border-radius - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "borderRadius"; - - using DeclaredValue = - CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::length(0.0f, CSSLengthUnit::Px); - } -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderTopLeftRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderTopRightRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBottomLeftRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBottomRightRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderStartStartRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderStartEndRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderEndStartRadius"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderEndEndRadius"; -}; - -/** - * CSS "border-style" properties - * https://www.w3.org/TR/css-backgrounds-3/#border-style - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "borderStyle"; - - enum class Keyword : std::underlying_type_t { - None = to_underlying(CSSKeyword::None), - Hidden = to_underlying(CSSKeyword::Hidden), - Dotted = to_underlying(CSSKeyword::Dotted), - Dashed = to_underlying(CSSKeyword::Dashed), - Solid = to_underlying(CSSKeyword::Solid), - Double = to_underlying(CSSKeyword::Double), - Groove = to_underlying(CSSKeyword::Groove), - Ridge = to_underlying(CSSKeyword::Ridge), - Inset = to_underlying(CSSKeyword::Inset), - Outset = to_underlying(CSSKeyword::Outset), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::None) - : DeclaredValue::keyword(Keyword::Solid); - } -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBlockEndStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBlockStartStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBlockStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBottomStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderInlineEndStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderInlineStartStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderInlineStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderLeftStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderRightStyle"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderTopStyle"; -}; - -/** - * CSS "border-width" properties - * https://www.w3.org/TR/css-backgrounds-3/#border-width - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "borderWidth"; - - enum class Keyword : std::underlying_type_t { - Thin = to_underlying(CSSKeyword::Thin), - Medium = to_underlying(CSSKeyword::Medium), - Thick = to_underlying(CSSKeyword::Thick), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C - ? DeclaredValue::keyword(Keyword::Medium) - : DeclaredValue::length(0.0f, CSSLengthUnit::Px); - } -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBlockEndWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBlockStartWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBlockWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderBottomWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderEndWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderHorizontalWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderInlineEndWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderInlineStartWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderInlineWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderLeftWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderRightWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderStartWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderTopWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "borderVerticalWidth"; -}; - -/** - * CSS "direction" property. - * https://www.w3.org/TR/css-writing-modes-3/#direction - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "direction"; - - enum class Keyword : std::underlying_type_t { - Ltr = to_underlying(CSSKeyword::Ltr), - Rtl = to_underlying(CSSKeyword::Rtl), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return true; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Ltr); - } -}; - -/** - * CSS "display" property. - * https://www.w3.org/TR/css-display-3/#display-type - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "display"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::Inline) - : DeclaredValue::keyword(Keyword::Flex); - } -}; - -/** - * 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. https://reactnative.dev/docs/layout-props#flex - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "flex"; - - enum class Keyword : std::underlying_type_t { - Auto = to_underlying(CSSKeyword::Auto), - None = to_underlying(CSSKeyword::None), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::number(0.0f); - } -}; - -/** - * CSS "flex-basis" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-basis-property - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "flexBasis"; - - enum class Keyword : std::underlying_type_t { - Auto = to_underlying(CSSKeyword::Auto), - Content = to_underlying(CSSKeyword::Content), - }; - - using DeclaredValue = - CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Auto); - } -}; - -/** - * CSS "flex-direction" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-direction-property - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "flexDirection"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::Row) - : DeclaredValue::keyword(Keyword::Column); - } -}; - -/** - * CSS "flex-grow" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-grow-property - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "flexGrow"; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::number(0.0f); - } -}; - -/** - * CSS "flex-shrink" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-shrink-property - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "flexShrink"; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C ? DeclaredValue::number(1.0f) - : DeclaredValue::number(0.0f); - } -}; - -/** - * CSS "flex-wrap" property. - * https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "flexWrap"; - - enum class Keyword : std::underlying_type_t { - NoWrap = to_underlying(CSSKeyword::NoWrap), - Wrap = to_underlying(CSSKeyword::Wrap), - WrapReverse = to_underlying(CSSKeyword::WrapReverse), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::NoWrap); - } -}; - -/** - * CSS gutter properties. - * https://www.w3.org/TR/css-align-3/#column-row-gap - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "gap"; - - enum class Keyword : std::underlying_type_t { - Normal = to_underlying(CSSKeyword::Normal), - }; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Normal); - } -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "columnGap"; -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "rowGap"; -}; - -/** - * CSS sizing properties - * https://www.w3.org/TR/css-sizing-3/#sizing-properties - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "height"; - - enum class Keyword : std::underlying_type_t { - Auto = to_underlying(CSSKeyword::Auto), - MaxContent = to_underlying(CSSKeyword::MaxContent), - MinContent = to_underlying(CSSKeyword::MinContent), - }; - - using DeclaredValue = - CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Auto); - } -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "width"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "minWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "minHeight"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "maxWidth"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "maxHeight"; -}; - -/** - * CSS box inset properties - * https://drafts.csswg.org/css-position-3/#insets - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "inset"; - - enum class Keyword : std::underlying_type_t { - Auto = to_underlying(CSSKeyword::Auto), - }; - - using DeclaredValue = - CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Auto); - } -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "top"; -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "right"; -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "bottom"; -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "left"; -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "start"; -}; - -template <> -struct CSSPropDefinition : CSSPropDefinition { - constexpr static std::string_view kName = "end"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "insetBlock"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "insetBlockEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "insetBlockStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "insetInline"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "insetInlineEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "insetInlineStart"; -}; - -/** - * 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 { - constexpr static std::string_view kName = "justifyContent"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::FlexStart); - } -}; - -/** - * CSS "margin" properties - * https://www.w3.org/TR/css-box-4/#margins - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "margin"; - - enum class Keyword : std::underlying_type_t { - Auto = to_underlying(CSSKeyword::Auto), - }; - - using DeclaredValue = - CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::length(0.0f, CSSLengthUnit::Px); - } -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginBlock"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginBlockEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginBlockStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginBottom"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginHorizontal"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginInline"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginInlineEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginInlineStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginLeft"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginRight"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginTop"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "marginVertical"; -}; - -/** - * CSS "opacity" property - * https://www.w3.org/TR/css-color-3/#transparency - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "opacity"; - - using DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::number(1.0f); - } -}; - -/** - * CSS "overflow" property. - * https://www.w3.org/TR/css-overflow-3/#overflow-control - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "overflow"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::keyword(Keyword::Visible); - } -}; - -/** - * CSS padding properties - * https://www.w3.org/TR/css-box-4/#paddings - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "padding"; - - using DeclaredValue = - CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor /*flavor*/) { - return DeclaredValue::length(0.0f, CSSLengthUnit::Px); - } -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingBlock"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingBlockEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingBlockStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingBottom"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingHorizontal"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingInline"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingInlineEnd"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingInlineStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingLeft"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingRight"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingStart"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingTop"; -}; - -template <> -struct CSSPropDefinition - : CSSPropDefinition { - constexpr static std::string_view kName = "paddingVertical"; -}; - -/** - * CSS "position" property. - * https://www.w3.org/TR/css-position-3/#position-property - */ -template <> -struct CSSPropDefinition { - constexpr static std::string_view kName = "position"; - - 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 DeclaredValue = CSSValueVariant; - using SpecifiedValue = CSSValueVariant; - using ComputedValue = CSSValueVariant; - - constexpr static bool isInherited() { - return false; - } - - constexpr static DeclaredValue initialValue(CSSFlavor flavor) { - return flavor == CSSFlavor::W3C ? DeclaredValue::keyword(Keyword::Static) - : DeclaredValue::keyword(Keyword::Relative); - } -}; - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h new file mode 100644 index 00000000000..f6ed8d1ea5d --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSRatio.h @@ -0,0 +1,77 @@ +/* + * 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 +#include + +#include +#include +#include + +namespace facebook::react { + +/** + * Representation of CSS data type + * https://www.w3.org/TR/css-values-4/#ratios + */ +struct CSSRatio { + float numerator{}; + float denominator{}; +}; + +template <> +struct CSSDataTypeParser { + static constexpr auto consumePreservedToken( + const CSSPreservedToken& token, + CSSSyntaxParser& parser) -> std::optional { + // = [ / ]? + // https://www.w3.org/TR/css-values-4/#ratio + if (isValidRatioPart(token.numericValue())) { + float numerator = token.numericValue(); + + CSSSyntaxParser lookaheadParser{parser}; + + auto hasSolidus = lookaheadParser.consumeComponentValue( + CSSComponentValueDelimiter::Whitespace, + [&](const CSSPreservedToken& token) { + return token.type() == CSSTokenType::Delim && + token.stringValue() == "/"; + }); + + if (!hasSolidus) { + parser = lookaheadParser; + return CSSRatio{numerator, 1.0f}; + } + + auto denominator = parseNextCSSValue( + lookaheadParser, CSSComponentValueDelimiter::Whitespace); + + if (std::holds_alternative(denominator) && + isValidRatioPart(std::get(denominator).value)) { + parser = lookaheadParser; + return CSSRatio{numerator, std::get(denominator).value}; + } + } + + return {}; + } + + private: + static constexpr bool isValidRatioPart(float value) { + // If either number in the is 0 or infinite, it represents a + // degenerate ratio (and, generally, won’t do anything). + // https://www.w3.org/TR/css-values-4/#ratios + return value > 0.0f && value != +std::numeric_limits::infinity() && + value != -std::numeric_limits::infinity(); + } +}; + +static_assert(CSSDataType); + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h index a46e490fade..2b5eaf78020 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h @@ -251,50 +251,63 @@ struct CSSComponentValueVisitorDispatcher { return consumeComponentValue(std::forward(visitors)...); } - constexpr std::optional visitFunction(const VisitorsT&... visitors) { - for (auto visitor : {visitors...}) { - if constexpr (CSSFunctionVisitor) { - auto functionValue = - visitor({.name = parser.consumeToken().stringValue()}); - parser.consumeWhitespace(); - if (parser.peek().type() == CSSTokenType::CloseParen) { - parser.consumeToken(); - return functionValue; - } - - return {}; + constexpr std::optional visitFunction( + const CSSComponentValueVisitor auto& visitor, + const CSSComponentValueVisitor auto&... rest) { + if constexpr (CSSFunctionVisitor) { + auto functionValue = + visitor({.name = parser.consumeToken().stringValue()}); + parser.consumeWhitespace(); + if (parser.peek().type() == CSSTokenType::CloseParen) { + parser.consumeToken(); + return functionValue; } + + return {}; } + return visitFunction(rest...); + } + + constexpr std::optional visitFunction() { return {}; } + // Can be one of std::monostate (variant null-type), CSSWideKeyword, + // CSSLength, or CSSPercentage + constexpr std::optional visitSimpleBlock( CSSTokenType endToken, - const VisitorsT&... visitors) { - for (auto visitor : {visitors...}) { - if constexpr (CSSSimpleBlockVisitor) { - auto blockValue = - visitor({.openBracketType = parser.consumeToken().type()}); - parser.consumeWhitespace(); - if (parser.peek().type() == endToken) { - parser.consumeToken(); - return blockValue; - } - - return {}; + const CSSComponentValueVisitor auto& visitor, + const CSSComponentValueVisitor auto&... rest) { + if constexpr (CSSSimpleBlockVisitor) { + auto blockValue = + visitor({.openBracketType = parser.consumeToken().type()}); + parser.consumeWhitespace(); + if (parser.peek().type() == endToken) { + parser.consumeToken(); + return blockValue; } + + return {}; } + return visitSimpleBlock(endToken, rest...); + } + + constexpr std::optional visitSimpleBlock(CSSTokenType /*endToken*/) { return {}; } constexpr std::optional visitPreservedToken( - const VisitorsT&... visitors) { - for (auto visitor : {visitors...}) { - if constexpr (CSSPreservedTokenVisitor) { - return visitor(parser.consumeToken()); - } + const CSSComponentValueVisitor auto& visitor, + const CSSComponentValueVisitor auto&... rest) { + if constexpr (CSSPreservedTokenVisitor) { + return visitor(parser.consumeToken()); } + return visitPreservedToken(rest...); + } + + constexpr std::optional visitPreservedToken() { return {}; } }; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h deleted file mode 100644 index 1b39c78caf5..00000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValue.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 -#include - -#include -#include - -namespace facebook::react { - -/** - * Represents a CSS component value type. - * https://www.w3.org/TR/css-values-4/#component-types - */ -enum class CSSValueType : uint8_t { - CSSWideKeyword, - Keyword, - Length, - Number, - Percentage, - Ratio, - Angle, - Color, -}; - -/** - * Concrete representation for a CSS basic data type, or keywords - * https://www.w3.org/TR/css-values-4/#component-types - */ -template -concept CSSDataType = std::is_trivially_destructible_v && - std::is_copy_constructible_v && std::is_default_constructible_v; - -#pragma pack(push, 1) -/** - * Representation of CSS data type - * https://www.w3.org/TR/css-values-4/#lengths - */ -struct CSSLength { - float value{}; - CSSLengthUnit unit{CSSLengthUnit::Px}; -}; -#pragma pack(pop) - -/** - * Representation of CSS data type - * https://www.w3.org/TR/css-values-4/#percentages - */ -struct CSSPercentage { - float value{}; -}; - -/** - * Representation of CSS data type - * https://www.w3.org/TR/css-values-4/#numbers - */ -struct CSSNumber { - float value{}; -}; - -/** - * Representation of CSS data type - * https://www.w3.org/TR/css-values-4/#ratios - */ -struct CSSRatio { - float numerator{}; - float denominator{}; -}; - -/** - * Representation of CSS data type - * https://www.w3.org/TR/css-values-4/#angles - */ -struct CSSAngle { - float degrees{}; -}; - -/** - * Representation of CSS data type - * https://www.w3.org/TR/css-color-5/#typedef-color - */ -struct CSSColor { - uint8_t r{}; - uint8_t g{}; - uint8_t b{}; - uint8_t a{}; -}; - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h index 7a196e2e4b2..953bceea5e9 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h @@ -9,83 +9,49 @@ #include #include +#include -#include -#include -#include +#include +#include #include -#include namespace facebook::react { namespace detail { -template class CSSValueParser { - using CSSValue = CSSValueVariant; - public: - explicit constexpr CSSValueParser(std::string_view css) : parser_{css} {} + explicit constexpr CSSValueParser(CSSSyntaxParser& parser) + : parser_{parser} {} /* * Attempts to parse the characters starting at the current component value - * into one of the given data types. + * into one of the given data types. Types are attempted in order, which the + * caller must consider (e.g. a number token should be preferred to be a + * before ). */ - constexpr CSSValue consumeValue( + template + constexpr std::variant consumeValue( CSSComponentValueDelimiter delimeter = CSSComponentValueDelimiter::None) { - return parser_.consumeComponentValue( - delimeter, [&](const CSSPreservedToken& token) { - // CSS-global keywords - if constexpr (hasType()) { - if (auto cssWideKeyword = consumeCSSWideKeyword(token)) { - return *cssWideKeyword; - } - } - // Property-specific keywords - if constexpr (hasType()) { - if (auto keyword = consumeKeyword(token)) { - return *keyword; - } - } - // - if constexpr (hasType()) { - if (auto ratio = consumeRatio(token)) { - return *ratio; - } - } - // - if constexpr (hasType()) { - if (auto number = consumeNumber(token)) { - return *number; - } - } - // - if constexpr (hasType()) { - if (auto length = consumeLength(token)) { - return *length; - } - } - // - if constexpr (hasType()) { - if (auto angle = consumeAngle(token)) { - return *angle; - } - } - // - if constexpr (hasType()) { - if (auto percentage = consumePercentage(token)) { - return *percentage; - } - } - // - if constexpr (hasType()) { - if (auto colorValue = consumeColorToken(token)) { - return *colorValue; - } - } - return CSSValue{}; + using ReturnT = std::variant; + + return parser_.consumeComponentValue( + delimeter, + [&](const CSSPreservedToken& token) { + return tryConsumePreservedToken< + ReturnT, + CSSDataTypeParser...>(token); + }, + [&](const CSSSimpleBlock& block) { + return tryConsumeSimpleBlock< + ReturnT, + CSSDataTypeParser...>(block); + }, + [&](const CSSFunctionBlock& func) { + return tryConsumeFunctionBlock< + ReturnT, + CSSDataTypeParser...>(func); }); - // TODO: support function component values and simple blocks } constexpr bool isFinished() const { @@ -97,221 +63,108 @@ class CSSValueParser { } private: - template - constexpr static bool hasType() { - return traits::containsType(); - } - - template - constexpr static bool hasType() { - return false; - } - - constexpr std::optional consumeKeyword( - const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Ident) { - if (auto keyword = parseCSSKeyword( - token.stringValue())) { - return CSSValue::keyword(*keyword); - } - } + template + constexpr ReturnT tryConsumePreservedToken( + const CSSPreservedToken& /*token*/) { return {}; } - constexpr std::optional consumeCSSWideKeyword( - const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Ident) { - if (auto keyword = parseCSSKeyword(token.stringValue())) { - return CSSValue::cssWideKeyword(*keyword); - } - } - return {}; - } - - constexpr std::optional consumeAngle( - const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Dimension) { - if (auto unit = parseCSSAngleUnit(token.unit())) { - return CSSValue::angle(canonicalize(token.numericValue(), *unit)); - } - } - return {}; - } - - constexpr std::optional consumePercentage( - const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Percentage) { - return CSSValue::percentage(token.numericValue()); - } - - return {}; - } - - constexpr std::optional consumeNumber( - const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Number) { - return CSSValue::number(token.numericValue()); - } - - return {}; - } - - constexpr std::optional consumeLength( - const CSSPreservedToken& token) { - switch (token.type()) { - case CSSTokenType::Dimension: - if (auto unit = parseCSSLengthUnit(token.unit())) { - return CSSValue::length(token.numericValue(), *unit); - } - break; - case CSSTokenType::Number: - // For zero lengths the unit identifier is optional (i.e. can be - // syntactically represented as the 0). However, if a 0 - // could be parsed as either a or a in a - // property (such as line-height), it must parse as a . - // https://www.w3.org/TR/css-values-4/#lengths - if (token.numericValue() == 0) { - return CSSValue::length(token.numericValue(), CSSLengthUnit::Px); - } - break; - default: - break; - } - - return {}; - } - - constexpr std::optional consumeRatio( - const CSSPreservedToken& token) { - // = [ / ]? - // https://www.w3.org/TR/css-values-4/#ratio - if (isValidRatioPart(token.numericValue())) { - float numerator = token.numericValue(); - - CSSSyntaxParser lookaheadParser{parser_}; - - auto hasSolidus = lookaheadParser.consumeComponentValue( - CSSComponentValueDelimiter::Whitespace, - [&](const CSSPreservedToken& token) { - return token.type() == CSSTokenType::Delim && - token.stringValue() == "/"; - }); - - if (!hasSolidus) { - return CSSValue::ratio(numerator, 1.0f); - } - - // TODO: support math expression substituion for - auto denominator = - lookaheadParser.consumeComponentValue>( - CSSComponentValueDelimiter::Whitespace, - [&](const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Number && - isValidRatioPart(token.numericValue())) { - return std::optional(token.numericValue()); - } - return std::optional{}; - }); - - if (denominator.has_value()) { - parser_ = lookaheadParser; - return CSSValue::ratio(numerator, *denominator); + template < + typename ReturnT, + CSSValidDataTypeParser ParserT, + CSSValidDataTypeParser... RestParserT> + constexpr ReturnT tryConsumePreservedToken(const CSSPreservedToken& token) { + if constexpr (CSSPreservedTokenSink) { + if (auto ret = ParserT::consumePreservedToken(token, parser_)) { + return *ret; } } - return {}; - } - - constexpr bool isValidRatioPart(float value) { - // If either number in the is 0 or infinite, it represents a - // degenerate ratio (and, generally, won’t do anything). - // https://www.w3.org/TR/css-values-4/#ratios - return value > 0.0f && value != +std::numeric_limits::infinity() && - value != -std::numeric_limits::infinity(); - } - - constexpr std::optional consumeColorToken( - const CSSPreservedToken& token) { - if (token.type() == CSSTokenType::Ident) { - return parseCSSNamedColor(token.stringValue()); - } else if (token.type() != CSSTokenType::Hash) { - return {}; - } - - // https://www.w3.org/TR/css-color-4/#hex-color - std::string_view hexColorValue = token.stringValue(); - if (isValidHexColor(hexColorValue)) { - if (hexColorValue.length() == 3) { - return CSSValue::color( - hexToNumeric(hexColorValue.substr(0, 1), HexColorType::Short), - hexToNumeric(hexColorValue.substr(1, 1), HexColorType::Short), - hexToNumeric(hexColorValue.substr(2, 1), HexColorType::Short), - 255u); - } else if (hexColorValue.length() == 4) { - return CSSValue::color( - hexToNumeric(hexColorValue.substr(0, 1), HexColorType::Short), - hexToNumeric(hexColorValue.substr(1, 1), HexColorType::Short), - hexToNumeric(hexColorValue.substr(2, 1), HexColorType::Short), - hexToNumeric(hexColorValue.substr(3, 1), HexColorType::Short)); - } else if (hexColorValue.length() == 6) { - return CSSValue::color( - hexToNumeric(hexColorValue.substr(0, 2), HexColorType::Long), - hexToNumeric(hexColorValue.substr(2, 2), HexColorType::Long), - hexToNumeric(hexColorValue.substr(4, 2), HexColorType::Long), - 255u); - } else if (hexColorValue.length() == 8) { - return CSSValue::color( - hexToNumeric(hexColorValue.substr(0, 2), HexColorType::Long), - hexToNumeric(hexColorValue.substr(2, 2), HexColorType::Long), - hexToNumeric(hexColorValue.substr(4, 2), HexColorType::Long), - hexToNumeric(hexColorValue.substr(6, 2), HexColorType::Long)); + if constexpr (CSSSimplePreservedTokenSink) { + if (auto ret = ParserT::consumePreservedToken(token)) { + return *ret; } } + + return tryConsumePreservedToken(token); + } + + template + constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& /*token*/) { return {}; } - CSSSyntaxParser parser_; -}; + template < + typename ReturnT, + CSSValidDataTypeParser ParserT, + CSSValidDataTypeParser... RestParserT> + constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& block) { + if constexpr (CSSSimpleBlockSink) { + if (auto ret = ParserT::consumeSimpleBlock(block, parser_)) { + return *ret; + } + } -template -constexpr void parseCSSValue( - std::string_view css, - CSSValueVariant& value) { - detail::CSSValueParser parser(css); - - parser.consumeWhitespace(); - auto componentValue = parser.consumeValue(); - parser.consumeWhitespace(); - - if (parser.isFinished()) { - value = std::move(componentValue); - } else { - value = {}; + return tryConsumeSimpleBlock(block); } + + template + constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& /*func*/) { + return {}; + } + + template < + typename ReturnT, + CSSValidDataTypeParser ParserT, + CSSValidDataTypeParser... RestParserT> + constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& func) { + if constexpr (CSSFunctionBlockSink) { + if (auto ret = ParserT::consumeFunctionBlock(func, parser_)) { + return *ret; + } + } + + return tryConsumeFunctionBlock(func); + } + + CSSSyntaxParser& parser_; }; } // namespace detail /** - * Parse a single CSS value. Returns a default-constructed - * CSSValueVariant (CSSKeyword::Unset) on syntax error. + * Parse a single CSS property value. Returns a variant holding std::monostate + * on syntax error. */ template -CSSValueVariant parseCSSValue(std::string_view css) { - CSSValueVariant value; - detail::parseCSSValue(css, value); - return value; +constexpr auto parseCSSProperty(std::string_view css) + -> std::variant { + CSSSyntaxParser syntaxParser(css); + detail::CSSValueParser parser(syntaxParser); + + parser.consumeWhitespace(); + auto value = parser.consumeValue(); + parser.consumeWhitespace(); + + if (parser.isFinished()) { + return value; + } + + return {}; }; /** - * Parses a CSS property into its declared value type. + * 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 if + * successful. */ -template -constexpr CSSDeclaredValue parseCSSProp(std::string_view css) { - // For now we only allow parsing props composed of a single component value. - CSSDeclaredValue value; - detail::parseCSSValue(css, value); - return value; +template +constexpr auto parseNextCSSValue( + CSSSyntaxParser& syntaxParser, + CSSComponentValueDelimiter delimeter = CSSComponentValueDelimiter::None) + -> std::variant { + detail::CSSValueParser valueParser(syntaxParser); + return valueParser.consumeValue(delimeter); } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSValueVariant.h b/packages/react-native/ReactCommon/react/renderer/css/CSSValueVariant.h deleted file mode 100644 index b063c13d4e2..00000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSValueVariant.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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 -#include -#include - -#include -#include - -namespace facebook::react { - -/** - * CSSValueVariant represents a CSS component value: - * https://www.w3.org/TR/css-values-4/#component-types - * - * A CSSValueVariant must be constrained to the set of possible CSS types it may - * encounter. E.g. a dimension which accepts a CSS-wide keywords, "auto" or a - * would be modeled as - * `CSSValueVariant`. This - * allows for efficient storage, and customizing parsing based on the allowed - * set of values. - */ -#pragma pack(push, 1) -template -class CSSValueVariant { - template - static constexpr bool canRepresent() { - return traits::containsType(); - } - - 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(); - } - } - - 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}); - } - - static constexpr CSSValueVariant length(float value, CSSLengthUnit unit) - requires(canRepresent()) - { - return CSSValueVariant(CSSValueType::Length, CSSLength{value, unit}); - } - - static constexpr CSSValueVariant number(float value) - requires(canRepresent()) - { - return CSSValueVariant(CSSValueType::Number, CSSNumber{value}); - } - - static constexpr CSSValueVariant percentage(float value) - requires(canRepresent()) - { - return CSSValueVariant(CSSValueType::Percentage, CSSPercentage{value}); - } - - static constexpr CSSValueVariant ratio(float numerator, float denominator) - requires(canRepresent()) - { - return CSSValueVariant( - CSSValueType::Ratio, CSSRatio{numerator, denominator}); - } - - static constexpr CSSValueVariant angle(float degrees) - requires(canRepresent()) - { - return CSSValueVariant(CSSValueType::Angle, CSSAngle{degrees}); - } - - static constexpr CSSValueVariant - color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) - requires(canRepresent()) - { - return CSSValueVariant(CSSValueType::Color, CSSColor{r, g, b, a}); - } - - constexpr CSSValueType type() const { - return type_; - } - - constexpr CSSWideKeyword getCSSWideKeyword() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr Keyword getKeyword() const - requires(hasKeywordSet()) - { - return getIf(); - } - - constexpr CSSLength getLength() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr CSSNumber getNumber() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr CSSPercentage getPercentage() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr CSSRatio getRatio() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr CSSAngle getAngle() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr CSSColor getColor() const - requires(canRepresent()) - { - return getIf(); - } - - constexpr bool hasValue() const - requires(canRepresent()) - { - return type() != CSSValueType::CSSWideKeyword || - getCSSWideKeyword() != CSSWideKeyword::Unset; - } - - constexpr operator bool() const { - return hasValue(); - } - - constexpr bool operator==(const CSSValueVariant& other) const { - if (type() != other.type()) { - return false; - } - switch (type()) { - case CSSValueType::CSSWideKeyword: - return getCSSWideKeyword() == other.getCSSWideKeyword(); - case CSSValueType::Keyword: - return getKeyword() == other.getKeyword(); - case CSSValueType::Length: - return getLength() == other.getLength(); - case CSSValueType::Number: - return getNumber() == other.getNumber(); - case CSSValueType::Percentage: - return getPercentage() == other.getPercentage(); - case CSSValueType::Ratio: - return getRatio() == other.getRatio(); - case CSSValueType::Angle: - return getAngle() == other.getAngle(); - case CSSValueType::Color: - return getColor() == other.getColor(); - } - - return false; - } - - private: - template - constexpr ValueT getIf() const { - if (type_ == Type) { - return getFromUnion(data_); - } else { - return ValueT{}; - } - } - - template - union RecursiveUnion { - ValueT first; - RecursiveUnion rest; - }; - - template - union RecursiveUnion { - ValueT first; - }; - - template - constexpr const ValueT& getFromUnion(const UnionT& u) const { - if constexpr (std::is_same_v) { - return u.first; - } else { - return getFromUnion(u.rest); - } - } - - template - constexpr CSSValueVariant(CSSValueType type, DataTypeT&& value) - : type_{type}, - data_{constructIntoUnion( - std::forward(value))} {} - - template - constexpr UnionT constructIntoUnion(DataTypeT&& value) { - if constexpr (std::is_same_v) { - return UnionT{.first = std::forward(value)}; - } else { - return UnionT{ - .rest = constructIntoUnion( - std::forward(value))}; - } - } - - CSSValueType type_; - RecursiveUnion data_; -}; -#pragma pack(pop) - -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); - -} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp deleted file mode 100644 index 393d48b1618..00000000000 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSDeclaredStyleTest.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include - -namespace facebook::react { - -TEST(CSSDeclaredStyle, unset_keyword) { - CSSDeclaredStyle style; - auto value = style.get(); - EXPECT_EQ(value.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(value.getCSSWideKeyword(), CSSWideKeyword::Unset); -} - -TEST(CSSDeclaredStyle, unset_ratio) { - CSSDeclaredStyle style; - auto value = style.get(); - EXPECT_EQ(value.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(value.getCSSWideKeyword(), CSSWideKeyword::Unset); -} - -TEST(CSSDeclaredStyle, set_keyword) { - CSSDeclaredStyle style; - - style.set("row"); - - auto value = style.get(); - EXPECT_EQ(value.type(), CSSValueType::Keyword); - EXPECT_EQ(value.getKeyword(), CSSKeyword::Row); -} - -TEST(CSSDeclaredStyle, set_ratio) { - CSSDeclaredStyle style; - - style.set("16 / 9"); - - auto value = style.get(); - EXPECT_EQ(value.type(), CSSValueType::Ratio); - EXPECT_EQ(value.getRatio().numerator, 16.0f); - EXPECT_EQ(value.getRatio().denominator, 9.0f); -} - -TEST(CSSDeclaredStyle, overwrite_ratio) { - CSSDeclaredStyle style; - - style.set("16 / 9"); - - auto value1 = style.get(); - EXPECT_EQ(value1.type(), CSSValueType::Ratio); - EXPECT_EQ(value1.getRatio().numerator, 16.0f); - EXPECT_EQ(value1.getRatio().denominator, 9.0f); - - style.set("4/3"); - - auto value2 = style.get(); - EXPECT_EQ(value2.type(), CSSValueType::Ratio); - EXPECT_EQ(value2.getRatio().numerator, 4.0f); - EXPECT_EQ(value2.getRatio().denominator, 3.0f); -} - -TEST(CSSDeclaredStyle, set_multiple) { - CSSDeclaredStyle style; - - style.set("row"); - style.set("16 / 9"); - - auto flexDirection = style.get(); - EXPECT_EQ(flexDirection.type(), CSSValueType::Keyword); - EXPECT_EQ(flexDirection.getKeyword(), CSSKeyword::Row); - - auto aspectRatio = style.get(); - EXPECT_EQ(aspectRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(aspectRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(aspectRatio.getRatio().denominator, 9.0f); -} - -TEST(CSSDeclaredStyle, set_multiple_overwrite) { - CSSDeclaredStyle style; - - style.set("row"); - style.set("16 / 9"); - style.set("column"); - - auto flexDirection = style.get(); - EXPECT_EQ(flexDirection.type(), CSSValueType::Keyword); - EXPECT_EQ(flexDirection.getKeyword(), CSSKeyword::Column); - - auto aspectRatio = style.get(); - EXPECT_EQ(aspectRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(aspectRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(aspectRatio.getRatio().denominator, 9.0f); -} - -TEST(CSSDeclaredStyle, set_multiple_reset) { - CSSDeclaredStyle style; - - style.set("row"); - style.set("16 / 9"); - style.set(""); - - auto flexDirection = style.get(); - EXPECT_EQ(flexDirection.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(flexDirection.getCSSWideKeyword(), CSSWideKeyword::Unset); - - auto aspectRatio = style.get(); - EXPECT_EQ(aspectRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(aspectRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(aspectRatio.getRatio().denominator, 9.0f); -} - -TEST(CSSDeclaredStyle, get_with_precedence) { - CSSDeclaredStyle style; - - style.set("1px"); - - auto margin1 = style.get< - CSSProp::MarginStart, - CSSProp::MarginLeft, - CSSProp::MarginInline, - CSSProp::MarginHorizontal, - CSSProp::Margin>(); - - EXPECT_EQ(margin1.type(), CSSValueType::Length); - EXPECT_EQ(margin1.getLength().value, 1.0f); - EXPECT_EQ(margin1.getLength().unit, CSSLengthUnit::Px); - - style.set("3px"); - auto margin2 = style.get< - CSSProp::MarginStart, - CSSProp::MarginLeft, - CSSProp::MarginInline, - CSSProp::MarginHorizontal, - CSSProp::Margin>(); - - EXPECT_EQ(margin2.type(), CSSValueType::Length); - EXPECT_EQ(margin2.getLength().value, 3.0f); - EXPECT_EQ(margin2.getLength().unit, CSSLengthUnit::Px); -} - -TEST(CSSDeclaredStyle, set_from_string) { - CSSDeclaredStyle style; - - EXPECT_TRUE(style.set("flexDirection", "row")); - - EXPECT_EQ(style.get().type(), CSSValueType::Keyword); - EXPECT_EQ(style.get().getKeyword(), CSSKeyword::Row); - - EXPECT_TRUE(style.set("aspectRatio", "16 / 9")); - - EXPECT_EQ(style.get().type(), CSSValueType::Ratio); - auto ratio = style.get(); - EXPECT_FLOAT_EQ(ratio.getRatio().numerator, 16.0f); - EXPECT_FLOAT_EQ(ratio.getRatio().denominator, 9.0f); - - EXPECT_FALSE(style.set("aspectRatio", "16 / 9 / 2")); -} - -} // 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 5908316ba71..a229b5b9ad7 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSValueParserTest.cpp @@ -6,423 +6,292 @@ */ #include +#include +#include +#include +#include +#include +#include +#include #include namespace facebook::react { TEST(CSSValueParser, keyword_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); - auto autoValue = parseCSSValue("auto"); - EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); - EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); + auto inheritValue = parseCSSProperty<>("inherit"); + EXPECT_TRUE(std::holds_alternative(inheritValue)); + EXPECT_EQ(std::get(inheritValue), CSSWideKeyword::Inherit); - auto autoCapsValue = parseCSSValue("AuTO"); - EXPECT_EQ(autoCapsValue.type(), CSSValueType::Keyword); - EXPECT_EQ(autoCapsValue.getKeyword(), CSSKeyword::Auto); + auto autoValue = parseCSSProperty("auto"); + EXPECT_TRUE(std::holds_alternative(autoValue)); + EXPECT_EQ(std::get(autoValue), CSSKeyword::Auto); - auto autoDisallowedValue = parseCSSValue("auto"); - EXPECT_EQ(autoDisallowedValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(autoDisallowedValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto autoCapsValue = parseCSSProperty("AuTO"); + EXPECT_TRUE(std::holds_alternative(autoCapsValue)); + EXPECT_EQ(std::get(autoCapsValue), CSSKeyword::Auto); - auto whitespaceValue = - parseCSSValue(" flex-start "); - EXPECT_EQ(whitespaceValue.type(), CSSValueType::Keyword); - EXPECT_EQ(whitespaceValue.getKeyword(), CSSKeyword::FlexStart); + auto autoDisallowedValue = parseCSSProperty<>("auto"); + EXPECT_TRUE(std::holds_alternative(autoDisallowedValue)); - auto badIdentValue = parseCSSValue("bad"); - EXPECT_EQ(badIdentValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(badIdentValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto whitespaceValue = parseCSSProperty(" flex-start "); + EXPECT_TRUE(std::holds_alternative(whitespaceValue)); + EXPECT_EQ(std::get(whitespaceValue), CSSKeyword::FlexStart); - auto pxValue = parseCSSValue("20px"); - EXPECT_EQ(pxValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(pxValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto badIdentValue = parseCSSProperty("bad"); + EXPECT_TRUE(std::holds_alternative(badIdentValue)); - auto multiValue = parseCSSValue("auto flex-start"); - EXPECT_EQ(multiValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(multiValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto pxValue = parseCSSProperty<>("20px"); + EXPECT_TRUE(std::holds_alternative(pxValue)); + + auto multiValue = parseCSSProperty<>("auto flex-start"); + EXPECT_TRUE(std::holds_alternative(multiValue)); } TEST(CSSValueParser, length_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); - auto autoValue = parseCSSValue("auto"); - EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); - EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); + auto autoValue = parseCSSProperty("auto"); + EXPECT_TRUE(std::holds_alternative(autoValue)); + EXPECT_EQ(std::get(autoValue), CSSKeyword::Auto); - auto pxValue = parseCSSValue("20px"); - EXPECT_EQ(pxValue.type(), CSSValueType::Length); - EXPECT_EQ(pxValue.getLength().value, 20.0f); - EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); + auto pxValue = parseCSSProperty("20px"); + EXPECT_TRUE(std::holds_alternative(pxValue)); + EXPECT_EQ(std::get(pxValue).value, 20.0f); + EXPECT_EQ(std::get(pxValue).unit, CSSLengthUnit::Px); - auto capsValue = parseCSSValue("50PX"); - EXPECT_EQ(capsValue.type(), CSSValueType::Length); - EXPECT_EQ(capsValue.getLength().value, 50.0f); - EXPECT_EQ(capsValue.getLength().unit, CSSLengthUnit::Px); + auto capsValue = parseCSSProperty("50PX"); + EXPECT_TRUE(std::holds_alternative(capsValue)); + EXPECT_EQ(std::get(capsValue).value, 50.0f); + EXPECT_EQ(std::get(capsValue).unit, CSSLengthUnit::Px); - auto cmValue = parseCSSValue("453cm"); - EXPECT_EQ(cmValue.type(), CSSValueType::Length); - EXPECT_EQ(cmValue.getLength().value, 453.0f); - EXPECT_EQ(cmValue.getLength().unit, CSSLengthUnit::Cm); + auto cmValue = parseCSSProperty("453cm"); + EXPECT_TRUE(std::holds_alternative(cmValue)); + EXPECT_TRUE(std::get(cmValue).value == 453.0f); + EXPECT_EQ(std::get(cmValue).unit, CSSLengthUnit::Cm); - auto unitlessZeroValue = parseCSSValue("0"); - EXPECT_EQ(unitlessZeroValue.type(), CSSValueType::Length); - EXPECT_EQ(unitlessZeroValue.getLength().value, 0.0f); - EXPECT_EQ(unitlessZeroValue.getLength().unit, CSSLengthUnit::Px); + auto unitlessZeroValue = parseCSSProperty("0"); + EXPECT_TRUE(std::holds_alternative(unitlessZeroValue)); + EXPECT_EQ(std::get(unitlessZeroValue).value, 0.0f); + EXPECT_EQ(std::get(unitlessZeroValue).unit, CSSLengthUnit::Px); - auto unitlessNonzeroValue = parseCSSValue("123"); - EXPECT_EQ(unitlessNonzeroValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(unitlessNonzeroValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto unitlessNonzeroValue = parseCSSProperty("123"); + EXPECT_TRUE(std::holds_alternative(unitlessNonzeroValue)); - auto pctValue = parseCSSValue("-40%"); - EXPECT_EQ(pctValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(pctValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto pctValue = parseCSSProperty("-40%"); + EXPECT_TRUE(std::holds_alternative(pctValue)); } TEST(CSSValueParser, length_percentage_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); auto autoValue = - parseCSSValue( - "auto"); - EXPECT_EQ(autoValue.type(), CSSValueType::Keyword); - EXPECT_EQ(autoValue.getKeyword(), CSSKeyword::Auto); + parseCSSProperty("auto"); + EXPECT_TRUE(std::holds_alternative(autoValue)); + EXPECT_EQ(std::get(autoValue), CSSKeyword::Auto); - auto pxValue = - parseCSSValue("20px"); - EXPECT_EQ(pxValue.type(), CSSValueType::Length); - EXPECT_EQ(pxValue.getLength().value, 20.0f); - EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); + auto pxValue = parseCSSProperty("20px"); + EXPECT_TRUE(std::holds_alternative(pxValue)); + EXPECT_EQ(std::get(pxValue).value, 20.0f); + EXPECT_EQ(std::get(pxValue).unit, CSSLengthUnit::Px); - auto pctValue = - parseCSSValue("-40%"); - EXPECT_EQ(pctValue.type(), CSSValueType::Percentage); - EXPECT_EQ(pctValue.getPercentage().value, -40.0f); + auto pctValue = parseCSSProperty("-40%"); + EXPECT_TRUE(std::holds_alternative(pctValue)); + EXPECT_EQ(std::get(pctValue).value, -40.0f); } TEST(CSSValueParser, number_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); - auto inheritValue = parseCSSValue("inherit"); - EXPECT_EQ(inheritValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(inheritValue.getCSSWideKeyword(), CSSWideKeyword::Inherit); + auto pxValue = parseCSSProperty("20px"); + EXPECT_TRUE(std::holds_alternative(pxValue)); - auto pxValue = parseCSSValue("20px"); - EXPECT_EQ(pxValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(pxValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto numberValue = parseCSSProperty("123.456"); + EXPECT_TRUE(std::holds_alternative(numberValue)); + EXPECT_EQ(std::get(numberValue).value, 123.456f); - auto numberValue = - parseCSSValue("123.456"); - EXPECT_EQ(numberValue.type(), CSSValueType::Number); - EXPECT_EQ(numberValue.getNumber().value, 123.456f); - - auto unitlessZeroValue = - parseCSSValue("0"); - EXPECT_EQ(unitlessZeroValue.type(), CSSValueType::Number); - EXPECT_EQ(unitlessZeroValue.getNumber().value, 0.0f); + auto unitlessZeroValue = parseCSSProperty("0"); + EXPECT_TRUE(std::holds_alternative(unitlessZeroValue)); + EXPECT_EQ(std::get(unitlessZeroValue).value, 0.0f); } TEST(CSSValueParser, ratio_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); - auto validRatio = parseCSSValue("16/9"); - EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); + auto validRatio = parseCSSProperty("16/9"); + EXPECT_TRUE(std::holds_alternative(validRatio)); + EXPECT_EQ(std::get(validRatio).numerator, 16.0f); + EXPECT_EQ(std::get(validRatio).denominator, 9.0f); - auto validRatioWithWhitespace = - parseCSSValue("16 / 9"); - EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); - EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); - EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); + auto validRatioWithWhitespace = parseCSSProperty("16 / 9"); + EXPECT_TRUE(std::holds_alternative(validRatioWithWhitespace)); + EXPECT_EQ(std::get(validRatioWithWhitespace).numerator, 16.0f); + EXPECT_EQ(std::get(validRatioWithWhitespace).denominator, 9.0f); - auto singleNumberRatio = parseCSSValue("16"); - EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); + auto singleNumberRatio = parseCSSProperty("16"); + EXPECT_TRUE(std::holds_alternative(singleNumberRatio)); + EXPECT_EQ(std::get(singleNumberRatio).numerator, 16.0f); + EXPECT_EQ(std::get(singleNumberRatio).denominator, 1.0f); - auto fractionalNumber = parseCSSValue("16.5"); - EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); - EXPECT_EQ(fractionalNumber.getRatio().denominator, 1.0f); + auto fractionalNumber = parseCSSProperty("16.5"); + EXPECT_TRUE(std::holds_alternative(fractionalNumber)); + EXPECT_EQ(std::get(fractionalNumber).numerator, 16.5f); + EXPECT_EQ(std::get(fractionalNumber).denominator, 1.0f); - auto negativeNumber = parseCSSValue("-16"); - EXPECT_EQ(negativeNumber.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(negativeNumber.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto negativeNumber = parseCSSProperty("-16"); + EXPECT_TRUE(std::holds_alternative(negativeNumber)); - auto missingDenominator = parseCSSValue("16/"); - EXPECT_EQ(missingDenominator.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(missingDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto missingDenominator = parseCSSProperty("16/"); + EXPECT_TRUE(std::holds_alternative(missingDenominator)); - auto negativeNumerator = parseCSSValue("-16/9"); - EXPECT_EQ(negativeNumerator.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(negativeNumerator.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto negativeNumerator = parseCSSProperty("-16/9"); + EXPECT_TRUE(std::holds_alternative(negativeNumerator)); - auto negativeDenominator = parseCSSValue("16/-9"); - EXPECT_EQ(negativeDenominator.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(negativeDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto negativeDenominator = parseCSSProperty("16/-9"); + EXPECT_TRUE(std::holds_alternative(negativeDenominator)); - auto fractionalNumerator = parseCSSValue("16.5/9"); - EXPECT_EQ(fractionalNumerator.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalNumerator.getRatio().numerator, 16.5f); - EXPECT_EQ(fractionalNumerator.getRatio().denominator, 9.0f); + auto fractionalNumerator = parseCSSProperty("16.5/9"); + EXPECT_TRUE(std::holds_alternative(fractionalNumerator)); + EXPECT_EQ(std::get(fractionalNumerator).numerator, 16.5f); + EXPECT_EQ(std::get(fractionalNumerator).denominator, 9.0f); - auto fractionalDenominator = - parseCSSValue("16/9.5"); - EXPECT_EQ(fractionalDenominator.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalDenominator.getRatio().numerator, 16.0f); - EXPECT_EQ(fractionalDenominator.getRatio().denominator, 9.5f); + auto fractionalDenominator = parseCSSProperty("16/9.5"); + EXPECT_TRUE(std::holds_alternative(fractionalDenominator)); + EXPECT_EQ(std::get(fractionalDenominator).numerator, 16.0f); - auto degenerateRatio = parseCSSValue("0"); - EXPECT_EQ(degenerateRatio.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(degenerateRatio.getCSSWideKeyword(), CSSWideKeyword::Unset); -} - -TEST(CSSValueParser, number_ratio_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); - - auto validRatio = parseCSSValue("16/9"); - EXPECT_EQ(validRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(validRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(validRatio.getRatio().denominator, 9.0f); - - auto validRatioWithWhitespace = - parseCSSValue("16 / 9"); - EXPECT_EQ(validRatioWithWhitespace.type(), CSSValueType::Ratio); - EXPECT_EQ(validRatioWithWhitespace.getRatio().numerator, 16.0f); - EXPECT_EQ(validRatioWithWhitespace.getRatio().denominator, 9.0f); - - auto singleNumberRatio = - parseCSSValue("16"); - EXPECT_EQ(singleNumberRatio.type(), CSSValueType::Ratio); - EXPECT_EQ(singleNumberRatio.getRatio().numerator, 16.0f); - EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); - - auto fractionalNumber = - parseCSSValue("16.5"); - EXPECT_EQ(fractionalNumber.type(), CSSValueType::Ratio); - EXPECT_EQ(fractionalNumber.getRatio().numerator, 16.5f); - EXPECT_EQ(singleNumberRatio.getRatio().denominator, 1.0f); - - auto negativeNumber = - parseCSSValue("-16"); - EXPECT_EQ(negativeNumber.type(), CSSValueType::Number); - EXPECT_EQ(negativeNumber.getNumber().value, -16.0f); - - auto missingDenominator = - parseCSSValue("16/"); - EXPECT_EQ(missingDenominator.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(missingDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); - - auto negativeNumerator = - parseCSSValue("-16/9"); - EXPECT_EQ(negativeNumerator.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(negativeNumerator.getCSSWideKeyword(), CSSWideKeyword::Unset); - - auto negativeDenominator = - parseCSSValue("16/-9"); - EXPECT_EQ(negativeDenominator.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(negativeDenominator.getCSSWideKeyword(), CSSWideKeyword::Unset); - - auto fractionalNumerator = - parseCSSValue("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 = - parseCSSValue("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 = - parseCSSValue("0"); - EXPECT_EQ(degenerateRatio.type(), CSSValueType::Number); - EXPECT_EQ(degenerateRatio.getNumber().value, 0.0f); + auto degenerateRatio = parseCSSProperty("0"); + EXPECT_TRUE(std::holds_alternative(degenerateRatio)); } TEST(CSSValueParser, angle_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); - auto degreeValue = parseCSSValue("10deg"); - EXPECT_EQ(degreeValue.type(), CSSValueType::Angle); - EXPECT_EQ(degreeValue.getAngle().degrees, 10.0f); + auto degreeValue = parseCSSProperty("10deg"); + EXPECT_TRUE(std::holds_alternative(degreeValue)); + EXPECT_EQ(std::get(degreeValue).degrees, 10.0f); - auto spongebobCaseValue = parseCSSValue("20dEg"); - EXPECT_EQ(spongebobCaseValue.type(), CSSValueType::Angle); - EXPECT_EQ(spongebobCaseValue.getAngle().degrees, 20.0f); + auto spongebobCaseValue = parseCSSProperty("20dEg"); + EXPECT_TRUE(std::holds_alternative(spongebobCaseValue)); + EXPECT_EQ(std::get(spongebobCaseValue).degrees, 20.0f); - auto radianValue = parseCSSValue("10rad"); - EXPECT_EQ(radianValue.type(), CSSValueType::Angle); - EXPECT_NEAR(radianValue.getAngle().degrees, 572.958f, 0.001f); + auto radianValue = parseCSSProperty("10rad"); + EXPECT_TRUE(std::holds_alternative(radianValue)); + ASSERT_NEAR(std::get(radianValue).degrees, 572.958f, 0.001f); - auto negativeRadianValue = parseCSSValue("-10rad"); - EXPECT_EQ(negativeRadianValue.type(), CSSValueType::Angle); - EXPECT_NEAR(negativeRadianValue.getAngle().degrees, -572.958f, 0.001f); + auto negativeRadianValue = parseCSSProperty("-10rad"); + EXPECT_TRUE(std::holds_alternative(negativeRadianValue)); + ASSERT_NEAR( + std::get(negativeRadianValue).degrees, -572.958f, 0.001f); - auto gradianValue = parseCSSValue("10grad"); - EXPECT_EQ(gradianValue.type(), CSSValueType::Angle); - ASSERT_NEAR(gradianValue.getAngle().degrees, 9.0f, 0.001f); + auto gradianValue = parseCSSProperty("10grad"); + EXPECT_TRUE(std::holds_alternative(gradianValue)); + ASSERT_NEAR(std::get(gradianValue).degrees, 9.0f, 0.001f); - auto turnValue = parseCSSValue(".25turn"); - EXPECT_EQ(turnValue.type(), CSSValueType::Angle); - EXPECT_EQ(turnValue.getAngle().degrees, 90.0f); + auto turnValue = parseCSSProperty(".25turn"); + EXPECT_TRUE(std::holds_alternative(turnValue)); + EXPECT_EQ(std::get(turnValue).degrees, 90.0f); } -TEST(CSSValueParser, parse_prop) { - auto emptyValue = parseCSSProp(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); +TEST(CSSValueParser, parse_constexpr) { + [[maybe_unused]] constexpr auto rowValue = + parseCSSProperty("row"); - 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(), CSSKeyword::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); - - auto keywordlessValue = parseCSSProp("50px"); - EXPECT_EQ(keywordlessValue.type(), CSSValueType::Length); - EXPECT_EQ(keywordlessValue.getLength().value, 50.0f); - EXPECT_EQ(keywordlessValue.getLength().unit, CSSLengthUnit::Px); -} - -TEST(CSSValueParser, parse_keyword_prop_constexpr) { - constexpr auto rowValue = parseCSSProp("row"); - EXPECT_EQ(rowValue.type(), CSSValueType::Keyword); - EXPECT_EQ(rowValue.getKeyword(), CSSKeyword::Row); -} - -TEST(CSSValueParser, parse_length_prop_constexpr) { - constexpr auto pxValue = parseCSSProp("2px"); - EXPECT_EQ(pxValue.type(), CSSValueType::Length); - EXPECT_EQ(pxValue.getLength().value, 2.0f); - EXPECT_EQ(pxValue.getLength().unit, CSSLengthUnit::Px); + [[maybe_unused]] constexpr auto pxValue = parseCSSProperty("2px"); } TEST(CSSValueParser, hex_color_values) { - auto emptyValue = parseCSSValue(""); - EXPECT_EQ(emptyValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ(emptyValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto emptyValue = parseCSSProperty(""); + EXPECT_TRUE(std::holds_alternative(emptyValue)); - auto hex3DigitColorValue = parseCSSValue("#fff"); - EXPECT_EQ(hex3DigitColorValue.type(), CSSValueType::Color); - EXPECT_EQ(hex3DigitColorValue.getColor().r, 255); - EXPECT_EQ(hex3DigitColorValue.getColor().g, 255); - EXPECT_EQ(hex3DigitColorValue.getColor().b, 255); - EXPECT_EQ(hex3DigitColorValue.getColor().a, 255); + auto hex3DigitColorValue = parseCSSProperty("#fff"); + EXPECT_TRUE(std::holds_alternative(hex3DigitColorValue)); + EXPECT_EQ(std::get(hex3DigitColorValue).r, 255); + EXPECT_EQ(std::get(hex3DigitColorValue).g, 255); + EXPECT_EQ(std::get(hex3DigitColorValue).b, 255); + EXPECT_EQ(std::get(hex3DigitColorValue).a, 255); - auto hex4DigitColorValue = parseCSSValue("#ffff"); - EXPECT_EQ(hex4DigitColorValue.type(), CSSValueType::Color); - EXPECT_EQ(hex4DigitColorValue.getColor().r, 255); - EXPECT_EQ(hex4DigitColorValue.getColor().g, 255); - EXPECT_EQ(hex4DigitColorValue.getColor().b, 255); - EXPECT_EQ(hex4DigitColorValue.getColor().a, 255); + auto hex4DigitColorValue = parseCSSProperty("#ffff"); + EXPECT_TRUE(std::holds_alternative(hex4DigitColorValue)); + EXPECT_EQ(std::get(hex4DigitColorValue).r, 255); + EXPECT_EQ(std::get(hex4DigitColorValue).g, 255); + EXPECT_EQ(std::get(hex4DigitColorValue).b, 255); + EXPECT_EQ(std::get(hex4DigitColorValue).a, 255); - auto hex6DigitColorValue = parseCSSValue("#ffffff"); - EXPECT_EQ(hex6DigitColorValue.type(), CSSValueType::Color); - EXPECT_EQ(hex6DigitColorValue.getColor().r, 255); - EXPECT_EQ(hex6DigitColorValue.getColor().g, 255); - EXPECT_EQ(hex6DigitColorValue.getColor().b, 255); - EXPECT_EQ(hex6DigitColorValue.getColor().a, 255); + auto hex6DigitColorValue = parseCSSProperty("#ffffff"); + EXPECT_TRUE(std::holds_alternative(hex6DigitColorValue)); + EXPECT_EQ(std::get(hex6DigitColorValue).r, 255); + EXPECT_EQ(std::get(hex6DigitColorValue).g, 255); + EXPECT_EQ(std::get(hex6DigitColorValue).b, 255); + EXPECT_EQ(std::get(hex6DigitColorValue).a, 255); - auto hex8DigitColorValue = - parseCSSValue("#ffffffff"); - EXPECT_EQ(hex8DigitColorValue.type(), CSSValueType::Color); - EXPECT_EQ(hex8DigitColorValue.getColor().r, 255); - EXPECT_EQ(hex8DigitColorValue.getColor().g, 255); - EXPECT_EQ(hex8DigitColorValue.getColor().b, 255); - EXPECT_EQ(hex8DigitColorValue.getColor().a, 255); + auto hex8DigitColorValue = parseCSSProperty("#ffffffff"); + EXPECT_TRUE(std::holds_alternative(hex8DigitColorValue)); + EXPECT_EQ(std::get(hex8DigitColorValue).r, 255); + EXPECT_EQ(std::get(hex8DigitColorValue).g, 255); + EXPECT_EQ(std::get(hex8DigitColorValue).b, 255); + EXPECT_EQ(std::get(hex8DigitColorValue).a, 255); - auto hexMixedCaseColorValue = - parseCSSValue("#FFCc99"); - EXPECT_EQ(hexMixedCaseColorValue.type(), CSSValueType::Color); - EXPECT_EQ(hexMixedCaseColorValue.getColor().r, 255); - EXPECT_EQ(hexMixedCaseColorValue.getColor().g, 204); - EXPECT_EQ(hexMixedCaseColorValue.getColor().b, 153); - EXPECT_EQ(hexMixedCaseColorValue.getColor().a, 255); + auto hexMixedCaseColorValue = parseCSSProperty("#FFCc99"); + EXPECT_TRUE(std::holds_alternative(hexMixedCaseColorValue)); + EXPECT_EQ(std::get(hexMixedCaseColorValue).r, 255); + EXPECT_EQ(std::get(hexMixedCaseColorValue).g, 204); + EXPECT_EQ(std::get(hexMixedCaseColorValue).b, 153); + EXPECT_EQ(std::get(hexMixedCaseColorValue).a, 255); - auto hexDigitOnlyColorValue = parseCSSValue("#369"); - EXPECT_EQ(hexDigitOnlyColorValue.type(), CSSValueType::Color); - EXPECT_EQ(hexDigitOnlyColorValue.getColor().r, 51); - EXPECT_EQ(hexDigitOnlyColorValue.getColor().g, 102); - EXPECT_EQ(hexDigitOnlyColorValue.getColor().b, 153); - EXPECT_EQ(hexDigitOnlyColorValue.getColor().a, 255); + auto hexDigitOnlyColorValue = parseCSSProperty("#369"); + EXPECT_TRUE(std::holds_alternative(hexDigitOnlyColorValue)); + EXPECT_EQ(std::get(hexDigitOnlyColorValue).r, 51); + EXPECT_EQ(std::get(hexDigitOnlyColorValue).g, 102); + EXPECT_EQ(std::get(hexDigitOnlyColorValue).b, 153); + EXPECT_EQ(std::get(hexDigitOnlyColorValue).a, 255); - auto hexAlphaTestValue = parseCSSValue("#FFFFFFCC"); - EXPECT_EQ(hexAlphaTestValue.type(), CSSValueType::Color); - EXPECT_EQ(hexAlphaTestValue.getColor().r, 255); - EXPECT_EQ(hexAlphaTestValue.getColor().g, 255); - EXPECT_EQ(hexAlphaTestValue.getColor().b, 255); - EXPECT_EQ(hexAlphaTestValue.getColor().a, 204); + auto hexAlphaTestValue = parseCSSProperty("#FFFFFFCC"); + EXPECT_TRUE(std::holds_alternative(hexAlphaTestValue)); + EXPECT_EQ(std::get(hexAlphaTestValue).r, 255); + EXPECT_EQ(std::get(hexAlphaTestValue).g, 255); + EXPECT_EQ(std::get(hexAlphaTestValue).b, 255); + EXPECT_EQ(std::get(hexAlphaTestValue).a, 204); } TEST(CSSValueParser, named_colors) { - auto invalidNamedColorTestValue = - parseCSSValue("redd"); - EXPECT_EQ(invalidNamedColorTestValue.type(), CSSValueType::CSSWideKeyword); - EXPECT_EQ( - invalidNamedColorTestValue.getCSSWideKeyword(), CSSWideKeyword::Unset); + auto invalidNamedColorTestValue = parseCSSProperty("redd"); + EXPECT_TRUE( + std::holds_alternative(invalidNamedColorTestValue)); - auto namedColorTestValue1 = parseCSSValue("red"); - EXPECT_EQ(namedColorTestValue1.type(), CSSValueType::Color); - EXPECT_EQ(namedColorTestValue1.getColor().r, 255); - EXPECT_EQ(namedColorTestValue1.getColor().g, 0); - EXPECT_EQ(namedColorTestValue1.getColor().b, 0); - EXPECT_EQ(namedColorTestValue1.getColor().a, 255); + auto namedColorTestValue1 = parseCSSProperty("red"); + EXPECT_TRUE(std::holds_alternative(namedColorTestValue1)); + EXPECT_EQ(std::get(namedColorTestValue1).r, 255); + EXPECT_EQ(std::get(namedColorTestValue1).g, 0); + EXPECT_EQ(std::get(namedColorTestValue1).b, 0); + EXPECT_EQ(std::get(namedColorTestValue1).a, 255); - auto namedColorTestValue2 = - parseCSSValue("cornsilk"); - EXPECT_EQ(namedColorTestValue2.type(), CSSValueType::Color); - EXPECT_EQ(namedColorTestValue2.getColor().r, 255); - EXPECT_EQ(namedColorTestValue2.getColor().g, 248); - EXPECT_EQ(namedColorTestValue2.getColor().b, 220); - EXPECT_EQ(namedColorTestValue2.getColor().a, 255); + auto namedColorTestValue2 = parseCSSProperty("cornsilk"); + EXPECT_TRUE(std::holds_alternative(namedColorTestValue2)); + EXPECT_EQ(std::get(namedColorTestValue2).r, 255); + EXPECT_EQ(std::get(namedColorTestValue2).g, 248); + EXPECT_EQ(std::get(namedColorTestValue2).b, 220); + EXPECT_EQ(std::get(namedColorTestValue2).a, 255); - auto namedColorMixedCaseTestValue = - parseCSSValue("sPrINgGrEEn"); - EXPECT_EQ(namedColorMixedCaseTestValue.type(), CSSValueType::Color); - EXPECT_EQ(namedColorMixedCaseTestValue.getColor().r, 0); - EXPECT_EQ(namedColorMixedCaseTestValue.getColor().g, 255); - EXPECT_EQ(namedColorMixedCaseTestValue.getColor().b, 127); - EXPECT_EQ(namedColorMixedCaseTestValue.getColor().a, 255); + auto namedColorMixedCaseTestValue = parseCSSProperty("sPrINgGrEEn"); + EXPECT_TRUE(std::holds_alternative(namedColorMixedCaseTestValue)); + EXPECT_EQ(std::get(namedColorMixedCaseTestValue).r, 0); + EXPECT_EQ(std::get(namedColorMixedCaseTestValue).g, 255); + EXPECT_EQ(std::get(namedColorMixedCaseTestValue).b, 127); + EXPECT_EQ(std::get(namedColorMixedCaseTestValue).a, 255); - auto transparentColor = - parseCSSValue("transparent"); - EXPECT_EQ(transparentColor.type(), CSSValueType::Color); - EXPECT_EQ(transparentColor.getColor().r, 0); - EXPECT_EQ(transparentColor.getColor().g, 0); - EXPECT_EQ(transparentColor.getColor().b, 0); - EXPECT_EQ(transparentColor.getColor().a, 0); + auto transparentColor = parseCSSProperty("transparent"); + EXPECT_TRUE(std::holds_alternative(transparentColor)); + EXPECT_EQ(std::get(transparentColor).r, 0); + EXPECT_EQ(std::get(transparentColor).g, 0); + EXPECT_EQ(std::get(transparentColor).b, 0); + EXPECT_EQ(std::get(transparentColor).a, 0); } } // namespace facebook::react