From 8bd01c7d0199b5adcc61cb9e1d6a9224aed034b3 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 7 Feb 2025 18:14:42 -0800 Subject: [PATCH] Fix incorrect tokenization of non-exponential numbers ending with "E" (#49280) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49280 We were incorrectly consuming an `E` at the end of number tokens, even if not followed by a digit, which breaks dimension tokens where the unit starts with "E", like `em`. Follow the spec the right way: https://www.w3.org/TR/css-syntax-3/#consume-number > If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+), followed by a digit, then... Changelog: [Internal] Reviewed By: joevilches Differential Revision: D69330975 fbshipit-source-id: a9bd5bceac9efbf02c1b7fb60659093774bb7228 --- .../ReactCommon/react/renderer/css/CSSTokenizer.h | 4 +++- .../ReactCommon/react/renderer/css/tests/CSSLengthTest.cpp | 5 +++++ .../react/renderer/css/tests/CSSTokenizerTest.cpp | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSTokenizer.h b/packages/react-native/ReactCommon/react/renderer/css/CSSTokenizer.h index c5e28c75e6e..487a09cf06e 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSTokenizer.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSTokenizer.h @@ -163,7 +163,9 @@ class CSSTokenizer { int32_t exponentSign = 1.0; int32_t exponentPart = 0; - if (peek() == 'e' || peek() == 'E') { + if ((peek() == 'e' || peek() == 'E') && + (isDigit(peek(1)) || + ((peek(1) == '+' || peek(1) == '-') && isDigit(peek(2))))) { advance(); if (peek() == '+' || peek() == '-') { if (peek() == '-') { diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSLengthTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSLengthTest.cpp index c991ba5197b..d7346c5ffc2 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSLengthTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSLengthTest.cpp @@ -44,6 +44,11 @@ TEST(CSSLength, length_values) { auto pctValue = parseCSSProperty("-40%"); EXPECT_TRUE(std::holds_alternative(pctValue)); + + auto negativeValue = parseCSSProperty("-20em"); + EXPECT_TRUE(std::holds_alternative(negativeValue)); + EXPECT_EQ(std::get(negativeValue).value, -20.0f); + EXPECT_EQ(std::get(negativeValue).unit, CSSLengthUnit::Em); } TEST(CSSLength, parse_constexpr) { diff --git a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSTokenizerTest.cpp b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSTokenizerTest.cpp index 3d44b58f9f4..cfcc7b59c97 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/tests/CSSTokenizerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/css/tests/CSSTokenizerTest.cpp @@ -112,6 +112,11 @@ TEST(CSSTokenizer, dimension_values) { ".3xyz", CSSToken{CSSTokenType::Dimension, 0.3, "xyz"}, CSSToken{CSSTokenType::EndOfFile}); + + EXPECT_TOKENS( + "-0.5em", + CSSToken{CSSTokenType::Dimension, -0.5, "em"}, + CSSToken{CSSTokenType::EndOfFile}); } TEST(CSSTokenizer, percent_values) {