summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@gmail.com>2021-07-09 21:04:34 +0100
committerAndreas Kling <kling@serenityos.org>2021-07-11 23:19:56 +0200
commit6b6bf4a0aa724539d98e31ee14479a89598f061c (patch)
tree39263ccb4bd115b1438c3992da9d2a8329c41471 /Userland
parentc91c00f58731c091b2d9b17356b97ba433e34243 (diff)
downloadserenity-6b6bf4a0aa724539d98e31ee14479a89598f061c.zip
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.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp218
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.h5
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Token.h6
3 files changed, 224 insertions, 5 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index 3187066ef9..367ff360fe 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -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 {};
}
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
index 4727f2cea4..0dbd2891b3 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
@@ -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;
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Token.h b/Userland/Libraries/LibWeb/CSS/Parser/Token.h
index 683fe65a68..34e0699701 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Token.h
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Token.h
@@ -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;