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 format; }; FontFace(FlyString font_family, Vector sources, Vector 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 Parser::parse_font_face_rule(TokenStream& tokens continue; } if (declaration.name().equals_ignoring_case("src"sv)) { - Vector 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 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 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 Parser::parse_font_face_rule(TokenStream& tokens return CSSFontFaceRule::create(FontFace { font_family.release_value(), move(src), move(unicode_range) }); } +Vector Parser::parse_font_face_src(TokenStream& 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 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(); + + // [ 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 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 Parser::parse_list_style_value(Vector 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 #include #include +#include #include #include #include @@ -204,6 +205,7 @@ private: Optional parse_general_enclosed(TokenStream&); RefPtr parse_font_face_rule(TokenStream&); + Vector parse_font_face_src(TokenStream&); RefPtr convert_to_rule(NonnullRefPtr); RefPtr convert_to_style_declaration(Vector declarations); diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index 98079aae46..c08352f876 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -578,7 +578,7 @@ void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rul builder.append("sources:\n"); for (auto const& source : font_face.sources()) { indent(builder, indent_levels + 2); - builder.appendff("{}\n", source.url); + builder.appendff("url={}, format={}\n", source.url, source.format.value_or("???")); } indent(builder, indent_levels + 1);