Source code

Revision control

Other Tools

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::{AttrSelectorOperation, NamespaceConstraint, ParsedAttrSelectorOperation};
6
use crate::bloom::{BloomFilter, BLOOM_HASH_MASK};
7
use crate::nth_index_cache::NthIndexCacheInner;
8
use crate::parser::{AncestorHashes, Combinator, Component, LocalName};
9
use crate::parser::{NonTSPseudoClass, Selector, SelectorImpl, SelectorIter, SelectorList};
10
use crate::tree::Element;
11
use std::borrow::Borrow;
12
use std::iter;
13
14
pub use crate::context::*;
15
16
// The bloom filter for descendant CSS selectors will have a <1% false
17
// positive rate until it has this many selectors in it, then it will
18
// rapidly increase.
19
pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
20
21
bitflags! {
22
/// Set of flags that are set on either the element or its parent (depending
23
/// on the flag) if the element could potentially match a selector.
24
pub struct ElementSelectorFlags: usize {
25
/// When a child is added or removed from the parent, all the children
26
/// must be restyled, because they may match :nth-last-child,
27
/// :last-of-type, :nth-last-of-type, or :only-of-type.
28
const HAS_SLOW_SELECTOR = 1 << 0;
29
30
/// When a child is added or removed from the parent, any later
31
/// children must be restyled, because they may match :nth-child,
32
/// :first-of-type, or :nth-of-type.
33
const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1;
34
35
/// When a child is added or removed from the parent, the first and
36
/// last children must be restyled, because they may match :first-child,
37
/// :last-child, or :only-child.
38
const HAS_EDGE_CHILD_SELECTOR = 1 << 2;
39
40
/// The element has an empty selector, so when a child is appended we
41
/// might need to restyle the parent completely.
42
const HAS_EMPTY_SELECTOR = 1 << 3;
43
}
44
}
45
46
impl ElementSelectorFlags {
47
/// Returns the subset of flags that apply to the element.
48
pub fn for_self(self) -> ElementSelectorFlags {
49
self & (ElementSelectorFlags::HAS_EMPTY_SELECTOR)
50
}
51
52
/// Returns the subset of flags that apply to the parent.
53
pub fn for_parent(self) -> ElementSelectorFlags {
54
self & (ElementSelectorFlags::HAS_SLOW_SELECTOR |
55
ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS |
56
ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR)
57
}
58
}
59
60
/// Holds per-compound-selector data.
61
struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
62
shared: &'a mut MatchingContext<'b, Impl>,
63
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk,
64
}
65
66
#[inline(always)]
67
pub fn matches_selector_list<E>(
68
selector_list: &SelectorList<E::Impl>,
69
element: &E,
70
context: &mut MatchingContext<E::Impl>,
71
) -> bool
72
where
73
E: Element,
74
{
75
// This is pretty much any(..) but manually inlined because the compiler
76
// refuses to do so from querySelector / querySelectorAll.
77
for selector in &selector_list.0 {
78
let matches = matches_selector(selector, 0, None, element, context, &mut |_, _| {});
79
80
if matches {
81
return true;
82
}
83
}
84
85
false
86
}
87
88
#[inline(always)]
89
fn may_match(hashes: &AncestorHashes, bf: &BloomFilter) -> bool {
90
// Check the first three hashes. Note that we can check for zero before
91
// masking off the high bits, since if any of the first three hashes is
92
// zero the fourth will be as well. We also take care to avoid the
93
// special-case complexity of the fourth hash until we actually reach it,
94
// because we usually don't.
95
//
96
// To be clear: this is all extremely hot.
97
for i in 0..3 {
98
let packed = hashes.packed_hashes[i];
99
if packed == 0 {
100
// No more hashes left - unable to fast-reject.
101
return true;
102
}
103
104
if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) {
105
// Hooray! We fast-rejected on this hash.
106
return false;
107
}
108
}
109
110
// Now do the slighty-more-complex work of synthesizing the fourth hash,
111
// and check it against the filter if it exists.
112
let fourth = hashes.fourth_hash();
113
fourth == 0 || bf.might_contain_hash(fourth)
114
}
115
116
/// A result of selector matching, includes 3 failure types,
117
///
118
/// NotMatchedAndRestartFromClosestLaterSibling
119
/// NotMatchedAndRestartFromClosestDescendant
120
/// NotMatchedGlobally
121
///
122
/// When NotMatchedGlobally appears, stop selector matching completely since
123
/// the succeeding selectors never matches.
124
/// It is raised when
125
/// Child combinator cannot find the candidate element.
126
/// Descendant combinator cannot find the candidate element.
127
///
128
/// When NotMatchedAndRestartFromClosestDescendant appears, the selector
129
/// matching does backtracking and restarts from the closest Descendant
130
/// combinator.
131
/// It is raised when
132
/// NextSibling combinator cannot find the candidate element.
133
/// LaterSibling combinator cannot find the candidate element.
134
/// Child combinator doesn't match on the found element.
135
///
136
/// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector
137
/// matching does backtracking and restarts from the closest LaterSibling
138
/// combinator.
139
/// It is raised when
140
/// NextSibling combinator doesn't match on the found element.
141
///
142
/// For example, when the selector "d1 d2 a" is provided and we cannot *find*
143
/// an appropriate ancestor element for "d1", this selector matching raises
144
/// NotMatchedGlobally since even if "d2" is moved to more upper element, the
145
/// candidates for "d1" becomes less than before and d1 .
146
///
147
/// The next example is siblings. When the selector "b1 + b2 ~ d1 a" is
148
/// provided and we cannot *find* an appropriate brother element for b1,
149
/// the selector matching raises NotMatchedAndRestartFromClosestDescendant.
150
/// The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1".
151
///
152
/// The additional example is child and sibling. When the selector
153
/// "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on
154
/// the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling.
155
/// However since the selector "c1" raises
156
/// NotMatchedAndRestartFromClosestDescendant. So the selector
157
/// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
158
#[derive(Clone, Copy, Eq, PartialEq)]
159
enum SelectorMatchingResult {
160
Matched,
161
NotMatchedAndRestartFromClosestLaterSibling,
162
NotMatchedAndRestartFromClosestDescendant,
163
NotMatchedGlobally,
164
}
165
166
/// Whether the :hover and :active quirk applies.
167
///
169
#[derive(Clone, Copy, Debug, PartialEq)]
170
enum MatchesHoverAndActiveQuirk {
171
Yes,
172
No,
173
}
174
175
/// Matches a selector, fast-rejecting against a bloom filter.
176
///
177
/// We accept an offset to allow consumers to represent and match against
178
/// partial selectors (indexed from the right). We use this API design, rather
179
/// than having the callers pass a SelectorIter, because creating a SelectorIter
180
/// requires dereferencing the selector to get the length, which adds an
181
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
182
/// (which the caller can store inline with the selector pointer).
183
#[inline(always)]
184
pub fn matches_selector<E, F>(
185
selector: &Selector<E::Impl>,
186
offset: usize,
187
hashes: Option<&AncestorHashes>,
188
element: &E,
189
context: &mut MatchingContext<E::Impl>,
190
flags_setter: &mut F,
191
) -> bool
192
where
193
E: Element,
194
F: FnMut(&E, ElementSelectorFlags),
195
{
196
// Use the bloom filter to fast-reject.
197
if let Some(hashes) = hashes {
198
if let Some(filter) = context.bloom_filter {
199
if !may_match(hashes, filter) {
200
return false;
201
}
202
}
203
}
204
205
matches_complex_selector(selector.iter_from(offset), element, context, flags_setter)
206
}
207
208
/// Whether a compound selector matched, and whether it was the rightmost
209
/// selector inside the complex selector.
210
pub enum CompoundSelectorMatchingResult {
211
/// The selector was fully matched.
212
FullyMatched,
213
/// The compound selector matched, and the next combinator offset is
214
/// `next_combinator_offset`.
215
Matched { next_combinator_offset: usize },
216
/// The selector didn't match.
217
NotMatched,
218
}
219
220
/// Matches a compound selector belonging to `selector`, starting at offset
221
/// `from_offset`, matching left to right.
222
///
223
/// Requires that `from_offset` points to a `Combinator`.
224
///
225
/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
226
/// complex selector, but it happens to be the case we don't need it.
227
pub fn matches_compound_selector_from<E>(
228
selector: &Selector<E::Impl>,
229
mut from_offset: usize,
230
context: &mut MatchingContext<E::Impl>,
231
element: &E,
232
) -> CompoundSelectorMatchingResult
233
where
234
E: Element,
235
{
236
if cfg!(debug_assertions) && from_offset != 0 {
237
selector.combinator_at_parse_order(from_offset - 1); // This asserts.
238
}
239
240
let mut local_context = LocalMatchingContext {
241
shared: context,
242
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk::No,
243
};
244
245
// Find the end of the selector or the next combinator, then match
246
// backwards, so that we match in the same order as
247
// matches_complex_selector, which is usually faster.
248
let start_offset = from_offset;
249
for component in selector.iter_raw_parse_order_from(from_offset) {
250
if matches!(*component, Component::Combinator(..)) {
251
debug_assert_ne!(from_offset, 0, "Selector started with a combinator?");
252
break;
253
}
254
255
from_offset += 1;
256
}
257
258
debug_assert!(from_offset >= 1);
259
debug_assert!(from_offset <= selector.len());
260
261
let iter = selector.iter_from(selector.len() - from_offset);
262
debug_assert!(
263
iter.clone().next().is_some() ||
264
(from_offset != selector.len() &&
265
matches!(
266
selector.combinator_at_parse_order(from_offset),
267
Combinator::SlotAssignment | Combinator::PseudoElement
268
)),
269
"Got the math wrong: {:?} | {:?} | {} {}",
270
selector,
271
selector.iter_raw_match_order().as_slice(),
272
from_offset,
273
start_offset
274
);
275
276
for component in iter {
277
if !matches_simple_selector(component, element, &mut local_context, &mut |_, _| {}) {
278
return CompoundSelectorMatchingResult::NotMatched;
279
}
280
}
281
282
if from_offset != selector.len() {
283
return CompoundSelectorMatchingResult::Matched {
284
next_combinator_offset: from_offset,
285
};
286
}
287
288
CompoundSelectorMatchingResult::FullyMatched
289
}
290
291
/// Matches a complex selector.
292
#[inline(always)]
293
pub fn matches_complex_selector<E, F>(
294
mut iter: SelectorIter<E::Impl>,
295
element: &E,
296
context: &mut MatchingContext<E::Impl>,
297
flags_setter: &mut F,
298
) -> bool
299
where
300
E: Element,
301
F: FnMut(&E, ElementSelectorFlags),
302
{
303
// If this is the special pseudo-element mode, consume the ::pseudo-element
304
// before proceeding, since the caller has already handled that part.
305
if context.matching_mode() == MatchingMode::ForStatelessPseudoElement && !context.is_nested() {
306
// Consume the pseudo.
307
match *iter.next().unwrap() {
308
Component::PseudoElement(ref pseudo) => {
309
if let Some(ref f) = context.pseudo_element_matching_fn {
310
if !f(pseudo) {
311
return false;
312
}
313
}
314
},
315
_ => {
316
debug_assert!(
317
false,
318
"Used MatchingMode::ForStatelessPseudoElement \
319
in a non-pseudo selector"
320
);
321
},
322
}
323
324
// The only other parser-allowed Component in this sequence is a state
325
// class. We just don't match in that case.
326
if let Some(s) = iter.next() {
327
debug_assert!(
328
matches!(*s, Component::NonTSPseudoClass(..)),
329
"Someone messed up pseudo-element parsing"
330
);
331
return false;
332
}
333
334
// Advance to the non-pseudo-element part of the selector.
335
let next_sequence = iter.next_sequence().unwrap();
336
debug_assert_eq!(next_sequence, Combinator::PseudoElement);
337
}
338
339
let result =
340
matches_complex_selector_internal(iter, element, context, flags_setter, Rightmost::Yes);
341
342
match result {
343
SelectorMatchingResult::Matched => true,
344
_ => false,
345
}
346
}
347
348
#[inline]
349
fn matches_hover_and_active_quirk<Impl: SelectorImpl>(
350
selector_iter: &SelectorIter<Impl>,
351
context: &MatchingContext<Impl>,
352
rightmost: Rightmost,
353
) -> MatchesHoverAndActiveQuirk {
354
if context.quirks_mode() != QuirksMode::Quirks {
355
return MatchesHoverAndActiveQuirk::No;
356
}
357
358
if context.is_nested() {
359
return MatchesHoverAndActiveQuirk::No;
360
}
361
362
// This compound selector had a pseudo-element to the right that we
363
// intentionally skipped.
364
if rightmost == Rightmost::Yes &&
365
context.matching_mode() == MatchingMode::ForStatelessPseudoElement
366
{
367
return MatchesHoverAndActiveQuirk::No;
368
}
369
370
let all_match = selector_iter.clone().all(|simple| match *simple {
371
Component::LocalName(_) |
372
Component::AttributeInNoNamespaceExists { .. } |
373
Component::AttributeInNoNamespace { .. } |
374
Component::AttributeOther(_) |
375
Component::ID(_) |
376
Component::Class(_) |
377
Component::PseudoElement(_) |
378
Component::Negation(_) |
379
Component::FirstChild |
380
Component::LastChild |
381
Component::OnlyChild |
382
Component::Empty |
383
Component::NthChild(_, _) |
384
Component::NthLastChild(_, _) |
385
Component::NthOfType(_, _) |
386
Component::NthLastOfType(_, _) |
387
Component::FirstOfType |
388
Component::LastOfType |
389
Component::OnlyOfType => false,
390
Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),
391
_ => true,
392
});
393
394
if all_match {
395
MatchesHoverAndActiveQuirk::Yes
396
} else {
397
MatchesHoverAndActiveQuirk::No
398
}
399
}
400
401
#[derive(Clone, Copy, PartialEq)]
402
enum Rightmost {
403
Yes,
404
No,
405
}
406
407
#[inline(always)]
408
fn next_element_for_combinator<E>(
409
element: &E,
410
combinator: Combinator,
411
selector: &SelectorIter<E::Impl>,
412
context: &MatchingContext<E::Impl>,
413
) -> Option<E>
414
where
415
E: Element,
416
{
417
match combinator {
418
Combinator::NextSibling | Combinator::LaterSibling => element.prev_sibling_element(),
419
Combinator::Child | Combinator::Descendant => {
420
match element.parent_element() {
421
Some(e) => return Some(e),
422
None => {},
423
}
424
425
if !element.parent_node_is_shadow_root() {
426
return None;
427
}
428
430
//
431
// For the purpose of Selectors, a shadow host also appears in
432
// its shadow tree, with the contents of the shadow tree treated
433
// as its children. (In other words, the shadow host is treated as
434
// replacing the shadow root node.)
435
//
436
// and also:
437
//
438
// When considered within its own shadow trees, the shadow host is
439
// featureless. Only the :host, :host(), and :host-context()
440
// pseudo-classes are allowed to match it.
441
//
442
// Since we know that the parent is a shadow root, we necessarily
443
// are in a shadow tree of the host, and the next selector will only
444
// match if the selector is a featureless :host selector.
445
if !selector.clone().is_featureless_host_selector() {
446
return None;
447
}
448
449
element.containing_shadow_host()
450
},
451
Combinator::Part => element.containing_shadow_host(),
452
Combinator::SlotAssignment => {
453
debug_assert!(element
454
.assigned_slot()
455
.map_or(true, |s| s.is_html_slot_element()));
456
let scope = context.current_host?;
457
let mut current_slot = element.assigned_slot()?;
458
while current_slot.containing_shadow_host().unwrap().opaque() != scope {
459
current_slot = current_slot.assigned_slot()?;
460
}
461
Some(current_slot)
462
},
463
Combinator::PseudoElement => element.pseudo_element_originating_element(),
464
}
465
}
466
467
fn matches_complex_selector_internal<E, F>(
468
mut selector_iter: SelectorIter<E::Impl>,
469
element: &E,
470
context: &mut MatchingContext<E::Impl>,
471
flags_setter: &mut F,
472
rightmost: Rightmost,
473
) -> SelectorMatchingResult
474
where
475
E: Element,
476
F: FnMut(&E, ElementSelectorFlags),
477
{
478
debug!(
479
"Matching complex selector {:?} for {:?}",
480
selector_iter, element
481
);
482
483
let matches_compound_selector = matches_compound_selector(
484
&mut selector_iter,
485
element,
486
context,
487
flags_setter,
488
rightmost,
489
);
490
491
let combinator = selector_iter.next_sequence();
492
if combinator.map_or(false, |c| c.is_sibling()) {
493
flags_setter(
494
element,
495
ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS,
496
);
497
}
498
499
if !matches_compound_selector {
500
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
501
}
502
503
let combinator = match combinator {
504
None => return SelectorMatchingResult::Matched,
505
Some(c) => c,
506
};
507
508
let candidate_not_found = match combinator {
509
Combinator::NextSibling | Combinator::LaterSibling => {
510
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
511
},
512
Combinator::Child |
513
Combinator::Descendant |
514
Combinator::SlotAssignment |
515
Combinator::Part |
516
Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally,
517
};
518
519
let mut next_element =
520
next_element_for_combinator(element, combinator, &selector_iter, &context);
521
522
// Stop matching :visited as soon as we find a link, or a combinator for
523
// something that isn't an ancestor.
524
let mut visited_handling = if element.is_link() || combinator.is_sibling() {
525
VisitedHandlingMode::AllLinksUnvisited
526
} else {
527
context.visited_handling()
528
};
529
530
loop {
531
let element = match next_element {
532
None => return candidate_not_found,
533
Some(next_element) => next_element,
534
};
535
536
let result = context.with_visited_handling_mode(visited_handling, |context| {
537
matches_complex_selector_internal(
538
selector_iter.clone(),
539
&element,
540
context,
541
flags_setter,
542
Rightmost::No,
543
)
544
});
545
546
match (result, combinator) {
547
// Return the status immediately.
548
(SelectorMatchingResult::Matched, _) |
549
(SelectorMatchingResult::NotMatchedGlobally, _) |
550
(_, Combinator::NextSibling) => {
551
return result;
552
},
553
554
// Upgrade the failure status to
555
// NotMatchedAndRestartFromClosestDescendant.
556
(_, Combinator::PseudoElement) | (_, Combinator::Child) => {
557
return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant;
558
},
559
560
// If the failure status is
561
// NotMatchedAndRestartFromClosestDescendant and combinator is
562
// Combinator::LaterSibling, give up this Combinator::LaterSibling
563
// matching and restart from the closest descendant combinator.
564
(
565
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
566
Combinator::LaterSibling,
567
) => {
568
return result;
569
},
570
571
// The Combinator::Descendant combinator and the status is
572
// NotMatchedAndRestartFromClosestLaterSibling or
573
// NotMatchedAndRestartFromClosestDescendant, or the
574
// Combinator::LaterSibling combinator and the status is
575
// NotMatchedAndRestartFromClosestDescendant, we can continue to
576
// matching on the next candidate element.
577
_ => {},
578
}
579
580
if element.is_link() {
581
visited_handling = VisitedHandlingMode::AllLinksUnvisited;
582
}
583
584
next_element = next_element_for_combinator(&element, combinator, &selector_iter, &context);
585
}
586
}
587
588
#[inline]
589
fn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool
590
where
591
E: Element,
592
{
593
let name = select_name(
594
element.is_html_element_in_html_document(),
595
&local_name.name,
596
&local_name.lower_name,
597
)
598
.borrow();
599
element.has_local_name(name)
600
}
601
602
/// Determines whether the given element matches the given compound selector.
603
#[inline]
604
fn matches_compound_selector<E, F>(
605
selector_iter: &mut SelectorIter<E::Impl>,
606
element: &E,
607
context: &mut MatchingContext<E::Impl>,
608
flags_setter: &mut F,
609
rightmost: Rightmost,
610
) -> bool
611
where
612
E: Element,
613
F: FnMut(&E, ElementSelectorFlags),
614
{
615
let matches_hover_and_active_quirk =
616
matches_hover_and_active_quirk(&selector_iter, context, rightmost);
617
618
// Handle some common cases first.
619
// We may want to get rid of this at some point if we can make the
620
// generic case fast enough.
621
let mut selector = selector_iter.next();
622
if let Some(&Component::LocalName(ref local_name)) = selector {
623
if !matches_local_name(element, local_name) {
624
return false;
625
}
626
selector = selector_iter.next();
627
}
628
let class_and_id_case_sensitivity = context.classes_and_ids_case_sensitivity();
629
if let Some(&Component::ID(ref id)) = selector {
630
if !element.has_id(id, class_and_id_case_sensitivity) {
631
return false;
632
}
633
selector = selector_iter.next();
634
}
635
while let Some(&Component::Class(ref class)) = selector {
636
if !element.has_class(class, class_and_id_case_sensitivity) {
637
return false;
638
}
639
selector = selector_iter.next();
640
}
641
let selector = match selector {
642
Some(s) => s,
643
None => return true,
644
};
645
646
let mut local_context = LocalMatchingContext {
647
shared: context,
648
matches_hover_and_active_quirk,
649
};
650
iter::once(selector)
651
.chain(selector_iter)
652
.all(|simple| matches_simple_selector(simple, element, &mut local_context, flags_setter))
653
}
654
655
/// Determines whether the given element matches the given single selector.
656
fn matches_simple_selector<E, F>(
657
selector: &Component<E::Impl>,
658
element: &E,
659
context: &mut LocalMatchingContext<E::Impl>,
660
flags_setter: &mut F,
661
) -> bool
662
where
663
E: Element,
664
F: FnMut(&E, ElementSelectorFlags),
665
{
666
debug_assert!(context.shared.is_nested() || !context.shared.in_negation());
667
668
match *selector {
669
Component::Combinator(_) => unreachable!(),
670
Component::Part(ref parts) => parts.iter().all(|part| element.is_part(part)),
671
Component::Slotted(ref selector) => {
672
// <slots> are never flattened tree slottables.
673
!element.is_html_slot_element() &&
674
context.shared.nest(|context| {
675
matches_complex_selector(selector.iter(), element, context, flags_setter)
676
})
677
},
678
Component::PseudoElement(ref pseudo) => {
679
element.match_pseudo_element(pseudo, context.shared)
680
},
681
Component::LocalName(ref local_name) => matches_local_name(element, local_name),
682
Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true,
683
Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
684
element.has_namespace(&url.borrow())
685
},
686
Component::ExplicitNoNamespace => {
687
let ns = crate::parser::namespace_empty_string::<E::Impl>();
688
element.has_namespace(&ns.borrow())
689
},
690
Component::ID(ref id) => {
691
element.has_id(id, context.shared.classes_and_ids_case_sensitivity())
692
},
693
Component::Class(ref class) => {
694
element.has_class(class, context.shared.classes_and_ids_case_sensitivity())
695
},
696
Component::AttributeInNoNamespaceExists {
697
ref local_name,
698
ref local_name_lower,
699
} => {
700
let is_html = element.is_html_element_in_html_document();
701
element.attr_matches(
702
&NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
703
select_name(is_html, local_name, local_name_lower),
704
&AttrSelectorOperation::Exists,
705
)
706
},
707
Component::AttributeInNoNamespace {
708
ref local_name,
709
ref value,
710
operator,
711
case_sensitivity,
712
never_matches,
713
} => {
714
if never_matches {
715
return false;
716
}
717
let is_html = element.is_html_element_in_html_document();
718
element.attr_matches(
719
&NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
720
local_name,
721
&AttrSelectorOperation::WithValue {
722
operator: operator,
723
case_sensitivity: case_sensitivity.to_unconditional(is_html),
724
expected_value: value,
725
},
726
)
727
},
728
Component::AttributeOther(ref attr_sel) => {
729
if attr_sel.never_matches {
730
return false;
731
}
732
let is_html = element.is_html_element_in_html_document();
733
let empty_string;
734
let namespace = match attr_sel.namespace() {
735
Some(ns) => ns,
736
None => {
737
empty_string = crate::parser::namespace_empty_string::<E::Impl>();
738
NamespaceConstraint::Specific(&empty_string)
739
},
740
};
741
element.attr_matches(
742
&namespace,
743
select_name(is_html, &attr_sel.local_name, &attr_sel.local_name_lower),
744
&match attr_sel.operation {
745
ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists,
746
ParsedAttrSelectorOperation::WithValue {
747
operator,
748
case_sensitivity,
749
ref expected_value,
750
} => AttrSelectorOperation::WithValue {
751
operator: operator,
752
case_sensitivity: case_sensitivity.to_unconditional(is_html),
753
expected_value: expected_value,
754
},
755
},
756
)
757
},
758
Component::NonTSPseudoClass(ref pc) => {
759
if context.matches_hover_and_active_quirk == MatchesHoverAndActiveQuirk::Yes &&
760
!context.shared.is_nested() &&
761
pc.is_active_or_hover() &&
762
!element.is_link()
763
{
764
return false;
765
}
766
767
element.match_non_ts_pseudo_class(pc, &mut context.shared, flags_setter)
768
},
769
Component::FirstChild => matches_first_child(element, flags_setter),
770
Component::LastChild => matches_last_child(element, flags_setter),
771
Component::OnlyChild => {
772
matches_first_child(element, flags_setter) && matches_last_child(element, flags_setter)
773
},
774
Component::Root => element.is_root(),
775
Component::Empty => {
776
flags_setter(element, ElementSelectorFlags::HAS_EMPTY_SELECTOR);
777
element.is_empty()
778
},
779
Component::Host(ref selector) => {
780
context
781
.shared
782
.shadow_host()
783
.map_or(false, |host| host == element.opaque()) &&
784
selector.as_ref().map_or(true, |selector| {
785
context.shared.nest(|context| {
786
matches_complex_selector(selector.iter(), element, context, flags_setter)
787
})
788
})
789
},
790
Component::Scope => match context.shared.scope_element {
791
Some(ref scope_element) => element.opaque() == *scope_element,
792
None => element.is_root(),
793
},
794
Component::NthChild(a, b) => {
795
matches_generic_nth_child(element, context, a, b, false, false, flags_setter)
796
},
797
Component::NthLastChild(a, b) => {
798
matches_generic_nth_child(element, context, a, b, false, true, flags_setter)
799
},
800
Component::NthOfType(a, b) => {
801
matches_generic_nth_child(element, context, a, b, true, false, flags_setter)
802
},
803
Component::NthLastOfType(a, b) => {
804
matches_generic_nth_child(element, context, a, b, true, true, flags_setter)
805
},
806
Component::FirstOfType => {
807
matches_generic_nth_child(element, context, 0, 1, true, false, flags_setter)
808
},
809
Component::LastOfType => {
810
matches_generic_nth_child(element, context, 0, 1, true, true, flags_setter)
811
},
812
Component::OnlyOfType => {
813
matches_generic_nth_child(element, context, 0, 1, true, false, flags_setter) &&
814
matches_generic_nth_child(element, context, 0, 1, true, true, flags_setter)
815
},
816
Component::Negation(ref negated) => context.shared.nest_for_negation(|context| {
817
let mut local_context = LocalMatchingContext {
818
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk::No,
819
shared: context,
820
};
821
!negated
822
.iter()
823
.all(|ss| matches_simple_selector(ss, element, &mut local_context, flags_setter))
824
}),
825
}
826
}
827
828
#[inline(always)]
829
fn select_name<'a, T>(is_html: bool, local_name: &'a T, local_name_lower: &'a T) -> &'a T {
830
if is_html {
831
local_name_lower
832
} else {
833
local_name
834
}
835
}
836
837
#[inline]
838
fn matches_generic_nth_child<E, F>(
839
element: &E,
840
context: &mut LocalMatchingContext<E::Impl>,
841
a: i32,
842
b: i32,
843
is_of_type: bool,
844
is_from_end: bool,
845
flags_setter: &mut F,
846
) -> bool
847
where
848
E: Element,
849
F: FnMut(&E, ElementSelectorFlags),
850
{
851
if element.ignores_nth_child_selectors() {
852
return false;
853
}
854
855
flags_setter(
856
element,
857
if is_from_end {
858
ElementSelectorFlags::HAS_SLOW_SELECTOR
859
} else {
860
ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS
861
},
862
);
863
864
// Grab a reference to the appropriate cache.
865
let mut cache = context
866
.shared
867
.nth_index_cache
868
.as_mut()
869
.map(|c| c.get(is_of_type, is_from_end));
870
871
// Lookup or compute the index.
872
let index = if let Some(i) = cache.as_mut().and_then(|c| c.lookup(element.opaque())) {
873
i
874
} else {
875
let i = nth_child_index(
876
element,
877
is_of_type,
878
is_from_end,
879
cache.as_mut().map(|s| &mut **s),
880
);
881
cache.as_mut().map(|c| c.insert(element.opaque(), i));
882
i
883
};
884
debug_assert_eq!(
885
index,
886
nth_child_index(element, is_of_type, is_from_end, None),
887
"invalid cache"
888
);
889
890
// Is there a non-negative integer n such that An+B=index?
891
match index.checked_sub(b) {
892
None => false,
893
Some(an) => match an.checked_div(a) {
894
Some(n) => n >= 0 && a * n == an,
895
None /* a == 0 */ => an == 0,
896
},
897
}
898
}
899
900
#[inline]
901
fn nth_child_index<E>(
902
element: &E,
903
is_of_type: bool,
904
is_from_end: bool,
905
mut cache: Option<&mut NthIndexCacheInner>,
906
) -> i32
907
where
908
E: Element,
909
{
910
// The traversal mostly processes siblings left to right. So when we walk
911
// siblings to the right when computing NthLast/NthLastOfType we're unlikely
912
// to get cache hits along the way. As such, we take the hit of walking the
913
// siblings to the left checking the cache in the is_from_end case (this
914
// matches what Gecko does). The indices-from-the-left is handled during the
915
// regular look further below.
916
if let Some(ref mut c) = cache {
917
if is_from_end && !c.is_empty() {
918
let mut index: i32 = 1;
919
let mut curr = element.clone();
920
while let Some(e) = curr.prev_sibling_element() {
921
curr = e;
922
if !is_of_type || element.is_same_type(&curr) {
923
if let Some(i) = c.lookup(curr.opaque()) {
924
return i - index;
925
}
926
index += 1;
927
}
928
}
929
}
930
}
931
932
let mut index: i32 = 1;
933
let mut curr = element.clone();
934
let next = |e: E| {
935
if is_from_end {
936
e.next_sibling_element()
937
} else {
938
e.prev_sibling_element()
939
}
940
};
941
while let Some(e) = next(curr) {
942
curr = e;
943
if !is_of_type || element.is_same_type(&curr) {
944
// If we're computing indices from the left, check each element in the
945
// cache. We handle the indices-from-the-right case at the top of this
946
// function.
947
if !is_from_end {
948
if let Some(i) = cache.as_mut().and_then(|c| c.lookup(curr.opaque())) {
949
return i + index;
950
}
951
}
952
index += 1;
953
}
954
}
955
956
index
957
}
958
959
#[inline]
960
fn matches_first_child<E, F>(element: &E, flags_setter: &mut F) -> bool
961
where
962
E: Element,
963
F: FnMut(&E, ElementSelectorFlags),
964
{
965
flags_setter(element, ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
966
element.prev_sibling_element().is_none()
967
}
968
969
#[inline]
970
fn matches_last_child<E, F>(element: &E, flags_setter: &mut F) -> bool
971
where
972
E: Element,
973
F: FnMut(&E, ElementSelectorFlags),
974
{
975
flags_setter(element, ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
976
element.next_sibling_element().is_none()
977
}