jiuyiUniapp/service/node_modules/react-native/ReactCommon/react/renderer/css/CSSValueParser.h

318 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <optional>
#include <type_traits>
#include <react/renderer/css/CSSAngleUnit.h>
#include <react/renderer/css/CSSColorUtils.h>
#include <react/renderer/css/CSSProperties.h>
#include <react/renderer/css/CSSSyntaxParser.h>
#include <react/renderer/css/CSSValueVariant.h>
namespace facebook::react {
namespace detail {
template <CSSDataType... AllowedTypesT>
class CSSValueParser {
using CSSValue = CSSValueVariant<AllowedTypesT...>;
public:
explicit constexpr CSSValueParser(std::string_view css) : parser_{css} {}
/*
* Attempts to parse the characters starting at the current component value
* into one of the given data types.
*/
constexpr CSSValue consumeValue(
CSSComponentValueDelimiter delimeter = CSSComponentValueDelimiter::None) {
return parser_.consumeComponentValue<CSSValue>(
delimeter, [&](const CSSPreservedToken& token) {
// CSS-global keywords
if constexpr (hasType<CSSWideKeyword>()) {
if (auto cssWideKeyword = consumeCSSWideKeyword(token)) {
return *cssWideKeyword;
}
}
// Property-specific keywords
if constexpr (hasType<typename CSSValue::Keyword>()) {
if (auto keyword = consumeKeyword(token)) {
return *keyword;
}
}
// <ratio>
if constexpr (hasType<CSSRatio>()) {
if (auto ratio = consumeRatio(token)) {
return *ratio;
}
}
// <number>
if constexpr (hasType<CSSNumber>()) {
if (auto number = consumeNumber(token)) {
return *number;
}
}
// <length>
if constexpr (hasType<CSSLength>()) {
if (auto length = consumeLength(token)) {
return *length;
}
}
// <angle>
if constexpr (hasType<CSSAngle>()) {
if (auto angle = consumeAngle(token)) {
return *angle;
}
}
// <percentage>
if constexpr (hasType<CSSPercentage>()) {
if (auto percentage = consumePercentage(token)) {
return *percentage;
}
}
// <color>
if constexpr (hasType<CSSColor>()) {
if (auto colorValue = consumeColorToken(token)) {
return *colorValue;
}
}
return CSSValue{};
});
// TODO: support function component values and simple blocks
}
constexpr bool isFinished() const {
return parser_.isFinished();
}
constexpr void consumeWhitespace() {
parser_.consumeWhitespace();
}
private:
template <CSSDataType T>
constexpr static bool hasType() {
return traits::containsType<T, AllowedTypesT...>();
}
template <typename T>
constexpr static bool hasType() {
return false;
}
constexpr std::optional<CSSValue> consumeKeyword(
const CSSPreservedToken& token) {
if (token.type() == CSSTokenType::Ident) {
if (auto keyword = parseCSSKeyword<typename CSSValue::Keyword>(
token.stringValue())) {
return CSSValue::keyword(*keyword);
}
}
return {};
}
constexpr std::optional<CSSValue> consumeCSSWideKeyword(
const CSSPreservedToken& token) {
if (token.type() == CSSTokenType::Ident) {
if (auto keyword = parseCSSKeyword<CSSWideKeyword>(token.stringValue())) {
return CSSValue::cssWideKeyword(*keyword);
}
}
return {};
}
constexpr std::optional<CSSValue> 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<CSSValue> consumePercentage(
const CSSPreservedToken& token) {
if (token.type() == CSSTokenType::Percentage) {
return CSSValue::percentage(token.numericValue());
}
return {};
}
constexpr std::optional<CSSValue> consumeNumber(
const CSSPreservedToken& token) {
if (token.type() == CSSTokenType::Number) {
return CSSValue::number(token.numericValue());
}
return {};
}
constexpr std::optional<CSSValue> 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 <number> 0). However, if a 0
// could be parsed as either a <number> or a <length> in a
// property (such as line-height), it must parse as a <number>.
// 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<CSSValue> consumeRatio(
const CSSPreservedToken& token) {
// <ratio> = <number [0,∞]> [ / <number [0,∞]> ]?
// https://www.w3.org/TR/css-values-4/#ratio
if (isValidRatioPart(token.numericValue())) {
float numerator = token.numericValue();
CSSSyntaxParser lookaheadParser{parser_};
auto hasSolidus = lookaheadParser.consumeComponentValue<bool>(
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 <number>
auto denominator =
lookaheadParser.consumeComponentValue<std::optional<float>>(
CSSComponentValueDelimiter::Whitespace,
[&](const CSSPreservedToken& token) {
if (token.type() == CSSTokenType::Number &&
isValidRatioPart(token.numericValue())) {
return std::optional(token.numericValue());
}
return std::optional<float>{};
});
if (denominator.has_value()) {
parser_ = lookaheadParser;
return CSSValue::ratio(numerator, *denominator);
}
}
return {};
}
constexpr bool isValidRatioPart(float value) {
// If either number in the <ratio> is 0 or infinite, it represents a
// degenerate ratio (and, generally, wont do anything).
// https://www.w3.org/TR/css-values-4/#ratios
return value > 0.0f && value != +std::numeric_limits<float>::infinity() &&
value != -std::numeric_limits<float>::infinity();
}
constexpr std::optional<CSSValue> consumeColorToken(
const CSSPreservedToken& token) {
if (token.type() == CSSTokenType::Ident) {
return parseCSSNamedColor<CSSValue>(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));
}
}
return {};
}
CSSSyntaxParser parser_;
};
template <CSSDataType... AllowedTypesT>
constexpr void parseCSSValue(
std::string_view css,
CSSValueVariant<AllowedTypesT...>& value) {
detail::CSSValueParser<AllowedTypesT...> parser(css);
parser.consumeWhitespace();
auto componentValue = parser.consumeValue();
parser.consumeWhitespace();
if (parser.isFinished()) {
value = std::move(componentValue);
} else {
value = {};
}
};
} // namespace detail
/**
* Parse a single CSS value. Returns a default-constructed
* CSSValueVariant (CSSKeyword::Unset) on syntax error.
*/
template <CSSDataType... AllowedTypesT>
CSSValueVariant<AllowedTypesT...> parseCSSValue(std::string_view css) {
CSSValueVariant<AllowedTypesT...> value;
detail::parseCSSValue<AllowedTypesT...>(css, value);
return value;
};
/**
* Parses a CSS property into its declared value type.
*/
template <CSSProp Prop>
constexpr CSSDeclaredValue<Prop> parseCSSProp(std::string_view css) {
// For now we only allow parsing props composed of a single component value.
CSSDeclaredValue<Prop> value;
detail::parseCSSValue(css, value);
return value;
}
} // namespace facebook::react