Browse Source

LibWeb: Support the :scope pseudo class

Simon Wanner 2 years ago
parent
commit
c8ebacb1c9

+ 2 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -479,6 +479,8 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
             return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Root);
         if (pseudo_name.equals_ignoring_ascii_case("visited"sv))
             return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Visited);
+        if (pseudo_name.equals_ignoring_ascii_case("scope"sv))
+            return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Scope);
 
         // Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
         // https://www.w3.org/TR/selectors/#pseudo-element-syntax

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Selector.cpp

@@ -227,6 +227,7 @@ ErrorOr<String> Selector::SimpleSelector::serialize() const
         case Selector::SimpleSelector::PseudoClass::Type::Enabled:
         case Selector::SimpleSelector::PseudoClass::Type::Checked:
         case Selector::SimpleSelector::PseudoClass::Type::Active:
+        case Selector::SimpleSelector::PseudoClass::Type::Scope:
             // If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s.
             TRY(s.try_append(':'));
             TRY(s.try_append(pseudo_class_name(pseudo_class.type)));

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

@@ -111,6 +111,7 @@ public:
                 Where,
                 Active,
                 Lang,
+                Scope,
             };
             Type type;
 
@@ -292,6 +293,8 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty
         return "where"sv;
     case Selector::SimpleSelector::PseudoClass::Type::Lang:
         return "lang"sv;
+    case Selector::SimpleSelector::PseudoClass::Type::Scope:
+        return "scope"sv;
     }
     VERIFY_NOT_REACHED();
 }

+ 13 - 11
Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp

@@ -202,7 +202,7 @@ static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element c
     return nullptr;
 }
 
-static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element)
+static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
 {
     switch (pseudo_class.type) {
     case CSS::Selector::SimpleSelector::PseudoClass::Type::Link:
@@ -245,6 +245,8 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
     }
     case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
         return is<HTML::HTMLHtmlElement>(element);
+    case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope:
+        return scope ? &element == scope : is<HTML::HTMLHtmlElement>(element);
     case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
         return !previous_sibling_with_same_tag_name(element);
     case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
@@ -373,7 +375,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
     return false;
 }
 
-static inline bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element const& element)
+static inline bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
 {
     switch (component.type) {
     case CSS::Selector::SimpleSelector::Type::Universal:
@@ -390,7 +392,7 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, DOM::
     case CSS::Selector::SimpleSelector::Type::Attribute:
         return matches_attribute(component.attribute(), element);
     case CSS::Selector::SimpleSelector::Type::PseudoClass:
-        return matches_pseudo_class(component.pseudo_class(), element);
+        return matches_pseudo_class(component.pseudo_class(), element, scope);
     case CSS::Selector::SimpleSelector::Type::PseudoElement:
         // Pseudo-element matching/not-matching is handled in the top level matches().
         return true;
@@ -399,11 +401,11 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, DOM::
     }
 }
 
-static inline bool matches(CSS::Selector const& selector, int component_list_index, DOM::Element const& element)
+static inline bool matches(CSS::Selector const& selector, int component_list_index, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
 {
     auto& relative_selector = selector.compound_selectors()[component_list_index];
     for (auto& simple_selector : relative_selector.simple_selectors) {
-        if (!matches(simple_selector, element))
+        if (!matches(simple_selector, element, scope))
             return false;
     }
     switch (relative_selector.combinator) {
@@ -414,7 +416,7 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind
         for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) {
             if (!is<DOM::Element>(*ancestor))
                 continue;
-            if (matches(selector, component_list_index - 1, static_cast<DOM::Element const&>(*ancestor)))
+            if (matches(selector, component_list_index - 1, static_cast<DOM::Element const&>(*ancestor), scope))
                 return true;
         }
         return false;
@@ -422,16 +424,16 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind
         VERIFY(component_list_index != 0);
         if (!element.parent() || !is<DOM::Element>(*element.parent()))
             return false;
-        return matches(selector, component_list_index - 1, static_cast<DOM::Element const&>(*element.parent()));
+        return matches(selector, component_list_index - 1, static_cast<DOM::Element const&>(*element.parent()), scope);
     case CSS::Selector::Combinator::NextSibling:
         VERIFY(component_list_index != 0);
         if (auto* sibling = element.previous_element_sibling())
-            return matches(selector, component_list_index - 1, *sibling);
+            return matches(selector, component_list_index - 1, *sibling, scope);
         return false;
     case CSS::Selector::Combinator::SubsequentSibling:
         VERIFY(component_list_index != 0);
         for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
-            if (matches(selector, component_list_index - 1, *sibling))
+            if (matches(selector, component_list_index - 1, *sibling, scope))
                 return true;
         }
         return false;
@@ -441,14 +443,14 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind
     VERIFY_NOT_REACHED();
 }
 
-bool matches(CSS::Selector const& selector, DOM::Element const& element, Optional<CSS::Selector::PseudoElement> pseudo_element)
+bool matches(CSS::Selector const& selector, DOM::Element const& element, Optional<CSS::Selector::PseudoElement> pseudo_element, JS::GCPtr<DOM::ParentNode const> scope)
 {
     VERIFY(!selector.compound_selectors().is_empty());
     if (pseudo_element.has_value() && selector.pseudo_element() != pseudo_element)
         return false;
     if (!pseudo_element.has_value() && selector.pseudo_element().has_value())
         return false;
-    return matches(selector, selector.compound_selectors().size() - 1, element);
+    return matches(selector, selector.compound_selectors().size() - 1, element, scope);
 }
 
 }

+ 1 - 1
Userland/Libraries/LibWeb/CSS/SelectorEngine.h

@@ -11,6 +11,6 @@
 
 namespace Web::SelectorEngine {
 
-bool matches(CSS::Selector const&, DOM::Element const&, Optional<CSS::Selector::PseudoElement> = {});
+bool matches(CSS::Selector const&, DOM::Element const&, Optional<CSS::Selector::PseudoElement> = {}, JS::GCPtr<DOM::ParentNode const> scope = {});
 
 }

+ 3 - 0
Userland/Libraries/LibWeb/Dump.cpp

@@ -456,6 +456,9 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
                 case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang:
                     pseudo_class_description = "Lang";
                     break;
+                case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope:
+                    pseudo_class_description = "Scope";
+                    break;
                 }
 
                 builder.appendff(" pseudo_class={}", pseudo_class_description);