mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 00:31:14 +00:00
LibWeb: Parse @font-face
rules
This is very limited for now, only caring about `font-family` and `src`.
This commit is contained in:
parent
804b8c85e8
commit
6672c19c93
Notes:
sideshowbarker
2024-07-17 16:36:59 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/6672c19c93 Pull-request: https://github.com/SerenityOS/serenity/pull/13318
|
@ -11,6 +11,7 @@
|
|||
#include <AK/Debug.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/SourceLocation.h>
|
||||
#include <LibWeb/CSS/CSSFontFaceRule.h>
|
||||
#include <LibWeb/CSS/CSSImportRule.h>
|
||||
#include <LibWeb/CSS/CSSMediaRule.h>
|
||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||
|
@ -2054,23 +2055,13 @@ RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
|
|||
if (rule->m_type == StyleRule::Type::At) {
|
||||
if (has_ignored_vendor_prefix(rule->m_name)) {
|
||||
return {};
|
||||
} else if (rule->m_name.equals_ignoring_case("media"sv)) {
|
||||
|
||||
auto media_query_tokens = TokenStream { rule->prelude() };
|
||||
auto media_query_list = parse_a_media_query_list(media_query_tokens);
|
||||
if (media_query_list.is_empty() || !rule->block())
|
||||
} else if (rule->m_name.equals_ignoring_case("font-face"sv)) {
|
||||
if (rule->prelude().is_empty() || !rule->block()->is_curly()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "@font-face rule is malformed.");
|
||||
return {};
|
||||
|
||||
auto child_tokens = TokenStream { rule->block()->values() };
|
||||
auto parser_rules = consume_a_list_of_rules(child_tokens, false);
|
||||
NonnullRefPtrVector<CSSRule> child_rules;
|
||||
for (auto& raw_rule : parser_rules) {
|
||||
if (auto child_rule = convert_to_rule(raw_rule))
|
||||
child_rules.append(*child_rule);
|
||||
}
|
||||
|
||||
return CSSMediaRule::create(MediaList::create(move(media_query_list)), move(child_rules));
|
||||
|
||||
TokenStream tokens { rule->block()->values() };
|
||||
return parse_font_face_rule(tokens);
|
||||
} else if (rule->m_name.equals_ignoring_case("import"sv) && !rule->prelude().is_empty()) {
|
||||
|
||||
Optional<AK::URL> url;
|
||||
|
@ -2094,6 +2085,23 @@ RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
|
|||
else
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Unable to parse url from @import rule");
|
||||
|
||||
} else if (rule->m_name.equals_ignoring_case("media"sv)) {
|
||||
|
||||
auto media_query_tokens = TokenStream { rule->prelude() };
|
||||
auto media_query_list = parse_a_media_query_list(media_query_tokens);
|
||||
if (media_query_list.is_empty() || !rule->block())
|
||||
return {};
|
||||
|
||||
auto child_tokens = TokenStream { rule->block()->values() };
|
||||
auto parser_rules = consume_a_list_of_rules(child_tokens, false);
|
||||
NonnullRefPtrVector<CSSRule> child_rules;
|
||||
for (auto& raw_rule : parser_rules) {
|
||||
if (auto child_rule = convert_to_rule(raw_rule))
|
||||
child_rules.append(*child_rule);
|
||||
}
|
||||
|
||||
return CSSMediaRule::create(MediaList::create(move(media_query_list)), move(child_rules));
|
||||
|
||||
} else if (rule->m_name.equals_ignoring_case("supports"sv)) {
|
||||
|
||||
auto supports_tokens = TokenStream { rule->prelude() };
|
||||
|
@ -3610,6 +3618,24 @@ RefPtr<StyleValue> Parser::parse_flex_flow_value(Vector<StyleComponentValueRule>
|
|||
return FlexFlowStyleValue::create(flex_direction.release_nonnull(), flex_wrap.release_nonnull());
|
||||
}
|
||||
|
||||
static bool is_generic_font_family(ValueID identifier)
|
||||
{
|
||||
switch (identifier) {
|
||||
case ValueID::Cursive:
|
||||
case ValueID::Fantasy:
|
||||
case ValueID::Monospace:
|
||||
case ValueID::Serif:
|
||||
case ValueID::SansSerif:
|
||||
case ValueID::UiMonospace:
|
||||
case ValueID::UiRounded:
|
||||
case ValueID::UiSerif:
|
||||
case ValueID::UiSansSerif:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_font_value(Vector<StyleComponentValueRule> const& component_values)
|
||||
{
|
||||
RefPtr<StyleValue> font_style;
|
||||
|
@ -3703,23 +3729,6 @@ RefPtr<StyleValue> Parser::parse_font_value(Vector<StyleComponentValueRule> cons
|
|||
|
||||
RefPtr<StyleValue> Parser::parse_font_family_value(Vector<StyleComponentValueRule> const& component_values, size_t start_index)
|
||||
{
|
||||
auto is_generic_font_family = [](ValueID identifier) -> bool {
|
||||
switch (identifier) {
|
||||
case ValueID::Cursive:
|
||||
case ValueID::Fantasy:
|
||||
case ValueID::Monospace:
|
||||
case ValueID::Serif:
|
||||
case ValueID::SansSerif:
|
||||
case ValueID::UiMonospace:
|
||||
case ValueID::UiRounded:
|
||||
case ValueID::UiSerif:
|
||||
case ValueID::UiSansSerif:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto is_comma_or_eof = [&](size_t i) -> bool {
|
||||
if (i < component_values.size()) {
|
||||
auto& maybe_comma = component_values[i];
|
||||
|
@ -3791,6 +3800,101 @@ RefPtr<StyleValue> Parser::parse_font_family_value(Vector<StyleComponentValueRul
|
|||
return StyleValueList::create(move(font_families), StyleValueList::Separator::Comma);
|
||||
}
|
||||
|
||||
RefPtr<CSSRule> Parser::parse_font_face_rule(TokenStream<StyleComponentValueRule>& tokens)
|
||||
{
|
||||
auto declarations_and_at_rules = parse_a_list_of_declarations(tokens);
|
||||
|
||||
Optional<FlyString> font_family;
|
||||
Vector<FontFace::Source> src;
|
||||
|
||||
for (auto& declaration_or_at_rule : declarations_and_at_rules) {
|
||||
if (declaration_or_at_rule.is_at_rule()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: CSS at-rules are not allowed in @font-family; discarding.");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& declaration = declaration_or_at_rule.declaration();
|
||||
if (declaration.m_name.equals_ignoring_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<String> font_family_parts;
|
||||
bool had_syntax_error = false;
|
||||
for (size_t i = 0; i < declaration.m_values.size(); ++i) {
|
||||
auto& part = declaration.m_values[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)) {
|
||||
auto value_id = value_id_from_string(part.token().ident());
|
||||
if (is_generic_font_family(value_id)) {
|
||||
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())
|
||||
continue;
|
||||
|
||||
font_family = String::join(' ', font_family_parts);
|
||||
continue;
|
||||
}
|
||||
if (declaration.m_name.equals_ignoring_case("src"sv)) {
|
||||
Vector<FontFace::Source> supported_sources;
|
||||
// FIXME: Implement `local()`.
|
||||
// FIXME: Implement `format()`.
|
||||
TokenStream token_stream { declaration.m_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);
|
||||
}
|
||||
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Unrecognized descriptor '{}' in @font-family; discarding.", declaration.m_name);
|
||||
}
|
||||
|
||||
if (!font_family.has_value()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: Failed to parse @font-face: no font-family!");
|
||||
return {};
|
||||
}
|
||||
|
||||
return CSSFontFaceRule::create(FontFace { font_family.release_value(), move(src) });
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_list_style_value(Vector<StyleComponentValueRule> const& component_values)
|
||||
{
|
||||
if (component_values.size() > 3)
|
||||
|
@ -4955,6 +5059,13 @@ bool Parser::has_ignored_vendor_prefix(StringView string)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Parser::is_builtin(StringView name)
|
||||
{
|
||||
return name.equals_ignoring_case("inherit"sv)
|
||||
|| name.equals_ignoring_case("initial"sv)
|
||||
|| name.equals_ignoring_case("unset"sv);
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_css_value(Badge<StyleComputer>, ParsingContext const& context, PropertyID property_id, Vector<StyleComponentValueRule> const& tokens)
|
||||
{
|
||||
if (tokens.is_empty() || property_id == CSS::PropertyID::Invalid || property_id == CSS::PropertyID::Custom)
|
||||
|
|
|
@ -188,6 +188,8 @@ private:
|
|||
|
||||
[[nodiscard]] Optional<GeneralEnclosed> parse_general_enclosed(TokenStream<StyleComponentValueRule>&);
|
||||
|
||||
RefPtr<CSSRule> parse_font_face_rule(TokenStream<StyleComponentValueRule>&);
|
||||
|
||||
[[nodiscard]] RefPtr<CSSRule> convert_to_rule(NonnullRefPtr<StyleRule>);
|
||||
[[nodiscard]] RefPtr<PropertyOwningCSSStyleDeclaration> convert_to_style_declaration(Vector<DeclarationOrAtRule> declarations);
|
||||
[[nodiscard]] Optional<StyleProperty> convert_to_style_property(StyleDeclarationRule const&);
|
||||
|
@ -339,6 +341,7 @@ private:
|
|||
Optional<Supports::Feature> parse_supports_feature(TokenStream<StyleComponentValueRule>&);
|
||||
|
||||
static bool has_ignored_vendor_prefix(StringView);
|
||||
static bool is_builtin(StringView);
|
||||
|
||||
ParsingContext m_context;
|
||||
|
||||
|
|
Loading…
Reference in a new issue