LibWeb: Get CSS @import rules working in new parser

Also added css-import.html, which tests the 3 syntax variations on
`@import` statements. Note that the optional media-query parameter to
`@import` is not handled yet.
This commit is contained in:
Sam Atkins 2021-07-22 17:51:07 +01:00 committed by Andreas Kling
parent 8b2e76b838
commit 7439fbd896
9 changed files with 82 additions and 38 deletions

View file

@ -0,0 +1 @@
p.first { background-color: lime; }

View file

@ -0,0 +1 @@
p.second { background-color: lime; }

View file

@ -0,0 +1 @@
p.third { background-color: lime; }

View file

@ -0,0 +1,17 @@
<html>
<head>
<title>@import test</title>
<style>
@import "css-import-1.css";
@import url("css-import-2.css");
@import url(css-import-3.css);
</style>
</head>
<body>
<div>
<p class="first">If this is green, <code>@import "string";</code> works!</p>
<p class="second">If this is green, <code>@import url("string");</code> works!</p>
<p class="third">If this is green, <code>@import url(unquoted-string);</code> works!</p>
</div>
</body>
</html>

View file

@ -62,6 +62,7 @@
<li><a href="cookie.html">document.cookie</a></li>
<li><a href="last-of-type.html">CSS :last-of-type selector</a></li>
<li><a href="first-of-type.html">CSS :first-of-type selector</a></li>
<li><a href="css-import.html">CSS @import</a></li>
<li><a href="background-repeat-test.html">background image with repetition rules</a></li>
<li><a href="link-over-zindex-block.html">link elements with background box placed with z-index</a></li>
<li><a href="contenteditable.html">contenteditable</a></li>

View file

