/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Visitor traits for selectors. #![deny(missing_docs)] use crate::attr::NamespaceConstraint; use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl}; /// A trait to visit selector properties. /// /// All the `visit_foo` methods return a boolean indicating whether the /// traversal should continue or not. pub trait SelectorVisitor: Sized { /// The selector implementation this visitor wants to visit. type Impl: SelectorImpl; /// Visit an attribute selector that may match (there are other selectors /// that may never match, like those containing whitespace or the empty /// string). fn visit_attribute_selector( &mut self, _namespace: &NamespaceConstraint<&::NamespaceUrl>, _local_name: &::LocalName, _local_name_lower: &::LocalName, ) -> bool { true } /// Visit a simple selector. fn visit_simple_selector(&mut self, _: &Component) -> bool { 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. /// /// The default implementation does this. fn visit_selector_list( &mut self, _list_kind: SelectorListKind, list: &[Selector], ) -> bool { for nested in list { if !nested.visit(self) { return false; } } true } /// Visits a complex selector. /// /// Gets the combinator to the right of the selector, or `None` if the /// selector is the rightmost one. fn visit_complex_selector(&mut self, _combinator_to_right: Option) -> bool { true } } bitflags! { /// The kinds of components the visitor is visiting the selector list of, if any #[derive(Clone, Copy, Default)] pub struct SelectorListKind: u8 { /// The visitor is inside :not(..) const NEGATION = 1 << 0; /// The visitor is inside :is(..) const IS = 1 << 1; /// The visitor is inside :where(..) const WHERE = 1 << 2; /// 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; } } impl SelectorListKind { /// Construct a SelectorListKind for the corresponding component. pub fn from_component(component: &Component) -> Self { match component { Component::Negation(_) => SelectorListKind::NEGATION, Component::Is(_) => SelectorListKind::IS, Component::Where(_) => SelectorListKind::WHERE, Component::NthOf(_) => SelectorListKind::NTH_OF, _ => SelectorListKind::empty(), } } /// Whether the visitor is inside :not(..) pub fn in_negation(&self) -> bool { self.intersects(SelectorListKind::NEGATION) } /// Whether the visitor is inside :is(..) pub fn in_is(&self) -> bool { self.intersects(SelectorListKind::IS) } /// Whether the visitor is inside :where(..) pub fn in_where(&self) -> bool { self.intersects(SelectorListKind::WHERE) } /// Whether the visitor is inside :nth-child(.. of ) or /// :nth-last-child(.. of ) 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() } }