selectors/visitor.rs
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
9use crate::attr::NamespaceConstraint;
10use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl};
11use 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.
17pub 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
72bitflags! {
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
90impl 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}