1717#include < react/renderer/css/CSSPercentage.h>
1818#include < react/renderer/css/CSSSyntaxParser.h>
1919#include < react/renderer/css/CSSValueParser.h>
20+ #include < react/utils/PackTraits.h>
2021#include < react/utils/fnv1a.h>
2122
2223namespace facebook ::react {
@@ -33,72 +34,157 @@ constexpr uint8_t clamp255Component(float f) {
3334 return static_cast <uint8_t >(std::clamp (ceiled, 0 , 255 ));
3435}
3536
37+ constexpr std::optional<float > normalizeNumberComponent (
38+ const std::variant<std::monostate, CSSNumber>& component) {
39+ if (std::holds_alternative<CSSNumber>(component)) {
40+ return std::get<CSSNumber>(component).value ;
41+ }
42+
43+ return {};
44+ }
45+
46+ template <typename ... ComponentT>
47+ requires (
48+ (std::is_same_v<CSSNumber, ComponentT> ||
49+ std::is_same_v<CSSPercentage, ComponentT>) &&
50+ ...)
51+ constexpr std::optional<float > normalizeComponent (
52+ const std::variant<std::monostate, ComponentT...>& component,
53+ float baseValue) {
54+ if constexpr (traits::containsType<CSSPercentage, ComponentT...>()) {
55+ if (std::holds_alternative<CSSPercentage>(component)) {
56+ return std::get<CSSPercentage>(component).value / 100 .0f * baseValue;
57+ }
58+ }
59+
60+ if constexpr (traits::containsType<CSSNumber, ComponentT...>()) {
61+ if (std::holds_alternative<CSSNumber>(component)) {
62+ return std::get<CSSNumber>(component).value ;
63+ }
64+ }
65+
66+ return {};
67+ }
68+
69+ template <CSSDataType... FirstComponentAllowedTypesT>
70+ constexpr bool isLegacyColorFunction (CSSSyntaxParser& parser) {
71+ auto lookahead = parser;
72+ auto next = parseNextCSSValue<FirstComponentAllowedTypesT...>(lookahead);
73+ if (std::holds_alternative<std::monostate>(next)) {
74+ return false ;
75+ }
76+
77+ return lookahead.consumeComponentValue <bool >(
78+ CSSDelimiter::OptionalWhitespace, [](CSSPreservedToken token) {
79+ return token.type () == CSSTokenType::Comma;
80+ });
81+ }
82+
3683/* *
37- * Parses an rgb() or rgba() function and returns a CSSColor if it is valid.
38- * Some invalid syntax (like mixing commas and whitespace) are allowed for
39- * backwards compatibility with normalize-color.
40- * https://www.w3.org/TR/css-color-4/#funcdef-rgb
84+ * Parses a legacy syntax rgb() or rgba() function and returns a CSSColor if it
85+ * is valid.
86+ * https://www.w3.org/TR/css-color-4/#typedef-legacy-rgb-syntax
4187 */
4288template <typename CSSColor>
43- constexpr std::optional<CSSColor> parseRgbFunction (CSSSyntaxParser& parser) {
44- auto firstValue = parseNextCSSValue<CSSNumber, CSSPercentage>(parser);
45- if (std::holds_alternative<std::monostate>(firstValue)) {
89+ constexpr std::optional<CSSColor> parseLegacyRgbFunction (
90+ CSSSyntaxParser& parser) {
91+ auto rawRed = parseNextCSSValue<CSSNumber, CSSPercentage>(parser);
92+ bool usesNumber = std::holds_alternative<CSSNumber>(rawRed);
93+
94+ auto red = normalizeComponent (rawRed, 255 .0f );
95+ if (!red.has_value ()) {
4696 return {};
4797 }
4898
49- float redNumber = 0 ;
50- float greenNumber = 0 ;
51- float blueNumber = 0 ;
99+ auto green = usesNumber
100+ ? normalizeNumberComponent (
101+ parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::Comma))
102+ : normalizeComponent (
103+ parseNextCSSValue<CSSPercentage>(parser, CSSDelimiter::Comma),
104+ 255 .0f );
105+ if (!green.has_value ()) {
106+ return {};
107+ }
52108
53- if (std::holds_alternative<CSSNumber>(firstValue)) {
54- redNumber = std::get<CSSNumber>(firstValue).value ;
109+ auto blue = usesNumber
110+ ? normalizeNumberComponent (
111+ parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::Comma))
112+ : normalizeComponent (
113+ parseNextCSSValue<CSSPercentage>(parser, CSSDelimiter::Comma),
114+ 255 .0f );
115+ if (!blue.has_value ()) {
116+ return {};
117+ }
55118
56- auto green =
57- parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::CommaOrWhitespace);
58- if (!std::holds_alternative<CSSNumber>(green)) {
59- return {};
60- }
61- greenNumber = std::get<CSSNumber>(green).value ;
119+ auto alpha = normalizeComponent (
120+ parseNextCSSValue<CSSNumber, CSSPercentage>(parser, CSSDelimiter::Comma),
121+ 1 .0f );
62122
63- auto blue =
64- parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::CommaOrWhitespace);
65- if (!std::holds_alternative<CSSNumber>(blue)) {
66- return {};
67- }
68- blueNumber = std::get<CSSNumber>(blue). value ;
69- } else {
70- redNumber = std::get<CSSPercentage>(firstValue). value * 2 . 55f ;
123+ return CSSColor{
124+ . r = clamp255Component (*red),
125+ . g = clamp255Component (*green),
126+ . b = clamp255Component (*blue),
127+ . a = alpha. has_value () ? clamp255Component (*alpha * 255 . 0f )
128+ : static_cast < uint8_t >( 255u ),
129+ };
130+ }
71131
72- auto green = parseNextCSSValue<CSSPercentage>(
73- parser, CSSDelimiter::CommaOrWhitespace);
74- if (!std::holds_alternative<CSSPercentage>(green)) {
75- return {};
76- }
77- greenNumber = std::get<CSSPercentage>(green).value * 2 .55f ;
132+ /* *
133+ * Parses a modern syntax rgb() or rgba() function and returns a CSSColor if it
134+ * is valid.
135+ * https://www.w3.org/TR/css-color-4/#typedef-modern-rgb-syntax
136+ */
137+ template <typename CSSColor>
138+ constexpr std::optional<CSSColor> parseModernRgbFunction (
139+ CSSSyntaxParser& parser) {
140+ auto red = normalizeComponent (
141+ parseNextCSSValue<CSSNumber, CSSPercentage>(parser), 255 .0f );
142+ if (!red.has_value ()) {
143+ return {};
144+ }
78145
79- auto blue = parseNextCSSValue<CSSPercentage> (
80- parser, CSSDelimiter::CommaOrWhitespace);
81- if (!std::holds_alternative<CSSPercentage>(blue)) {
82- return {} ;
83- }
84- blueNumber = std::get<CSSPercentage>(blue). value * 2 . 55f ;
146+ auto green = normalizeComponent (
147+ parseNextCSSValue<CSSNumber, CSSPercentage>(
148+ parser, CSSDelimiter::Whitespace),
149+ 255 . 0f ) ;
150+ if (!green. has_value ()) {
151+ return {} ;
85152 }
86153
87- auto alphaValue = parseNextCSSValue<CSSNumber, CSSPercentage>(
88- parser, CSSDelimiter::CommaOrWhitespaceOrSolidus);
154+ auto blue = normalizeComponent (
155+ parseNextCSSValue<CSSNumber, CSSPercentage>(
156+ parser, CSSDelimiter::Whitespace),
157+ 255 .0f );
158+ if (!blue.has_value ()) {
159+ return {};
160+ }
89161
90- float alphaNumber = std::holds_alternative<std::monostate>(alphaValue) ? 1 . 0f
91- : std::holds_alternative <CSSNumber>(alphaValue)
92- ? std::get<CSSNumber>(alphaValue). value
93- : std::get<CSSPercentage>(alphaValue). value / 100 . 0f ;
162+ auto alpha = normalizeComponent (
163+ parseNextCSSValue <CSSNumber, CSSPercentage>(
164+ parser, CSSDelimiter::SolidusOrWhitespace),
165+ 1 . 0f ) ;
94166
95167 return CSSColor{
96- .r = clamp255Component (redNumber),
97- .g = clamp255Component (greenNumber),
98- .b = clamp255Component (blueNumber),
99- .a = clamp255Component (alphaNumber * 255 .0f ),
168+ .r = clamp255Component (*red),
169+ .g = clamp255Component (*green),
170+ .b = clamp255Component (*blue),
171+ .a = alpha.has_value () ? clamp255Component (*alpha * 255 .0f )
172+ : static_cast <uint8_t >(255u ),
100173 };
101174}
175+
176+ /* *
177+ * Parses an rgb() or rgba() function and returns a CSSColor if it is valid.
178+ * https://www.w3.org/TR/css-color-4/#funcdef-rgb
179+ */
180+ template <typename CSSColor>
181+ constexpr std::optional<CSSColor> parseRgbFunction (CSSSyntaxParser& parser) {
182+ if (isLegacyColorFunction<CSSNumber, CSSPercentage>(parser)) {
183+ return parseLegacyRgbFunction<CSSColor>(parser);
184+ } else {
185+ return parseModernRgbFunction<CSSColor>(parser);
186+ }
187+ }
102188} // namespace detail
103189
104190/* *
0 commit comments