mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-08 01:00:05 +00:00
LibWeb/CSS: Split out @keyframes
parsing code
Changes are very minimal, this is just a code move.
This commit is contained in:
parent
e5737232c0
commit
d5f3a610ac
Notes:
github-actions[bot]
2024-08-10 08:39:01 +00:00
Author: https://github.com/AtkinsSJ
Commit: d5f3a610ac
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1018
Reviewed-by: https://github.com/awesomekling
2 changed files with 117 additions and 103 deletions
|
@ -1259,112 +1259,12 @@ CSSRule* Parser::convert_to_rule(NonnullRefPtr<Rule> rule)
|
||||||
if (rule->at_rule_name().equals_ignoring_ascii_case("import"sv))
|
if (rule->at_rule_name().equals_ignoring_ascii_case("import"sv))
|
||||||
return convert_to_import_rule(rule);
|
return convert_to_import_rule(rule);
|
||||||
|
|
||||||
|
if (rule->at_rule_name().equals_ignoring_ascii_case("keyframes"sv))
|
||||||
|
return convert_to_keyframes_rule(rule);
|
||||||
|
|
||||||
if (rule->at_rule_name().equals_ignoring_ascii_case("media"sv))
|
if (rule->at_rule_name().equals_ignoring_ascii_case("media"sv))
|
||||||
return convert_to_media_rule(rule);
|
return convert_to_media_rule(rule);
|
||||||
|
|
||||||
if (rule->at_rule_name().equals_ignoring_ascii_case("keyframes"sv)) {
|
|
||||||
auto prelude_stream = TokenStream { rule->prelude() };
|
|
||||||
prelude_stream.skip_whitespace();
|
|
||||||
auto token = prelude_stream.next_token();
|
|
||||||
if (!token.is_token()) {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes has invalid prelude, prelude = {}; discarding.", rule->prelude());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto name_token = token.token();
|
|
||||||
prelude_stream.skip_whitespace();
|
|
||||||
|
|
||||||
if (prelude_stream.has_next_token()) {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes has invalid prelude, prelude = {}; discarding.", rule->prelude());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name_token.is(Token::Type::Ident) && (is_css_wide_keyword(name_token.ident()) || name_token.ident().equals_ignoring_ascii_case("none"sv))) {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule name is invalid: {}; discarding.", name_token.ident());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!name_token.is(Token::Type::String) && !name_token.is(Token::Type::Ident)) {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule name is invalid: {}; discarding.", name_token.to_debug_string());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto name = name_token.to_string();
|
|
||||||
|
|
||||||
if (!rule->block())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto child_tokens = TokenStream { rule->block()->values() };
|
|
||||||
|
|
||||||
JS::MarkedVector<CSSRule*> keyframes(m_context.realm().heap());
|
|
||||||
while (child_tokens.has_next_token()) {
|
|
||||||
child_tokens.skip_whitespace();
|
|
||||||
// keyframe-selector = <keyframe-keyword> | <percentage>
|
|
||||||
// keyframe-keyword = "from" | "to"
|
|
||||||
// selector = <keyframe-selector>#
|
|
||||||
// keyframes-block = "{" <declaration-list>? "}"
|
|
||||||
// keyframe-rule = <selector> <keyframes-block>
|
|
||||||
|
|
||||||
auto selectors = Vector<CSS::Percentage> {};
|
|
||||||
while (child_tokens.has_next_token()) {
|
|
||||||
child_tokens.skip_whitespace();
|
|
||||||
if (!child_tokens.has_next_token())
|
|
||||||
break;
|
|
||||||
auto tok = child_tokens.next_token();
|
|
||||||
if (!tok.is_token()) {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule has invalid selector: {}; discarding.", tok.to_debug_string());
|
|
||||||
child_tokens.reconsume_current_input_token();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto token = tok.token();
|
|
||||||
auto read_a_selector = false;
|
|
||||||
if (token.is(Token::Type::Ident)) {
|
|
||||||
if (token.ident().equals_ignoring_ascii_case("from"sv)) {
|
|
||||||
selectors.append(CSS::Percentage(0));
|
|
||||||
read_a_selector = true;
|
|
||||||
}
|
|
||||||
if (token.ident().equals_ignoring_ascii_case("to"sv)) {
|
|
||||||
selectors.append(CSS::Percentage(100));
|
|
||||||
read_a_selector = true;
|
|
||||||
}
|
|
||||||
} else if (token.is(Token::Type::Percentage)) {
|
|
||||||
selectors.append(CSS::Percentage(token.percentage()));
|
|
||||||
read_a_selector = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read_a_selector) {
|
|
||||||
child_tokens.skip_whitespace();
|
|
||||||
if (child_tokens.next_token().is(Token::Type::Comma))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
child_tokens.reconsume_current_input_token();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!child_tokens.has_next_token())
|
|
||||||
break;
|
|
||||||
|
|
||||||
child_tokens.skip_whitespace();
|
|
||||||
auto token = child_tokens.next_token();
|
|
||||||
if (token.is_block()) {
|
|
||||||
auto block_tokens = token.block().values();
|
|
||||||
auto block_stream = TokenStream { block_tokens };
|
|
||||||
|
|
||||||
auto block_declarations = parse_a_list_of_declarations(block_stream);
|
|
||||||
auto style = convert_to_style_declaration(block_declarations);
|
|
||||||
for (auto& selector : selectors) {
|
|
||||||
auto keyframe_rule = CSSKeyframeRule::create(m_context.realm(), selector, *style);
|
|
||||||
keyframes.append(keyframe_rule);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule has invalid block: {}; discarding.", token.to_debug_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CSSKeyframesRule::create(m_context.realm(), name, CSSRuleList::create(m_context.realm(), move(keyframes)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rule->at_rule_name().equals_ignoring_ascii_case("namespace"sv))
|
if (rule->at_rule_name().equals_ignoring_ascii_case("namespace"sv))
|
||||||
return convert_to_namespace_rule(rule);
|
return convert_to_namespace_rule(rule);
|
||||||
|
|
||||||
|
@ -1458,6 +1358,119 @@ JS::GCPtr<CSSImportRule> Parser::convert_to_import_rule(Rule& rule)
|
||||||
return CSSImportRule::create(url.value(), const_cast<DOM::Document&>(*m_context.document()));
|
return CSSImportRule::create(url.value(), const_cast<DOM::Document&>(*m_context.document()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS::GCPtr<CSSKeyframesRule> Parser::convert_to_keyframes_rule(Rule& rule)
|
||||||
|
{
|
||||||
|
// https://www.w3.org/TR/css-animations-1/#keyframes
|
||||||
|
|
||||||
|
if (rule.prelude().is_empty()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @keyframes rule: Empty prelude.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rule.block()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @keyframes rule: No block.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prelude_stream = TokenStream { rule.prelude() };
|
||||||
|
prelude_stream.skip_whitespace();
|
||||||
|
auto& token = prelude_stream.next_token();
|
||||||
|
if (!token.is_token()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes has invalid prelude, prelude = {}; discarding.", rule.prelude());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name_token = token.token();
|
||||||
|
prelude_stream.skip_whitespace();
|
||||||
|
|
||||||
|
if (prelude_stream.has_next_token()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes has invalid prelude, prelude = {}; discarding.", rule.prelude());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name_token.is(Token::Type::Ident) && (is_css_wide_keyword(name_token.ident()) || name_token.ident().equals_ignoring_ascii_case("none"sv))) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule name is invalid: {}; discarding.", name_token.ident());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name_token.is(Token::Type::String) && !name_token.is(Token::Type::Ident)) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule name is invalid: {}; discarding.", name_token.to_debug_string());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = name_token.to_string();
|
||||||
|
|
||||||
|
auto child_tokens = TokenStream { rule.block()->values() };
|
||||||
|
|
||||||
|
JS::MarkedVector<CSSRule*> keyframes(m_context.realm().heap());
|
||||||
|
while (child_tokens.has_next_token()) {
|
||||||
|
child_tokens.skip_whitespace();
|
||||||
|
// keyframe-selector = <keyframe-keyword> | <percentage>
|
||||||
|
// keyframe-keyword = "from" | "to"
|
||||||
|
// selector = <keyframe-selector>#
|
||||||
|
// keyframes-block = "{" <declaration-list>? "}"
|
||||||
|
// keyframe-rule = <selector> <keyframes-block>
|
||||||
|
|
||||||
|
auto selectors = Vector<CSS::Percentage> {};
|
||||||
|
while (child_tokens.has_next_token()) {
|
||||||
|
child_tokens.skip_whitespace();
|
||||||
|
if (!child_tokens.has_next_token())
|
||||||
|
break;
|
||||||
|
auto tok = child_tokens.next_token();
|
||||||
|
if (!tok.is_token()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule has invalid selector: {}; discarding.", tok.to_debug_string());
|
||||||
|
child_tokens.reconsume_current_input_token();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto token = tok.token();
|
||||||
|
auto read_a_selector = false;
|
||||||
|
if (token.is(Token::Type::Ident)) {
|
||||||
|
if (token.ident().equals_ignoring_ascii_case("from"sv)) {
|
||||||
|
selectors.append(CSS::Percentage(0));
|
||||||
|
read_a_selector = true;
|
||||||
|
}
|
||||||
|
if (token.ident().equals_ignoring_ascii_case("to"sv)) {
|
||||||
|
selectors.append(CSS::Percentage(100));
|
||||||
|
read_a_selector = true;
|
||||||
|
}
|
||||||
|
} else if (token.is(Token::Type::Percentage)) {
|
||||||
|
selectors.append(CSS::Percentage(token.percentage()));
|
||||||
|
read_a_selector = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_a_selector) {
|
||||||
|
child_tokens.skip_whitespace();
|
||||||
|
if (child_tokens.next_token().is(Token::Type::Comma))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
child_tokens.reconsume_current_input_token();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!child_tokens.has_next_token())
|
||||||
|
break;
|
||||||
|
|
||||||
|
child_tokens.skip_whitespace();
|
||||||
|
auto token = child_tokens.next_token();
|
||||||
|
if (token.is_block()) {
|
||||||
|
auto block_tokens = token.block().values();
|
||||||
|
auto block_stream = TokenStream { block_tokens };
|
||||||
|
|
||||||
|
auto block_declarations = parse_a_list_of_declarations(block_stream);
|
||||||
|
auto style = convert_to_style_declaration(block_declarations);
|
||||||
|
for (auto& selector : selectors) {
|
||||||
|
auto keyframe_rule = CSSKeyframeRule::create(m_context.realm(), selector, *style);
|
||||||
|
keyframes.append(keyframe_rule);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "CSSParser: @keyframes rule has invalid block: {}; discarding.", token.to_debug_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CSSKeyframesRule::create(m_context.realm(), name, CSSRuleList::create(m_context.realm(), move(keyframes)));
|
||||||
|
}
|
||||||
|
|
||||||
JS::GCPtr<CSSNamespaceRule> Parser::convert_to_namespace_rule(Rule& rule)
|
JS::GCPtr<CSSNamespaceRule> Parser::convert_to_namespace_rule(Rule& rule)
|
||||||
{
|
{
|
||||||
// https://drafts.csswg.org/css-namespaces/#syntax
|
// https://drafts.csswg.org/css-namespaces/#syntax
|
||||||
|
|
|
@ -222,6 +222,7 @@ private:
|
||||||
|
|
||||||
CSSRule* convert_to_rule(NonnullRefPtr<Rule>);
|
CSSRule* convert_to_rule(NonnullRefPtr<Rule>);
|
||||||
CSSMediaRule* convert_to_media_rule(NonnullRefPtr<Rule>);
|
CSSMediaRule* convert_to_media_rule(NonnullRefPtr<Rule>);
|
||||||
|
JS::GCPtr<CSSKeyframesRule> convert_to_keyframes_rule(Rule&);
|
||||||
JS::GCPtr<CSSImportRule> convert_to_import_rule(Rule&);
|
JS::GCPtr<CSSImportRule> convert_to_import_rule(Rule&);
|
||||||
JS::GCPtr<CSSNamespaceRule> convert_to_namespace_rule(Rule&);
|
JS::GCPtr<CSSNamespaceRule> convert_to_namespace_rule(Rule&);
|
||||||
JS::GCPtr<CSSSupportsRule> convert_to_supports_rule(Rule&);
|
JS::GCPtr<CSSSupportsRule> convert_to_supports_rule(Rule&);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue