diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2022-03-31 21:18:54 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-04-07 21:20:14 +0200 |
commit | 12b8570ce39519ee3e21b672fda21ad5e1a1fe4f (patch) | |
tree | 443d0754826c1c34c9d5420e9ba707a7e898dd22 /Userland/Libraries/LibWeb/CSS | |
parent | dbb0b68175613885654cc0541e19d0792169d52d (diff) | |
download | serenity-12b8570ce39519ee3e21b672fda21ad5e1a1fe4f.zip |
LibWeb: Understand the `format()` part of a `@font-face`'s `src`
This is used to skip downloading fonts in formats that we don't support.
Currently we only support TTF as far as I am aware.
The parts of a `src` are in a fixed order, unusually, which makes the
parsing more nesty instead of loopy.
Diffstat (limited to 'Userland/Libraries/LibWeb/CSS')
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/FontFace.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 113 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 2 |
3 files changed, 90 insertions, 27 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/FontFace.h b/Userland/Libraries/LibWeb/CSS/FontFace.h index 821a33cd46..cbabd7a018 100644 --- a/Userland/Libraries/LibWeb/CSS/FontFace.h +++ b/Userland/Libraries/LibWeb/CSS/FontFace.h @@ -16,6 +16,8 @@ class FontFace { public: struct Source { AK::URL url; + // FIXME: Do we need to keep this around, or is it only needed to discard unwanted formats during parsing? + Optional<FlyString> format; }; FontFace(FlyString font_family, Vector<Source> sources, Vector<UnicodeRange> unicode_ranges); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 53da4a29dd..4961ac76f0 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -4438,34 +4438,10 @@ RefPtr<CSSRule> Parser::parse_font_face_rule(TokenStream<ComponentValue>& tokens continue; } if (declaration.name().equals_ignoring_case("src"sv)) { - Vector<FontFace::Source> supported_sources; - // FIXME: Implement `local()`. - // FIXME: Implement `format()`. TokenStream token_stream { declaration.values() }; - auto list_of_source_token_lists = parse_a_comma_separated_list_of_component_values(token_stream); - for (auto const& source_token_list : list_of_source_token_lists) { - Optional<AK::URL> url; - bool had_syntax_error = false; - for (auto const& source_token : source_token_list) { - // FIXME: Allow data urls for fonts. - if (auto maybe_url = parse_url_function(source_token); maybe_url.has_value()) { - if (url.has_value()) { - dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src format invalid; discarding."); - had_syntax_error = true; - break; - } - url = maybe_url.release_value(); - } - } - if (had_syntax_error) - continue; - if (!url.has_value()) - continue; - supported_sources.empend(url.release_value()); - } - if (supported_sources.is_empty()) - continue; - src = move(supported_sources); + Vector<FontFace::Source> supported_sources = parse_font_face_src(token_stream); + if (!supported_sources.is_empty()) + src = move(supported_sources); continue; } if (declaration.name().equals_ignoring_case("unicode-range"sv)) { @@ -4506,6 +4482,89 @@ RefPtr<CSSRule> Parser::parse_font_face_rule(TokenStream<ComponentValue>& tokens return CSSFontFaceRule::create(FontFace { font_family.release_value(), move(src), move(unicode_range) }); } +Vector<FontFace::Source> Parser::parse_font_face_src(TokenStream<ComponentValue>& component_values) +{ + // FIXME: Get this information from the system somehow? + // Format-name table: https://www.w3.org/TR/css-fonts-4/#font-format-definitions + auto font_format_is_supported = [](StringView name) { + // The spec requires us to treat opentype and truetype as synonymous. + if (name.is_one_of_ignoring_case("opentype"sv, "truetype"sv)) + return true; + return false; + }; + + Vector<FontFace::Source> supported_sources; + + auto list_of_source_token_lists = parse_a_comma_separated_list_of_component_values(component_values); + for (auto const& source_token_list : list_of_source_token_lists) { + TokenStream source_tokens { source_token_list }; + source_tokens.skip_whitespace(); + auto& first = source_tokens.next_token(); + + // <url> [ format(<font-format>)]? + // FIXME: Allow data urls for fonts. + // FIXME: Implement optional tech() function from CSS-Fonts-4. + if (auto maybe_url = parse_url_function(first); maybe_url.has_value()) { + auto url = maybe_url.release_value(); + Optional<FlyString> format; + + source_tokens.skip_whitespace(); + if (!source_tokens.has_next_token()) { + supported_sources.empend(move(url), format); + continue; + } + + auto maybe_function = source_tokens.next_token(); + if (!maybe_function.is_function()) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (token after `url()` that isn't a function: {}); discarding.", maybe_function.to_debug_string()); + return {}; + } + + auto& function = maybe_function.function(); + if (function.name().equals_ignoring_case("format"sv)) { + TokenStream format_tokens { function.values() }; + format_tokens.skip_whitespace(); + auto& format_name_token = format_tokens.next_token(); + StringView format_name; + if (format_name_token.is(Token::Type::Ident)) { + format_name = format_name_token.token().ident(); + } else if (format_name_token.is(Token::Type::String)) { + format_name = format_name_token.token().string(); + } else { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (`format()` parameter not an ident or string; is: {}); discarding.", format_name_token.to_debug_string()); + return {}; + } + + if (!font_format_is_supported(format_name)) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src format({}) not supported; skipping.", format_name); + continue; + } + + format = format_name; + } else { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (unrecognized function token `{}`); discarding.", function.name()); + return {}; + } + + source_tokens.skip_whitespace(); + if (source_tokens.has_next_token()) { + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (extra token `{}`); discarding.", source_tokens.peek_token().to_debug_string()); + return {}; + } + + supported_sources.empend(move(url), format); + continue; + + } else { + // FIXME: Implement `local()`. + dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @font-face src invalid (failed to parse url from: {}); discarding.", first.to_debug_string()); + return {}; + } + } + + return supported_sources; +} + RefPtr<StyleValue> Parser::parse_list_style_value(Vector<ComponentValue> const& component_values) { if (component_values.size() > 3) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 699e150060..df67e78445 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -13,6 +13,7 @@ #include <AK/Result.h> #include <AK/Vector.h> #include <LibWeb/CSS/CSSStyleDeclaration.h> +#include <LibWeb/CSS/FontFace.h> #include <LibWeb/CSS/GeneralEnclosed.h> #include <LibWeb/CSS/MediaQuery.h> #include <LibWeb/CSS/Parser/ComponentValue.h> @@ -204,6 +205,7 @@ private: Optional<GeneralEnclosed> parse_general_enclosed(TokenStream<ComponentValue>&); RefPtr<CSSRule> parse_font_face_rule(TokenStream<ComponentValue>&); + Vector<FontFace::Source> parse_font_face_src(TokenStream<ComponentValue>&); RefPtr<CSSRule> convert_to_rule(NonnullRefPtr<StyleRule>); RefPtr<PropertyOwningCSSStyleDeclaration> convert_to_style_declaration(Vector<DeclarationOrAtRule> declarations); |