Skip to content

Commit 1ca0d23

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Restrict CSSDataTypeParser function and simple block parsing to block scope (#48768)
Summary: Right now, if something like `CSSColor` accepted a function notation block (e.g. for `rgb()` syntax), it is given a syntax parser which may extend beyond the scope of the current function block. This is confusing, but also problematic for the `CSSSyntaxParser` block visiting logic to validate a correct scope exit. This change makes it so that the `CSSSyntaxParser` passsed to `CSSDataTypeParser` for function and simple blocks is limited to the syntax within the given block. This prevents extra visiblity beyond the block we are trying to parse, and allows the function/simple block visitor to reliably fail parsing if the block is not correctly terminated, or there are unconsumed component values within the block not handled by the data type parser. Changelog: [Internal] Differential Revision: D68340127
1 parent 135277a commit 1ca0d23

File tree

3 files changed

+169
-69
lines changed

3 files changed

+169
-69
lines changed

packages/react-native/ReactCommon/react/renderer/css/CSSSyntaxParser.h

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
namespace facebook::react {
1717

18+
class CSSSyntaxParser;
19+
1820
/**
1921
* Describes context for a CSS function component value.
2022
*/
@@ -41,9 +43,10 @@ struct CSSSimpleBlock {
4143
* of the function block.
4244
*/
4345
template <typename T, typename ReturnT>
44-
concept CSSFunctionVisitor = requires(T visitor, CSSFunctionBlock func) {
45-
{ visitor(func) } -> std::convertible_to<ReturnT>;
46-
};
46+
concept CSSFunctionVisitor =
47+
requires(T visitor, CSSFunctionBlock func, CSSSyntaxParser& blockParser) {
48+
{ visitor(func, blockParser) } -> std::convertible_to<ReturnT>;
49+
};
4750

4851
/**
4952
* A CSSPreservedTokenVisitor is called after parsing a preserved token
@@ -61,9 +64,10 @@ concept CSSPreservedTokenVisitor =
6164
* of the block.
6265
*/
6366
template <typename T, typename ReturnT>
64-
concept CSSSimpleBlockVisitor = requires(T visitor, CSSSimpleBlock block) {
65-
{ visitor(block) } -> std::convertible_to<ReturnT>;
66-
};
67+
concept CSSSimpleBlockVisitor =
68+
requires(T visitor, CSSSimpleBlock block, CSSSyntaxParser& blockParser) {
69+
{ visitor(block, blockParser) } -> std::convertible_to<ReturnT>;
70+
};
6771

6872
/**
6973
* Any visitor for a component value.
@@ -164,6 +168,11 @@ class CSSSyntaxParser {
164168
}
165169

166170
private:
171+
CSSSyntaxParser(CSSSyntaxParser& parser, CSSTokenType terminator)
172+
: tokenizer_{parser.tokenizer_},
173+
currentToken_{parser.currentToken_},
174+
terminator_{terminator} {}
175+
167176
constexpr const CSSToken& peek() const {
168177
return currentToken_;
169178
}
@@ -174,8 +183,14 @@ class CSSSyntaxParser {
174183
return prevToken;
175184
}
176185

186+
constexpr void advanceToBlockParser(CSSSyntaxParser& blockParser) {
187+
currentToken_ = blockParser.currentToken_;
188+
tokenizer_ = blockParser.tokenizer_;
189+
}
190+
177191
CSSTokenizer tokenizer_;
178192
CSSToken currentToken_;
193+
CSSTokenType terminator_{CSSTokenType::EndOfFile};
179194
};
180195

181196
template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
@@ -201,6 +216,10 @@ struct CSSComponentValueVisitorDispatcher {
201216
break;
202217
}
203218

219+
if (parser.peek().type() == parser.terminator_) {
220+
return {};
221+
}
222+
204223
switch (parser.peek().type()) {
205224
case CSSTokenType::Function:
206225
if (auto ret = visitFunction(visitors...)) {
@@ -235,28 +254,22 @@ struct CSSComponentValueVisitorDispatcher {
235254
return ReturnT{};
236255
}
237256

238-
constexpr ReturnT consumeNextCommaDelimitedValue(
239-
const VisitorsT&... visitors) {
240-
parser.consumeWhitespace();
241-
if (parser.consumeToken().type() != CSSTokenType::Comma) {
242-
return {};
243-
}
244-
parser.consumeWhitespace();
245-
return consumeComponentValue(std::forward<VisitorsT>(visitors)...);
246-
}
247-
248-
constexpr ReturnT consumeNextWhitespaceDelimitedValue(
249-
const VisitorsT&... visitors) {
250-
parser.consumeWhitespace();
251-
return consumeComponentValue(std::forward<VisitorsT>(visitors)...);
252-
}
253-
254257
constexpr std::optional<ReturnT> visitFunction(
255258
const CSSComponentValueVisitor<ReturnT> auto& visitor,
256259
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
257260
if constexpr (CSSFunctionVisitor<decltype(visitor), ReturnT>) {
258-
auto functionValue =
259-
visitor({.name = parser.consumeToken().stringValue()});
261+
auto name = parser.consumeToken().stringValue();
262+
263+
// CSS syntax spec says whitespace is a preserved token, but CSS values
264+
// spec allows whitespace around parens for all function notation, so we
265+
// allow this to let the visitors not need to deal with leading/trailing
266+
// whitespace. https://www.w3.org/TR/css-values-3/#functional-notations
267+
parser.consumeWhitespace();
268+
269+
auto blockParser =
270+
CSSSyntaxParser{parser, CSSTokenType::CloseParen /*terminator*/};
271+
auto functionValue = visitor({name}, blockParser);
272+
parser.advanceToBlockParser(blockParser);
260273
parser.consumeWhitespace();
261274
if (parser.peek().type() == CSSTokenType::CloseParen) {
262275
parser.consumeToken();
@@ -273,16 +286,16 @@ struct CSSComponentValueVisitorDispatcher {
273286
return {};
274287
}
275288

276-
// Can be one of std::monostate (variant null-type), CSSWideKeyword,
277-
// CSSLength, or CSSPercentage
278-
279289
constexpr std::optional<ReturnT> visitSimpleBlock(
280290
CSSTokenType endToken,
281291
const CSSComponentValueVisitor<ReturnT> auto& visitor,
282292
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
283293
if constexpr (CSSSimpleBlockVisitor<decltype(visitor), ReturnT>) {
284-
auto blockValue =
285-
visitor({.openBracketType = parser.consumeToken().type()});
294+
auto openBracketType = parser.consumeToken().type();
295+
parser.consumeWhitespace();
296+
auto blockParser = CSSSyntaxParser{parser, endToken};
297+
auto blockValue = visitor({openBracketType}, blockParser);
298+
parser.advanceToBlockParser(blockParser);
286299
parser.consumeWhitespace();
287300
if (parser.peek().type() == endToken) {
288301
parser.consumeToken();

packages/react-native/ReactCommon/react/renderer/css/CSSValueParser.h

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ class CSSValueParser {
4242
ReturnT,
4343
CSSDataTypeParser<AllowedTypesT>...>(token);
4444
},
45-
[&](const CSSSimpleBlock& block) {
45+
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
4646
return tryConsumeSimpleBlock<
4747
ReturnT,
48-
CSSDataTypeParser<AllowedTypesT>...>(block);
48+
CSSDataTypeParser<AllowedTypesT>...>(block, blockParser);
4949
},
50-
[&](const CSSFunctionBlock& func) {
50+
[&](const CSSFunctionBlock& func, CSSSyntaxParser& blockParser) {
5151
return tryConsumeFunctionBlock<
5252
ReturnT,
53-
CSSDataTypeParser<AllowedTypesT>...>(func);
53+
CSSDataTypeParser<AllowedTypesT>...>(func, blockParser);
5454
});
5555
}
5656

@@ -90,41 +90,49 @@ class CSSValueParser {
9090
}
9191

9292
template <typename ReturnT>
93-
constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& /*token*/) {
93+
constexpr ReturnT tryConsumeSimpleBlock(
94+
const CSSSimpleBlock& /*token*/,
95+
CSSSyntaxParser& /*blockParser*/) {
9496
return {};
9597
}
9698

9799
template <
98100
typename ReturnT,
99101
CSSValidDataTypeParser ParserT,
100102
CSSValidDataTypeParser... RestParserT>
101-
constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& block) {
103+
constexpr ReturnT tryConsumeSimpleBlock(
104+
const CSSSimpleBlock& block,
105+
CSSSyntaxParser& blockParser) {
102106
if constexpr (CSSSimpleBlockSink<ParserT>) {
103-
if (auto ret = ParserT::consumeSimpleBlock(block, parser_)) {
107+
if (auto ret = ParserT::consumeSimpleBlock(block, blockParser)) {
104108
return *ret;
105109
}
106110
}
107111

108-
return tryConsumeSimpleBlock<ReturnT, RestParserT...>(block);
112+
return tryConsumeSimpleBlock<ReturnT, RestParserT...>(block, blockParser);
109113
}
110114

111115
template <typename ReturnT>
112-
constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& /*func*/) {
116+
constexpr ReturnT tryConsumeFunctionBlock(
117+
const CSSFunctionBlock& /*func*/,
118+
CSSSyntaxParser& /*blockParser*/) {
113119
return {};
114120
}
115121

116122
template <
117123
typename ReturnT,
118124
CSSValidDataTypeParser ParserT,
119125
CSSValidDataTypeParser... RestParserT>
120-
constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& func) {
126+
constexpr ReturnT tryConsumeFunctionBlock(
127+
const CSSFunctionBlock& func,
128+
CSSSyntaxParser& blockParser) {
121129
if constexpr (CSSFunctionBlockSink<ParserT>) {
122-
if (auto ret = ParserT::consumeFunctionBlock(func, parser_)) {
130+
if (auto ret = ParserT::consumeFunctionBlock(func, blockParser)) {
123131
return *ret;
124132
}
125133
}
126134

127-
return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func);
135+
return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func, blockParser);
128136
}
129137

130138
CSSSyntaxParser& parser_;

0 commit comments

Comments
 (0)