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 use crate::attr::CaseSensitivity; 6 use crate::bloom::BloomFilter; 7 use crate::nth_index_cache::NthIndexCache; 8 use crate::parser::SelectorImpl; 9 use crate::tree::{Element, OpaqueElement}; 10 11 /// What kind of selector matching mode we should use. 12 /// 13 /// There are two modes of selector matching. The difference is only noticeable 14 /// in presence of pseudo-elements. 15 #[derive(Clone, Copy, Debug, PartialEq)] 16 pub enum MatchingMode { 17 /// Don't ignore any pseudo-element selectors. 18 Normal, 19 20 /// Ignores any stateless pseudo-element selectors in the rightmost sequence 21 /// of simple selectors. 22 /// 23 /// This is useful, for example, to match against ::before when you aren't a 24 /// pseudo-element yourself. 25 /// 26 /// For example, in presence of `::before:hover`, it would never match, but 27 /// `::before` would be ignored as in "matching". 28 /// 29 /// It's required for all the selectors you match using this mode to have a 30 /// pseudo-element. 31 ForStatelessPseudoElement, 32 } 33 34 /// The mode to use when matching unvisited and visited links. 35 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 36 pub enum VisitedHandlingMode { 37 /// All links are matched as if they are unvisted. 38 AllLinksUnvisited, 39 /// All links are matched as if they are visited and unvisited (both :link 40 /// and :visited match). 41 /// 42 /// This is intended to be used from invalidation code, to be conservative 43 /// about whether we need to restyle a link. 44 AllLinksVisitedAndUnvisited, 45 /// A element's "relevant link" is the element being matched if it is a link 46 /// or the nearest ancestor link. The relevant link is matched as though it 47 /// is visited, and all other links are matched as if they are unvisited. 48 RelevantLinkVisited, 49 } 50 51 impl VisitedHandlingMode { 52 #[inline] matches_visited(&self) -> bool53 pub fn matches_visited(&self) -> bool { 54 matches!( 55 *self, 56 VisitedHandlingMode::RelevantLinkVisited | 57 VisitedHandlingMode::AllLinksVisitedAndUnvisited 58 ) 59 } 60 61 #[inline] matches_unvisited(&self) -> bool62 pub fn matches_unvisited(&self) -> bool { 63 matches!( 64 *self, 65 VisitedHandlingMode::AllLinksUnvisited | 66 VisitedHandlingMode::AllLinksVisitedAndUnvisited 67 ) 68 } 69 } 70 71 /// Which quirks mode is this document in. 72 /// 73 /// See: https://quirks.spec.whatwg.org/ 74 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 75 pub enum QuirksMode { 76 /// Quirks mode. 77 Quirks, 78 /// Limited quirks mode. 79 LimitedQuirks, 80 /// No quirks mode. 81 NoQuirks, 82 } 83 84 impl QuirksMode { 85 #[inline] classes_and_ids_case_sensitivity(self) -> CaseSensitivity86 pub fn classes_and_ids_case_sensitivity(self) -> CaseSensitivity { 87 match self { 88 QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive, 89 QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive, 90 } 91 } 92 } 93 94 /// Data associated with the matching process for a element. This context is 95 /// used across many selectors for an element, so it's not appropriate for 96 /// transient data that applies to only a single selector. 97 pub struct MatchingContext<'a, Impl> 98 where 99 Impl: SelectorImpl, 100 { 101 /// Input with the matching mode we should use when matching selectors. 102 matching_mode: MatchingMode, 103 /// Input with the bloom filter used to fast-reject selectors. 104 pub bloom_filter: Option<&'a BloomFilter>, 105 /// An optional cache to speed up nth-index-like selectors. 106 pub nth_index_cache: Option<&'a mut NthIndexCache>, 107 /// The element which is going to match :scope pseudo-class. It can be 108 /// either one :scope element, or the scoping element. 109 /// 110 /// Note that, although in theory there can be multiple :scope elements, 111 /// in current specs, at most one is specified, and when there is one, 112 /// scoping element is not relevant anymore, so we use a single field for 113 /// them. 114 /// 115 /// When this is None, :scope will match the root element. 116 /// 117 /// See https://drafts.csswg.org/selectors-4/#scope-pseudo 118 pub scope_element: Option<OpaqueElement>, 119 120 /// The current shadow host we're collecting :host rules for. 121 pub current_host: Option<OpaqueElement>, 122 123 /// Controls how matching for links is handled. 124 visited_handling: VisitedHandlingMode, 125 126 /// The current nesting level of selectors that we're matching. 127 /// 128 /// FIXME(emilio): Consider putting the mutable stuff in a Cell, then make 129 /// MatchingContext immutable again. 130 nesting_level: usize, 131 132 /// Whether we're inside a negation or not. 133 in_negation: bool, 134 135 /// An optional hook function for checking whether a pseudo-element 136 /// should match when matching_mode is ForStatelessPseudoElement. 137 pub pseudo_element_matching_fn: Option<&'a dyn Fn(&Impl::PseudoElement) -> bool>, 138 139 /// Extra implementation-dependent matching data. 140 pub extra_data: Impl::ExtraMatchingData, 141 142 quirks_mode: QuirksMode, 143 classes_and_ids_case_sensitivity: CaseSensitivity, 144 _impl: ::std::marker::PhantomData<Impl>, 145 } 146 147 impl<'a, Impl> MatchingContext<'a, Impl> 148 where 149 Impl: SelectorImpl, 150 { 151 /// Constructs a new `MatchingContext`. new( matching_mode: MatchingMode, bloom_filter: Option<&'a BloomFilter>, nth_index_cache: Option<&'a mut NthIndexCache>, quirks_mode: QuirksMode, ) -> Self152 pub fn new( 153 matching_mode: MatchingMode, 154 bloom_filter: Option<&'a BloomFilter>, 155 nth_index_cache: Option<&'a mut NthIndexCache>, 156 quirks_mode: QuirksMode, 157 ) -> Self { 158 Self::new_for_visited( 159 matching_mode, 160 bloom_filter, 161 nth_index_cache, 162 VisitedHandlingMode::AllLinksUnvisited, 163 quirks_mode, 164 ) 165 } 166 167 /// Constructs a new `MatchingContext` for use in visited matching. new_for_visited( matching_mode: MatchingMode, bloom_filter: Option<&'a BloomFilter>, nth_index_cache: Option<&'a mut NthIndexCache>, visited_handling: VisitedHandlingMode, quirks_mode: QuirksMode, ) -> Self168 pub fn new_for_visited( 169 matching_mode: MatchingMode, 170 bloom_filter: Option<&'a BloomFilter>, 171 nth_index_cache: Option<&'a mut NthIndexCache>, 172 visited_handling: VisitedHandlingMode, 173 quirks_mode: QuirksMode, 174 ) -> Self { 175 Self { 176 matching_mode, 177 bloom_filter, 178 visited_handling, 179 nth_index_cache, 180 quirks_mode, 181 classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(), 182 scope_element: None, 183 current_host: None, 184 nesting_level: 0, 185 in_negation: false, 186 pseudo_element_matching_fn: None, 187 extra_data: Default::default(), 188 _impl: ::std::marker::PhantomData, 189 } 190 } 191 192 /// Whether we're matching a nested selector. 193 #[inline] is_nested(&self) -> bool194 pub fn is_nested(&self) -> bool { 195 self.nesting_level != 0 196 } 197 198 /// Whether we're matching inside a :not(..) selector. 199 #[inline] in_negation(&self) -> bool200 pub fn in_negation(&self) -> bool { 201 self.in_negation 202 } 203 204 /// The quirks mode of the document. 205 #[inline] quirks_mode(&self) -> QuirksMode206 pub fn quirks_mode(&self) -> QuirksMode { 207 self.quirks_mode 208 } 209 210 /// The matching-mode for this selector-matching operation. 211 #[inline] matching_mode(&self) -> MatchingMode212 pub fn matching_mode(&self) -> MatchingMode { 213 self.matching_mode 214 } 215 216 /// The case-sensitivity for class and ID selectors 217 #[inline] classes_and_ids_case_sensitivity(&self) -> CaseSensitivity218 pub fn classes_and_ids_case_sensitivity(&self) -> CaseSensitivity { 219 self.classes_and_ids_case_sensitivity 220 } 221 222 /// Runs F with a deeper nesting level. 223 #[inline] nest<F, R>(&mut self, f: F) -> R where F: FnOnce(&mut Self) -> R,224 pub fn nest<F, R>(&mut self, f: F) -> R 225 where 226 F: FnOnce(&mut Self) -> R, 227 { 228 self.nesting_level += 1; 229 let result = f(self); 230 self.nesting_level -= 1; 231 result 232 } 233 234 /// Runs F with a deeper nesting level, and marking ourselves in a negation, 235 /// for a :not(..) selector, for example. 236 #[inline] nest_for_negation<F, R>(&mut self, f: F) -> R where F: FnOnce(&mut Self) -> R,237 pub fn nest_for_negation<F, R>(&mut self, f: F) -> R 238 where 239 F: FnOnce(&mut Self) -> R, 240 { 241 let old_in_negation = self.in_negation; 242 self.in_negation = true; 243 let result = self.nest(f); 244 self.in_negation = old_in_negation; 245 result 246 } 247 248 #[inline] visited_handling(&self) -> VisitedHandlingMode249 pub fn visited_handling(&self) -> VisitedHandlingMode { 250 self.visited_handling 251 } 252 253 /// Runs F with a different VisitedHandlingMode. 254 #[inline] with_visited_handling_mode<F, R>( &mut self, handling_mode: VisitedHandlingMode, f: F, ) -> R where F: FnOnce(&mut Self) -> R,255 pub fn with_visited_handling_mode<F, R>( 256 &mut self, 257 handling_mode: VisitedHandlingMode, 258 f: F, 259 ) -> R 260 where 261 F: FnOnce(&mut Self) -> R, 262 { 263 let original_handling_mode = self.visited_handling; 264 self.visited_handling = handling_mode; 265 let result = f(self); 266 self.visited_handling = original_handling_mode; 267 result 268 } 269 270 /// Runs F with a given shadow host which is the root of the tree whose 271 /// rules we're matching. 272 #[inline] with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R where E: Element, F: FnOnce(&mut Self) -> R,273 pub fn with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R 274 where 275 E: Element, 276 F: FnOnce(&mut Self) -> R, 277 { 278 let original_host = self.current_host.take(); 279 self.current_host = host.map(|h| h.opaque()); 280 let result = f(self); 281 self.current_host = original_host; 282 result 283 } 284 285 /// Returns the current shadow host whose shadow root we're matching rules 286 /// against. 287 #[inline] shadow_host(&self) -> Option<OpaqueElement>288 pub fn shadow_host(&self) -> Option<OpaqueElement> { 289 self.current_host 290 } 291 } 292