@ -131,8 +131,13 @@ template<typename T>
void TokenStream<T>::dump_all_tokens()
{
dbgln("Dumping all tokens:");
for (auto& token : m_tokens)
dbgln("{}", token.to_debug_string());
for (size_t i = 0; i < m_tokens.size(); ++i) {
auto& token = m_tokens[i];
if ((i - 1) == (size_t)m_iterator_offset)
dbgln("-> {}", token.to_debug_string());
else
dbgln(" {}", token.to_debug_string());
}
}
Parser::Parser(ParsingContext const& context, StringView const& input, String const& encoding)
@ -623,10 +628,10 @@ NonnullRefPtr<StyleRule> Parser::consume_an_at_rule(TokenStream<T>& tokens)
dbgln_if(CSS_PARSER_TRACE, "Parser::consume_an_at_rule");
auto name_ident = tokens.next_token();
VERIFY(name_ident.is(Token::Type::Ident));
VERIFY(name_ident.is(Token::Type::AtKeyword));
NonnullRefPtr<StyleRule> rule = create<StyleRule>(StyleRule::Type::At);
rule->m_name = ((Token)name_ident).ident();
rule->m_name = ((Token)name_ident).at_keyword();
for (;;) {
auto token = tokens.next_token();
@ -1112,6 +1117,31 @@ Vector<Vector<StyleComponentValueRule>> Parser::parse_as_comma_separated_list_of
return lists;
}
Optional<URL> Parser::parse_url_function(ParsingContext const& context, StyleComponentValueRule const& component_value)
{
// FIXME: Handle list of media queries. https://www.w3.org/TR/css-cascade-3/#conditional-import
if (component_value.is(Token::Type::Url))
return context.complete_url(component_value.token().url());
if (component_value.is_function() && component_value.function().name().equals_ignoring_case("url")) {
auto& function_values = component_value.function().values();
// FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
for (size_t i = 0; i < function_values.size(); ++i) {
auto& value = function_values[i];
if (value.is(Token::Type::Whitespace))
continue;
if (value.is(Token::Type::String)) {
// FIXME: RFC2397
if (value.token().string().starts_with("data:"))
break;
return context.complete_url(value.token().string());
}
}
}
return {};
}
RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
{
dbgln_if(CSS_PARSER_TRACE, "Parser::convert_to_rule");
@ -1119,25 +1149,26 @@ RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
if (rule->m_type == StyleRule::Type::At) {
if (rule->m_name.equals_ignoring_case("import"sv) && !rule->prelude().is_empty()) {
Optional<String> url;
auto url_token = rule->prelude().first();
if (url_token.is_function()) {
auto& function = url_token.function();
if (function.name().equals_ignoring_case("url"sv) && !function.values().is_empty()) {
auto& argument_token = url_token.function().values().first();
if (argument_token.is(Token::Type::String))
url = argument_token.token().string();
else
dbgln("First argument to url() was not a string: '{}'", argument_token.to_debug_string());
Optional<URL> url;
for (auto& token : rule->prelude()) {
if (token.is(Token::Type::Whitespace))
continue;
if (token.is(Token::Type::String)) {
url = m_context.complete_url(token.token().string());
} else {
url = parse_url_function(m_context, token);
}
// FIXME: Handle list of media queries. https://www.w3.org/TR/css-cascade-3/#conditional-import
if (url.has_value())
break;
}
if (url_token.is(Token::Type::String))
url = url_token.token().string();
// FIXME: Handle list of media queries. https://www.w3.org/TR/css-cascade-3/#conditional-import
if (url.has_value())
return CSSImportRule::create(m_context.complete_url(url.value()));
return CSSImportRule::create(url.value());
else
dbgln("Unable to parse url from @import rule");
} else {
dbgln("Unrecognized CSS at-rule: {}", rule->m_name);
}
@ -1591,24 +1622,9 @@ RefPtr<StyleValue> Parser::parse_string_value(ParsingContext const&, StyleCompon
RefPtr<StyleValue> Parser::parse_image_value(ParsingContext const& context, StyleComponentValueRule const& component_value)
{
if (component_value.is(Token::Type::Url))
return ImageStyleValue::create(context.complete_url(component_value.token().url()), *context.document());
if (component_value.is_function() && component_value.function().name().equals_ignoring_case("url")) {
auto& function_values = component_value.function().values();
// FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
for (size_t i = 0; i < function_values.size(); ++i) {
auto& value = function_values[i];
if (value.is(Token::Type::Whitespace))
continue;
if (value.is(Token::Type::String)) {
// FIXME: RFC2397
if (value.token().string().starts_with("data:"))
continue;
return ImageStyleValue::create(context.complete_url(value.token().string()), *context.document());
}
}
}
auto url = parse_url_function(context, component_value);
if (url.has_value())
return ImageStyleValue::create(url.value(), *context.document());
// FIXME: Handle gradients.
return {};

View file

@ -167,6 +167,7 @@ private:
[[nodiscard]] Optional<StyleProperty> convert_to_style_property(StyleDeclarationRule&);
static Optional<float> try_parse_float(StringView string);
static Optional<URL> parse_url_function(ParsingContext const&, StyleComponentValueRule const&);
static RefPtr<StyleValue> parse_keyword_or_custom_value(ParsingContext const&, StyleComponentValueRule const&);
static RefPtr<StyleValue> parse_length_value(ParsingContext const&, StyleComponentValueRule const&);

View file

@ -82,6 +82,12 @@ public:
return m_value.string_view();
}
StringView at_keyword() const
{
VERIFY(m_type == Type::AtKeyword);
return m_value.string_view();
}
bool is(NumberType number_type) const { return is(Token::Type::Number) && m_number_type == number_type; }
int integer() const

View file

@ -846,7 +846,7 @@ Token Tokenizer::consume_a_token()
if (would_start_an_identifier()) {
auto name = consume_a_name();
return create_value_token(Token::Type::AtKeyword, input);
return create_value_token(Token::Type::AtKeyword, name);
}
return create_value_token(Token::Type::Delim, input);