From da31c10ce1844a47c94955fa52c83e2302085c0c Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 14 Nov 2024 12:48:50 +0000 Subject: [PATCH] LibWeb/CSS: Allow Selector::absolutized() to return null It's possible for absolutizing a selector to return an invalid selector (eg, it could cause `:has()` inside `:has()`) so we need to be able to express that. --- Libraries/LibWeb/CSS/CSSStyleRule.cpp | 6 +++-- Libraries/LibWeb/CSS/Selector.cpp | 33 +++++++++++++++++++-------- Libraries/LibWeb/CSS/Selector.h | 6 ++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Libraries/LibWeb/CSS/CSSStyleRule.cpp b/Libraries/LibWeb/CSS/CSSStyleRule.cpp index 6fd731377df..88a3571f023 100644 --- a/Libraries/LibWeb/CSS/CSSStyleRule.cpp +++ b/Libraries/LibWeb/CSS/CSSStyleRule.cpp @@ -187,8 +187,10 @@ SelectorList const& CSSStyleRule::absolutized_selectors() const }, }; SelectorList absolutized_selectors; - for (auto const& selector : selectors()) - absolutized_selectors.append(selector->absolutized(parent_selector)); + for (auto const& selector : selectors()) { + if (auto absolutized = selector->absolutized(parent_selector)) + absolutized_selectors.append(absolutized.release_nonnull()); + } m_cached_absolutized_selectors = move(absolutized_selectors); } else { // NOTE: We can't actually replace & with :scope, because & has to have 0 specificity. diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index 916f83fd68b..3db36bd626e 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -586,27 +586,37 @@ NonnullRefPtr Selector::relative_to(SimpleSelector const& parent) cons return Selector::create(move(copied_compound_selectors)); } -NonnullRefPtr Selector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const +RefPtr Selector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const { if (!contains_the_nesting_selector()) return *this; Vector absolutized_compound_selectors; absolutized_compound_selectors.ensure_capacity(m_compound_selectors.size()); - for (auto const& compound_selector : m_compound_selectors) - absolutized_compound_selectors.append(compound_selector.absolutized(selector_for_nesting)); + for (auto const& compound_selector : m_compound_selectors) { + if (auto absolutized = compound_selector.absolutized(selector_for_nesting); absolutized.has_value()) { + absolutized_compound_selectors.append(absolutized.release_value()); + } else { + return nullptr; + } + } return Selector::create(move(absolutized_compound_selectors)); } -Selector::CompoundSelector Selector::CompoundSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const +Optional Selector::CompoundSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const { // TODO: Cache if it contains the nesting selector? Vector absolutized_simple_selectors; absolutized_simple_selectors.ensure_capacity(simple_selectors.size()); - for (auto const& simple_selector : simple_selectors) - absolutized_simple_selectors.append(simple_selector.absolutized(selector_for_nesting)); + for (auto const& simple_selector : simple_selectors) { + if (auto absolutized = simple_selector.absolutized(selector_for_nesting); absolutized.has_value()) { + absolutized_simple_selectors.append(absolutized.release_value()); + } else { + return {}; + } + } return CompoundSelector { .combinator = this->combinator, @@ -614,7 +624,7 @@ Selector::CompoundSelector Selector::CompoundSelector::absolutized(Selector::Sim }; } -Selector::SimpleSelector Selector::SimpleSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const +Optional Selector::SimpleSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const { switch (type) { case Type::Nesting: @@ -628,8 +638,13 @@ Selector::SimpleSelector Selector::SimpleSelector::absolutized(Selector::SimpleS if (!pseudo_class.argument_selector_list.is_empty()) { SelectorList new_selector_list; new_selector_list.ensure_capacity(pseudo_class.argument_selector_list.size()); - for (auto const& argument_selector : pseudo_class.argument_selector_list) - new_selector_list.append(argument_selector->absolutized(selector_for_nesting)); + for (auto const& argument_selector : pseudo_class.argument_selector_list) { + if (auto absolutized = argument_selector->absolutized(selector_for_nesting)) { + new_selector_list.append(absolutized.release_nonnull()); + } else if (!pseudo_class.is_forgiving) { + return {}; + } + } pseudo_class.argument_selector_list = move(new_selector_list); } return SimpleSelector { diff --git a/Libraries/LibWeb/CSS/Selector.h b/Libraries/LibWeb/CSS/Selector.h index ca5e30f261c..894e0f3f1f4 100644 --- a/Libraries/LibWeb/CSS/Selector.h +++ b/Libraries/LibWeb/CSS/Selector.h @@ -222,7 +222,7 @@ public: String serialize() const; - SimpleSelector absolutized(SimpleSelector const& selector_for_nesting) const; + Optional absolutized(SimpleSelector const& selector_for_nesting) const; }; enum class Combinator { @@ -240,7 +240,7 @@ public: Combinator combinator { Combinator::None }; Vector simple_selectors; - CompoundSelector absolutized(SimpleSelector const& selector_for_nesting) const; + Optional absolutized(SimpleSelector const& selector_for_nesting) const; }; static NonnullRefPtr create(Vector&& compound_selectors) @@ -254,7 +254,7 @@ public: Optional const& pseudo_element() const { return m_pseudo_element; } NonnullRefPtr relative_to(SimpleSelector const&) const; bool contains_the_nesting_selector() const { return m_contains_the_nesting_selector; } - NonnullRefPtr absolutized(SimpleSelector const& selector_for_nesting) const; + RefPtr absolutized(SimpleSelector const& selector_for_nesting) const; u32 specificity() const; String serialize() const;