17
17
#include < react/renderer/css/CSSPercentage.h>
18
18
#include < react/renderer/css/CSSSyntaxParser.h>
19
19
#include < react/renderer/css/CSSValueParser.h>
20
+ #include < react/utils/PackTraits.h>
20
21
#include < react/utils/fnv1a.h>
21
22
22
23
namespace facebook ::react {
@@ -33,72 +34,157 @@ constexpr uint8_t clamp255Component(float f) {
33
34
return static_cast <uint8_t >(std::clamp (ceiled, 0 , 255 ));
34
35
}
35
36
37
+ constexpr std::optional<float > normNumberComponent (
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 > normComponent (
52
+ const std::variant<std::monostate, ComponentT...>& component,
53
+ float fullPercentage) {
54
+ if constexpr (traits::containsType<CSSPercentage, ComponentT...>()) {
55
+ if (std::holds_alternative<CSSPercentage>(component)) {
56
+ return std::get<CSSPercentage>(component).value / 100 .0f * fullPercentage;
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.peekComponentValue <bool >(
78
+ CSSDelimiter::OptionalWhitespace, [](CSSPreservedToken token) {
79
+ return token.type () == CSSTokenType::Comma;
80
+ });
81
+ }
82
+
36
83
/* *
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
41
87
*/
42
88
template <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 = normComponent (rawRed, 255 .0f );
95
+ if (!red.has_value ()) {
46
96
return {};
47
97
}
48
98
49
- float redNumber = 0 ;
50
- float greenNumber = 0 ;
51
- float blueNumber = 0 ;
99
+ auto green = usesNumber
100
+ ? normNumberComponent (
101
+ parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::Comma))
102
+ : normComponent (
103
+ parseNextCSSValue<CSSPercentage>(parser, CSSDelimiter::Comma),
104
+ 255 .0f );
105
+ if (!green.has_value ()) {
106
+ return {};
107
+ }
52
108
53
- if (std::holds_alternative<CSSNumber>(firstValue)) {
54
- redNumber = std::get<CSSNumber>(firstValue).value ;
109
+ auto blue = usesNumber
110
+ ? normNumberComponent (
111
+ parseNextCSSValue<CSSNumber>(parser, CSSDelimiter::Comma))
112
+ : normComponent (
113
+ parseNextCSSValue<CSSPercentage>(parser, CSSDelimiter::Comma),
114
+ 255 .0f );
115
+ if (!blue.has_value ()) {
116
+ return {};
117
+ }
55
118
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 = normComponent (
120
+ parseNextCSSValue<CSSNumber, CSSPercentage>(parser, CSSDelimiter::Comma),
121
+ 1 .0f );
62
122
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
+ }
71
131
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 = normComponent (
141
+ parseNextCSSValue<CSSNumber, CSSPercentage>(parser), 255 .0f );
142
+ if (!red.has_value ()) {
143
+ return {};
144
+ }
78
145
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 = normComponent (
147
+ parseNextCSSValue<CSSNumber, CSSPercentage>(
148
+ parser, CSSDelimiter::Whitespace),
149
+ 255 . 0f ) ;
150
+ if (!green. has_value ()) {
151
+ return {} ;
85
152
}
86
153
87
- auto alphaValue = parseNextCSSValue<CSSNumber, CSSPercentage>(
88
- parser, CSSDelimiter::CommaOrWhitespaceOrSolidus);
154
+ auto blue = normComponent (
155
+ parseNextCSSValue<CSSNumber, CSSPercentage>(
156
+ parser, CSSDelimiter::Whitespace),
157
+ 255 .0f );
158
+ if (!blue.has_value ()) {
159
+ return {};
160
+ }
89
161
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 = normComponent (
163
+ parseNextCSSValue <CSSNumber, CSSPercentage>(
164
+ parser, CSSDelimiter::SolidusOrWhitespace),
165
+ 1 . 0f ) ;
94
166
95
167
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 ),
100
173
};
101
174
}
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
+ }
102
188
} // namespace detail
103
189
104
190
/* *
0 commit comments