From f87b454fa97f06a8c3ace963fff9723c0c1b6260 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 3 Apr 2025 12:05:49 +0100 Subject: [PATCH] LibWeb/CSS: Parse `@font-face` descriptors as style values CSSFontFaceRule now stores its values as a CSSFontFaceDescriptors, with a ParsedFontFace produced on request. This is exposed via the `style` attribute, so we pass a lot of tests that try to read values from that. We have one test regression, which we passed by mistake before: The test wanted to ensure we don't allow `@font-face` nested inside other rules. We passed it just because we discarded any `@font-face` without a `font-family`. What we're supposed to do is 1) keep at-rules with missing required descriptors and just not use them, and 2) reject certain ones when nested. We may want to cache the ParsedFontFace in the future, but I didn't here because 1) it's called rarely, and 2) that would mean knowing to invalidate it when the CSSFontFaceDescriptors changes, which isn't obvious to me right now. --- Libraries/LibWeb/CSS/CSSFontFaceRule.cpp | 46 +-- Libraries/LibWeb/CSS/CSSFontFaceRule.h | 14 +- .../LibWeb/CSS/Parser/DescriptorParsing.cpp | 7 +- Libraries/LibWeb/CSS/Parser/RuleParsing.cpp | 294 +----------------- Libraries/LibWeb/Dump.cpp | 2 +- .../LibWeb/Text/expected/css/CSSRule-type.txt | 1 + .../parsing/font-face-size-adjust.txt | 7 +- .../parsing/font-face-src-format.txt | 45 +-- .../css-fonts/parsing/font-face-src-list.txt | 13 +- .../css-fonts/parsing/font-face-src-local.txt | 15 +- .../css-fonts/parsing/font-face-src-tech.txt | 47 +-- .../css/css-nesting/invalid-inner-rules.txt | 5 +- 12 files changed, 118 insertions(+), 378 deletions(-) diff --git a/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp b/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp index aa41229d260..eb7d3046562 100644 --- a/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp +++ b/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, Sam Atkins + * Copyright (c) 2022-2025, Sam Atkins * Copyright (c) 2022-2023, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause @@ -17,14 +17,14 @@ namespace Web::CSS { GC_DEFINE_ALLOCATOR(CSSFontFaceRule); -GC::Ref CSSFontFaceRule::create(JS::Realm& realm, ParsedFontFace&& font_face) +GC::Ref CSSFontFaceRule::create(JS::Realm& realm, GC::Ref style) { - return realm.create(realm, move(font_face)); + return realm.create(realm, style); } -CSSFontFaceRule::CSSFontFaceRule(JS::Realm& realm, ParsedFontFace&& font_face) +CSSFontFaceRule::CSSFontFaceRule(JS::Realm& realm, GC::Ref style) : CSSRule(realm, Type::FontFace) - , m_font_face(move(font_face)) + , m_style(style) { } @@ -34,15 +34,15 @@ void CSSFontFaceRule::initialize(JS::Realm& realm) WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSFontFaceRule); } -CSSStyleDeclaration* CSSFontFaceRule::style() +ParsedFontFace CSSFontFaceRule::font_face() const { - // FIXME: Return a CSSStyleDeclaration subclass that directs changes to the ParsedFontFace. - return nullptr; + return ParsedFontFace::from_descriptors(m_style); } // https://www.w3.org/TR/cssom/#ref-for-cssfontfacerule String CSSFontFaceRule::serialized() const { + auto font_face = this->font_face(); StringBuilder builder; // The result of concatenating the following: @@ -53,18 +53,18 @@ String CSSFontFaceRule::serialized() const builder.append("font-family: "sv); // 3. The result of performing serialize a string on the rule’s font family name. - serialize_a_string(builder, m_font_face.font_family()); + serialize_a_string(builder, font_face.font_family()); // 4. The string ";", i.e., SEMICOLON (U+003B). builder.append(';'); // 5. If the rule’s associated source list is not empty, follow these substeps: - if (!m_font_face.sources().is_empty()) { + if (!font_face.sources().is_empty()) { // 1. A single SPACE (U+0020), followed by the string "src:", followed by a single SPACE (U+0020). builder.append(" src: "sv); // 2. The result of invoking serialize a comma-separated list on performing serialize a URL or serialize a LOCAL for each source on the source list. - serialize_a_comma_separated_list(builder, m_font_face.sources(), [&](StringBuilder& builder, ParsedFontFace::Source source) -> void { + serialize_a_comma_separated_list(builder, font_face.sources(), [&](StringBuilder& builder, ParsedFontFace::Source source) -> void { source.local_or_url.visit( [&builder](URL::URL const& url) { serialize_a_url(builder, url.to_string()); @@ -89,7 +89,7 @@ String CSSFontFaceRule::serialized() const // 6. If rule’s associated unicode-range descriptor is present, a single SPACE (U+0020), followed by the string "unicode-range:", followed by a single SPACE (U+0020), followed by the result of performing serialize a <'unicode-range'>, followed by the string ";", i.e., SEMICOLON (U+003B). builder.append(" unicode-range: "sv); - serialize_unicode_ranges(builder, m_font_face.unicode_ranges()); + serialize_unicode_ranges(builder, font_face.unicode_ranges()); builder.append(';'); // FIXME: 7. If rule’s associated font-variant descriptor is present, a single SPACE (U+0020), @@ -101,8 +101,8 @@ String CSSFontFaceRule::serialized() const // followed by the string "font-feature-settings:", followed by a single SPACE (U+0020), // followed by the result of performing serialize a <'font-feature-settings'>, // followed by the string ";", i.e., SEMICOLON (U+003B). - if (m_font_face.font_feature_settings().has_value()) { - auto const& feature_settings = m_font_face.font_feature_settings().value(); + if (font_face.font_feature_settings().has_value()) { + auto const& feature_settings = font_face.font_feature_settings().value(); builder.append(" font-feature-settings: "sv); // NOTE: We sort the tags during parsing, so they're already in the correct order. bool first = true; @@ -126,12 +126,12 @@ String CSSFontFaceRule::serialized() const // followed by the result of performing serialize a <'font-stretch'>, // followed by the string ";", i.e., SEMICOLON (U+003B). // NOTE: font-stretch is now an alias for font-width, so we use that instead. - if (m_font_face.width().has_value()) { + if (font_face.width().has_value()) { builder.append(" font-width: "sv); // NOTE: font-width is supposed to always be serialized as a percentage. // Right now, it's stored as a Gfx::FontWidth value, so we have to lossily convert it back. float percentage = 100.0f; - switch (m_font_face.width().value()) { + switch (font_face.width().value()) { case Gfx::FontWidth::UltraCondensed: percentage = 50.0f; break; @@ -170,8 +170,8 @@ String CSSFontFaceRule::serialized() const // followed by the string "font-weight:", followed by a single SPACE (U+0020), // followed by the result of performing serialize a <'font-weight'>, // followed by the string ";", i.e., SEMICOLON (U+003B). - if (m_font_face.weight().has_value()) { - auto weight = m_font_face.weight().value(); + if (font_face.weight().has_value()) { + auto weight = font_face.weight().value(); builder.append(" font-weight: "sv); if (weight == 400) builder.append("normal"sv); @@ -186,8 +186,8 @@ String CSSFontFaceRule::serialized() const // followed by the string "font-style:", followed by a single SPACE (U+0020), // followed by the result of performing serialize a <'font-style'>, // followed by the string ";", i.e., SEMICOLON (U+003B). - if (m_font_face.slope().has_value()) { - auto slope = m_font_face.slope().value(); + if (font_face.slope().has_value()) { + auto slope = font_face.slope().value(); builder.append(" font-style: "sv); if (slope == Gfx::name_to_slope("Normal"sv)) builder.append("normal"sv); @@ -206,4 +206,10 @@ String CSSFontFaceRule::serialized() const return MUST(builder.to_string()); } +void CSSFontFaceRule::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_style); +} + } diff --git a/Libraries/LibWeb/CSS/CSSFontFaceRule.h b/Libraries/LibWeb/CSS/CSSFontFaceRule.h index 89cdc2cf7be..fc6894360a8 100644 --- a/Libraries/LibWeb/CSS/CSSFontFaceRule.h +++ b/Libraries/LibWeb/CSS/CSSFontFaceRule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Sam Atkins + * Copyright (c) 2022-2025, Sam Atkins * Copyright (c) 2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -17,20 +18,21 @@ class CSSFontFaceRule final : public CSSRule { GC_DECLARE_ALLOCATOR(CSSFontFaceRule); public: - [[nodiscard]] static GC::Ref create(JS::Realm&, ParsedFontFace&&); + [[nodiscard]] static GC::Ref create(JS::Realm&, GC::Ref); virtual ~CSSFontFaceRule() override = default; - ParsedFontFace const& font_face() const { return m_font_face; } - CSSStyleDeclaration* style(); + ParsedFontFace font_face() const; + CSSStyleDeclaration* style() { return m_style; } private: - CSSFontFaceRule(JS::Realm&, ParsedFontFace&&); + CSSFontFaceRule(JS::Realm&, GC::Ref); virtual void initialize(JS::Realm&) override; virtual String serialized() const override; + virtual void visit_edges(Visitor&) override; - ParsedFontFace m_font_face; + GC::Ref m_style; }; template<> diff --git a/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp b/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp index b4b1545571b..50bc317a5e1 100644 --- a/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/DescriptorParsing.cpp @@ -45,16 +45,17 @@ Parser::ParseErrorOr> Parser::parse_descriptor_valu return parse_all_as_single_keyword_value(tokens, keyword); }, [&](PropertyID property_id) -> RefPtr { - auto value_for_property = parse_css_value_for_property(property_id, tokens); - if (!value_for_property) + auto value_or_error = parse_css_value(property_id, tokens); + if (value_or_error.is_error()) return nullptr; + auto value_for_property = value_or_error.release_value(); // Descriptors don't accept the following, which properties do: // - CSS-wide keywords // - Shorthands // - Arbitrary substitution functions (so, UnresolvedStyleValue) if (value_for_property->is_css_wide_keyword() || value_for_property->is_shorthand() || value_for_property->is_unresolved()) return nullptr; - return value_for_property.release_nonnull(); + return value_for_property; }, [&](DescriptorMetadata::ValueType value_type) -> RefPtr { switch (value_type) { diff --git a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp index cf662a36505..cce79fb7b3d 100644 --- a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp @@ -699,296 +699,22 @@ template Vector Parser::parse_font_face_src(TokenStream< GC::Ptr Parser::convert_to_font_face_rule(AtRule const& rule) { // https://drafts.csswg.org/css-fonts/#font-face-rule - - Optional font_family; - Optional font_named_instance; - Vector src; - Vector unicode_range; - Optional weight; - Optional slope; - Optional width; - Optional ascent_override; - Optional descent_override; - Optional line_gap_override; - FontDisplay font_display = FontDisplay::Auto; - Optional language_override; - Optional> font_feature_settings; - Optional> font_variation_settings; - - // "normal" is returned as nullptr - auto parse_as_percentage_or_normal = [&](Vector const& values) -> ErrorOr> { - // normal | - TokenStream tokens { values }; - if (auto percentage_value = parse_percentage_value(tokens)) { - tokens.discard_whitespace(); - if (tokens.has_next_token()) - return Error::from_string_literal("Unexpected trailing tokens"); - - if (percentage_value->is_percentage() && percentage_value->as_percentage().percentage().value() >= 0) - return percentage_value->as_percentage().percentage(); - - // TODO: Once we implement calc-simplification in the parser, we should no longer see math values here, - // unless they're impossible to resolve and thus invalid. - if (percentage_value->is_calculated()) { - if (auto result = percentage_value->as_calculated().resolve_percentage({}); result.has_value()) - return result.value(); - } - - return Error::from_string_literal("Invalid percentage"); - } - - tokens.discard_whitespace(); - if (!tokens.consume_a_token().is_ident("normal"sv)) - return Error::from_string_literal("Expected `normal | `"); - tokens.discard_whitespace(); - if (tokens.has_next_token()) - return Error::from_string_literal("Unexpected trailing tokens"); - - return OptionalNone {}; - }; - + Vector descriptors; + HashTable seen_descriptor_ids; rule.for_each_as_declaration_list([&](auto& declaration) { - if (declaration.name.equals_ignoring_ascii_case("ascent-override"sv)) { - auto value = parse_as_percentage_or_normal(declaration.value); - if (value.is_error()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse @font-face ascent-override: {}", value.error()); + if (auto descriptor = convert_to_descriptor(AtRuleID::FontFace, declaration); descriptor.has_value()) { + if (seen_descriptor_ids.contains(descriptor->descriptor_id)) { + descriptors.remove_first_matching([&descriptor](Descriptor const& existing) { + return existing.descriptor_id == descriptor->descriptor_id; + }); } else { - ascent_override = value.release_value(); + seen_descriptor_ids.set(descriptor->descriptor_id); } - return; + descriptors.append(descriptor.release_value()); } - if (declaration.name.equals_ignoring_ascii_case("descent-override"sv)) { - auto value = parse_as_percentage_or_normal(declaration.value); - if (value.is_error()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse @font-face descent-override: {}", value.error()); - } else { - descent_override = value.release_value(); - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-display"sv)) { - TokenStream token_stream { declaration.value }; - if (auto keyword_value = parse_keyword_value(token_stream)) { - token_stream.discard_whitespace(); - if (token_stream.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unexpected trailing tokens in font-display"); - } else { - auto value = keyword_to_font_display(keyword_value->to_keyword()); - if (value.has_value()) { - font_display = *value; - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: `{}` is not a valid value for font-display", keyword_value->to_string(CSSStyleValue::SerializationMode::Normal)); - } - } - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-family"sv)) { - // FIXME: This is very similar to, but different from, the logic in parse_font_family_value(). - // Ideally they could share code. - Vector font_family_parts; - bool had_syntax_error = false; - for (size_t i = 0; i < declaration.value.size(); ++i) { - auto const& part = declaration.value[i]; - if (part.is(Token::Type::Whitespace)) - continue; - if (part.is(Token::Type::String)) { - if (!font_family_parts.is_empty()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face font-family format invalid; discarding."); - had_syntax_error = true; - break; - } - font_family_parts.append(part.token().string()); - continue; - } - if (part.is(Token::Type::Ident)) { - if (is_css_wide_keyword(part.token().ident())) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face font-family format invalid; discarding."); - had_syntax_error = true; - break; - } - auto keyword = keyword_from_string(part.token().ident()); - if (keyword.has_value() && keyword_to_generic_font_family(keyword.value()).has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face font-family format invalid; discarding."); - had_syntax_error = true; - break; - } - font_family_parts.append(part.token().ident()); - continue; - } - - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face font-family format invalid; discarding."); - had_syntax_error = true; - break; - } - if (had_syntax_error || font_family_parts.is_empty()) - return; - - font_family = String::join(' ', font_family_parts).release_value_but_fixme_should_propagate_errors(); - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-feature-settings"sv)) { - TokenStream token_stream { declaration.value }; - if (auto value = parse_css_value(CSS::PropertyID::FontFeatureSettings, token_stream); !value.is_error()) { - if (value.value()->to_keyword() == Keyword::Normal) { - font_feature_settings.clear(); - } else if (value.value()->is_value_list()) { - auto const& feature_tags = value.value()->as_value_list().values(); - OrderedHashMap settings; - settings.ensure_capacity(feature_tags.size()); - for (auto const& feature_tag : feature_tags) { - if (!feature_tag->is_open_type_tagged()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-feature-settings descriptor is not an OpenTypeTaggedStyleValue; skipping"); - continue; - } - auto const& setting_value = feature_tag->as_open_type_tagged().value(); - if (setting_value->is_integer()) { - settings.set(feature_tag->as_open_type_tagged().tag(), setting_value->as_integer().integer()); - } else if (setting_value->is_calculated() && setting_value->as_calculated().resolves_to_number()) { - if (auto integer = setting_value->as_calculated().resolve_integer({}); integer.has_value()) { - settings.set(feature_tag->as_open_type_tagged().tag(), *integer); - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-feature-settings descriptor cannot be resolved at parse time; skipping"); - } - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-feature-settings descriptor is not an OpenTypeTaggedStyleValue holding a ; skipping"); - } - } - font_feature_settings = move(settings); - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-feature-settings descriptor, not compatible with value returned from parsing font-feature-settings property: {}", value.value()->to_string(CSSStyleValue::SerializationMode::Normal)); - } - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-language-override"sv)) { - TokenStream token_stream { declaration.value }; - if (auto maybe_value = parse_css_value(CSS::PropertyID::FontLanguageOverride, token_stream); !maybe_value.is_error()) { - auto& value = maybe_value.value(); - if (value->is_string()) { - language_override = value->as_string().string_value(); - } else { - language_override.clear(); - } - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-named-instance"sv)) { - // auto | - TokenStream token_stream { declaration.value }; - token_stream.discard_whitespace(); - auto& token = token_stream.consume_a_token(); - token_stream.discard_whitespace(); - if (token_stream.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unexpected trailing tokens in font-named-instance"); - return; - } - - if (token.is_ident("auto"sv)) { - font_named_instance.clear(); - } else if (token.is(Token::Type::String)) { - font_named_instance = token.token().string(); - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-named-instance from {}", token.to_debug_string()); - } - - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-style"sv)) { - TokenStream token_stream { declaration.value }; - if (auto value = parse_css_value(CSS::PropertyID::FontStyle, token_stream); !value.is_error()) { - slope = value.value()->to_font_slope(); - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-variation-settings"sv)) { - TokenStream token_stream { declaration.value }; - if (auto value = parse_css_value(CSS::PropertyID::FontVariationSettings, token_stream); !value.is_error()) { - if (value.value()->to_keyword() == Keyword::Normal) { - font_variation_settings.clear(); - } else if (value.value()->is_value_list()) { - auto const& variation_tags = value.value()->as_value_list().values(); - OrderedHashMap settings; - settings.ensure_capacity(variation_tags.size()); - for (auto const& variation_tag : variation_tags) { - if (!variation_tag->is_open_type_tagged()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-variation-settings descriptor is not an OpenTypeTaggedStyleValue; skipping"); - continue; - } - auto const& setting_value = variation_tag->as_open_type_tagged().value(); - if (setting_value->is_number()) { - settings.set(variation_tag->as_open_type_tagged().tag(), setting_value->as_number().number()); - } else if (setting_value->is_calculated() && setting_value->as_calculated().resolves_to_number()) { - if (auto number = setting_value->as_calculated().resolve_number({}); number.has_value()) { - settings.set(variation_tag->as_open_type_tagged().tag(), *number); - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Calculated value in font-variation-settings descriptor cannot be resolved at parse time; skipping"); - } - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Value in font-variation-settings descriptor is not an OpenTypeTaggedStyleValue holding a ; skipping"); - } - } - font_variation_settings = move(settings); - } else { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse font-variation-settings descriptor, not compatible with value returned from parsing font-variation-settings property: {}", value.value()->to_string(CSSStyleValue::SerializationMode::Normal)); - } - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-weight"sv)) { - TokenStream token_stream { declaration.value }; - if (auto value = parse_css_value(CSS::PropertyID::FontWeight, token_stream); !value.is_error()) { - weight = value.value()->to_font_weight(); - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("font-width"sv) - || declaration.name.equals_ignoring_ascii_case("font-stretch"sv)) { - TokenStream token_stream { declaration.value }; - if (auto value = parse_css_value(CSS::PropertyID::FontWidth, token_stream); !value.is_error()) { - width = value.value()->to_font_width(); - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("line-gap-override"sv)) { - auto value = parse_as_percentage_or_normal(declaration.value); - if (value.is_error()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse @font-face line-gap-override: {}", value.error()); - } else { - line_gap_override = value.release_value(); - } - return; - } - if (declaration.name.equals_ignoring_ascii_case("src"sv)) { - TokenStream token_stream { declaration.value }; - Vector supported_sources = parse_font_face_src(token_stream); - if (!supported_sources.is_empty()) - src = move(supported_sources); - return; - } - if (declaration.name.equals_ignoring_ascii_case("unicode-range"sv)) { - TokenStream token_stream { declaration.value }; - auto unicode_ranges = parse_unicode_ranges(token_stream); - if (unicode_ranges.is_empty()) - return; - - unicode_range = move(unicode_ranges); - return; - } - - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unrecognized descriptor '{}' in @font-face; discarding.", declaration.name); }); - if (!font_family.has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse @font-face: no font-family!"); - return {}; - } - - if (unicode_range.is_empty()) { - unicode_range.empend(0x0u, 0x10FFFFu); - } - - return CSSFontFaceRule::create(realm(), ParsedFontFace { font_family.release_value(), move(weight), move(slope), move(width), move(src), move(unicode_range), move(ascent_override), move(descent_override), move(line_gap_override), font_display, move(font_named_instance), move(language_override), move(font_feature_settings), move(font_variation_settings) }); + return CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), move(descriptors))); } } diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp index 76aad860870..4625bb8c3f3 100644 --- a/Libraries/LibWeb/Dump.cpp +++ b/Libraries/LibWeb/Dump.cpp @@ -701,7 +701,7 @@ void dump_rule(StringBuilder& builder, CSS::CSSRule const& rule, int indent_leve void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rule, int indent_levels) { - auto& font_face = rule.font_face(); + auto const font_face = rule.font_face(); indent(builder, indent_levels + 1); builder.appendff("font-family: {}\n", font_face.font_family()); diff --git a/Tests/LibWeb/Text/expected/css/CSSRule-type.txt b/Tests/LibWeb/Text/expected/css/CSSRule-type.txt index a23632c8203..66077a22f8b 100644 --- a/Tests/LibWeb/Text/expected/css/CSSRule-type.txt +++ b/Tests/LibWeb/Text/expected/css/CSSRule-type.txt @@ -2,6 +2,7 @@ CSSImportRule type = 3 CSSNamespaceRule type = 10 CSSStyleRule type = 1 CSSMediaRule type = 4 +CSSFontFaceRule type = 5 CSSKeyframesRule type = 7 CSSSupportsRule type = 12 CSSLayerStatementRule type = 0 diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-size-adjust.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-size-adjust.txt index c63e1a276b2..b8fc5dce7fa 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-size-adjust.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-size-adjust.txt @@ -2,10 +2,11 @@ Harness status: OK Found 6 tests -6 Fail +2 Pass +4 Fail Fail Check that size-adjust: 100% is valid Fail Check that size-adjust: 0% is valid Fail Check that size-adjust: 110% is valid Fail Check that size-adjust: 100000000000% is valid -Fail Check that size-adjust: -100% is invalid -Fail Check that size-adjust: -1% is invalid \ No newline at end of file +Pass Check that size-adjust: -100% is invalid +Pass Check that size-adjust: -1% is invalid \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt index bd1f1263d35..67b2e029ed3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt @@ -2,33 +2,34 @@ Harness status: OK Found 35 tests -35 Fail -Fail Check that src: url("foo.ttf") is valid -Fail Check that src: url("foo.ttf"), url("bar.ttf") is valid -Fail Check that src: url("foo.ttf") format() is invalid -Fail Check that src: url("foo.ttf") dummy() is invalid -Fail Check that src: url("foo.ttf") format("woff") dummy() is invalid -Fail Check that src: url("foo.ttf") dummy() format("woff") is invalid +21 Pass +14 Fail +Pass Check that src: url("foo.ttf") is valid +Pass Check that src: url("foo.ttf"), url("bar.ttf") is valid +Pass Check that src: url("foo.ttf") format() is invalid +Pass Check that src: url("foo.ttf") dummy() is invalid +Pass Check that src: url("foo.ttf") format("woff") dummy() is invalid +Pass Check that src: url("foo.ttf") dummy() format("woff") is invalid Fail Check that src: url("foo.ttf") format("collection") is valid -Fail Check that src: url("foo.ttf") format("opentype") is valid -Fail Check that src: url("foo.ttf") format("truetype") is valid -Fail Check that src: url("foo.ttf") format("woff") is valid -Fail Check that src: url("foo.ttf") format("woff2") is valid +Pass Check that src: url("foo.ttf") format("opentype") is valid +Pass Check that src: url("foo.ttf") format("truetype") is valid +Pass Check that src: url("foo.ttf") format("woff") is valid +Pass Check that src: url("foo.ttf") format("woff2") is valid Fail Check that src: url("foo.ttf") format("opentype", "truetype") is invalid Fail Check that src: url("foo.ttf") format(collection) is valid -Fail Check that src: url("foo.ttf") format(opentype) is valid -Fail Check that src: url("foo.ttf") format(truetype) is valid -Fail Check that src: url("foo.ttf") format(woff) is valid -Fail Check that src: url("foo.ttf") format(woff2) is valid +Pass Check that src: url("foo.ttf") format(opentype) is valid +Pass Check that src: url("foo.ttf") format(truetype) is valid +Pass Check that src: url("foo.ttf") format(woff) is valid +Pass Check that src: url("foo.ttf") format(woff2) is valid Fail Check that src: url("foo.ttf") format(opentype, truetype) is invalid Fail Check that src: url("foo.ttf") format(opentype truetype) is invalid -Fail Check that src: url("foo.ttf") format(auto) is invalid -Fail Check that src: url("foo.ttf") format(default) is invalid -Fail Check that src: url("foo.ttf") format(inherit) is invalid -Fail Check that src: url("foo.ttf") format(initial) is invalid -Fail Check that src: url("foo.ttf") format(none) is invalid -Fail Check that src: url("foo.ttf") format(normal) is invalid -Fail Check that src: url("foo.ttf") format(xyzzy) is invalid +Pass Check that src: url("foo.ttf") format(auto) is invalid +Pass Check that src: url("foo.ttf") format(default) is invalid +Pass Check that src: url("foo.ttf") format(inherit) is invalid +Pass Check that src: url("foo.ttf") format(initial) is invalid +Pass Check that src: url("foo.ttf") format(none) is invalid +Pass Check that src: url("foo.ttf") format(normal) is invalid +Pass Check that src: url("foo.ttf") format(xyzzy) is invalid Fail Check that src: url("foo.ttf") format("embedded-opentype"), url("bar.html") is valid Fail Check that src: url("foo.ttf") format(embedded-opentype), url("bar.html") is valid Fail Check that src: url("foo.ttf") format("svg"), url("bar.html") is valid diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-list.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-list.txt index 6e19c0e85bc..a28d413ac38 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-list.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-list.txt @@ -2,7 +2,8 @@ Harness status: OK Found 17 tests -17 Fail +5 Pass +12 Fail Fail Check that src: local(inherit), url(foo.ttf) is valid Fail Check that src: local("myfont"), local(unset) is valid Fail Check that src: local(), url(foo.ttf) is valid @@ -15,8 +16,8 @@ Fail Check that src: url(foo.ttf) tech(color-COLRv0) otherfunc(othervalue), url( Fail Check that src: url(foo.ttf), url(something.ttf) format(broken) is valid Fail Check that src: /* an empty component */, url(foo.ttf) is valid Fail Check that src: local(""), url(foo.ttf), unparseable-garbage, local("another font name") is valid -Fail Check that src: local(), local(initial) is invalid -Fail Check that src: local("textfont") format(opentype), local("emoji") tech(color-COLRv0) is invalid -Fail Check that src: local(), /*empty*/, url(should be quoted.ttf), junk is invalid -Fail Check that src: url(foo.ttf) format(unknown), url(bar.ttf) tech(broken) is invalid -Fail Check that src: url(foo.ttf) tech(color-COLRv0) otherfunc(othervalue), junk is invalid \ No newline at end of file +Pass Check that src: local(), local(initial) is invalid +Pass Check that src: local("textfont") format(opentype), local("emoji") tech(color-COLRv0) is invalid +Pass Check that src: local(), /*empty*/, url(should be quoted.ttf), junk is invalid +Pass Check that src: url(foo.ttf) format(unknown), url(bar.ttf) tech(broken) is invalid +Pass Check that src: url(foo.ttf) tech(color-COLRv0) otherfunc(othervalue), junk is invalid \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-local.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-local.txt index a5eb8c66407..2169885f61e 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-local.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-local.txt @@ -2,13 +2,14 @@ Harness status: OK Found 18 tests -18 Fail -Fail Check that src: local(A) dummy() is invalid -Fail Check that src: dummy() local(A) is invalid -Fail Check that src: local( A ) is valid -Fail Check that src: local(A B) is valid -Fail Check that src: local(A B) is valid -Fail Check that src: local( A B ) is valid +6 Pass +12 Fail +Pass Check that src: local(A) dummy() is invalid +Pass Check that src: dummy() local(A) is invalid +Pass Check that src: local( A ) is valid +Pass Check that src: local(A B) is valid +Pass Check that src: local(A B) is valid +Pass Check that src: local( A B ) is valid Fail Check that src: local(default) is invalid Fail Check that src: local(inherit) is invalid Fail Check that src: local(revert) is invalid diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt index 25db010e04b..604970ecaa1 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt @@ -2,9 +2,10 @@ Harness status: OK Found 39 tests -39 Fail -Fail Check that src: url("foo.ttf") is valid -Fail Check that src: url("foo.ttf") tech() is invalid +22 Pass +17 Fail +Pass Check that src: url("foo.ttf") is valid +Pass Check that src: url("foo.ttf") tech() is invalid Fail Check that src: url("foo.ttf") tech(features-opentype) is valid Fail Check that src: url("foo.ttf") tech(features-aat) is valid Fail Check that src: url("foo.ttf") tech(color-COLRv0) is valid @@ -13,32 +14,32 @@ Fail Check that src: url("foo.ttf") tech(color-sbix) is valid Fail Check that src: url("foo.ttf") tech(color-CBDT) is valid Fail Check that src: url("foo.ttf") tech(variations) is valid Fail Check that src: url("foo.ttf") tech(palettes) is valid -Fail Check that src: url("foo.ttf") tech("features-opentype") is invalid -Fail Check that src: url("foo.ttf") tech("color-COLRv0") is invalid -Fail Check that src: url("foo.ttf") tech("variations") is invalid +Pass Check that src: url("foo.ttf") tech("features-opentype") is invalid +Pass Check that src: url("foo.ttf") tech("color-COLRv0") is invalid +Pass Check that src: url("foo.ttf") tech("variations") is invalid Fail Check that src: url("foo.ttf") tech(features-opentype, color-COLRv0, variations, palettes) is valid -Fail Check that src: url("foo.ttf") tech(features-opentype color-COLRv0 variations palettes) is invalid -Fail Check that src: url("foo.ttf") tech(feature-opentype) is invalid -Fail Check that src: url("foo.ttf") tech(feature-aat) is invalid -Fail Check that src: url("foo.ttf") tech(feature-graphite) is invalid -Fail Check that src: url("foo.ttf") tech(auto) is invalid -Fail Check that src: url("foo.ttf") tech(default) is invalid -Fail Check that src: url("foo.ttf") tech(inherit) is invalid -Fail Check that src: url("foo.ttf") tech(initial) is invalid -Fail Check that src: url("foo.ttf") tech(none) is invalid -Fail Check that src: url("foo.ttf") tech(normal) is invalid -Fail Check that src: url("foo.ttf") tech(xyzzy) is invalid -Fail Check that src: url("foo.ttf") tech(xyzzy, features-opentype) is invalid -Fail Check that src: url("foo.ttf") tech(features-opentype, xyzzy) is invalid +Pass Check that src: url("foo.ttf") tech(features-opentype color-COLRv0 variations palettes) is invalid +Pass Check that src: url("foo.ttf") tech(feature-opentype) is invalid +Pass Check that src: url("foo.ttf") tech(feature-aat) is invalid +Pass Check that src: url("foo.ttf") tech(feature-graphite) is invalid +Pass Check that src: url("foo.ttf") tech(auto) is invalid +Pass Check that src: url("foo.ttf") tech(default) is invalid +Pass Check that src: url("foo.ttf") tech(inherit) is invalid +Pass Check that src: url("foo.ttf") tech(initial) is invalid +Pass Check that src: url("foo.ttf") tech(none) is invalid +Pass Check that src: url("foo.ttf") tech(normal) is invalid +Pass Check that src: url("foo.ttf") tech(xyzzy) is invalid +Pass Check that src: url("foo.ttf") tech(xyzzy, features-opentype) is invalid +Pass Check that src: url("foo.ttf") tech(features-opentype, xyzzy) is invalid Fail Check that src: url("foo.ttf") format(opentype) tech(features-opentype) is valid -Fail Check that src: url("foo.ttf") tech(features-opentype) format(opentype) is invalid +Pass Check that src: url("foo.ttf") tech(features-opentype) format(opentype) is invalid Fail Check that src: url("foo.ttf") tech(incremental), url("bar.html") is valid Fail Check that src: url("foo.ttf") tech(incremental, color-SVG, features-graphite, features-aat), url("bar.html") is valid Fail Check that src: url("foo.ttf") tech(color-SVG, features-graphite), url("bar.html") is valid Fail Check that src: url("foo.ttf") tech(color-SVG), url("bar.html") is valid Fail Check that src: url("foo.ttf") tech(features-graphite), url("bar.html") is valid -Fail Check that src: url("foo.ttf") dummy("opentype") tech(variations) is invalid -Fail Check that src: url("foo.ttf") dummy("opentype") dummy(variations) is invalid -Fail Check that src: url("foo.ttf") format(opentype) tech(features-opentype) dummy(something) is invalid +Pass Check that src: url("foo.ttf") dummy("opentype") tech(variations) is invalid +Pass Check that src: url("foo.ttf") dummy("opentype") dummy(variations) is invalid +Pass Check that src: url("foo.ttf") format(opentype) tech(features-opentype) dummy(something) is invalid Fail Check that src: url("foo.ttf") format(dummy), url("foo.ttf") tech(variations) is valid Fail Check that src: url("foo.ttf") tech(color), url("bar.html") is valid \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-nesting/invalid-inner-rules.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-nesting/invalid-inner-rules.txt index 083010be2af..c4e8cede63a 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-nesting/invalid-inner-rules.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-nesting/invalid-inner-rules.txt @@ -2,7 +2,6 @@ Harness status: OK Found 2 tests -1 Pass -1 Fail -Pass Simple CSSOM manipulation of subrules +2 Fail +Fail Simple CSSOM manipulation of subrules Fail Simple CSSOM manipulation of subrules 1 \ No newline at end of file