diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 31a45cdee7..24acb508bf 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -2082,21 +2082,21 @@ RefPtr Parser::parse_identifier_value(ParsingContext const&, StyleCo return {}; } -Optional Parser::parse_color(ParsingContext const&, StyleComponentValueRule const& component_value) +Optional Parser::parse_color(ParsingContext const& context, StyleComponentValueRule const& component_value) { // https://www.w3.org/TR/css-color-3/ if (component_value.is(Token::Type::Ident)) { auto ident = component_value.token().ident(); - auto color = Color::from_string(ident.to_string().to_lowercase()); + auto color = Color::from_string(ident.to_lowercase_string()); if (color.has_value()) return color; } else if (component_value.is(Token::Type::Hash)) { - // FIXME: Read it directly - auto color = Color::from_string(String::formatted("#{}", component_value.token().m_value.to_string().to_lowercase())); + auto color = Color::from_string(String::formatted("#{}", component_value.token().m_value.to_string())); if (color.has_value()) return color; + return {}; } else if (component_value.is_function()) { auto& function = component_value.function(); @@ -2247,6 +2247,66 @@ Optional Parser::parse_color(ParsingContext const&, StyleComponentValueRu return {}; } + // https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk + if (context.in_quirks_mode() && property_has_quirk(context.current_property_id(), Quirk::HashlessHexColor)) { + // The value of a quirky color is obtained from the possible component values using the following algorithm, + // aborting on the first step that returns a value: + + // 1. Let cv be the component value. + auto& cv = component_value; + String serialization; + // 2. If cv is a or a , follow these substeps: + if (cv.is(Token::Type::Number) || cv.is(Token::Type::Dimension)) { + // 1. If cv’s type flag is not "integer", return an error. + // This means that values that happen to use scientific notation, e.g., 5e5e5e, will fail to parse. + if (cv.token().number_type() != Token::NumberType::Integer) + return {}; + + // 2. If cv’s value is less than zero, return an error. + auto value = cv.is(Token::Type::Number) ? cv.token().to_integer() : cv.token().dimension_value_int(); + if (value < 0) + return {}; + + // 3. Let serialization be the serialization of cv’s value, as a base-ten integer using digits 0-9 (U+0030 to U+0039) in the shortest form possible. + StringBuilder serialization_builder; + serialization_builder.appendff("{}", value); + + // 4. If cv is a , append the unit to serialization. + if (cv.is(Token::Type::Dimension)) + serialization_builder.append(cv.token().dimension_unit()); + + // 5. If serialization consists of fewer than six characters, prepend zeros (U+0030) so that it becomes six characters. + serialization = serialization_builder.to_string(); + if (serialization_builder.length() < 6) { + StringBuilder builder; + for (size_t i = 0; i < (6 - serialization_builder.length()); i++) + builder.append('0'); + builder.append(serialization_builder.string_view()); + serialization = builder.to_string(); + } + } + // 3. Otherwise, cv is an ; let serialization be cv’s value. + else { + if (!cv.is(Token::Type::Ident)) + return {}; + serialization = cv.token().ident(); + } + + // 4. If serialization does not consist of three or six characters, return an error. + if (serialization.length() != 3 && serialization.length() != 6) + return {}; + + // 5. If serialization contains any characters not in the range [0-9A-Fa-f] (U+0030 to U+0039, U+0041 to U+0046, U+0061 to U+0066), return an error. + for (auto c : serialization) { + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) + return {}; + } + + // 6. Return the concatenation of "#" (U+0023) and serialization. + String concatenation = String::formatted("#{}", serialization); + return Color::from_string(concatenation); + } + return {}; } @@ -3227,6 +3287,10 @@ RefPtr Parser::parse_css_value(ParsingContext const& context, StyleC if (auto dynamic = parse_dynamic_value(context, component_value)) return dynamic; + // We parse colors before numbers, to catch hashless hex colors. + if (auto color = parse_color_value(context, component_value)) + return color; + if (auto length = parse_length_value(context, component_value)) return length; @@ -3236,9 +3300,6 @@ RefPtr Parser::parse_css_value(ParsingContext const& context, StyleC if (auto identifier = parse_identifier_value(context, component_value)) return identifier; - if (auto color = parse_color_value(context, component_value)) - return color; - if (auto string = parse_string_value(context, component_value)) return string;