mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 03:32:45 +00:00 
			
		
		
		
	LibWeb: Support the :scope pseudo class
This commit is contained in:
		
							parent
							
								
									a96ba912b3
								
							
						
					
					
						commit
						c8ebacb1c9
					
				
					 6 changed files with 23 additions and 12 deletions
				
			
		|  | @ -479,6 +479,8 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec | ||||||
|             return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Root); |             return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Root); | ||||||
|         if (pseudo_name.equals_ignoring_ascii_case("visited"sv)) |         if (pseudo_name.equals_ignoring_ascii_case("visited"sv)) | ||||||
|             return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Visited); |             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.
 |         // Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
 | ||||||
|         // https://www.w3.org/TR/selectors/#pseudo-element-syntax
 |         // https://www.w3.org/TR/selectors/#pseudo-element-syntax
 | ||||||
|  |  | ||||||
|  | @ -227,6 +227,7 @@ ErrorOr<String> Selector::SimpleSelector::serialize() const | ||||||
|         case Selector::SimpleSelector::PseudoClass::Type::Enabled: |         case Selector::SimpleSelector::PseudoClass::Type::Enabled: | ||||||
|         case Selector::SimpleSelector::PseudoClass::Type::Checked: |         case Selector::SimpleSelector::PseudoClass::Type::Checked: | ||||||
|         case Selector::SimpleSelector::PseudoClass::Type::Active: |         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.
 |             // 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(':')); | ||||||
|             TRY(s.try_append(pseudo_class_name(pseudo_class.type))); |             TRY(s.try_append(pseudo_class_name(pseudo_class.type))); | ||||||
|  |  | ||||||
|  | @ -111,6 +111,7 @@ public: | ||||||
|                 Where, |                 Where, | ||||||
|                 Active, |                 Active, | ||||||
|                 Lang, |                 Lang, | ||||||
|  |                 Scope, | ||||||
|             }; |             }; | ||||||
|             Type type; |             Type type; | ||||||
| 
 | 
 | ||||||
|  | @ -292,6 +293,8 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty | ||||||
|         return "where"sv; |         return "where"sv; | ||||||
|     case Selector::SimpleSelector::PseudoClass::Type::Lang: |     case Selector::SimpleSelector::PseudoClass::Type::Lang: | ||||||
|         return "lang"sv; |         return "lang"sv; | ||||||
|  |     case Selector::SimpleSelector::PseudoClass::Type::Scope: | ||||||
|  |         return "scope"sv; | ||||||
|     } |     } | ||||||
|     VERIFY_NOT_REACHED(); |     VERIFY_NOT_REACHED(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -202,7 +202,7 @@ static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element c | ||||||
|     return nullptr; |     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) { |     switch (pseudo_class.type) { | ||||||
|     case CSS::Selector::SimpleSelector::PseudoClass::Type::Link: |     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: |     case CSS::Selector::SimpleSelector::PseudoClass::Type::Root: | ||||||
|         return is<HTML::HTMLHtmlElement>(element); |         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: |     case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType: | ||||||
|         return !previous_sibling_with_same_tag_name(element); |         return !previous_sibling_with_same_tag_name(element); | ||||||
|     case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType: |     case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType: | ||||||
|  | @ -373,7 +375,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla | ||||||
|     return false; |     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) { |     switch (component.type) { | ||||||
|     case CSS::Selector::SimpleSelector::Type::Universal: |     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: |     case CSS::Selector::SimpleSelector::Type::Attribute: | ||||||
|         return matches_attribute(component.attribute(), element); |         return matches_attribute(component.attribute(), element); | ||||||
|     case CSS::Selector::SimpleSelector::Type::PseudoClass: |     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: |     case CSS::Selector::SimpleSelector::Type::PseudoElement: | ||||||
|         // Pseudo-element matching/not-matching is handled in the top level matches().
 |         // Pseudo-element matching/not-matching is handled in the top level matches().
 | ||||||
|         return true; |         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]; |     auto& relative_selector = selector.compound_selectors()[component_list_index]; | ||||||
|     for (auto& simple_selector : relative_selector.simple_selectors) { |     for (auto& simple_selector : relative_selector.simple_selectors) { | ||||||
|         if (!matches(simple_selector, element)) |         if (!matches(simple_selector, element, scope)) | ||||||
|             return false; |             return false; | ||||||
|     } |     } | ||||||
|     switch (relative_selector.combinator) { |     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()) { |         for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) { | ||||||
|             if (!is<DOM::Element>(*ancestor)) |             if (!is<DOM::Element>(*ancestor)) | ||||||
|                 continue; |                 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 true; | ||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|  | @ -422,16 +424,16 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind | ||||||
|         VERIFY(component_list_index != 0); |         VERIFY(component_list_index != 0); | ||||||
|         if (!element.parent() || !is<DOM::Element>(*element.parent())) |         if (!element.parent() || !is<DOM::Element>(*element.parent())) | ||||||
|             return false; |             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: |     case CSS::Selector::Combinator::NextSibling: | ||||||
|         VERIFY(component_list_index != 0); |         VERIFY(component_list_index != 0); | ||||||
|         if (auto* sibling = element.previous_element_sibling()) |         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; |         return false; | ||||||
|     case CSS::Selector::Combinator::SubsequentSibling: |     case CSS::Selector::Combinator::SubsequentSibling: | ||||||
|         VERIFY(component_list_index != 0); |         VERIFY(component_list_index != 0); | ||||||
|         for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { |         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 true; | ||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|  | @ -441,14 +443,14 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind | ||||||
|     VERIFY_NOT_REACHED(); |     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()); |     VERIFY(!selector.compound_selectors().is_empty()); | ||||||
|     if (pseudo_element.has_value() && selector.pseudo_element() != pseudo_element) |     if (pseudo_element.has_value() && selector.pseudo_element() != pseudo_element) | ||||||
|         return false; |         return false; | ||||||
|     if (!pseudo_element.has_value() && selector.pseudo_element().has_value()) |     if (!pseudo_element.has_value() && selector.pseudo_element().has_value()) | ||||||
|         return false; |         return false; | ||||||
|     return matches(selector, selector.compound_selectors().size() - 1, element); |     return matches(selector, selector.compound_selectors().size() - 1, element, scope); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace Web::SelectorEngine { | 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 = {}); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -456,6 +456,9 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector) | ||||||
|                 case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang: |                 case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang: | ||||||
|                     pseudo_class_description = "Lang"; |                     pseudo_class_description = "Lang"; | ||||||
|                     break; |                     break; | ||||||
|  |                 case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope: | ||||||
|  |                     pseudo_class_description = "Scope"; | ||||||
|  |                     break; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 builder.appendff(" pseudo_class={}", pseudo_class_description); |                 builder.appendff(" pseudo_class={}", pseudo_class_description); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Simon Wanner
						Simon Wanner