summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/CSS
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@serenityos.org>2022-03-31 21:18:54 +0100
committerAndreas Kling <kling@serenityos.org>2022-04-07 21:20:14 +0200
commit12b8570ce39519ee3e21b672fda21ad5e1a1fe4f (patch)
tree443d0754826c1c34c9d5420e9ba707a7e898dd22 /Userland/Libraries/LibWeb/CSS
parentdbb0b68175613885654cc0541e19d0792169d52d (diff)
downloadserenity-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.h2
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp113
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.h2
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);