diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index f2d2c593cb..aeeb719822 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -6483,23 +6484,285 @@ bool Parser::has_ignored_vendor_prefix(StringView string) return true; } -RefPtr Parser::parse_calculated_value(Badge, ParsingContext const& context, ComponentValue const& token) +NonnullRefPtr Parser::resolve_unresolved_style_value(Badge, ParsingContext const& context, DOM::Element& element, Optional pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved) { - auto parser = MUST(Parser::create(context, ""sv)); - return parser.parse_calculated_value(token); -} + // Unresolved always contains a var() or attr(), unless it is a custom property's value, in which case we shouldn't be trying + // to produce a different StyleValue from it. + VERIFY(unresolved.contains_var_or_attr()); -RefPtr Parser::parse_css_value(Badge, ParsingContext const& context, PropertyID property_id, Vector const& tokens) -{ - if (tokens.is_empty() || property_id == CSS::PropertyID::Invalid || property_id == CSS::PropertyID::Custom) - return nullptr; + // If the value is invalid, we fall back to `unset`: https://www.w3.org/TR/css-variables-1/#invalid-at-computed-value-time auto parser = MUST(Parser::create(context, ""sv)); - TokenStream token_stream { tokens }; - auto result = parser.parse_css_value(property_id, token_stream); - if (result.is_error()) - return nullptr; - return result.release_value(); + return parser.resolve_unresolved_style_value(element, pseudo_element, property_id, unresolved); +} + +class PropertyDependencyNode : public RefCounted { +public: + static NonnullRefPtr create(String name) + { + return adopt_ref(*new PropertyDependencyNode(move(name))); + } + + void add_child(NonnullRefPtr new_child) + { + for (auto const& child : m_children) { + if (child->m_name == new_child->m_name) + return; + } + + // We detect self-reference already. + VERIFY(new_child->m_name != m_name); + m_children.append(move(new_child)); + } + + bool has_cycles() + { + if (m_marked) + return true; + + TemporaryChange change { m_marked, true }; + for (auto& child : m_children) { + if (child->has_cycles()) + return true; + } + return false; + } + +private: + explicit PropertyDependencyNode(String name) + : m_name(move(name)) + { + } + + String m_name; + Vector> m_children; + bool m_marked { false }; +}; + +NonnullRefPtr Parser::resolve_unresolved_style_value(DOM::Element& element, Optional pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved) +{ + TokenStream unresolved_values_without_variables_expanded { unresolved.values() }; + Vector values_with_variables_expanded; + + HashMap> dependencies; + if (!expand_variables(element, pseudo_element, string_from_property_id(property_id), dependencies, unresolved_values_without_variables_expanded, values_with_variables_expanded)) + return UnsetStyleValue::the(); + + TokenStream unresolved_values_with_variables_expanded { values_with_variables_expanded }; + Vector expanded_values; + if (!expand_unresolved_values(element, string_from_property_id(property_id), unresolved_values_with_variables_expanded, expanded_values)) + return UnsetStyleValue::the(); + + auto expanded_value_tokens = TokenStream { expanded_values }; + if (auto parsed_value = parse_css_value(property_id, expanded_value_tokens); !parsed_value.is_error()) + return parsed_value.release_value(); + + return UnsetStyleValue::the(); +} + +static RefPtr get_custom_property(DOM::Element const& element, Optional pseudo_element, FlyString const& custom_property_name) +{ + if (pseudo_element.has_value()) { + if (auto it = element.custom_properties(pseudo_element).find(custom_property_name.to_string().to_deprecated_string()); it != element.custom_properties(pseudo_element).end()) + return it->value.value; + } + + for (auto const* current_element = &element; current_element; current_element = current_element->parent_element()) { + if (auto it = current_element->custom_properties({}).find(custom_property_name.to_string().to_deprecated_string()); it != current_element->custom_properties({}).end()) + return it->value.value; + } + return nullptr; +} + +bool Parser::expand_variables(DOM::Element& element, Optional pseudo_element, StringView property_name, HashMap>& dependencies, TokenStream& source, Vector& dest) +{ + // Arbitrary large value chosen to avoid the billion-laughs attack. + // https://www.w3.org/TR/css-variables-1/#long-variables + size_t const MAX_VALUE_COUNT = 16384; + if (source.remaining_token_count() + dest.size() > MAX_VALUE_COUNT) { + dbgln("Stopped expanding CSS variables: maximum length reached."); + return false; + } + + auto get_dependency_node = [&](FlyString name) -> NonnullRefPtr { + if (auto existing = dependencies.get(name); existing.has_value()) + return *existing.value(); + auto new_node = PropertyDependencyNode::create(name.to_string()); + dependencies.set(name, new_node); + return new_node; + }; + + while (source.has_next_token()) { + auto const& value = source.next_token(); + if (value.is_block()) { + auto const& source_block = value.block(); + Vector block_values; + TokenStream source_block_contents { source_block.values() }; + if (!expand_variables(element, pseudo_element, property_name, dependencies, source_block_contents, block_values)) + return false; + NonnullRefPtr block = Block::create(source_block.token(), move(block_values)); + dest.empend(block); + continue; + } + if (!value.is_function()) { + dest.empend(value); + continue; + } + if (!value.function().name().equals_ignoring_ascii_case("var"sv)) { + auto const& source_function = value.function(); + Vector function_values; + TokenStream source_function_contents { source_function.values() }; + if (!expand_variables(element, pseudo_element, property_name, dependencies, source_function_contents, function_values)) + return false; + NonnullRefPtr function = Function::create(FlyString::from_utf8(source_function.name()).release_value_but_fixme_should_propagate_errors(), move(function_values)); + dest.empend(function); + continue; + } + + TokenStream var_contents { value.function().values() }; + var_contents.skip_whitespace(); + if (!var_contents.has_next_token()) + return false; + + auto const& custom_property_name_token = var_contents.next_token(); + if (!custom_property_name_token.is(Token::Type::Ident)) + return false; + auto custom_property_name = custom_property_name_token.token().ident(); + if (!custom_property_name.starts_with("--"sv)) + return false; + + // Detect dependency cycles. https://www.w3.org/TR/css-variables-1/#cycles + // We do not do this by the spec, since we are not keeping a graph of var dependencies around, + // but rebuilding it every time. + if (custom_property_name == property_name) + return false; + auto parent = get_dependency_node(FlyString::from_utf8(property_name).release_value_but_fixme_should_propagate_errors()); + auto child = get_dependency_node(FlyString::from_utf8(custom_property_name).release_value_but_fixme_should_propagate_errors()); + parent->add_child(child); + if (parent->has_cycles()) + return false; + + if (auto custom_property_value = get_custom_property(element, pseudo_element, FlyString::from_utf8(custom_property_name).release_value_but_fixme_should_propagate_errors())) { + VERIFY(custom_property_value->is_unresolved()); + TokenStream custom_property_tokens { custom_property_value->as_unresolved().values() }; + if (!expand_variables(element, pseudo_element, custom_property_name, dependencies, custom_property_tokens, dest)) + return false; + continue; + } + + // Use the provided fallback value, if any. + var_contents.skip_whitespace(); + if (var_contents.has_next_token()) { + auto const& comma_token = var_contents.next_token(); + if (!comma_token.is(Token::Type::Comma)) + return false; + var_contents.skip_whitespace(); + if (!expand_variables(element, pseudo_element, property_name, dependencies, var_contents, dest)) + return false; + } + } + return true; +} + +bool Parser::expand_unresolved_values(DOM::Element& element, StringView property_name, TokenStream& source, Vector& dest) +{ + while (source.has_next_token()) { + auto const& value = source.next_token(); + if (value.is_function()) { + if (value.function().name().equals_ignoring_ascii_case("attr"sv)) { + // https://drafts.csswg.org/css-values-5/#attr-substitution + TokenStream attr_contents { value.function().values() }; + attr_contents.skip_whitespace(); + if (!attr_contents.has_next_token()) + return false; + + auto const& attr_name_token = attr_contents.next_token(); + if (!attr_name_token.is(Token::Type::Ident)) + return false; + auto attr_name = attr_name_token.token().ident(); + + auto attr_value = element.get_attribute(attr_name); + // 1. If the attr() function has a substitution value, replace the attr() function by the substitution value. + if (!attr_value.is_null()) { + // FIXME: attr() should also accept an optional type argument, not just strings. + dest.empend(Token::of_string(FlyString::from_deprecated_fly_string(attr_value).release_value_but_fixme_should_propagate_errors())); + continue; + } + + // 2. Otherwise, if the attr() function has a fallback value as its last argument, replace the attr() function by the fallback value. + // If there are any var() or attr() references in the fallback, substitute them as well. + attr_contents.skip_whitespace(); + if (attr_contents.has_next_token()) { + auto const& comma_token = attr_contents.next_token(); + if (!comma_token.is(Token::Type::Comma)) + return false; + attr_contents.skip_whitespace(); + if (!expand_unresolved_values(element, property_name, attr_contents, dest)) + return false; + continue; + } + + // 3. Otherwise, the property containing the attr() function is invalid at computed-value time. + return false; + } + + if (auto maybe_calc_value = parse_calculated_value(value); maybe_calc_value && maybe_calc_value->is_calculated()) { + auto& calc_value = maybe_calc_value->as_calculated(); + if (calc_value.resolves_to_angle()) { + auto resolved_value = calc_value.resolve_angle(); + dest.empend(Token::create_dimension(resolved_value->to_degrees(), "deg"_fly_string)); + continue; + } + if (calc_value.resolves_to_frequency()) { + auto resolved_value = calc_value.resolve_frequency(); + dest.empend(Token::create_dimension(resolved_value->to_hertz(), "hz"_fly_string)); + continue; + } + if (calc_value.resolves_to_length()) { + // FIXME: In order to resolve lengths, we need to know the font metrics in case a font-relative unit + // is used. So... we can't do that until style is computed? + // This might be easier once we have calc-simplification implemented. + } + if (calc_value.resolves_to_percentage()) { + auto resolved_value = calc_value.resolve_percentage(); + dest.empend(Token::create_percentage(resolved_value.value().value())); + continue; + } + if (calc_value.resolves_to_time()) { + auto resolved_value = calc_value.resolve_time(); + dest.empend(Token::create_dimension(resolved_value->to_seconds(), "s"_fly_string)); + continue; + } + if (calc_value.resolves_to_number()) { + auto resolved_value = calc_value.resolve_number(); + dest.empend(Token::create_number(resolved_value.value())); + continue; + } + } + + auto const& source_function = value.function(); + Vector function_values; + TokenStream source_function_contents { source_function.values() }; + if (!expand_unresolved_values(element, property_name, source_function_contents, function_values)) + return false; + NonnullRefPtr function = Function::create(source_function.name(), move(function_values)); + dest.empend(function); + continue; + } + if (value.is_block()) { + auto const& source_block = value.block(); + TokenStream source_block_values { source_block.values() }; + Vector block_values; + if (!expand_unresolved_values(element, property_name, source_block_values, block_values)) + return false; + NonnullRefPtr block = Block::create(source_block.token(), move(block_values)); + dest.empend(move(block)); + continue; + } + dest.empend(value.token()); + } + + return true; } } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index c1eb29d8f3..ca18d197bf 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -37,6 +37,8 @@ namespace Web::CSS::Parser { +class PropertyDependencyNode; + class Parser { public: static ErrorOr create(ParsingContext const&, StringView input, StringView encoding = "utf-8"sv); @@ -66,8 +68,7 @@ public: RefPtr parse_as_css_value(PropertyID); - static RefPtr parse_css_value(Badge, ParsingContext const&, PropertyID, Vector const&); - static RefPtr parse_calculated_value(Badge, ParsingContext const&, ComponentValue const&); + static NonnullRefPtr resolve_unresolved_style_value(Badge, ParsingContext const&, DOM::Element&, Optional, PropertyID, UnresolvedStyleValue const&); [[nodiscard]] LengthOrCalculated parse_as_sizes_attribute(); @@ -285,6 +286,10 @@ private: Optional parse_supports_in_parens(TokenStream&); Optional parse_supports_feature(TokenStream&); + NonnullRefPtr resolve_unresolved_style_value(DOM::Element&, Optional, PropertyID, UnresolvedStyleValue const&); + bool expand_variables(DOM::Element&, Optional, StringView property_name, HashMap>& dependencies, TokenStream& source, Vector& dest); + bool expand_unresolved_values(DOM::Element&, StringView property_name, TokenStream& source, Vector& dest); + static bool has_ignored_vendor_prefix(StringView); struct PropertiesAndCustomProperties { diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 607056dd2a..f2433a615d 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -943,7 +943,7 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional property_value = value; if (property_value->is_unresolved()) - property_value = resolve_unresolved_style_value(element, pseudo_element, property_id, property_value->as_unresolved()); + property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document }, element, pseudo_element, property_id, property_value->as_unresolved()); if (!property_value->is_unresolved()) set_property_expanding_shorthands(style, property_id, property_value, document, declaration, properties_for_revert); @@ -951,241 +951,6 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional get_custom_property(DOM::Element const& element, Optional pseudo_element, FlyString const& custom_property_name) -{ - if (pseudo_element.has_value()) { - if (auto it = element.custom_properties(pseudo_element).find(custom_property_name.to_string().to_deprecated_string()); it != element.custom_properties(pseudo_element).end()) - return it->value.value; - } - - for (auto const* current_element = &element; current_element; current_element = current_element->parent_element()) { - if (auto it = current_element->custom_properties({}).find(custom_property_name.to_string().to_deprecated_string()); it != current_element->custom_properties({}).end()) - return it->value.value; - } - return nullptr; -} - -bool StyleComputer::expand_variables(DOM::Element& element, Optional pseudo_element, StringView property_name, HashMap>& dependencies, Parser::TokenStream& source, Vector& dest) const -{ - // Arbitrary large value chosen to avoid the billion-laughs attack. - // https://www.w3.org/TR/css-variables-1/#long-variables - size_t const MAX_VALUE_COUNT = 16384; - if (source.remaining_token_count() + dest.size() > MAX_VALUE_COUNT) { - dbgln("Stopped expanding CSS variables: maximum length reached."); - return false; - } - - auto get_dependency_node = [&](FlyString name) -> NonnullRefPtr { - if (auto existing = dependencies.get(name); existing.has_value()) - return *existing.value(); - auto new_node = PropertyDependencyNode::create(name.to_string()); - dependencies.set(name, new_node); - return new_node; - }; - - while (source.has_next_token()) { - auto const& value = source.next_token(); - if (value.is_block()) { - auto const& source_block = value.block(); - Vector block_values; - Parser::TokenStream source_block_contents { source_block.values() }; - if (!expand_variables(element, pseudo_element, property_name, dependencies, source_block_contents, block_values)) - return false; - NonnullRefPtr block = Parser::Block::create(source_block.token(), move(block_values)); - dest.empend(block); - continue; - } - if (!value.is_function()) { - dest.empend(value); - continue; - } - if (!value.function().name().equals_ignoring_ascii_case("var"sv)) { - auto const& source_function = value.function(); - Vector function_values; - Parser::TokenStream source_function_contents { source_function.values() }; - if (!expand_variables(element, pseudo_element, property_name, dependencies, source_function_contents, function_values)) - return false; - NonnullRefPtr function = Parser::Function::create(FlyString::from_utf8(source_function.name()).release_value_but_fixme_should_propagate_errors(), move(function_values)); - dest.empend(function); - continue; - } - - Parser::TokenStream var_contents { value.function().values() }; - var_contents.skip_whitespace(); - if (!var_contents.has_next_token()) - return false; - - auto const& custom_property_name_token = var_contents.next_token(); - if (!custom_property_name_token.is(Parser::Token::Type::Ident)) - return false; - auto custom_property_name = custom_property_name_token.token().ident(); - if (!custom_property_name.starts_with("--"sv)) - return false; - - // Detect dependency cycles. https://www.w3.org/TR/css-variables-1/#cycles - // We do not do this by the spec, since we are not keeping a graph of var dependencies around, - // but rebuilding it every time. - if (custom_property_name == property_name) - return false; - auto parent = get_dependency_node(FlyString::from_utf8(property_name).release_value_but_fixme_should_propagate_errors()); - auto child = get_dependency_node(FlyString::from_utf8(custom_property_name).release_value_but_fixme_should_propagate_errors()); - parent->add_child(child); - if (parent->has_cycles()) - return false; - - if (auto custom_property_value = get_custom_property(element, pseudo_element, FlyString::from_utf8(custom_property_name).release_value_but_fixme_should_propagate_errors())) { - VERIFY(custom_property_value->is_unresolved()); - Parser::TokenStream custom_property_tokens { custom_property_value->as_unresolved().values() }; - if (!expand_variables(element, pseudo_element, custom_property_name, dependencies, custom_property_tokens, dest)) - return false; - continue; - } - - // Use the provided fallback value, if any. - var_contents.skip_whitespace(); - if (var_contents.has_next_token()) { - auto const& comma_token = var_contents.next_token(); - if (!comma_token.is(Parser::Token::Type::Comma)) - return false; - var_contents.skip_whitespace(); - if (!expand_variables(element, pseudo_element, property_name, dependencies, var_contents, dest)) - return false; - } - } - return true; -} - -bool StyleComputer::expand_unresolved_values(DOM::Element& element, StringView property_name, Parser::TokenStream& source, Vector& dest) const -{ - while (source.has_next_token()) { - auto const& value = source.next_token(); - if (value.is_function()) { - if (value.function().name().equals_ignoring_ascii_case("attr"sv)) { - // https://drafts.csswg.org/css-values-5/#attr-substitution - Parser::TokenStream attr_contents { value.function().values() }; - attr_contents.skip_whitespace(); - if (!attr_contents.has_next_token()) - return false; - - auto const& attr_name_token = attr_contents.next_token(); - if (!attr_name_token.is(Parser::Token::Type::Ident)) - return false; - auto attr_name = attr_name_token.token().ident(); - - auto attr_value = element.get_attribute(attr_name); - // 1. If the attr() function has a substitution value, replace the attr() function by the substitution value. - if (!attr_value.is_null()) { - // FIXME: attr() should also accept an optional type argument, not just strings. - dest.empend(Parser::Token::of_string(FlyString::from_deprecated_fly_string(attr_value).release_value_but_fixme_should_propagate_errors())); - continue; - } - - // 2. Otherwise, if the attr() function has a fallback value as its last argument, replace the attr() function by the fallback value. - // If there are any var() or attr() references in the fallback, substitute them as well. - attr_contents.skip_whitespace(); - if (attr_contents.has_next_token()) { - auto const& comma_token = attr_contents.next_token(); - if (!comma_token.is(Parser::Token::Type::Comma)) - return false; - attr_contents.skip_whitespace(); - if (!expand_unresolved_values(element, property_name, attr_contents, dest)) - return false; - continue; - } - - // 3. Otherwise, the property containing the attr() function is invalid at computed-value time. - return false; - } - - if (auto maybe_calc_value = Parser::Parser::parse_calculated_value({}, Parser::ParsingContext { document() }, value); maybe_calc_value && maybe_calc_value->is_calculated()) { - auto& calc_value = maybe_calc_value->as_calculated(); - if (calc_value.resolves_to_angle()) { - auto resolved_value = calc_value.resolve_angle(); - dest.empend(Parser::Token::create_dimension(resolved_value->to_degrees(), "deg"_fly_string)); - continue; - } - if (calc_value.resolves_to_frequency()) { - auto resolved_value = calc_value.resolve_frequency(); - dest.empend(Parser::Token::create_dimension(resolved_value->to_hertz(), "hz"_fly_string)); - continue; - } - if (calc_value.resolves_to_length()) { - // FIXME: In order to resolve lengths, we need to know the font metrics in case a font-relative unit - // is used. So... we can't do that until style is computed? - // This might be easier once we have calc-simplification implemented. - } - if (calc_value.resolves_to_percentage()) { - auto resolved_value = calc_value.resolve_percentage(); - dest.empend(Parser::Token::create_percentage(resolved_value.value().value())); - continue; - } - if (calc_value.resolves_to_time()) { - auto resolved_value = calc_value.resolve_time(); - dest.empend(Parser::Token::create_dimension(resolved_value->to_seconds(), "s"_fly_string)); - continue; - } - if (calc_value.resolves_to_number()) { - auto resolved_value = calc_value.resolve_number(); - dest.empend(Parser::Token::create_number(resolved_value.value())); - continue; - } - } - - auto const& source_function = value.function(); - Vector function_values; - Parser::TokenStream source_function_contents { source_function.values() }; - if (!expand_unresolved_values(element, property_name, source_function_contents, function_values)) - return false; - NonnullRefPtr function = Parser::Function::create(source_function.name(), move(function_values)); - dest.empend(function); - continue; - } - if (value.is_block()) { - auto const& source_block = value.block(); - Parser::TokenStream source_block_values { source_block.values() }; - Vector block_values; - if (!expand_unresolved_values(element, property_name, source_block_values, block_values)) - return false; - NonnullRefPtr block = Parser::Block::create(source_block.token(), move(block_values)); - dest.empend(move(block)); - continue; - } - dest.empend(value.token()); - } - - return true; -} - -NonnullRefPtr StyleComputer::resolve_unresolved_style_value(DOM::Element& element, Optional pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved) const -{ - // Unresolved always contains a var() or attr(), unless it is a custom property's value, in which case we shouldn't be trying - // to produce a different StyleValue from it. - VERIFY(unresolved.contains_var_or_attr()); - - // If the value is invalid, we fall back to `unset`: https://www.w3.org/TR/css-variables-1/#invalid-at-computed-value-time - - // FIXME: Do this better! - // We build a copy of the tree of ComponentValues, with all var()s and attr()s replaced with their contents. - // This is a very naive solution, and we could do better if the CSS Parser could accept tokens one at a time. - - Parser::TokenStream unresolved_values_without_variables_expanded { unresolved.values() }; - Vector values_with_variables_expanded; - - HashMap> dependencies; - if (!expand_variables(element, pseudo_element, string_from_property_id(property_id), dependencies, unresolved_values_without_variables_expanded, values_with_variables_expanded)) - return UnsetStyleValue::the(); - - Parser::TokenStream unresolved_values_with_variables_expanded { values_with_variables_expanded }; - Vector expanded_values; - if (!expand_unresolved_values(element, string_from_property_id(property_id), unresolved_values_with_variables_expanded, expanded_values)) - return UnsetStyleValue::the(); - - if (auto parsed_value = Parser::Parser::parse_css_value({}, Parser::ParsingContext { document() }, property_id, expanded_values)) - return parsed_value.release_nonnull(); - - return UnsetStyleValue::the(); -} - void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& element, Optional pseudo_element, Vector const& matching_rules, CascadeOrigin cascade_origin, Important important) const { auto properties_for_revert = style.properties(); @@ -1202,7 +967,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e auto property_value = property.value; if (property.value->is_unresolved()) - property_value = resolve_unresolved_style_value(element, pseudo_element, property.property_id, property.value->as_unresolved()); + property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) set_property_expanding_shorthands(style, property.property_id, property_value, m_document, &match.rule->declaration(), properties_for_revert); } @@ -1221,7 +986,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e auto property_value = property.value; if (property.value->is_unresolved()) - property_value = resolve_unresolved_style_value(element, pseudo_element, property.property_id, property.value->as_unresolved()); + property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) set_property_expanding_shorthands(style, property.property_id, property_value, m_document, inline_style, properties_for_revert); } @@ -1767,7 +1532,7 @@ ErrorOr StyleComputer::compute_cascaded_values(StyleProperties& style, DOM auto property_id = (CSS::PropertyID)i; auto& property = style.m_property_values[i]; if (property.has_value() && property->style->is_unresolved()) - property->style = resolve_unresolved_style_value(element, pseudo_element, property_id, property->style->as_unresolved()); + property->style = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property_id, property->style->as_unresolved()); } } } @@ -2566,36 +2331,6 @@ ErrorOr> StyleComputer::compute_style_impl(DOM::Element& return style; } -PropertyDependencyNode::PropertyDependencyNode(String name) - : m_name(move(name)) -{ -} - -void PropertyDependencyNode::add_child(NonnullRefPtr new_child) -{ - for (auto const& child : m_children) { - if (child->m_name == new_child->m_name) - return; - } - - // We detect self-reference already. - VERIFY(new_child->m_name != m_name); - m_children.append(move(new_child)); -} - -bool PropertyDependencyNode::has_cycles() -{ - if (m_marked) - return true; - - TemporaryChange change { m_marked, true }; - for (auto& child : m_children) { - if (child->has_cycles()) - return true; - } - return false; -} - void StyleComputer::build_rule_cache_if_needed() const { if (m_author_rule_cache && m_user_rule_cache && m_user_agent_rule_cache) diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index 53888af33e..2e83f3f9e9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include #include @@ -33,24 +31,6 @@ struct MatchingRule { bool contains_pseudo_element { false }; }; -class PropertyDependencyNode : public RefCounted { -public: - static NonnullRefPtr create(String name) - { - return adopt_ref(*new PropertyDependencyNode(move(name))); - } - - void add_child(NonnullRefPtr); - bool has_cycles(); - -private: - explicit PropertyDependencyNode(String name); - - String m_name; - Vector> m_children; - bool m_marked { false }; -}; - struct FontFaceKey { FlyString family_name; int weight { 0 }; @@ -153,10 +133,6 @@ private: void compute_defaulted_property_value(StyleProperties&, DOM::Element const*, CSS::PropertyID, Optional) const; - NonnullRefPtr resolve_unresolved_style_value(DOM::Element&, Optional, PropertyID, UnresolvedStyleValue const&) const; - bool expand_variables(DOM::Element&, Optional, StringView property_name, HashMap>& dependencies, Parser::TokenStream& source, Vector& dest) const; - bool expand_unresolved_values(DOM::Element&, StringView property_name, Parser::TokenStream& source, Vector& dest) const; - void set_all_properties(DOM::Element&, Optional, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert) const; template