mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibWeb: Implement CSS::parse_css_value()
A lot of this is not spec-compliant and copied from the old parser. In future PRs, we can revise it.
This commit is contained in:
parent
c91c00f587
commit
6b6bf4a0aa
Notes:
sideshowbarker
2024-07-18 09:13:45 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/6b6bf4a0aa7 Pull-request: https://github.com/SerenityOS/serenity/pull/8341
3 changed files with 224 additions and 5 deletions
|
@ -1084,10 +1084,222 @@ RefPtr<CSSStyleDeclaration> Parser::convert_to_declaration(NonnullRefPtr<StyleBl
|
|||
return CSSStyleDeclaration::create(move(properties), move(custom_properties));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RefPtr<StyleValue> Parser::parse_css_value(PropertyID, TokenStream<T>&)
|
||||
Optional<float> Parser::try_parse_float(StringView string)
|
||||
{
|
||||
// FIXME: This is mostly copied from the old, deprecated parser. It may or may not be to spec.
|
||||
// FIXME: This is copied from DeprecatedCSSParser, so may not be to spec.
|
||||
|
||||
const char* str = string.characters_without_null_termination();
|
||||
size_t len = string.length();
|
||||
size_t weight = 1;
|
||||
int exp_val = 0;
|
||||
float value = 0.0f;
|
||||
float fraction = 0.0f;
|
||||
bool has_sign = false;
|
||||
bool is_negative = false;
|
||||
bool is_fractional = false;
|
||||
bool is_scientific = false;
|
||||
|
||||
if (str[0] == '-') {
|
||||
is_negative = true;
|
||||
has_sign = true;
|
||||
}
|
||||
if (str[0] == '+') {
|
||||
has_sign = true;
|
||||
}
|
||||
|
||||
for (size_t i = has_sign; i < len; i++) {
|
||||
|
||||
// Looks like we're about to start working on the fractional part
|
||||
if (str[i] == '.') {
|
||||
is_fractional = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str[i] == 'e' || str[i] == 'E') {
|
||||
if (str[i + 1] == '-' || str[i + 1] == '+')
|
||||
exp_val = atoi(str + i + 2);
|
||||
else
|
||||
exp_val = atoi(str + i + 1);
|
||||
|
||||
is_scientific = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (str[i] < '0' || str[i] > '9' || exp_val != 0) {
|
||||
return {};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_fractional) {
|
||||
fraction *= 10;
|
||||
fraction += str[i] - '0';
|
||||
weight *= 10;
|
||||
} else {
|
||||
value = value * 10;
|
||||
value += str[i] - '0';
|
||||
}
|
||||
}
|
||||
|
||||
fraction /= weight;
|
||||
value += fraction;
|
||||
|
||||
if (is_scientific) {
|
||||
bool divide = exp_val < 0;
|
||||
if (divide)
|
||||
exp_val *= -1;
|
||||
|
||||
for (int i = 0; i < exp_val; i++) {
|
||||
if (divide)
|
||||
value /= 10;
|
||||
else
|
||||
value *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
return is_negative ? -value : value;
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_css_value(PropertyID property_id, TokenStream<StyleComponentValueRule>& tokens)
|
||||
{
|
||||
// FIXME: This is mostly copied from the old, deprecated parser. It is probably not to spec.
|
||||
|
||||
auto takes_integer_value = [](PropertyID property_id) -> bool {
|
||||
return property_id == PropertyID::ZIndex
|
||||
|| property_id == PropertyID::FontWeight
|
||||
|| property_id == PropertyID::Custom;
|
||||
};
|
||||
|
||||
auto parse_length = [&]() -> Optional<Length> {
|
||||
Length::Type type = Length::Type::Undefined;
|
||||
Optional<float> numeric_value;
|
||||
|
||||
auto token = tokens.next_token();
|
||||
|
||||
if (token.is(Token::Type::Dimension)) {
|
||||
auto length_string = token.token().m_value.string_view();
|
||||
auto unit_string = token.token().m_unit.string_view();
|
||||
|
||||
if (unit_string.equals_ignoring_case("%")) {
|
||||
type = Length::Type::Percentage;
|
||||
} else if (unit_string.equals_ignoring_case("px")) {
|
||||
type = Length::Type::Px;
|
||||
} else if (unit_string.equals_ignoring_case("pt")) {
|
||||
type = Length::Type::Pt;
|
||||
} else if (unit_string.equals_ignoring_case("pc")) {
|
||||
type = Length::Type::Pc;
|
||||
} else if (unit_string.equals_ignoring_case("mm")) {
|
||||
type = Length::Type::Mm;
|
||||
} else if (unit_string.equals_ignoring_case("rem")) {
|
||||
type = Length::Type::Rem;
|
||||
} else if (unit_string.equals_ignoring_case("em")) {
|
||||
type = Length::Type::Em;
|
||||
} else if (unit_string.equals_ignoring_case("ex")) {
|
||||
type = Length::Type::Ex;
|
||||
} else if (unit_string.equals_ignoring_case("vw")) {
|
||||
type = Length::Type::Vw;
|
||||
} else if (unit_string.equals_ignoring_case("vh")) {
|
||||
type = Length::Type::Vh;
|
||||
} else if (unit_string.equals_ignoring_case("vmax")) {
|
||||
type = Length::Type::Vmax;
|
||||
} else if (unit_string.equals_ignoring_case("vmin")) {
|
||||
type = Length::Type::Vmin;
|
||||
} else if (unit_string.equals_ignoring_case("cm")) {
|
||||
type = Length::Type::Cm;
|
||||
} else if (unit_string.equals_ignoring_case("in")) {
|
||||
type = Length::Type::In;
|
||||
} else if (unit_string.equals_ignoring_case("Q")) {
|
||||
type = Length::Type::Q;
|
||||
} else if (m_context.in_quirks_mode()) {
|
||||
type = Length::Type::Px;
|
||||
}
|
||||
|
||||
numeric_value = try_parse_float(length_string);
|
||||
} else if (token.is(Token::Type::Number)) {
|
||||
auto value_string = token.token().m_value.string_view();
|
||||
if (value_string == "0") {
|
||||
type = Length::Type::Px;
|
||||
numeric_value = 0;
|
||||
} else if (m_context.in_quirks_mode()) {
|
||||
type = Length::Type::Px;
|
||||
numeric_value = try_parse_float(value_string);
|
||||
}
|
||||
}
|
||||
|
||||
if (!numeric_value.has_value())
|
||||
return {};
|
||||
|
||||
return Length(numeric_value.value(), type);
|
||||
};
|
||||
|
||||
auto token = tokens.next_token();
|
||||
|
||||
if (takes_integer_value(property_id) && token.is(Token::Type::Number)) {
|
||||
auto number = token.token();
|
||||
if (number.m_number_type == Token::NumberType::Integer) {
|
||||
return LengthStyleValue::create(Length::make_px(number.integer()));
|
||||
}
|
||||
}
|
||||
|
||||
if (token.is(Token::Type::Dimension) || token.is(Token::Type::Number)) {
|
||||
tokens.reconsume_current_input_token();
|
||||
|
||||
auto length = parse_length();
|
||||
if (length.has_value())
|
||||
return LengthStyleValue::create(length.value());
|
||||
|
||||
auto value_string = token.token().m_value.string_view();
|
||||
auto float_number = try_parse_float(value_string);
|
||||
if (float_number.has_value())
|
||||
return NumericStyleValue::create(float_number.value());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (token.is(Token::Type::Ident)) {
|
||||
auto ident = token.token().ident();
|
||||
if (ident.equals_ignoring_case("inherit"))
|
||||
return InheritStyleValue::create();
|
||||
if (ident.equals_ignoring_case("initial"))
|
||||
return InitialStyleValue::create();
|
||||
if (ident.equals_ignoring_case("auto"))
|
||||
return LengthStyleValue::create(Length::make_auto());
|
||||
}
|
||||
|
||||
if (token.is_function() && token.function().name().equals_ignoring_case("var")) {
|
||||
// FIXME: Handle fallback value as second parameter
|
||||
// https://www.w3.org/TR/css-variables-1/#using-variables
|
||||
if (!token.function().values().is_empty()) {
|
||||
auto& property_name = token.function().values().first();
|
||||
return CustomStyleValue::create(property_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (token.is(Token::Type::Ident)) {
|
||||
auto value_id = value_id_from_string(token.token().ident());
|
||||
if (value_id != ValueID::Invalid)
|
||||
return IdentifierStyleValue::create(value_id);
|
||||
}
|
||||
|
||||
auto parse_css_color = [&]() -> Optional<Color> {
|
||||
if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_case("transparent"))
|
||||
return Color::from_rgba(0x00000000);
|
||||
|
||||
// FIXME: Handle all the different color notations.
|
||||
// https://www.w3.org/TR/css-color-3/
|
||||
// Right now, this uses non-CSS-specific parsing, and assumes the whole color value is one token,
|
||||
// which is isn't if it's a function-style syntax.
|
||||
auto color = Color::from_string(token.to_string().to_lowercase());
|
||||
if (color.has_value())
|
||||
return color;
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
auto color = parse_css_color();
|
||||
if (color.has_value())
|
||||
return ColorStyleValue::create(color.value());
|
||||
|
||||
if (token.is(Token::Type::String))
|
||||
return StringStyleValue::create(token.token().string());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -122,8 +122,7 @@ public:
|
|||
template<typename T>
|
||||
Vector<Selector> parse_a_relative_selector(TokenStream<T>&);
|
||||
|
||||
template<typename T>
|
||||
RefPtr<StyleValue> parse_css_value(PropertyID, TokenStream<T>&);
|
||||
RefPtr<StyleValue> parse_css_value(PropertyID, TokenStream<StyleComponentValueRule>&);
|
||||
|
||||
// FIXME: https://drafts.csswg.org/css-backgrounds-3/
|
||||
static Optional<String> as_valid_background_repeat(String input) { return input; }
|
||||
|
@ -171,6 +170,8 @@ private:
|
|||
[[nodiscard]] RefPtr<CSSRule> convert_to_rule(NonnullRefPtr<StyleRule>);
|
||||
[[nodiscard]] RefPtr<CSSStyleDeclaration> convert_to_declaration(NonnullRefPtr<StyleBlockRule>);
|
||||
|
||||
static Optional<float> try_parse_float(StringView string);
|
||||
|
||||
ParsingContext m_context;
|
||||
|
||||
Tokenizer m_tokenizer;
|
||||
|
|
|
@ -76,6 +76,12 @@ public:
|
|||
return m_value.string_view();
|
||||
}
|
||||
|
||||
int integer() const
|
||||
{
|
||||
VERIFY(m_type == Type::Number && m_number_type == NumberType::Integer);
|
||||
return m_value.string_view().to_int().value();
|
||||
}
|
||||
|
||||
Type mirror_variant() const;
|
||||
String bracket_string() const;
|
||||
String bracket_mirror_string() const;
|
||||
|
|
Loading…
Reference in a new issue