matching.rs (56830B)
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::{ 6 AttrSelectorOperation, AttrSelectorWithOptionalNamespace, CaseSensitivity, NamespaceConstraint, 7 ParsedAttrSelectorOperation, ParsedCaseSensitivity, 8 }; 9 use crate::bloom::{BloomFilter, BLOOM_HASH_MASK}; 10 use crate::kleene_value::KleeneValue; 11 use crate::parser::{ 12 AncestorHashes, Combinator, Component, LocalName, MatchesFeaturelessHost, NthSelectorData, 13 RelativeSelectorMatchHint, 14 }; 15 use crate::parser::{ 16 NonTSPseudoClass, RelativeSelector, Selector, SelectorImpl, SelectorIter, SelectorList, 17 }; 18 use crate::relative_selector::cache::RelativeSelectorCachedMatch; 19 use crate::tree::Element; 20 use bitflags::bitflags; 21 use debug_unreachable::debug_unreachable; 22 use log::debug; 23 use smallvec::SmallVec; 24 use std::borrow::Borrow; 25 26 pub use crate::context::*; 27 28 // The bloom filter for descendant CSS selectors will have a <1% false 29 // positive rate until it has this many selectors in it, then it will 30 // rapidly increase. 31 pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096; 32 33 bitflags! { 34 /// Set of flags that are set on either the element or its parent (depending 35 /// on the flag) if the element could potentially match a selector. 36 #[derive(Clone, Copy)] 37 pub struct ElementSelectorFlags: usize { 38 /// When a child is added or removed from the parent, all the children 39 /// must be restyled, because they may match :nth-last-child, 40 /// :last-of-type, :nth-last-of-type, or :only-of-type. 41 const HAS_SLOW_SELECTOR = 1 << 0; 42 43 /// When a child is added or removed from the parent, any later 44 /// children must be restyled, because they may match :nth-child, 45 /// :first-of-type, or :nth-of-type. 46 const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1; 47 48 /// HAS_SLOW_SELECTOR* was set by the presence of :nth (But not of). 49 const HAS_SLOW_SELECTOR_NTH = 1 << 2; 50 51 /// When a DOM mutation occurs on a child that might be matched by 52 /// :nth-last-child(.. of <selector list>), earlier children must be 53 /// restyled, and HAS_SLOW_SELECTOR will be set (which normally 54 /// indicates that all children will be restyled). 55 /// 56 /// Similarly, when a DOM mutation occurs on a child that might be 57 /// matched by :nth-child(.. of <selector list>), later children must be 58 /// restyled, and HAS_SLOW_SELECTOR_LATER_SIBLINGS will be set. 59 const HAS_SLOW_SELECTOR_NTH_OF = 1 << 3; 60 61 /// When a child is added or removed from the parent, the first and 62 /// last children must be restyled, because they may match :first-child, 63 /// :last-child, or :only-child. 64 const HAS_EDGE_CHILD_SELECTOR = 1 << 4; 65 66 /// The element has an empty selector, so when a child is appended we 67 /// might need to restyle the parent completely. 68 const HAS_EMPTY_SELECTOR = 1 << 5; 69 70 /// The element may anchor a relative selector. 71 const ANCHORS_RELATIVE_SELECTOR = 1 << 6; 72 73 /// The element may anchor a relative selector that is not the subject 74 /// of the whole selector. 75 const ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT = 1 << 7; 76 77 /// The element is reached by a relative selector search in the sibling direction. 78 const RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING = 1 << 8; 79 80 /// The element is reached by a relative selector search in the ancestor direction. 81 const RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR = 1 << 9; 82 83 // The element is reached by a relative selector search in both sibling and ancestor directions. 84 const RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING = 85 Self::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING.bits() | 86 Self::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR.bits(); 87 } 88 } 89 90 impl ElementSelectorFlags { 91 /// Returns the subset of flags that apply to the element. 92 pub fn for_self(self) -> ElementSelectorFlags { 93 self & (ElementSelectorFlags::HAS_EMPTY_SELECTOR 94 | ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR 95 | ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT 96 | ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING 97 | ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR) 98 } 99 100 /// Returns the subset of flags that apply to the parent. 101 pub fn for_parent(self) -> ElementSelectorFlags { 102 self & (ElementSelectorFlags::HAS_SLOW_SELECTOR 103 | ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS 104 | ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH 105 | ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF 106 | ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) 107 } 108 } 109 110 /// Holds per-compound-selector data. 111 struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> { 112 shared: &'a mut MatchingContext<'b, Impl>, 113 rightmost: SubjectOrPseudoElement, 114 quirks_data: Option<SelectorIter<'a, Impl>>, 115 } 116 117 #[inline(always)] 118 pub fn matches_selector_list<E>( 119 selector_list: &SelectorList<E::Impl>, 120 element: &E, 121 context: &mut MatchingContext<E::Impl>, 122 ) -> bool 123 where 124 E: Element, 125 { 126 // This is pretty much any(..) but manually inlined because the compiler 127 // refuses to do so from querySelector / querySelectorAll. 128 for selector in selector_list.slice() { 129 let matches = matches_selector(selector, 0, None, element, context); 130 if matches { 131 return true; 132 } 133 } 134 135 false 136 } 137 138 /// Given the ancestor hashes from a selector, see if the current element, 139 /// represented by the bloom filter, has a chance of matching at all. 140 #[inline(always)] 141 pub fn selector_may_match(hashes: &AncestorHashes, bf: &BloomFilter) -> bool { 142 // Check the first three hashes. Note that we can check for zero before 143 // masking off the high bits, since if any of the first three hashes is 144 // zero the fourth will be as well. We also take care to avoid the 145 // special-case complexity of the fourth hash until we actually reach it, 146 // because we usually don't. 147 // 148 // To be clear: this is all extremely hot. 149 for i in 0..3 { 150 let packed = hashes.packed_hashes[i]; 151 if packed == 0 { 152 // No more hashes left - unable to fast-reject. 153 return true; 154 } 155 156 if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) { 157 // Hooray! We fast-rejected on this hash. 158 return false; 159 } 160 } 161 162 // Now do the slighty-more-complex work of synthesizing the fourth hash, 163 // and check it against the filter if it exists. 164 let fourth = hashes.fourth_hash(); 165 fourth == 0 || bf.might_contain_hash(fourth) 166 } 167 168 /// A result of selector matching, includes 3 failure types, 169 /// 170 /// NotMatchedAndRestartFromClosestLaterSibling 171 /// NotMatchedAndRestartFromClosestDescendant 172 /// NotMatchedGlobally 173 /// 174 /// When NotMatchedGlobally appears, stop selector matching completely since 175 /// the succeeding selectors never matches. 176 /// It is raised when 177 /// Child combinator cannot find the candidate element. 178 /// Descendant combinator cannot find the candidate element. 179 /// 180 /// When NotMatchedAndRestartFromClosestDescendant appears, the selector 181 /// matching does backtracking and restarts from the closest Descendant 182 /// combinator. 183 /// It is raised when 184 /// NextSibling combinator cannot find the candidate element. 185 /// LaterSibling combinator cannot find the candidate element. 186 /// Child combinator doesn't match on the found element. 187 /// 188 /// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector 189 /// matching does backtracking and restarts from the closest LaterSibling 190 /// combinator. 191 /// It is raised when 192 /// NextSibling combinator doesn't match on the found element. 193 /// 194 /// For example, when the selector "d1 d2 a" is provided and we cannot *find* 195 /// an appropriate ancestor element for "d1", this selector matching raises 196 /// NotMatchedGlobally since even if "d2" is moved to more upper element, the 197 /// candidates for "d1" becomes less than before and d1 . 198 /// 199 /// The next example is siblings. When the selector "b1 + b2 ~ d1 a" is 200 /// provided and we cannot *find* an appropriate brother element for b1, 201 /// the selector matching raises NotMatchedAndRestartFromClosestDescendant. 202 /// The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1". 203 /// 204 /// The additional example is child and sibling. When the selector 205 /// "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on 206 /// the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling. 207 /// However since the selector "c1" raises 208 /// NotMatchedAndRestartFromClosestDescendant. So the selector 209 /// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1". 210 /// 211 /// There is also the unknown result, which is used during invalidation when 212 /// specific selector is being tested for before/after comparison. More specifically, 213 /// selectors that are too expensive to correctly compute during invalidation may 214 /// return unknown, as the computation will be thrown away and only to be recomputed 215 /// during styling. For most cases, the unknown result can be treated as matching. 216 /// This is because a compound of selectors acts like &&, and unknown && matched 217 /// == matched and unknown && not-matched == not-matched. However, some selectors, 218 /// like `:is()`, behave like || i.e. `:is(.a, .b)` == a || b. Treating unknown 219 /// == matching then causes these selectors to always return matching, which undesired 220 /// for before/after comparison. Coercing to not-matched doesn't work since each 221 /// inner selector may have compounds: e.g. Toggling `.foo` in `:is(.foo:has(..))` 222 /// with coersion to not-matched would result in an invalid before/after comparison 223 /// of not-matched/not-matched. 224 #[derive(Clone, Copy, Eq, PartialEq)] 225 enum SelectorMatchingResult { 226 Matched, 227 NotMatchedAndRestartFromClosestLaterSibling, 228 NotMatchedAndRestartFromClosestDescendant, 229 NotMatchedGlobally, 230 Unknown, 231 } 232 233 impl From<SelectorMatchingResult> for KleeneValue { 234 #[inline] 235 fn from(value: SelectorMatchingResult) -> Self { 236 match value { 237 SelectorMatchingResult::Matched => KleeneValue::True, 238 SelectorMatchingResult::Unknown => KleeneValue::Unknown, 239 SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling 240 | SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant 241 | SelectorMatchingResult::NotMatchedGlobally => KleeneValue::False, 242 } 243 } 244 } 245 246 /// Matches a selector, fast-rejecting against a bloom filter. 247 /// 248 /// We accept an offset to allow consumers to represent and match against 249 /// partial selectors (indexed from the right). We use this API design, rather 250 /// than having the callers pass a SelectorIter, because creating a SelectorIter 251 /// requires dereferencing the selector to get the length, which adds an 252 /// unnecessary cache miss for cases when we can fast-reject with AncestorHashes 253 /// (which the caller can store inline with the selector pointer). 254 #[inline(always)] 255 pub fn matches_selector<E>( 256 selector: &Selector<E::Impl>, 257 offset: usize, 258 hashes: Option<&AncestorHashes>, 259 element: &E, 260 context: &mut MatchingContext<E::Impl>, 261 ) -> bool 262 where 263 E: Element, 264 { 265 let result = matches_selector_kleene(selector, offset, hashes, element, context); 266 if cfg!(debug_assertions) && result == KleeneValue::Unknown { 267 debug_assert!( 268 context 269 .matching_for_invalidation_comparison() 270 .unwrap_or(false), 271 "How did we return unknown?" 272 ); 273 } 274 result.to_bool(true) 275 } 276 277 /// Same as matches_selector, but returns the Kleene value as-is. 278 #[inline(always)] 279 pub fn matches_selector_kleene<E>( 280 selector: &Selector<E::Impl>, 281 offset: usize, 282 hashes: Option<&AncestorHashes>, 283 element: &E, 284 context: &mut MatchingContext<E::Impl>, 285 ) -> KleeneValue 286 where 287 E: Element, 288 { 289 // Use the bloom filter to fast-reject. 290 if let Some(hashes) = hashes { 291 if let Some(filter) = context.bloom_filter { 292 if !selector_may_match(hashes, filter) { 293 return KleeneValue::False; 294 } 295 } 296 } 297 matches_complex_selector( 298 selector.iter_from(offset), 299 element, 300 context, 301 if selector.is_rightmost(offset) { 302 SubjectOrPseudoElement::Yes 303 } else { 304 SubjectOrPseudoElement::No 305 }, 306 ) 307 } 308 309 /// Whether a compound selector matched, and whether it was the rightmost 310 /// selector inside the complex selector. 311 pub enum CompoundSelectorMatchingResult { 312 /// The selector was fully matched. 313 FullyMatched, 314 /// The compound selector matched, and the next combinator offset is 315 /// `next_combinator_offset`. 316 Matched { next_combinator_offset: usize }, 317 /// The selector didn't match. 318 NotMatched, 319 } 320 321 fn complex_selector_early_reject_by_local_name<E: Element>( 322 list: &SelectorList<E::Impl>, 323 element: &E, 324 ) -> bool { 325 list.slice() 326 .iter() 327 .all(|s| early_reject_by_local_name(s, 0, element)) 328 } 329 330 /// Returns true if this compound would not match the given element by due 331 /// to a local name selector (If one exists). 332 pub fn early_reject_by_local_name<E: Element>( 333 selector: &Selector<E::Impl>, 334 from_offset: usize, 335 element: &E, 336 ) -> bool { 337 let iter = selector.iter_from(from_offset); 338 for component in iter { 339 if match component { 340 Component::LocalName(name) => !matches_local_name(element, name), 341 Component::Is(list) | Component::Where(list) => { 342 complex_selector_early_reject_by_local_name(list, element) 343 }, 344 _ => continue, 345 } { 346 return true; 347 } 348 } 349 false 350 } 351 352 /// Matches a compound selector belonging to `selector`, starting at offset 353 /// `from_offset`, matching left to right. 354 /// 355 /// Requires that `from_offset` points to a `Combinator`. 356 /// 357 /// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the 358 /// complex selector, but it happens to be the case we don't need it. 359 pub fn matches_compound_selector_from<E>( 360 selector: &Selector<E::Impl>, 361 mut from_offset: usize, 362 context: &mut MatchingContext<E::Impl>, 363 element: &E, 364 ) -> CompoundSelectorMatchingResult 365 where 366 E: Element, 367 { 368 debug_assert!( 369 !context 370 .matching_for_invalidation_comparison() 371 .unwrap_or(false), 372 "CompoundSelectorMatchingResult doesn't support unknown" 373 ); 374 if cfg!(debug_assertions) && from_offset != 0 { 375 selector.combinator_at_parse_order(from_offset - 1); // This asserts. 376 } 377 378 let mut local_context = LocalMatchingContext { 379 shared: context, 380 // We have no info if this is an outer selector. This function is called in 381 // an invalidation context, which only calls this for non-subject (i.e. 382 // Non-rightmost) positions. 383 rightmost: SubjectOrPseudoElement::No, 384 quirks_data: None, 385 }; 386 387 // Find the end of the selector or the next combinator, then match 388 // backwards, so that we match in the same order as 389 // matches_complex_selector, which is usually faster. 390 let start_offset = from_offset; 391 for component in selector.iter_raw_parse_order_from(from_offset) { 392 if matches!(*component, Component::Combinator(..)) { 393 debug_assert_ne!(from_offset, 0, "Selector started with a combinator?"); 394 break; 395 } 396 397 from_offset += 1; 398 } 399 400 debug_assert!(from_offset >= 1); 401 debug_assert!(from_offset <= selector.len()); 402 403 let iter = selector.iter_from(selector.len() - from_offset); 404 debug_assert!( 405 iter.clone().next().is_some() || from_offset != selector.len(), 406 "Got the math wrong: {:?} | {:?} | {} {}", 407 selector, 408 selector.iter_raw_match_order().as_slice(), 409 from_offset, 410 start_offset 411 ); 412 413 debug_assert!( 414 !local_context.shared.featureless(), 415 "Invalidating featureless element somehow?" 416 ); 417 418 for component in iter { 419 let result = matches_simple_selector(component, element, &mut local_context); 420 debug_assert!( 421 result != KleeneValue::Unknown, 422 "Returned unknown in non invalidation context?" 423 ); 424 if !result.to_bool(true) { 425 return CompoundSelectorMatchingResult::NotMatched; 426 } 427 } 428 429 if from_offset != selector.len() { 430 return CompoundSelectorMatchingResult::Matched { 431 next_combinator_offset: from_offset, 432 }; 433 } 434 435 CompoundSelectorMatchingResult::FullyMatched 436 } 437 438 /// Matches a complex selector. 439 #[inline(always)] 440 fn matches_complex_selector<E>( 441 mut iter: SelectorIter<E::Impl>, 442 element: &E, 443 context: &mut MatchingContext<E::Impl>, 444 rightmost: SubjectOrPseudoElement, 445 ) -> KleeneValue 446 where 447 E: Element, 448 { 449 // If this is the special pseudo-element mode, consume the ::pseudo-element 450 // before proceeding, since the caller has already handled that part. 451 if context.matching_mode() == MatchingMode::ForStatelessPseudoElement && !context.is_nested() { 452 // Consume the pseudo. 453 match *iter.next().unwrap() { 454 Component::PseudoElement(ref pseudo) => { 455 if let Some(ref f) = context.pseudo_element_matching_fn { 456 if !f(pseudo) { 457 return KleeneValue::False; 458 } 459 } 460 }, 461 ref other => { 462 debug_assert!( 463 false, 464 "Used MatchingMode::ForStatelessPseudoElement \ 465 in a non-pseudo selector {:?}", 466 other 467 ); 468 return KleeneValue::False; 469 }, 470 } 471 472 if !iter.matches_for_stateless_pseudo_element() { 473 return KleeneValue::False; 474 } 475 476 // Advance to the non-pseudo-element part of the selector. 477 let next_sequence = iter.next_sequence().unwrap(); 478 debug_assert_eq!(next_sequence, Combinator::PseudoElement); 479 } 480 481 matches_complex_selector_internal( 482 iter, 483 element, 484 context, 485 rightmost, 486 SubjectOrPseudoElement::Yes, 487 ) 488 .into() 489 } 490 491 /// Matches each selector of a list as a complex selector 492 fn matches_complex_selector_list<E: Element>( 493 list: &[Selector<E::Impl>], 494 element: &E, 495 context: &mut MatchingContext<E::Impl>, 496 rightmost: SubjectOrPseudoElement, 497 ) -> KleeneValue { 498 KleeneValue::any(list.iter(), |selector| { 499 matches_complex_selector(selector.iter(), element, context, rightmost) 500 }) 501 } 502 503 fn matches_relative_selector<E: Element>( 504 relative_selector: &RelativeSelector<E::Impl>, 505 element: &E, 506 context: &mut MatchingContext<E::Impl>, 507 rightmost: SubjectOrPseudoElement, 508 ) -> bool { 509 // Overall, we want to mark the path that we've traversed so that when an element 510 // is invalidated, we early-reject unnecessary relative selector invalidations. 511 if relative_selector.match_hint.is_descendant_direction() { 512 if context.needs_selector_flags() { 513 element.apply_selector_flags( 514 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR, 515 ); 516 } 517 let mut next_element = element.first_element_child(); 518 while let Some(el) = next_element { 519 if context.needs_selector_flags() { 520 el.apply_selector_flags( 521 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR, 522 ); 523 } 524 let mut matched = matches_complex_selector( 525 relative_selector.selector.iter(), 526 &el, 527 context, 528 rightmost, 529 ) 530 .to_bool(true); 531 if !matched && relative_selector.match_hint.is_subtree() { 532 matched = matches_relative_selector_subtree( 533 &relative_selector.selector, 534 &el, 535 context, 536 rightmost, 537 ); 538 } 539 if matched { 540 return true; 541 } 542 next_element = el.next_sibling_element(); 543 } 544 } else { 545 debug_assert!( 546 matches!( 547 relative_selector.match_hint, 548 RelativeSelectorMatchHint::InNextSibling 549 | RelativeSelectorMatchHint::InNextSiblingSubtree 550 | RelativeSelectorMatchHint::InSibling 551 | RelativeSelectorMatchHint::InSiblingSubtree 552 ), 553 "Not descendant direction, but also not sibling direction?" 554 ); 555 if context.needs_selector_flags() { 556 element.apply_selector_flags( 557 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING, 558 ); 559 } 560 let sibling_flag = if relative_selector.match_hint.is_subtree() { 561 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING 562 } else { 563 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING 564 }; 565 let mut next_element = element.next_sibling_element(); 566 while let Some(el) = next_element { 567 if context.needs_selector_flags() { 568 el.apply_selector_flags(sibling_flag); 569 } 570 let matched = if relative_selector.match_hint.is_subtree() { 571 matches_relative_selector_subtree( 572 &relative_selector.selector, 573 &el, 574 context, 575 rightmost, 576 ) 577 } else { 578 matches_complex_selector(relative_selector.selector.iter(), &el, context, rightmost) 579 .to_bool(true) 580 }; 581 if matched { 582 return true; 583 } 584 if relative_selector.match_hint.is_next_sibling() { 585 break; 586 } 587 next_element = el.next_sibling_element(); 588 } 589 } 590 return false; 591 } 592 593 fn relative_selector_match_early<E: Element>( 594 selector: &RelativeSelector<E::Impl>, 595 element: &E, 596 context: &mut MatchingContext<E::Impl>, 597 ) -> Option<bool> { 598 // See if we can return a cached result. 599 if let Some(cached) = context 600 .selector_caches 601 .relative_selector 602 .lookup(element.opaque(), selector) 603 { 604 return Some(cached.matched()); 605 } 606 // See if we can fast-reject. 607 if context 608 .selector_caches 609 .relative_selector_filter_map 610 .fast_reject(element, selector, context.quirks_mode()) 611 { 612 // Alright, add as unmatched to cache. 613 context.selector_caches.relative_selector.add( 614 element.opaque(), 615 selector, 616 RelativeSelectorCachedMatch::NotMatched, 617 ); 618 return Some(false); 619 } 620 None 621 } 622 623 fn match_relative_selectors<E: Element>( 624 selectors: &[RelativeSelector<E::Impl>], 625 element: &E, 626 context: &mut MatchingContext<E::Impl>, 627 rightmost: SubjectOrPseudoElement, 628 ) -> KleeneValue { 629 if context.relative_selector_anchor().is_some() { 630 // FIXME(emilio): This currently can happen with nesting, and it's not fully 631 // correct, arguably. But the ideal solution isn't super-clear either. For now, 632 // cope with it and explicitly reject it at match time. See [1] for discussion. 633 // 634 // [1]: https://github.com/w3c/csswg-drafts/issues/9600 635 return KleeneValue::False; 636 } 637 if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() { 638 // In the context of invalidation, :has is expensive, especially because we 639 // can't use caching/filtering due to now/then matches. DOM structure also 640 // may have changed. 641 return if may_return_unknown { 642 KleeneValue::Unknown 643 } else { 644 KleeneValue::from(!context.in_negation()) 645 }; 646 } 647 context 648 .nest_for_relative_selector(element.opaque(), |context| { 649 do_match_relative_selectors(selectors, element, context, rightmost) 650 }) 651 .into() 652 } 653 654 /// Matches a relative selector in a list of relative selectors. 655 fn do_match_relative_selectors<E: Element>( 656 selectors: &[RelativeSelector<E::Impl>], 657 element: &E, 658 context: &mut MatchingContext<E::Impl>, 659 rightmost: SubjectOrPseudoElement, 660 ) -> bool { 661 // Due to style sharing implications (See style sharing code), we mark the current styling context 662 // to mark elements considered for :has matching. Additionally, we want to mark the elements themselves, 663 // since we don't want to indiscriminately invalidate every element as a potential anchor. 664 if rightmost == SubjectOrPseudoElement::Yes { 665 if context.needs_selector_flags() { 666 element.apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR); 667 } 668 } else { 669 if context.needs_selector_flags() { 670 element 671 .apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT); 672 } 673 } 674 675 for relative_selector in selectors.iter() { 676 if let Some(result) = relative_selector_match_early(relative_selector, element, context) { 677 if result { 678 return true; 679 } 680 // Early return indicates no match, continue to next selector. 681 continue; 682 } 683 684 let matched = matches_relative_selector(relative_selector, element, context, rightmost); 685 context.selector_caches.relative_selector.add( 686 element.opaque(), 687 relative_selector, 688 if matched { 689 RelativeSelectorCachedMatch::Matched 690 } else { 691 RelativeSelectorCachedMatch::NotMatched 692 }, 693 ); 694 if matched { 695 return true; 696 } 697 } 698 699 false 700 } 701 702 fn matches_relative_selector_subtree<E: Element>( 703 selector: &Selector<E::Impl>, 704 element: &E, 705 context: &mut MatchingContext<E::Impl>, 706 rightmost: SubjectOrPseudoElement, 707 ) -> bool { 708 let mut current = element.first_element_child(); 709 710 while let Some(el) = current { 711 if context.needs_selector_flags() { 712 el.apply_selector_flags( 713 ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR, 714 ); 715 } 716 if matches_complex_selector(selector.iter(), &el, context, rightmost).to_bool(true) { 717 return true; 718 } 719 720 if matches_relative_selector_subtree(selector, &el, context, rightmost) { 721 return true; 722 } 723 724 current = el.next_sibling_element(); 725 } 726 727 false 728 } 729 730 /// Whether the :hover and :active quirk applies. 731 /// 732 /// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk 733 fn hover_and_active_quirk_applies<Impl: SelectorImpl>( 734 selector_iter: &SelectorIter<Impl>, 735 context: &MatchingContext<Impl>, 736 rightmost: SubjectOrPseudoElement, 737 ) -> bool { 738 debug_assert_eq!(context.quirks_mode(), QuirksMode::Quirks); 739 740 if context.is_nested() { 741 return false; 742 } 743 744 // This compound selector had a pseudo-element to the right that we 745 // intentionally skipped. 746 if rightmost == SubjectOrPseudoElement::Yes 747 && context.matching_mode() == MatchingMode::ForStatelessPseudoElement 748 { 749 return false; 750 } 751 752 selector_iter.clone().all(|simple| match *simple { 753 Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(), 754 _ => false, 755 }) 756 } 757 758 #[derive(Clone, Copy, PartialEq)] 759 enum SubjectOrPseudoElement { 760 Yes, 761 No, 762 } 763 764 fn host_for_part<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E> 765 where 766 E: Element, 767 { 768 let scope = context.current_host; 769 let mut curr = element.containing_shadow_host()?; 770 if scope == Some(curr.opaque()) { 771 return Some(curr); 772 } 773 loop { 774 let parent = curr.containing_shadow_host(); 775 if parent.as_ref().map(|h| h.opaque()) == scope { 776 return Some(curr); 777 } 778 curr = parent?; 779 } 780 } 781 782 fn assigned_slot<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E> 783 where 784 E: Element, 785 { 786 debug_assert!(element 787 .assigned_slot() 788 .map_or(true, |s| s.is_html_slot_element())); 789 let scope = context.current_host?; 790 let mut current_slot = element.assigned_slot()?; 791 while current_slot.containing_shadow_host().unwrap().opaque() != scope { 792 current_slot = current_slot.assigned_slot()?; 793 } 794 Some(current_slot) 795 } 796 797 struct NextElement<E> { 798 next_element: Option<E>, 799 featureless: bool, 800 } 801 802 impl<E> NextElement<E> { 803 #[inline(always)] 804 fn new(next_element: Option<E>, featureless: bool) -> Self { 805 Self { 806 next_element, 807 featureless, 808 } 809 } 810 } 811 812 #[inline(always)] 813 fn next_element_for_combinator<E>( 814 element: &E, 815 combinator: Combinator, 816 context: &MatchingContext<E::Impl>, 817 ) -> NextElement<E> 818 where 819 E: Element, 820 { 821 match combinator { 822 Combinator::NextSibling | Combinator::LaterSibling => { 823 NextElement::new(element.prev_sibling_element(), false) 824 }, 825 Combinator::Child | Combinator::Descendant => { 826 if let Some(parent) = element.parent_element() { 827 return NextElement::new(Some(parent), false); 828 } 829 830 let element = if element.parent_node_is_shadow_root() { 831 element.containing_shadow_host() 832 } else { 833 None 834 }; 835 NextElement::new(element, true) 836 }, 837 Combinator::Part => NextElement::new(host_for_part(element, context), false), 838 Combinator::SlotAssignment => NextElement::new(assigned_slot(element, context), false), 839 Combinator::PseudoElement => { 840 NextElement::new(element.pseudo_element_originating_element(), false) 841 }, 842 } 843 } 844 845 fn matches_complex_selector_internal<E>( 846 mut selector_iter: SelectorIter<E::Impl>, 847 element: &E, 848 context: &mut MatchingContext<E::Impl>, 849 mut rightmost: SubjectOrPseudoElement, 850 mut first_subject_compound: SubjectOrPseudoElement, 851 ) -> SelectorMatchingResult 852 where 853 E: Element, 854 { 855 debug!( 856 "Matching complex selector {:?} for {:?}", 857 selector_iter, element 858 ); 859 860 let matches_compound_selector = 861 matches_compound_selector(&mut selector_iter, element, context, rightmost); 862 863 let Some(combinator) = selector_iter.next_sequence() else { 864 return match matches_compound_selector { 865 KleeneValue::True => SelectorMatchingResult::Matched, 866 KleeneValue::Unknown => SelectorMatchingResult::Unknown, 867 KleeneValue::False => { 868 SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling 869 }, 870 }; 871 }; 872 873 let is_pseudo_combinator = combinator.is_pseudo_element(); 874 if context.featureless() && !is_pseudo_combinator { 875 // A featureless element shouldn't match any further combinator. 876 // TODO(emilio): Maybe we could avoid the compound matching more eagerly. 877 return SelectorMatchingResult::NotMatchedGlobally; 878 } 879 880 let is_sibling_combinator = combinator.is_sibling(); 881 if is_sibling_combinator && context.needs_selector_flags() { 882 // We need the flags even if we don't match. 883 element.apply_selector_flags(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS); 884 } 885 886 if matches_compound_selector == KleeneValue::False { 887 // We don't short circuit unknown here, since the rest of the selector 888 // to the left of this compound may still return false. 889 return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling; 890 } 891 892 if !is_pseudo_combinator { 893 rightmost = SubjectOrPseudoElement::No; 894 first_subject_compound = SubjectOrPseudoElement::No; 895 } 896 897 // Stop matching :visited as soon as we find a link, or a combinator for 898 // something that isn't an ancestor. 899 let mut visited_handling = if is_sibling_combinator { 900 VisitedHandlingMode::AllLinksUnvisited 901 } else { 902 context.visited_handling() 903 }; 904 905 let candidate_not_found = if is_sibling_combinator { 906 SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant 907 } else { 908 SelectorMatchingResult::NotMatchedGlobally 909 }; 910 911 let mut element = element.clone(); 912 loop { 913 if element.is_link() { 914 visited_handling = VisitedHandlingMode::AllLinksUnvisited; 915 } 916 917 let NextElement { 918 next_element, 919 featureless, 920 } = next_element_for_combinator(&element, combinator, &context); 921 element = match next_element { 922 None => return candidate_not_found, 923 Some(e) => e, 924 }; 925 926 let result = context.with_visited_handling_mode(visited_handling, |context| { 927 context.with_featureless(featureless, |context| { 928 matches_complex_selector_internal( 929 selector_iter.clone(), 930 &element, 931 context, 932 rightmost, 933 first_subject_compound, 934 ) 935 }) 936 }); 937 938 // Return the status immediately if it is one of the global states. 939 match result { 940 SelectorMatchingResult::Matched => { 941 debug_assert!( 942 matches_compound_selector.to_bool(true), 943 "Compound didn't match?" 944 ); 945 if !matches_compound_selector.to_bool(false) { 946 return SelectorMatchingResult::Unknown; 947 } 948 return result; 949 }, 950 SelectorMatchingResult::Unknown | SelectorMatchingResult::NotMatchedGlobally => { 951 return result 952 }, 953 _ => {}, 954 } 955 956 match combinator { 957 Combinator::Descendant => { 958 // The Descendant combinator and the status is 959 // NotMatchedAndRestartFromClosestLaterSibling or 960 // NotMatchedAndRestartFromClosestDescendant, or the LaterSibling combinator and 961 // the status is NotMatchedAndRestartFromClosestDescendant, we can continue to 962 // matching on the next candidate element. 963 }, 964 Combinator::Child => { 965 // Upgrade the failure status to NotMatchedAndRestartFromClosestDescendant. 966 return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant; 967 }, 968 Combinator::LaterSibling => { 969 // If the failure status is NotMatchedAndRestartFromClosestDescendant and combinator is 970 // LaterSibling, give up this LaterSibling matching and restart from the closest 971 // descendant combinator. 972 if matches!( 973 result, 974 SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant 975 ) { 976 return result; 977 } 978 }, 979 Combinator::NextSibling 980 | Combinator::PseudoElement 981 | Combinator::Part 982 | Combinator::SlotAssignment => { 983 // NOTE(emilio): Conceptually, PseudoElement / Part / SlotAssignment should return 984 // `candidate_not_found`, but it doesn't matter in practice since they don't have 985 // sibling / descendant combinators to the right of them. This hopefully saves one 986 // branch. 987 return result; 988 }, 989 } 990 991 if featureless { 992 // A featureless element didn't match the selector, we can stop matching now rather 993 // than looking at following elements for our combinator. 994 return candidate_not_found; 995 } 996 } 997 } 998 999 #[inline] 1000 fn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool 1001 where 1002 E: Element, 1003 { 1004 let name = select_name(element, &local_name.name, &local_name.lower_name).borrow(); 1005 element.has_local_name(name) 1006 } 1007 1008 fn matches_part<E>( 1009 element: &E, 1010 parts: &[<E::Impl as SelectorImpl>::Identifier], 1011 context: &mut MatchingContext<E::Impl>, 1012 ) -> bool 1013 where 1014 E: Element, 1015 { 1016 let mut hosts = SmallVec::<[E; 4]>::new(); 1017 1018 let mut host = match element.containing_shadow_host() { 1019 Some(h) => h, 1020 None => return false, 1021 }; 1022 1023 let current_host = context.current_host; 1024 if current_host != Some(host.opaque()) { 1025 loop { 1026 let outer_host = host.containing_shadow_host(); 1027 if outer_host.as_ref().map(|h| h.opaque()) == current_host { 1028 break; 1029 } 1030 let outer_host = match outer_host { 1031 Some(h) => h, 1032 None => return false, 1033 }; 1034 // TODO(emilio): if worth it, we could early return if 1035 // host doesn't have the exportparts attribute. 1036 hosts.push(host); 1037 host = outer_host; 1038 } 1039 } 1040 1041 // Translate the part into the right scope. 1042 parts.iter().all(|part| { 1043 let mut part = part.clone(); 1044 for host in hosts.iter().rev() { 1045 part = match host.imported_part(&part) { 1046 Some(p) => p, 1047 None => return false, 1048 }; 1049 } 1050 element.is_part(&part) 1051 }) 1052 } 1053 1054 fn matches_host<E>( 1055 element: &E, 1056 selector: Option<&Selector<E::Impl>>, 1057 context: &mut MatchingContext<E::Impl>, 1058 rightmost: SubjectOrPseudoElement, 1059 ) -> KleeneValue 1060 where 1061 E: Element, 1062 { 1063 let host = match context.shadow_host() { 1064 Some(h) => h, 1065 None => return KleeneValue::False, 1066 }; 1067 if host != element.opaque() { 1068 return KleeneValue::False; 1069 } 1070 let Some(selector) = selector else { 1071 return KleeneValue::True; 1072 }; 1073 context.nest(|context| { 1074 context.with_featureless(false, |context| { 1075 matches_complex_selector(selector.iter(), element, context, rightmost) 1076 }) 1077 }) 1078 } 1079 1080 fn matches_slotted<E>( 1081 element: &E, 1082 selector: &Selector<E::Impl>, 1083 context: &mut MatchingContext<E::Impl>, 1084 rightmost: SubjectOrPseudoElement, 1085 ) -> KleeneValue 1086 where 1087 E: Element, 1088 { 1089 // <slots> are never flattened tree slottables. 1090 if element.is_html_slot_element() { 1091 return KleeneValue::False; 1092 } 1093 context.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost)) 1094 } 1095 1096 fn matches_rare_attribute_selector<E>( 1097 element: &E, 1098 attr_sel: &AttrSelectorWithOptionalNamespace<E::Impl>, 1099 ) -> bool 1100 where 1101 E: Element, 1102 { 1103 let empty_string; 1104 let namespace = match attr_sel.namespace() { 1105 Some(ns) => ns, 1106 None => { 1107 empty_string = crate::parser::namespace_empty_string::<E::Impl>(); 1108 NamespaceConstraint::Specific(&empty_string) 1109 }, 1110 }; 1111 element.attr_matches( 1112 &namespace, 1113 select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower), 1114 &match attr_sel.operation { 1115 ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists, 1116 ParsedAttrSelectorOperation::WithValue { 1117 operator, 1118 case_sensitivity, 1119 ref value, 1120 } => AttrSelectorOperation::WithValue { 1121 operator, 1122 case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element), 1123 value, 1124 }, 1125 }, 1126 ) 1127 } 1128 1129 /// There are relatively few selectors in a given compound that may match a featureless element. 1130 /// Instead of adding a check to every selector that may not match, we handle it here in an out of 1131 /// line path. 1132 pub(crate) fn compound_matches_featureless_host<Impl: SelectorImpl>( 1133 iter: &mut SelectorIter<Impl>, 1134 scope_matches_featureless_host: bool, 1135 ) -> MatchesFeaturelessHost { 1136 let mut matches = MatchesFeaturelessHost::Only; 1137 for component in iter { 1138 match component { 1139 Component::Scope | Component::ImplicitScope if scope_matches_featureless_host => {}, 1140 // :host only matches featureless elements. 1141 Component::Host(..) => {}, 1142 // Pseudo-elements are allowed to match as well. 1143 Component::PseudoElement(..) => {}, 1144 // We allow logical pseudo-classes, but we'll fail matching of the inner selectors if 1145 // necessary. 1146 Component::Is(ref l) | Component::Where(ref l) => { 1147 let mut any_yes = false; 1148 let mut any_no = false; 1149 for selector in l.slice() { 1150 match selector.matches_featureless_host(scope_matches_featureless_host) { 1151 MatchesFeaturelessHost::Never => { 1152 any_no = true; 1153 }, 1154 MatchesFeaturelessHost::Yes => { 1155 any_yes = true; 1156 any_no = true; 1157 }, 1158 MatchesFeaturelessHost::Only => { 1159 any_yes = true; 1160 }, 1161 } 1162 } 1163 if !any_yes { 1164 return MatchesFeaturelessHost::Never; 1165 } 1166 if any_no { 1167 // Potentially downgrade since we might match non-featureless elements too. 1168 matches = MatchesFeaturelessHost::Yes; 1169 } 1170 }, 1171 Component::Negation(ref l) => { 1172 // For now preserving behavior, see 1173 // https://github.com/w3c/csswg-drafts/issues/10179 for existing resolutions that 1174 // tweak this behavior. 1175 for selector in l.slice() { 1176 if selector.matches_featureless_host(scope_matches_featureless_host) 1177 != MatchesFeaturelessHost::Only 1178 { 1179 return MatchesFeaturelessHost::Never; 1180 } 1181 } 1182 }, 1183 // Other components don't match the host scope. 1184 _ => return MatchesFeaturelessHost::Never, 1185 } 1186 } 1187 matches 1188 } 1189 1190 /// Determines whether the given element matches the given compound selector. 1191 #[inline] 1192 fn matches_compound_selector<E>( 1193 selector_iter: &mut SelectorIter<E::Impl>, 1194 element: &E, 1195 context: &mut MatchingContext<E::Impl>, 1196 rightmost: SubjectOrPseudoElement, 1197 ) -> KleeneValue 1198 where 1199 E: Element, 1200 { 1201 if context.featureless() 1202 && compound_matches_featureless_host( 1203 &mut selector_iter.clone(), 1204 /* scope_matches_featureless_host = */ true, 1205 ) == MatchesFeaturelessHost::Never 1206 { 1207 return KleeneValue::False; 1208 } 1209 let quirks_data = if context.quirks_mode() == QuirksMode::Quirks { 1210 Some(selector_iter.clone()) 1211 } else { 1212 None 1213 }; 1214 let mut local_context = LocalMatchingContext { 1215 shared: context, 1216 rightmost, 1217 quirks_data, 1218 }; 1219 KleeneValue::any_false(selector_iter, |simple| { 1220 matches_simple_selector(simple, element, &mut local_context) 1221 }) 1222 } 1223 1224 /// Determines whether the given element matches the given single selector. 1225 fn matches_simple_selector<E>( 1226 selector: &Component<E::Impl>, 1227 element: &E, 1228 context: &mut LocalMatchingContext<E::Impl>, 1229 ) -> KleeneValue 1230 where 1231 E: Element, 1232 { 1233 debug_assert!(context.shared.is_nested() || !context.shared.in_negation()); 1234 let rightmost = context.rightmost; 1235 KleeneValue::from(match *selector { 1236 Component::ID(ref id) => { 1237 element.has_id(id, context.shared.classes_and_ids_case_sensitivity()) 1238 }, 1239 Component::Class(ref class) => { 1240 element.has_class(class, context.shared.classes_and_ids_case_sensitivity()) 1241 }, 1242 Component::LocalName(ref local_name) => matches_local_name(element, local_name), 1243 Component::AttributeInNoNamespaceExists { 1244 ref local_name, 1245 ref local_name_lower, 1246 } => element.has_attr_in_no_namespace(select_name(element, local_name, local_name_lower)), 1247 Component::AttributeInNoNamespace { 1248 ref local_name, 1249 ref value, 1250 operator, 1251 case_sensitivity, 1252 } => element.attr_matches( 1253 &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()), 1254 local_name, 1255 &AttrSelectorOperation::WithValue { 1256 operator, 1257 case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element), 1258 value, 1259 }, 1260 ), 1261 Component::AttributeOther(ref attr_sel) => { 1262 matches_rare_attribute_selector(element, attr_sel) 1263 }, 1264 Component::Part(ref parts) => matches_part(element, parts, &mut context.shared), 1265 Component::Slotted(ref selector) => { 1266 return matches_slotted(element, selector, &mut context.shared, rightmost); 1267 }, 1268 Component::PseudoElement(ref pseudo) => { 1269 element.match_pseudo_element(pseudo, context.shared) 1270 }, 1271 Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true, 1272 Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => { 1273 element.has_namespace(&url.borrow()) 1274 }, 1275 Component::ExplicitNoNamespace => { 1276 let ns = crate::parser::namespace_empty_string::<E::Impl>(); 1277 element.has_namespace(&ns.borrow()) 1278 }, 1279 Component::NonTSPseudoClass(ref pc) => { 1280 if let Some(ref iter) = context.quirks_data { 1281 if pc.is_active_or_hover() 1282 && !element.is_link() 1283 && hover_and_active_quirk_applies(iter, context.shared, context.rightmost) 1284 { 1285 return KleeneValue::False; 1286 } 1287 } 1288 element.match_non_ts_pseudo_class(pc, &mut context.shared) 1289 }, 1290 Component::Root => element.is_root(), 1291 Component::Empty => { 1292 if context.shared.needs_selector_flags() { 1293 element.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR); 1294 } 1295 element.is_empty() 1296 }, 1297 Component::Host(ref selector) => { 1298 return matches_host(element, selector.as_ref(), &mut context.shared, rightmost); 1299 }, 1300 Component::ParentSelector => match context.shared.scope_element { 1301 Some(ref scope_element) => element.opaque() == *scope_element, 1302 None => element.is_root(), 1303 }, 1304 Component::Scope | Component::ImplicitScope => { 1305 if let Some(may_return_unknown) = context.shared.matching_for_invalidation_comparison() 1306 { 1307 return if may_return_unknown { 1308 KleeneValue::Unknown 1309 } else { 1310 KleeneValue::from(!context.shared.in_negation()) 1311 }; 1312 } else { 1313 match context.shared.scope_element { 1314 Some(ref scope_element) => element.opaque() == *scope_element, 1315 None => element.is_root(), 1316 } 1317 } 1318 }, 1319 Component::Nth(ref nth_data) => { 1320 return matches_generic_nth_child(element, context.shared, nth_data, &[], rightmost); 1321 }, 1322 Component::NthOf(ref nth_of_data) => { 1323 return context.shared.nest(|context| { 1324 matches_generic_nth_child( 1325 element, 1326 context, 1327 nth_of_data.nth_data(), 1328 nth_of_data.selectors(), 1329 rightmost, 1330 ) 1331 }) 1332 }, 1333 Component::Is(ref list) | Component::Where(ref list) => { 1334 return context.shared.nest(|context| { 1335 matches_complex_selector_list(list.slice(), element, context, rightmost) 1336 }) 1337 }, 1338 Component::Negation(ref list) => { 1339 return context.shared.nest_for_negation(|context| { 1340 !matches_complex_selector_list(list.slice(), element, context, rightmost) 1341 }) 1342 }, 1343 Component::Has(ref relative_selectors) => { 1344 return match_relative_selectors( 1345 relative_selectors, 1346 element, 1347 context.shared, 1348 rightmost, 1349 ); 1350 }, 1351 Component::Combinator(_) => unsafe { 1352 debug_unreachable!("Shouldn't try to selector-match combinators") 1353 }, 1354 Component::RelativeSelectorAnchor => { 1355 let anchor = context.shared.relative_selector_anchor(); 1356 // We may match inner relative selectors, in which case we want to always match. 1357 anchor.map_or(true, |a| a == element.opaque()) 1358 }, 1359 Component::Invalid(..) => false, 1360 }) 1361 } 1362 1363 #[inline(always)] 1364 pub fn select_name<'a, E: Element, T: PartialEq>( 1365 element: &E, 1366 local_name: &'a T, 1367 local_name_lower: &'a T, 1368 ) -> &'a T { 1369 if local_name == local_name_lower || element.is_html_element_in_html_document() { 1370 local_name_lower 1371 } else { 1372 local_name 1373 } 1374 } 1375 1376 #[inline(always)] 1377 pub fn to_unconditional_case_sensitivity<'a, E: Element>( 1378 parsed: ParsedCaseSensitivity, 1379 element: &E, 1380 ) -> CaseSensitivity { 1381 match parsed { 1382 ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => { 1383 CaseSensitivity::CaseSensitive 1384 }, 1385 ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive, 1386 ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => { 1387 if element.is_html_element_in_html_document() { 1388 CaseSensitivity::AsciiCaseInsensitive 1389 } else { 1390 CaseSensitivity::CaseSensitive 1391 } 1392 }, 1393 } 1394 } 1395 1396 fn matches_generic_nth_child<E>( 1397 element: &E, 1398 context: &mut MatchingContext<E::Impl>, 1399 nth_data: &NthSelectorData, 1400 selectors: &[Selector<E::Impl>], 1401 rightmost: SubjectOrPseudoElement, 1402 ) -> KleeneValue 1403 where 1404 E: Element, 1405 { 1406 if element.ignores_nth_child_selectors() { 1407 return KleeneValue::False; 1408 } 1409 let has_selectors = !selectors.is_empty(); 1410 let selectors_match = !has_selectors 1411 || matches_complex_selector_list(selectors, element, context, rightmost).to_bool(true); 1412 if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() { 1413 // Skip expensive indexing math in invalidation. 1414 return if selectors_match && may_return_unknown { 1415 KleeneValue::Unknown 1416 } else { 1417 KleeneValue::from(selectors_match && !context.in_negation()) 1418 }; 1419 } 1420 1421 let NthSelectorData { ty, an_plus_b, .. } = *nth_data; 1422 let is_of_type = ty.is_of_type(); 1423 if ty.is_only() { 1424 debug_assert!( 1425 !has_selectors, 1426 ":only-child and :only-of-type cannot have a selector list!" 1427 ); 1428 return KleeneValue::from( 1429 matches_generic_nth_child( 1430 element, 1431 context, 1432 &NthSelectorData::first(is_of_type), 1433 selectors, 1434 rightmost, 1435 ) 1436 .to_bool(true) 1437 && matches_generic_nth_child( 1438 element, 1439 context, 1440 &NthSelectorData::last(is_of_type), 1441 selectors, 1442 rightmost, 1443 ) 1444 .to_bool(true), 1445 ); 1446 } 1447 1448 let is_from_end = ty.is_from_end(); 1449 1450 // It's useful to know whether this can only select the first/last element 1451 // child for optimization purposes, see the `HAS_EDGE_CHILD_SELECTOR` flag. 1452 let is_edge_child_selector = nth_data.is_simple_edge() && !has_selectors; 1453 1454 if context.needs_selector_flags() { 1455 let mut flags = if is_edge_child_selector { 1456 ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR 1457 } else if is_from_end { 1458 ElementSelectorFlags::HAS_SLOW_SELECTOR 1459 } else { 1460 ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS 1461 }; 1462 flags |= if has_selectors { 1463 ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF 1464 } else { 1465 ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH 1466 }; 1467 element.apply_selector_flags(flags); 1468 } 1469 1470 if !selectors_match { 1471 return KleeneValue::False; 1472 } 1473 1474 // :first/last-child are rather trivial to match, don't bother with the 1475 // cache. 1476 if is_edge_child_selector { 1477 return if is_from_end { 1478 element.next_sibling_element() 1479 } else { 1480 element.prev_sibling_element() 1481 } 1482 .is_none() 1483 .into(); 1484 } 1485 1486 // Lookup or compute the index. 1487 let index = if let Some(i) = context 1488 .nth_index_cache(is_of_type, is_from_end, selectors) 1489 .lookup(element.opaque()) 1490 { 1491 i 1492 } else { 1493 let i = nth_child_index( 1494 element, 1495 context, 1496 selectors, 1497 is_of_type, 1498 is_from_end, 1499 /* check_cache = */ true, 1500 rightmost, 1501 ); 1502 context 1503 .nth_index_cache(is_of_type, is_from_end, selectors) 1504 .insert(element.opaque(), i); 1505 i 1506 }; 1507 debug_assert_eq!( 1508 index, 1509 nth_child_index( 1510 element, 1511 context, 1512 selectors, 1513 is_of_type, 1514 is_from_end, 1515 /* check_cache = */ false, 1516 rightmost, 1517 ), 1518 "invalid cache" 1519 ); 1520 1521 an_plus_b.matches_index(index).into() 1522 } 1523 1524 #[inline] 1525 fn nth_child_index<E>( 1526 element: &E, 1527 context: &mut MatchingContext<E::Impl>, 1528 selectors: &[Selector<E::Impl>], 1529 is_of_type: bool, 1530 is_from_end: bool, 1531 check_cache: bool, 1532 rightmost: SubjectOrPseudoElement, 1533 ) -> i32 1534 where 1535 E: Element, 1536 { 1537 // The traversal mostly processes siblings left to right. So when we walk 1538 // siblings to the right when computing NthLast/NthLastOfType we're unlikely 1539 // to get cache hits along the way. As such, we take the hit of walking the 1540 // siblings to the left checking the cache in the is_from_end case (this 1541 // matches what Gecko does). The indices-from-the-left is handled during the 1542 // regular look further below. 1543 if check_cache 1544 && is_from_end 1545 && !context 1546 .nth_index_cache(is_of_type, is_from_end, selectors) 1547 .is_empty() 1548 { 1549 let mut index: i32 = 1; 1550 let mut curr = element.clone(); 1551 while let Some(e) = curr.prev_sibling_element() { 1552 curr = e; 1553 let matches = if is_of_type { 1554 element.is_same_type(&curr) 1555 } else if !selectors.is_empty() { 1556 matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true) 1557 } else { 1558 true 1559 }; 1560 if !matches { 1561 continue; 1562 } 1563 if let Some(i) = context 1564 .nth_index_cache(is_of_type, is_from_end, selectors) 1565 .lookup(curr.opaque()) 1566 { 1567 return i - index; 1568 } 1569 index += 1; 1570 } 1571 } 1572 1573 let mut index: i32 = 1; 1574 let mut curr = element.clone(); 1575 let next = |e: E| { 1576 if is_from_end { 1577 e.next_sibling_element() 1578 } else { 1579 e.prev_sibling_element() 1580 } 1581 }; 1582 while let Some(e) = next(curr) { 1583 curr = e; 1584 let matches = if is_of_type { 1585 element.is_same_type(&curr) 1586 } else if !selectors.is_empty() { 1587 matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true) 1588 } else { 1589 true 1590 }; 1591 if !matches { 1592 continue; 1593 } 1594 // If we're computing indices from the left, check each element in the 1595 // cache. We handle the indices-from-the-right case at the top of this 1596 // function. 1597 if !is_from_end && check_cache { 1598 if let Some(i) = context 1599 .nth_index_cache(is_of_type, is_from_end, selectors) 1600 .lookup(curr.opaque()) 1601 { 1602 return i + index; 1603 } 1604 } 1605 index += 1; 1606 } 1607 1608 index 1609 }