1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 18:47:34 +00:00

LibWeb: Parse and resolve UnresolvedStyleValues

If a property is custom or contains a `var()` reference, it cannot be
parsed into a proper StyleValue immediately, so we store it as an
UnresolvedStyleValue until the property is compute. Then, at compute
time, we resolve them by expanding out any `var()` references, and
parsing the result.

The implementation here is very naive, and involves copying the
UnresolvedStyleValue's tree of StyleComponentValueRules while copying
the contents of any `var()`s it finds along the way. This is quite an
expensive operation to do every time that the style is computed.
This commit is contained in:
Sam Atkins 2021-12-03 12:32:12 +00:00 committed by Andreas Kling
parent 000fb5a70d
commit 23dc0dac88
6 changed files with 138 additions and 6 deletions

View file

@ -3435,8 +3435,19 @@ RefPtr<StyleValue> Parser::parse_as_css_value(PropertyID property_id)
Result<NonnullRefPtr<StyleValue>, Parser::ParsingResult> Parser::parse_css_value(PropertyID property_id, TokenStream<StyleComponentValueRule>& tokens)
{
auto block_contains_var = [](StyleBlockRule const& block, auto&& recurse) -> bool {
for (auto const& token : block.values()) {
if (token.is_function() && token.function().name().equals_ignoring_case("var"sv))
return true;
if (token.is_block() && recurse(token.block(), recurse))
return true;
}
return false;
};
m_context.set_current_property_id(property_id);
Vector<StyleComponentValueRule> component_values;
bool contains_var = false;
while (tokens.has_next_token()) {
auto& token = tokens.next_token();
@ -3446,15 +3457,27 @@ Result<NonnullRefPtr<StyleValue>, Parser::ParsingResult> Parser::parse_css_value
break;
}
if (token.is(Token::Type::Whitespace))
continue;
if (property_id != PropertyID::Custom) {
if (token.is(Token::Type::Whitespace))
continue;
if (token.is(Token::Type::Ident) && has_ignored_vendor_prefix(token.token().ident()))
return ParsingResult::IncludesIgnoredVendorPrefix;
if (token.is(Token::Type::Ident) && has_ignored_vendor_prefix(token.token().ident()))
return ParsingResult::IncludesIgnoredVendorPrefix;
}
if (!contains_var) {
if (token.is_function() && token.function().name().equals_ignoring_case("var"sv))
contains_var = true;
else if (token.is_block() && block_contains_var(token.block(), block_contains_var))
contains_var = true;
}
component_values.append(token);
}
if (property_id == PropertyID::Custom || contains_var)
return { UnresolvedStyleValue::create(move(component_values), contains_var) };
if (component_values.is_empty())
return ParsingResult::SyntaxError;
@ -4167,6 +4190,19 @@ bool Parser::has_ignored_vendor_prefix(StringView string)
return true;
}
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)
return {};
CSS::Parser parser(context, "");
CSS::TokenStream<CSS::StyleComponentValueRule> token_stream { tokens };
auto result = parser.parse_css_value(property_id, token_stream);
if (result.is_error())
return {};
return result.release_value();
}
}
namespace Web {