visitor.rs (4884B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Visitor traits for selectors. 6 7 #![deny(missing_docs)] 8 9 use crate::attr::NamespaceConstraint; 10 use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl}; 11 use bitflags::bitflags; 12 13 /// A trait to visit selector properties. 14 /// 15 /// All the `visit_foo` methods return a boolean indicating whether the 16 /// traversal should continue or not. 17 pub trait SelectorVisitor: Sized { 18 /// The selector implementation this visitor wants to visit. 19 type Impl: SelectorImpl; 20 21 /// Visit an attribute selector that may match (there are other selectors 22 /// that may never match, like those containing whitespace or the empty 23 /// string). 24 fn visit_attribute_selector( 25 &mut self, 26 _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>, 27 _local_name: &<Self::Impl as SelectorImpl>::LocalName, 28 _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName, 29 ) -> bool { 30 true 31 } 32 33 /// Visit a simple selector. 34 fn visit_simple_selector(&mut self, _: &Component<Self::Impl>) -> bool { 35 true 36 } 37 38 /// Visit a nested relative selector list. The caller is responsible to call visit 39 /// into the internal selectors if / as needed. 40 /// 41 /// The default implementation skips it altogether. 42 fn visit_relative_selector_list(&mut self, _list: &[RelativeSelector<Self::Impl>]) -> bool { 43 true 44 } 45 46 /// Visit a nested selector list. The caller is responsible to call visit 47 /// into the internal selectors if / as needed. 48 /// 49 /// The default implementation does this. 50 fn visit_selector_list( 51 &mut self, 52 _list_kind: SelectorListKind, 53 list: &[Selector<Self::Impl>], 54 ) -> bool { 55 for nested in list { 56 if !nested.visit(self) { 57 return false; 58 } 59 } 60 true 61 } 62 63 /// Visits a complex selector. 64 /// 65 /// Gets the combinator to the right of the selector, or `None` if the 66 /// selector is the rightmost one. 67 fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool { 68 true 69 } 70 } 71 72 bitflags! { 73 /// The kinds of components the visitor is visiting the selector list of, if any 74 #[derive(Clone, Copy, Default)] 75 pub struct SelectorListKind: u8 { 76 /// The visitor is inside :not(..) 77 const NEGATION = 1 << 0; 78 /// The visitor is inside :is(..) 79 const IS = 1 << 1; 80 /// The visitor is inside :where(..) 81 const WHERE = 1 << 2; 82 /// The visitor is inside :nth-child(.. of <selector list>) or 83 /// :nth-last-child(.. of <selector list>) 84 const NTH_OF = 1 << 3; 85 /// The visitor is inside :has(..) 86 const HAS = 1 << 4; 87 } 88 } 89 90 impl SelectorListKind { 91 /// Construct a SelectorListKind for the corresponding component. 92 pub fn from_component<Impl: SelectorImpl>(component: &Component<Impl>) -> Self { 93 match component { 94 Component::Negation(_) => SelectorListKind::NEGATION, 95 Component::Is(_) => SelectorListKind::IS, 96 Component::Where(_) => SelectorListKind::WHERE, 97 Component::NthOf(_) => SelectorListKind::NTH_OF, 98 _ => SelectorListKind::empty(), 99 } 100 } 101 102 /// Whether the visitor is inside :not(..) 103 pub fn in_negation(&self) -> bool { 104 self.intersects(SelectorListKind::NEGATION) 105 } 106 107 /// Whether the visitor is inside :is(..) 108 pub fn in_is(&self) -> bool { 109 self.intersects(SelectorListKind::IS) 110 } 111 112 /// Whether the visitor is inside :where(..) 113 pub fn in_where(&self) -> bool { 114 self.intersects(SelectorListKind::WHERE) 115 } 116 117 /// Whether the visitor is inside :nth-child(.. of <selector list>) or 118 /// :nth-last-child(.. of <selector list>) 119 pub fn in_nth_of(&self) -> bool { 120 self.intersects(SelectorListKind::NTH_OF) 121 } 122 123 /// Whether the visitor is inside :has(..) 124 pub fn in_has(&self) -> bool { 125 self.intersects(SelectorListKind::HAS) 126 } 127 128 /// Whether this nested selector is relevant for nth-of dependencies. 129 pub fn relevant_to_nth_of_dependencies(&self) -> bool { 130 // Order of nesting for `:has` and `:nth-child(.. of ..)` doesn't matter, because: 131 // * `:has(:nth-child(.. of ..))`: The location of the anchoring element is 132 // independent from where `:nth-child(.. of ..)` is applied. 133 // * `:nth-child(.. of :has(..))`: Invalidations inside `:has` must first use the 134 // `:has` machinary to find the anchor, then carry out the remaining invalidation. 135 self.in_nth_of() && !self.in_has() 136 } 137 }