diff --git a/Libraries/LibWeb/CSS/Enums.json b/Libraries/LibWeb/CSS/Enums.json index acc01301af8..1a931f71e36 100644 --- a/Libraries/LibWeb/CSS/Enums.json +++ b/Libraries/LibWeb/CSS/Enums.json @@ -258,6 +258,20 @@ "right", "oblique" ], + "font-tech": [ + "avar2", + "color-cbdt", + "color-colrv0", + "color-colrv1", + "color-sbix", + "color-svg", + "features-aat", + "features-graphite", + "features-opentype", + "incremental", + "palettes", + "variations" + ], "font-variant-alternates": [ "normal", "historical-forms" diff --git a/Libraries/LibWeb/CSS/FontFace.cpp b/Libraries/LibWeb/CSS/FontFace.cpp index 09ae802c17c..510aab3a76d 100644 --- a/Libraries/LibWeb/CSS/FontFace.cpp +++ b/Libraries/LibWeb/CSS/FontFace.cpp @@ -534,35 +534,47 @@ bool font_format_is_supported(FlyString const& name) return false; } -bool font_tech_is_supported(FlyString const& name) +bool font_tech_is_supported(FontTech font_tech) { // https://drafts.csswg.org/css-fonts-4/#font-tech-definitions // FIXME: Determine this automatically somehow? - if (name.equals_ignoring_ascii_case("features-opentype"sv)) + switch (font_tech) { + case FontTech::FeaturesOpentype: return true; - if (name.equals_ignoring_ascii_case("features-aat"sv)) + case FontTech::FeaturesAat: return false; - if (name.equals_ignoring_ascii_case("features-graphite"sv)) + case FontTech::FeaturesGraphite: return false; - if (name.equals_ignoring_ascii_case("variations"sv)) + case FontTech::Variations: return true; - if (name.equals_ignoring_ascii_case("color-colrv0"sv)) + case FontTech::ColorColrv0: return true; - if (name.equals_ignoring_ascii_case("color-colrv1"sv)) + case FontTech::ColorColrv1: return true; - if (name.equals_ignoring_ascii_case("color-svg"sv)) + case FontTech::ColorSvg: return false; - if (name.equals_ignoring_ascii_case("color-sbix"sv)) + case FontTech::ColorSbix: return false; - if (name.equals_ignoring_ascii_case("color-cbdt"sv)) + case FontTech::ColorCbdt: return false; - if (name.equals_ignoring_ascii_case("palettes"sv)) + case FontTech::Palettes: return false; - if (name.equals_ignoring_ascii_case("incremental"sv)) + case FontTech::Incremental: return false; // https://drafts.csswg.org/css-fonts-5/#font-tech-definitions - if (name.equals_ignoring_ascii_case("avar2"sv)) + case FontTech::Avar2: return false; + } + return false; +} + +bool font_tech_is_supported(FlyString const& name) +{ + if (auto keyword = keyword_from_string(name); keyword.has_value()) { + if (auto font_tech = keyword_to_font_tech(*keyword); font_tech.has_value()) { + return font_tech_is_supported(*font_tech); + } + } return false; } diff --git a/Libraries/LibWeb/CSS/FontFace.h b/Libraries/LibWeb/CSS/FontFace.h index 92a5ef497e1..f68d5e7940d 100644 --- a/Libraries/LibWeb/CSS/FontFace.h +++ b/Libraries/LibWeb/CSS/FontFace.h @@ -115,6 +115,8 @@ private: }; bool font_format_is_supported(FlyString const& name); + +bool font_tech_is_supported(FontTech); bool font_tech_is_supported(FlyString const& name); } diff --git a/Libraries/LibWeb/CSS/Keywords.json b/Libraries/LibWeb/CSS/Keywords.json index 57e89b13553..f9a1b308877 100644 --- a/Libraries/LibWeb/CSS/Keywords.json +++ b/Libraries/LibWeb/CSS/Keywords.json @@ -85,6 +85,7 @@ "appworkspace", "auto", "auto-add", + "avar2", "b4", "b5", "back", @@ -128,7 +129,12 @@ "collapse", "color", "color-burn", + "color-cbdt", + "color-colrv0", + "color-colrv1", "color-dodge", + "color-sbix", + "color-svg", "column", "column-reverse", "common-ligatures", @@ -191,6 +197,9 @@ "false", "fantasy", "fast", + "features-aat", + "features-graphite", + "features-opentype", "field", "fieldtext", "fill", @@ -231,6 +240,7 @@ "inactivecaption", "inactivecaptiontext", "increasing", + "incremental", "infinite", "infinity", "infobackground", @@ -363,6 +373,7 @@ "padding-box", "paged", "paint", + "palettes", "pan-down", "pan-left", "pan-right", @@ -523,6 +534,7 @@ "uppercase", "upright", "use-credentials", + "variations", "vertical-lr", "vertical-rl", "vertical-text", diff --git a/Libraries/LibWeb/CSS/ParsedFontFace.cpp b/Libraries/LibWeb/CSS/ParsedFontFace.cpp index 5a9eda365a0..cbc6766e872 100644 --- a/Libraries/LibWeb/CSS/ParsedFontFace.cpp +++ b/Libraries/LibWeb/CSS/ParsedFontFace.cpp @@ -37,11 +37,10 @@ Vector ParsedFontFace::sources_from_style_value(CSSStyle auto add_source = [&sources](FontSourceStyleValue const& font_source) { font_source.source().visit( [&](FontSourceStyleValue::Local const& local) { - sources.empend(extract_font_name(local.name), OptionalNone {}); + sources.empend(extract_font_name(local.name), OptionalNone {}, Vector {}); }, [&](URL const& url) { - // FIXME: tech() - sources.empend(url, font_source.format()); + sources.empend(url, font_source.format(), font_source.tech()); }); }; diff --git a/Libraries/LibWeb/CSS/ParsedFontFace.h b/Libraries/LibWeb/CSS/ParsedFontFace.h index 96d6642eaa0..165925cd9d3 100644 --- a/Libraries/LibWeb/CSS/ParsedFontFace.h +++ b/Libraries/LibWeb/CSS/ParsedFontFace.h @@ -20,8 +20,8 @@ class ParsedFontFace { public: struct Source { Variant local_or_url; - // FIXME: Do we need to keep this around, or is it only needed to discard unwanted formats during parsing? Optional format; + Vector tech; }; static Vector sources_from_style_value(CSSStyleValue const&); diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index 6d2ae2627b1..1e6734d1e39 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -3888,17 +3888,20 @@ RefPtr Parser::parse_font_source_value(TokenStream [ format()]? [ tech( #)]? + + // auto url = parse_url_function(tokens); if (!url.has_value()) return nullptr; Optional format; + Vector tech; tokens.discard_whitespace(); @@ -3920,10 +3923,10 @@ RefPtr Parser::parse_font_source_value(TokenStream Parser::parse_font_source_value(TokenStream#)]? + // [ tech( #)]? + if (tokens.next_token().is_function("tech"sv)) { + auto const& function = tokens.consume_a_token().function(); + auto context_guard = push_temporary_value_parsing_context(FunctionContext { function.name }); + + TokenStream function_tokens { function.value }; + auto tech_items = parse_a_comma_separated_list_of_component_values(function_tokens); + if (tech_items.is_empty()) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`tech()` has no arguments); discarding."); + return nullptr; + } + + for (auto const& tech_item : tech_items) { + TokenStream tech_tokens { tech_item }; + tech_tokens.discard_whitespace(); + auto& ident_token = tech_tokens.consume_a_token(); + if (!ident_token.is(Token::Type::Ident)) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`tech()` parameters must be idents, got: {}); discarding.", ident_token.to_debug_string()); + return nullptr; + } + tech_tokens.discard_whitespace(); + if (tech_tokens.has_next_token()) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`tech()` has trailing tokens); discarding."); + return nullptr; + } + + auto& font_tech_name = ident_token.token().ident(); + if (auto keyword = keyword_from_string(font_tech_name); keyword.has_value()) { + if (auto font_tech = keyword_to_font_tech(*keyword); font_tech.has_value()) { + + if (!font_tech_is_supported(*font_tech)) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source tech({}) not supported; skipping.", font_tech_name); + return nullptr; + } + + tech.append(font_tech.release_value()); + continue; + } + } + + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: font source invalid (`{}` is not a supported value in `tech()`); discarding.", font_tech_name); + return nullptr; + } + } transaction.commit(); - return FontSourceStyleValue::create(url.release_value(), move(format)); + return FontSourceStyleValue::create(url.release_value(), move(format), move(tech)); } NonnullRefPtr Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved) diff --git a/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp index eae65e1e34e..2754b0661ff 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.cpp @@ -9,10 +9,11 @@ namespace Web::CSS { -FontSourceStyleValue::FontSourceStyleValue(Source source, Optional format) +FontSourceStyleValue::FontSourceStyleValue(Source source, Optional format, Vector tech) : StyleValueWithDefaultOperators(Type::FontSource) , m_source(move(source)) , m_format(move(format)) + , m_tech(move(tech)) { } @@ -36,7 +37,6 @@ String FontSourceStyleValue::to_string(SerializationMode) const }, [this](URL const& url) { // [ format()]? [ tech( #)]? - // FIXME: tech() StringBuilder builder; builder.append(url.to_string()); @@ -46,6 +46,14 @@ String FontSourceStyleValue::to_string(SerializationMode) const builder.append(")"sv); } + if (!m_tech.is_empty()) { + builder.append(" tech("sv); + serialize_a_comma_separated_list(builder, m_tech, [](auto& builder, FontTech const tech) { + return builder.append(CSS::to_string(tech)); + }); + builder.append(")"sv); + } + return builder.to_string_without_validation(); }); } diff --git a/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h index 60fed041d8c..1bd426a2267 100644 --- a/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/FontSourceStyleValue.h @@ -8,6 +8,7 @@ #include #include +#include #include namespace Web::CSS { @@ -19,24 +20,26 @@ public: }; using Source = Variant; - static ValueComparingNonnullRefPtr create(Source source, Optional format) + static ValueComparingNonnullRefPtr create(Source source, Optional format, Vector tech) { - return adopt_ref(*new (nothrow) FontSourceStyleValue(move(source), move(format))); + return adopt_ref(*new (nothrow) FontSourceStyleValue(move(source), move(format), move(tech))); } virtual ~FontSourceStyleValue() override; Source const& source() const { return m_source; } Optional const& format() const { return m_format; } + Vector const& tech() const { return m_tech; } virtual String to_string(SerializationMode) const override; bool properties_equal(FontSourceStyleValue const&) const; private: - FontSourceStyleValue(Source source, Optional format); + FontSourceStyleValue(Source source, Optional format, Vector tech); Source m_source; Optional m_format; + Vector m_tech; }; } 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 b865e7674f0..ac27aa3efe5 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,17 +2,17 @@ Harness status: OK Found 39 tests -28 Pass -11 Fail +34 Pass +5 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 +Pass 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 -Fail Check that src: url("foo.ttf") tech(color-COLRv1) is valid +Pass Check that src: url("foo.ttf") tech(color-COLRv0) is valid +Pass Check that src: url("foo.ttf") tech(color-COLRv1) is valid 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 +Pass Check that src: url("foo.ttf") tech(variations) is valid Fail Check that src: url("foo.ttf") tech(palettes) is valid Pass Check that src: url("foo.ttf") tech("features-opentype") is invalid Pass Check that src: url("foo.ttf") tech("color-COLRv0") is invalid @@ -31,7 +31,7 @@ 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 +Pass Check that src: url("foo.ttf") format(opentype) tech(features-opentype) is valid Pass Check that src: url("foo.ttf") tech(features-opentype) format(opentype) is invalid Pass Check that src: url("foo.ttf") tech(incremental), url("bar.html") is valid Pass Check that src: url("foo.ttf") tech(incremental, color-SVG, features-graphite, features-aat), url("bar.html") is valid @@ -41,5 +41,5 @@ Pass Check that src: url("foo.ttf") tech(features-graphite), url("bar.html") is 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 +Pass Check that src: url("foo.ttf") format(dummy), url("foo.ttf") tech(variations) is valid Pass Check that src: url("foo.ttf") tech(color), url("bar.html") is valid \ No newline at end of file