mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-29 16:21:29 +00:00
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:
parent
8b2e76b838
commit
7439fbd896
Notes:
sideshowbarker
2024-07-18 07:43:07 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/7439fbd8966 Pull-request: https://github.com/SerenityOS/serenity/pull/9073 Reviewed-by: https://github.com/TobyAsE Reviewed-by: https://github.com/alimpfard
1
Base/res/html/misc/css-import-1.css
Normal file
1
Base/res/html/misc/css-import-1.css
Normal file
|
@ -0,0 +1 @@
|
||||||
|
p.first { background-color: lime; }
|
1
Base/res/html/misc/css-import-2.css
Normal file
1
Base/res/html/misc/css-import-2.css
Normal file
|
@ -0,0 +1 @@
|
||||||
|
p.second { background-color: lime; }
|
1
Base/res/html/misc/css-import-3.css
Normal file
1
Base/res/html/misc/css-import-3.css
Normal file
|
@ -0,0 +1 @@
|
||||||
|
p.third { background-color: lime; }
|
17
Base/res/html/misc/css-import.html
Normal file
17
Base/res/html/misc/css-import.html
Normal 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>
|
|
@ -62,6 +62,7 @@
|
||||||
<li><a href="cookie.html">document.cookie</a></li>
|
<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="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="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="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="link-over-zindex-block.html">link elements with background box placed with z-index</a></li>
|
||||||
<li><a href="contenteditable.html">contenteditable</a></li>
|
<li><a href="contenteditable.html">contenteditable</a></li>
|
||||||
|
|
|
@ -131,8 +131,13 @@ template<typename T>
|
||||||
void TokenStream<T>::dump_all_tokens()
|
void TokenStream<T>::dump_all_tokens()
|
||||||
{
|
{
|
||||||
dbgln("Dumping all tokens:");
|
dbgln("Dumping all tokens:");
|
||||||
for (auto& token : m_tokens)
|
for (size_t i = 0; i < m_tokens.size(); ++i) {
|
||||||
dbgln("{}", token.to_debug_string());
|
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)
|
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");
|
dbgln_if(CSS_PARSER_TRACE, "Parser::consume_an_at_rule");
|
||||||
|
|
||||||
auto name_ident = tokens.next_token();
|
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);
|
NonnullRefPtr<StyleRule> rule = create<StyleRule>(StyleRule::Type::At);
|
||||||
rule->m_name = ((Token)name_ident).ident();
|
rule->m_name = ((Token)name_ident).at_keyword();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto token = tokens.next_token();
|
auto token = tokens.next_token();
|
||||||
|
@ -1112,6 +1117,31 @@ Vector<Vector<StyleComponentValueRule>> Parser::parse_as_comma_separated_list_of
|
||||||
return lists;
|
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)
|
RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
|
||||||
{
|
{
|
||||||
dbgln_if(CSS_PARSER_TRACE, "Parser::convert_to_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_type == StyleRule::Type::At) {
|
||||||
if (rule->m_name.equals_ignoring_case("import"sv) && !rule->prelude().is_empty()) {
|
if (rule->m_name.equals_ignoring_case("import"sv) && !rule->prelude().is_empty()) {
|
||||||
|
|
||||||
Optional<String> url;
|
Optional<URL> url;
|
||||||
auto url_token = rule->prelude().first();
|
for (auto& token : rule->prelude()) {
|
||||||
if (url_token.is_function()) {
|
if (token.is(Token::Type::Whitespace))
|
||||||
auto& function = url_token.function();
|
continue;
|
||||||
if (function.name().equals_ignoring_case("url"sv) && !function.values().is_empty()) {
|
|
||||||
auto& argument_token = url_token.function().values().first();
|
if (token.is(Token::Type::String)) {
|
||||||
if (argument_token.is(Token::Type::String))
|
url = m_context.complete_url(token.token().string());
|
||||||
url = argument_token.token().string();
|
} else {
|
||||||
else
|
url = parse_url_function(m_context, token);
|
||||||
dbgln("First argument to url() was not a string: '{}'", argument_token.to_debug_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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())
|
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 {
|
} else {
|
||||||
dbgln("Unrecognized CSS at-rule: {}", rule->m_name);
|
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)
|
RefPtr<StyleValue> Parser::parse_image_value(ParsingContext const& context, StyleComponentValueRule const& component_value)
|
||||||
{
|
{
|
||||||
if (component_value.is(Token::Type::Url))
|
auto url = parse_url_function(context, component_value);
|
||||||
return ImageStyleValue::create(context.complete_url(component_value.token().url()), *context.document());
|
if (url.has_value())
|
||||||
if (component_value.is_function() && component_value.function().name().equals_ignoring_case("url")) {
|
return ImageStyleValue::create(url.value(), *context.document());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FIXME: Handle gradients.
|
// FIXME: Handle gradients.
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -167,6 +167,7 @@ private:
|
||||||
[[nodiscard]] Optional<StyleProperty> convert_to_style_property(StyleDeclarationRule&);
|
[[nodiscard]] Optional<StyleProperty> convert_to_style_property(StyleDeclarationRule&);
|
||||||
|
|
||||||
static Optional<float> try_parse_float(StringView string);
|
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_keyword_or_custom_value(ParsingContext const&, StyleComponentValueRule const&);
|
||||||
static RefPtr<StyleValue> parse_length_value(ParsingContext const&, StyleComponentValueRule const&);
|
static RefPtr<StyleValue> parse_length_value(ParsingContext const&, StyleComponentValueRule const&);
|
||||||
|
|
|
@ -82,6 +82,12 @@ public:
|
||||||
return m_value.string_view();
|
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; }
|
bool is(NumberType number_type) const { return is(Token::Type::Number) && m_number_type == number_type; }
|
||||||
|
|
||||||
int integer() const
|
int integer() const
|
||||||
|
|
|
@ -846,7 +846,7 @@ Token Tokenizer::consume_a_token()
|
||||||
if (would_start_an_identifier()) {
|
if (would_start_an_identifier()) {
|
||||||
auto name = consume_a_name();
|
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);
|
return create_value_token(Token::Type::Delim, input);
|
||||||
|
|
Loading…
Reference in a new issue