Selaa lähdekoodia

LibWeb/CSS: Reject invalid :has() contents after absolutizing nesting

After we absolutize the contents of :has(), we check that those child
selectors don't contain anything that :has() rejects.

This is a separate path than the checks inside the parser, which is
unfortunate.

Fixes a WPT ref test. :^)
Sam Atkins 8 kuukautta sitten
vanhempi
commit
3a71b8cda3
2 muutettua tiedostoa jossa 38 lisäystä ja 1 poistoa
  1. 38 0
      Libraries/LibWeb/CSS/Selector.cpp
  2. 0 1
      Tests/LibWeb/TestConfig.ini

+ 38 - 0
Libraries/LibWeb/CSS/Selector.cpp

@@ -624,6 +624,33 @@ Optional<Selector::CompoundSelector> Selector::CompoundSelector::absolutized(Sel
     };
     };
 }
 }
 
 
+static bool contains_invalid_contents_for_has(Selector const& selector)
+{
+    // :has() has special validity rules:
+    // - It can't appear inside itself
+    // - It bans most pseudo-elements
+    // https://drafts.csswg.org/selectors/#relational
+
+    for (auto const& compound_selector : selector.compound_selectors()) {
+        for (auto const& simple_selector : compound_selector.simple_selectors) {
+            if (simple_selector.type == Selector::SimpleSelector::Type::PseudoElement) {
+                if (!is_has_allowed_pseudo_element(simple_selector.pseudo_element().type()))
+                    return true;
+            }
+            if (simple_selector.type == Selector::SimpleSelector::Type::PseudoClass) {
+                if (simple_selector.pseudo_class().type == PseudoClass::Has)
+                    return true;
+                for (auto& child_selector : simple_selector.pseudo_class().argument_selector_list) {
+                    if (contains_invalid_contents_for_has(*child_selector))
+                        return true;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
 Optional<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) {
     switch (type) {
@@ -647,6 +674,17 @@ Optional<Selector::SimpleSelector> Selector::SimpleSelector::absolutized(Selecto
             }
             }
             pseudo_class.argument_selector_list = move(new_selector_list);
             pseudo_class.argument_selector_list = move(new_selector_list);
         }
         }
+
+        // :has() has special validity rules
+        if (pseudo_class.type == PseudoClass::Has) {
+            for (auto const& selector : pseudo_class.argument_selector_list) {
+                if (contains_invalid_contents_for_has(selector)) {
+                    dbgln_if(CSS_PARSER_DEBUG, "After absolutizing, :has() would contain invalid contents; rejecting");
+                    return {};
+                }
+            }
+        }
+
         return SimpleSelector {
         return SimpleSelector {
             .type = Type::PseudoClass,
             .type = Type::PseudoClass,
             .value = move(pseudo_class),
             .value = move(pseudo_class),

+ 0 - 1
Tests/LibWeb/TestConfig.ini

@@ -59,7 +59,6 @@ Text/input/wpt-import/html/infrastructure/safe-passing-of-structured-data/resour
 Text/input/wpt-import/css/css-flexbox/flex-item-compressible-001.html
 Text/input/wpt-import/css/css-flexbox/flex-item-compressible-001.html
 
 
 ; WPT ref-tests that currently fail
 ; WPT ref-tests that currently fail
-Ref/input/wpt-import/css/css-nesting/has-nesting.html
 Ref/input/wpt-import/css/css-nesting/host-nesting-003.html
 Ref/input/wpt-import/css/css-nesting/host-nesting-003.html
 Ref/input/wpt-import/css/css-nesting/host-nesting-004.html
 Ref/input/wpt-import/css/css-nesting/host-nesting-004.html
 Ref/input/wpt-import/css/CSS2/floats/floats-wrap-top-below-bfc-002r.xht
 Ref/input/wpt-import/css/CSS2/floats/floats-wrap-top-below-bfc-002r.xht