From aa280d1e2915e6008769155cf1488f2fe3ad646f Mon Sep 17 00:00:00 2001 From: David Shin Date: Thu, 14 Sep 2023 22:21:25 +0000 Subject: [PATCH] Bug 1792501: Part 3 - Let :has contribute to dependencies. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D185676 --- servo/components/selectors/parser.rs | 5 +++ servo/components/selectors/visitor.rs | 27 ++++++++++++- servo/components/style/stylist.rs | 56 ++++++++++++++++++--------- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/servo/components/selectors/parser.rs b/servo/components/selectors/parser.rs index 26aaee93a46d..72b2d1d4bec7 100644 --- a/servo/components/selectors/parser.rs +++ b/servo/components/selectors/parser.rs @@ -1927,6 +1927,11 @@ impl Component { return false; } }, + Has(ref list) => { + if !visitor.visit_relative_selector_list(list) { + return false; + } + }, _ => {}, } diff --git a/servo/components/selectors/visitor.rs b/servo/components/selectors/visitor.rs index 785c12813a6b..8bf5a5656d46 100644 --- a/servo/components/selectors/visitor.rs +++ b/servo/components/selectors/visitor.rs @@ -7,7 +7,7 @@ #![deny(missing_docs)] use crate::attr::NamespaceConstraint; -use crate::parser::{Combinator, Component, Selector, SelectorImpl}; +use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl}; /// A trait to visit selector properties. /// @@ -34,6 +34,14 @@ pub trait SelectorVisitor: Sized { true } + /// Visit a nested relative selector list. The caller is responsible to call visit + /// into the internal selectors if / as needed. + /// + /// The default implementation skips it altogether. + fn visit_relative_selector_list(&mut self, _list: &[RelativeSelector]) -> bool { + true + } + /// Visit a nested selector list. The caller is responsible to call visit /// into the internal selectors if / as needed. /// @@ -73,6 +81,8 @@ bitflags! { /// The visitor is inside :nth-child(.. of ) or /// :nth-last-child(.. of ) const NTH_OF = 1 << 3; + /// The visitor is inside :has(..) + const HAS = 1 << 4; } } @@ -108,4 +118,19 @@ impl SelectorListKind { pub fn in_nth_of(&self) -> bool { self.intersects(SelectorListKind::NTH_OF) } + + /// Whether the visitor is inside :has(..) + pub fn in_has(&self) -> bool { + self.intersects(SelectorListKind::HAS) + } + + /// Whether this nested selector is relevant for nth-of dependencies. + pub fn relevant_to_nth_of_dependencies(&self) -> bool { + // Order of nesting for `:has` and `:nth-child(.. of ..)` doesn't matter, because: + // * `:has(:nth-child(.. of ..))`: The location of the anchoring element is + // independent from where `:nth-child(.. of ..)` is applied. + // * `:nth-child(.. of :has(..))`: Invalidations inside `:has` must first use the + // `:has` machinary to find the anchor, then carry out the remaining invalidation. + self.in_nth_of() && !self.in_has() + } } diff --git a/servo/components/style/stylist.rs b/servo/components/style/stylist.rs index b436111f27e7..0374b9c6626c 100644 --- a/servo/components/style/stylist.rs +++ b/servo/components/style/stylist.rs @@ -2003,6 +2003,25 @@ fn component_needs_revalidation( } } +impl<'a> StylistSelectorVisitor<'a> { + fn visit_nested_selector( + &mut self, + in_selector_list_of: SelectorListKind, + selector: &Selector + ) { + let old_passed_rightmost_selector = self.passed_rightmost_selector; + let old_in_selector_list_of = self.in_selector_list_of; + + self.passed_rightmost_selector = false; + self.in_selector_list_of = in_selector_list_of; + let _ret = selector.visit(self); + debug_assert!(_ret, "We never return false"); + + self.passed_rightmost_selector = old_passed_rightmost_selector; + self.in_selector_list_of = old_in_selector_list_of; + } +} + impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { type Impl = SelectorImpl; @@ -2026,21 +2045,18 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { ) -> bool { let in_selector_list_of = self.in_selector_list_of | list_kind; for selector in list { - let mut nested = StylistSelectorVisitor { - passed_rightmost_selector: false, - needs_revalidation: &mut *self.needs_revalidation, - in_selector_list_of, - mapped_ids: &mut *self.mapped_ids, - nth_of_mapped_ids: &mut *self.nth_of_mapped_ids, - attribute_dependencies: &mut *self.attribute_dependencies, - nth_of_class_dependencies: &mut *self.nth_of_class_dependencies, - nth_of_attribute_dependencies: &mut *self.nth_of_attribute_dependencies, - state_dependencies: &mut *self.state_dependencies, - nth_of_state_dependencies: &mut *self.nth_of_state_dependencies, - document_state_dependencies: &mut *self.document_state_dependencies, - }; - let _ret = selector.visit(&mut nested); - debug_assert!(_ret, "We never return false"); + self.visit_nested_selector(in_selector_list_of, selector); + } + true + } + + fn visit_relative_selector_list( + &mut self, + list: &[selectors::parser::RelativeSelector], + ) -> bool { + let in_selector_list_of = self.in_selector_list_of | SelectorListKind::HAS; + for selector in list { + self.visit_nested_selector(in_selector_list_of, &selector.selector); } true } @@ -2051,7 +2067,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { name: &LocalName, lower_name: &LocalName, ) -> bool { - if self.in_selector_list_of.in_nth_of() { + if self.in_selector_list_of.relevant_to_nth_of_dependencies() { self.nth_of_attribute_dependencies.insert(name.clone()); if name != lower_name { self.nth_of_attribute_dependencies @@ -2077,7 +2093,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { self.document_state_dependencies .insert(p.document_state_flag()); - if self.in_selector_list_of.in_nth_of() { + if self.in_selector_list_of.relevant_to_nth_of_dependencies() { self.nth_of_state_dependencies.insert(p.state_flag()); } }, @@ -2097,11 +2113,13 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { self.mapped_ids.insert(id.0.clone()); } - if self.in_selector_list_of.in_nth_of() { + if self.in_selector_list_of.relevant_to_nth_of_dependencies() { self.nth_of_mapped_ids.insert(id.0.clone()); } }, - Component::Class(ref class) if self.in_selector_list_of.in_nth_of() => { + Component::Class(ref class) + if self.in_selector_list_of.relevant_to_nth_of_dependencies() => + { self.nth_of_class_dependencies.insert(class.0.clone()); }, _ => {},