Browse Source

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.
Sam Atkins 8 months ago
parent
commit
da31c10ce1

+ 4 - 2
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.

+ 24 - 9
Libraries/LibWeb/CSS/Selector.cpp

@@ -586,27 +586,37 @@ NonnullRefPtr<Selector> Selector::relative_to(SimpleSelector const& parent) cons
     return Selector::create(move(copied_compound_selectors));
 }
 
-NonnullRefPtr<Selector> Selector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const
+RefPtr<Selector> Selector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const
 {
     if (!contains_the_nesting_selector())
         return *this;
 
     Vector<CompoundSelector> 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> Selector::CompoundSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const
 {
     // TODO: Cache if it contains the nesting selector?
 
     Vector<SimpleSelector> 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> 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 {

+ 3 - 3
Libraries/LibWeb/CSS/Selector.h

@@ -222,7 +222,7 @@ public:
 
         String serialize() const;
 
-        SimpleSelector absolutized(SimpleSelector const& selector_for_nesting) const;
+        Optional<SimpleSelector> absolutized(SimpleSelector const& selector_for_nesting) const;
     };
 
     enum class Combinator {
@@ -240,7 +240,7 @@ public:
         Combinator combinator { Combinator::None };
         Vector<SimpleSelector> simple_selectors;
 
-        CompoundSelector absolutized(SimpleSelector const& selector_for_nesting) const;
+        Optional<CompoundSelector> absolutized(SimpleSelector const& selector_for_nesting) const;
     };
 
     static NonnullRefPtr<Selector> create(Vector<CompoundSelector>&& compound_selectors)
@@ -254,7 +254,7 @@ public:
     Optional<PseudoElement> const& pseudo_element() const { return m_pseudo_element; }
     NonnullRefPtr<Selector> relative_to(SimpleSelector const&) const;
     bool contains_the_nesting_selector() const { return m_contains_the_nesting_selector; }
-    NonnullRefPtr<Selector> absolutized(SimpleSelector const& selector_for_nesting) const;
+    RefPtr<Selector> absolutized(SimpleSelector const& selector_for_nesting) const;
     u32 specificity() const;
     String serialize() const;