Skip to main content

style/
selector_map.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! A data structure to efficiently index structs containing selectors by local
6//! name, ids and hash.
7
8use crate::AllocErr;
9use crate::applicable_declarations::{ApplicableDeclarationList, ScopeProximity};
10use crate::context::QuirksMode;
11use crate::derives::*;
12use crate::dom::TElement;
13use crate::rule_tree::CascadeLevel;
14use crate::selector_parser::SelectorImpl;
15use crate::stylist::{CascadeData, ContainerConditionId, Rule, ScopeConditionId, Stylist};
16use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
17use dom::ElementState;
18use precomputed_hash::PrecomputedHash;
19use selectors::matching::MatchingContext;
20use selectors::parser::{Combinator, Component, SelectorIter};
21use smallvec::SmallVec;
22use std::collections::hash_map;
23use std::collections::{HashMap, HashSet};
24use std::hash::{BuildHasherDefault, Hash, Hasher};
25
26/// A hasher implementation that doesn't hash anything, because it expects its
27/// input to be a suitable u64 or u32 hash.
28#[derive(Default)]
29pub struct PrecomputedHasher {
30    hash: u64,
31    #[cfg(debug_assertions)]
32    initialized: bool,
33}
34
35/// A vector of relevant attributes, that can be useful for revalidation.
36pub type RelevantAttributes = thin_vec::ThinVec<LocalName>;
37
38/// This is a set of pseudo-classes that are both relatively-rare (they don't
39/// affect most elements by default) and likely or known to have global rules
40/// (in e.g., the UA sheets).
41///
42/// We can avoid selector-matching those global rules for all elements without
43/// these pseudo-class states.
44const RARE_PSEUDO_CLASS_STATES: ElementState = ElementState::from_bits_retain(
45    ElementState::FULLSCREEN.bits()
46        | ElementState::PICTURE_IN_PICTURE.bits()
47        | ElementState::VISITED_OR_UNVISITED.bits()
48        | ElementState::URLTARGET.bits()
49        | ElementState::INERT.bits()
50        | ElementState::FOCUS.bits()
51        | ElementState::FOCUSRING.bits()
52        | ElementState::TOPMOST_MODAL.bits()
53        | ElementState::SUPPRESS_FOR_PRINT_SELECTION.bits()
54        | ElementState::ACTIVE_VIEW_TRANSITION.bits()
55        | ElementState::HEADING_LEVEL_BITS.bits(),
56);
57
58/// A simple alias for a hashmap using PrecomputedHasher.
59pub type PrecomputedHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PrecomputedHasher>>;
60
61/// A simple alias for a hashset using PrecomputedHasher.
62pub type PrecomputedHashSet<K> = HashSet<K, BuildHasherDefault<PrecomputedHasher>>;
63
64impl Hasher for PrecomputedHasher {
65    #[inline]
66    fn write(&mut self, _: &[u8]) {
67        unreachable!(
68            "Called into PrecomputedHasher with something that isn't \
69             a u64 or u32"
70        )
71    }
72
73    #[inline]
74    fn write_u32(&mut self, i: u32) {
75        #[cfg(debug_assertions)]
76        debug_assert!(!self.initialized);
77        debug_assert_eq!(self.hash, 0);
78        let extended = i as u64;
79        self.hash = (extended << 32) | extended;
80        #[cfg(debug_assertions)]
81        {
82            self.initialized = true;
83        }
84    }
85
86    #[inline]
87    fn write_u64(&mut self, i: u64) {
88        #[cfg(debug_assertions)]
89        debug_assert!(!self.initialized);
90        debug_assert_eq!(self.hash, 0);
91        self.hash = i;
92        #[cfg(debug_assertions)]
93        {
94            self.initialized = true;
95        }
96    }
97
98    #[inline]
99    fn finish(&self) -> u64 {
100        #[cfg(debug_assertions)]
101        debug_assert!(self.initialized);
102        self.hash
103    }
104}
105
106/// A trait to abstract over a given selector map entry.
107pub trait SelectorMapEntry: Sized + Clone {
108    /// Gets the selector we should use to index in the selector map.
109    fn selector(&self) -> SelectorIter<'_, SelectorImpl>;
110    /// Notes the bucketing decision in the entry.
111    fn set_bucket_matches(&mut self, _: BucketMatches) {}
112}
113
114/// Map element data to selector-providing objects for which the last simple
115/// selector starts with them.
116///
117/// e.g.,
118/// "p > img" would go into the set of selectors corresponding to the
119/// element "img"
120/// "a .foo .bar.baz" would go into the set of selectors corresponding to
121/// the class "bar"
122///
123/// Because we match selectors right-to-left (i.e., moving up the tree
124/// from an element), we need to compare the last simple selector in the
125/// selector with the element.
126///
127/// So, if an element has ID "id1" and classes "foo" and "bar", then all
128/// the rules it matches will have their last simple selector starting
129/// either with "#id1" or with ".foo" or with ".bar".
130///
131/// Hence, the union of the rules keyed on each of element's classes, ID,
132/// element name, etc. will contain the Selectors that actually match that
133/// element.
134///
135/// We use a 1-entry SmallVec to avoid a separate heap allocation in the case
136/// where we only have one entry, which is quite common. See measurements in:
137/// * https://bugzilla.mozilla.org/show_bug.cgi?id=1363789#c5
138/// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755
139///
140/// TODO: Tune the initial capacity of the HashMap
141#[derive(Clone, Debug, MallocSizeOf)]
142pub struct SelectorMap<T: 'static> {
143    /// Rules that have `:root` selectors.
144    pub root: SmallVec<[T; 1]>,
145    /// A hash from an ID to rules which contain that ID selector.
146    pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
147    /// A hash from a class name to rules which contain that class selector.
148    pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
149    /// A hash from local name to rules which contain that local name selector.
150    pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
151    /// A hash from attributes to rules which contain that attribute selector.
152    pub attribute_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
153    /// A hash from namespace to rules which contain that namespace selector.
154    pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,
155    /// Rules for pseudo-states that are rare but have global selectors.
156    pub rare_pseudo_classes: SmallVec<[T; 1]>,
157    /// All other rules.
158    pub other: SmallVec<[T; 1]>,
159    /// The number of entries in this map.
160    pub count: usize,
161}
162
163impl<T: 'static> Default for SelectorMap<T> {
164    #[inline]
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl<T> SelectorMap<T> {
171    /// Trivially constructs an empty `SelectorMap`.
172    pub fn new() -> Self {
173        SelectorMap {
174            root: SmallVec::new(),
175            id_hash: MaybeCaseInsensitiveHashMap::new(),
176            class_hash: MaybeCaseInsensitiveHashMap::new(),
177            attribute_hash: HashMap::default(),
178            local_name_hash: HashMap::default(),
179            namespace_hash: HashMap::default(),
180            rare_pseudo_classes: SmallVec::new(),
181            other: SmallVec::new(),
182            count: 0,
183        }
184    }
185
186    /// Shrink the capacity of the map if needed.
187    pub fn shrink_if_needed(&mut self) {
188        self.id_hash.shrink_if_needed();
189        self.class_hash.shrink_if_needed();
190        self.attribute_hash.shrink_if_needed();
191        self.local_name_hash.shrink_if_needed();
192        self.namespace_hash.shrink_if_needed();
193    }
194
195    /// Clears the hashmap retaining storage.
196    pub fn clear(&mut self) {
197        self.root.clear();
198        self.id_hash.clear();
199        self.class_hash.clear();
200        self.attribute_hash.clear();
201        self.local_name_hash.clear();
202        self.namespace_hash.clear();
203        self.rare_pseudo_classes.clear();
204        self.other.clear();
205        self.count = 0;
206    }
207
208    /// Returns whether there are any entries in the map.
209    pub fn is_empty(&self) -> bool {
210        self.count == 0
211    }
212
213    /// Returns the number of entries.
214    pub fn len(&self) -> usize {
215        self.count
216    }
217}
218
219impl SelectorMap<Rule> {
220    /// Append to `rule_list` all Rules in `self` that match element.
221    ///
222    /// Extract matching rules as per element's ID, classes, tag name, etc..
223    /// Sort the Rules at the end to maintain cascading order.
224    pub fn get_all_matching_rules<E>(
225        &self,
226        element: E,
227        rule_hash_target: E,
228        matching_rules_list: &mut ApplicableDeclarationList,
229        matching_context: &mut MatchingContext<E::Impl>,
230        cascade_level: CascadeLevel,
231        cascade_data: &CascadeData,
232        stylist: &Stylist,
233    ) where
234        E: TElement,
235    {
236        if self.is_empty() {
237            return;
238        }
239
240        let quirks_mode = matching_context.quirks_mode();
241
242        if rule_hash_target.is_root() {
243            SelectorMap::get_matching_rules(
244                element,
245                &self.root,
246                matching_rules_list,
247                matching_context,
248                cascade_level,
249                cascade_data,
250                stylist,
251            );
252        }
253
254        if let Some(id) = rule_hash_target.id() {
255            if let Some(rules) = self.id_hash.get(id, quirks_mode) {
256                SelectorMap::get_matching_rules(
257                    element,
258                    rules,
259                    matching_rules_list,
260                    matching_context,
261                    cascade_level,
262                    cascade_data,
263                    stylist,
264                )
265            }
266        }
267
268        rule_hash_target.each_class(|class| {
269            if let Some(rules) = self.class_hash.get(class, quirks_mode) {
270                SelectorMap::get_matching_rules(
271                    element,
272                    rules,
273                    matching_rules_list,
274                    matching_context,
275                    cascade_level,
276                    cascade_data,
277                    stylist,
278                )
279            }
280        });
281
282        rule_hash_target.each_attr_name(|name| {
283            if let Some(rules) = self.attribute_hash.get(name) {
284                SelectorMap::get_matching_rules(
285                    element,
286                    rules,
287                    matching_rules_list,
288                    matching_context,
289                    cascade_level,
290                    cascade_data,
291                    stylist,
292                )
293            }
294        });
295
296        if let Some(rules) = self.local_name_hash.get(rule_hash_target.local_name()) {
297            SelectorMap::get_matching_rules(
298                element,
299                rules,
300                matching_rules_list,
301                matching_context,
302                cascade_level,
303                cascade_data,
304                stylist,
305            )
306        }
307
308        if rule_hash_target
309            .state()
310            .intersects(RARE_PSEUDO_CLASS_STATES)
311        {
312            SelectorMap::get_matching_rules(
313                element,
314                &self.rare_pseudo_classes,
315                matching_rules_list,
316                matching_context,
317                cascade_level,
318                cascade_data,
319                stylist,
320            );
321        }
322
323        if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
324            SelectorMap::get_matching_rules(
325                element,
326                rules,
327                matching_rules_list,
328                matching_context,
329                cascade_level,
330                cascade_data,
331                stylist,
332            )
333        }
334
335        SelectorMap::get_matching_rules(
336            element,
337            &self.other,
338            matching_rules_list,
339            matching_context,
340            cascade_level,
341            cascade_data,
342            stylist,
343        );
344    }
345
346    /// Adds rules in `rules` that match `element` to the `matching_rules` list.
347    pub(crate) fn get_matching_rules<E>(
348        element: E,
349        rules: &[Rule],
350        matching_rules: &mut ApplicableDeclarationList,
351        matching_context: &mut MatchingContext<E::Impl>,
352        cascade_level: CascadeLevel,
353        cascade_data: &CascadeData,
354        stylist: &Stylist,
355    ) where
356        E: TElement,
357    {
358        for rule in rules {
359            let scope_proximity = if rule.scope_condition_id == ScopeConditionId::none() {
360                if !rule.matches_selector(element, matching_context) {
361                    continue;
362                }
363                ScopeProximity::infinity()
364            } else {
365                let result =
366                    cascade_data.find_scope_proximity_if_matching(rule, element, matching_context);
367                if result == ScopeProximity::infinity() {
368                    continue;
369                }
370                result
371            };
372
373            if rule.container_condition_id != ContainerConditionId::none()
374                && !cascade_data.container_condition_matches(
375                    rule.container_condition_id,
376                    stylist,
377                    element,
378                    matching_context,
379                )
380            {
381                continue;
382            }
383            matching_rules.push(rule.to_applicable_declaration_block(
384                cascade_level,
385                cascade_data,
386                scope_proximity,
387            ));
388        }
389    }
390}
391
392impl<T: SelectorMapEntry> SelectorMap<T> {
393    /// Inserts an entry into the correct bucket(s).
394    pub fn insert(&mut self, mut entry: T, quirks_mode: QuirksMode) -> Result<(), AllocErr> {
395        self.count += 1;
396
397        // NOTE(emilio): It'd be nice for this to be a separate function, but
398        // then the compiler can't reason about the lifetime dependency between
399        // `entry` and `bucket`, and would force us to clone the rule in the
400        // common path.
401        let mut bucket_matches = BucketMatches::Full;
402        macro_rules! insert_into_bucket {
403            ($entry:ident, $bucket:expr) => {{
404                let vec = match $bucket {
405                    Bucket::Root => &mut self.root,
406                    Bucket::ID(id) => self
407                        .id_hash
408                        .try_entry(id.clone(), quirks_mode)?
409                        .or_default(),
410                    Bucket::Class(class) => self
411                        .class_hash
412                        .try_entry(class.clone(), quirks_mode)?
413                        .or_default(),
414                    Bucket::Attribute { name, lower_name }
415                    | Bucket::LocalName { name, lower_name } => {
416                        // If the local name in the selector isn't lowercase,
417                        // insert it into the rule hash twice. This means that,
418                        // during lookup, we can always find the rules based on
419                        // the local name of the element, regardless of whether
420                        // it's an html element in an html document (in which
421                        // case we match against lower_name) or not (in which
422                        // case we match against name).
423                        //
424                        // In the case of a non-html-element-in-html-document
425                        // with a lowercase localname and a non-lowercase
426                        // selector, the rulehash lookup may produce superfluous
427                        // selectors, but the subsequent selector matching work
428                        // will filter them out.
429                        let is_attribute = matches!($bucket, Bucket::Attribute { .. });
430                        let hash = if is_attribute {
431                            &mut self.attribute_hash
432                        } else {
433                            &mut self.local_name_hash
434                        };
435                        if name != lower_name {
436                            hash.try_reserve(1)?;
437                            let vec = hash.entry(lower_name.clone()).or_default();
438                            vec.try_reserve(1)?;
439                            let mut entry = $entry.clone();
440                            entry.set_bucket_matches(bucket_matches);
441                            vec.push(entry);
442                        }
443                        hash.try_reserve(1)?;
444                        hash.entry(name.clone()).or_default()
445                    },
446                    Bucket::Namespace(url) => {
447                        self.namespace_hash.try_reserve(1)?;
448                        self.namespace_hash.entry(url.clone()).or_default()
449                    },
450                    Bucket::RarePseudoClasses => &mut self.rare_pseudo_classes,
451                    Bucket::Universal => &mut self.other,
452                };
453                vec.try_reserve(1)?;
454                $entry.set_bucket_matches(bucket_matches);
455                vec.push($entry);
456            }};
457        }
458
459        let bucket = {
460            let mut disjoint_buckets = SmallVec::new();
461            let bucket = find_bucket(
462                entry.selector(),
463                quirks_mode,
464                &mut disjoint_buckets,
465                &mut bucket_matches,
466                /* nested = */ false,
467            );
468
469            // See if inserting this selector in multiple entries in the
470            // selector map would be worth it. Consider a case like:
471            //
472            //   .foo:where(div, #bar)
473            //
474            // There, `bucket` would be `Class(foo)`, and disjoint_buckets would
475            // be `[LocalName { div }, ID(bar)]`.
476            //
477            // Here we choose to insert the selector in the `.foo` bucket in
478            // such a case, as it's likely more worth it than inserting it in
479            // both `div` and `#bar`.
480            //
481            // This is specially true if there's any universal selector in the
482            // `disjoint_selectors` set, at which point we'd just be doing
483            // wasted work.
484            if !disjoint_buckets.is_empty()
485                && disjoint_buckets
486                    .iter()
487                    .all(|b| b.more_specific_than(&bucket))
488            {
489                for bucket in &disjoint_buckets {
490                    let mut entry = entry.clone();
491                    insert_into_bucket!(entry, *bucket);
492                }
493                return Ok(());
494            }
495            bucket
496        };
497
498        insert_into_bucket!(entry, bucket);
499        Ok(())
500    }
501
502    /// Looks up entries by id, class, local name, namespace, and other (in
503    /// order).
504    ///
505    /// Each entry is passed to the callback, which returns true to continue
506    /// iterating entries, or false to terminate the lookup.
507    ///
508    /// Returns false if the callback ever returns false.
509    ///
510    /// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
511    /// but that function is extremely hot and I'd rather not rearrange it.
512    pub fn lookup<'a, E, F>(
513        &'a self,
514        element: E,
515        quirks_mode: QuirksMode,
516        relevant_attributes: Option<&mut RelevantAttributes>,
517        f: F,
518    ) -> bool
519    where
520        E: TElement,
521        F: FnMut(&'a T) -> bool,
522    {
523        self.lookup_with_state(
524            element,
525            element.state(),
526            quirks_mode,
527            relevant_attributes,
528            f,
529        )
530    }
531
532    #[inline]
533    fn lookup_with_state<'a, E, F>(
534        &'a self,
535        element: E,
536        element_state: ElementState,
537        quirks_mode: QuirksMode,
538        mut relevant_attributes: Option<&mut RelevantAttributes>,
539        mut f: F,
540    ) -> bool
541    where
542        E: TElement,
543        F: FnMut(&'a T) -> bool,
544    {
545        if element.is_root() {
546            for entry in self.root.iter() {
547                if !f(entry) {
548                    return false;
549                }
550            }
551        }
552
553        if let Some(id) = element.id() {
554            if let Some(v) = self.id_hash.get(id, quirks_mode) {
555                for entry in v.iter() {
556                    if !f(entry) {
557                        return false;
558                    }
559                }
560            }
561        }
562
563        let mut done = false;
564        element.each_class(|class| {
565            if done {
566                return;
567            }
568            if let Some(v) = self.class_hash.get(class, quirks_mode) {
569                for entry in v.iter() {
570                    if !f(entry) {
571                        done = true;
572                        return;
573                    }
574                }
575            }
576        });
577
578        if done {
579            return false;
580        }
581
582        element.each_attr_name(|name| {
583            if done {
584                return;
585            }
586            if let Some(v) = self.attribute_hash.get(name) {
587                if let Some(ref mut relevant_attributes) = relevant_attributes {
588                    relevant_attributes.push(name.clone());
589                }
590                for entry in v.iter() {
591                    if !f(entry) {
592                        done = true;
593                        return;
594                    }
595                }
596            }
597        });
598
599        if done {
600            return false;
601        }
602
603        if let Some(v) = self.local_name_hash.get(element.local_name()) {
604            for entry in v.iter() {
605                if !f(entry) {
606                    return false;
607                }
608            }
609        }
610
611        if let Some(v) = self.namespace_hash.get(element.namespace()) {
612            for entry in v.iter() {
613                if !f(entry) {
614                    return false;
615                }
616            }
617        }
618
619        if element_state.intersects(RARE_PSEUDO_CLASS_STATES) {
620            for entry in self.rare_pseudo_classes.iter() {
621                if !f(entry) {
622                    return false;
623                }
624            }
625        }
626
627        for entry in self.other.iter() {
628            if !f(entry) {
629                return false;
630            }
631        }
632
633        true
634    }
635
636    /// Performs a normal lookup, and also looks up entries for the passed-in
637    /// id and classes.
638    ///
639    /// Each entry is passed to the callback, which returns true to continue
640    /// iterating entries, or false to terminate the lookup.
641    ///
642    /// Returns false if the callback ever returns false.
643    #[inline]
644    pub fn lookup_with_additional<'a, E, F>(
645        &'a self,
646        element: E,
647        quirks_mode: QuirksMode,
648        additional_id: Option<&WeakAtom>,
649        additional_classes: &[Atom],
650        additional_states: ElementState,
651        mut f: F,
652    ) -> bool
653    where
654        E: TElement,
655        F: FnMut(&'a T) -> bool,
656    {
657        // Do the normal lookup.
658        if !self.lookup_with_state(
659            element,
660            element.state() | additional_states,
661            quirks_mode,
662            /* relevant_attributes = */ None,
663            &mut f,
664        ) {
665            return false;
666        }
667
668        // Check the additional id.
669        if let Some(id) = additional_id {
670            if let Some(v) = self.id_hash.get(id, quirks_mode) {
671                for entry in v.iter() {
672                    if !f(entry) {
673                        return false;
674                    }
675                }
676            }
677        }
678
679        // Check the additional classes.
680        for class in additional_classes {
681            if let Some(v) = self.class_hash.get(class, quirks_mode) {
682                for entry in v.iter() {
683                    if !f(entry) {
684                        return false;
685                    }
686                }
687            }
688        }
689
690        true
691    }
692}
693
694#[derive(PartialEq)]
695enum Bucket<'a> {
696    Universal,
697    Namespace(&'a Namespace),
698    RarePseudoClasses,
699    LocalName {
700        name: &'a LocalName,
701        lower_name: &'a LocalName,
702    },
703    Attribute {
704        name: &'a LocalName,
705        lower_name: &'a LocalName,
706    },
707    Class(&'a Atom),
708    ID(&'a Atom),
709    Root,
710}
711
712impl<'a> Bucket<'a> {
713    /// root > id > class > local name > namespace > pseudo-classes > universal.
714    #[inline]
715    fn specificity(&self) -> usize {
716        match *self {
717            Bucket::Universal => 0,
718            Bucket::Namespace(..) => 1,
719            Bucket::RarePseudoClasses => 2,
720            Bucket::LocalName { .. } => 3,
721            Bucket::Attribute { .. } => 4,
722            Bucket::Class(..) => 5,
723            Bucket::ID(..) => 6,
724            Bucket::Root => 7,
725        }
726    }
727
728    #[inline]
729    fn more_or_equally_specific_than(&self, other: &Self) -> bool {
730        self.specificity() >= other.specificity()
731    }
732
733    #[inline]
734    fn more_specific_than(&self, other: &Self) -> bool {
735        self.specificity() > other.specificity()
736    }
737}
738
739type DisjointBuckets<'a> = SmallVec<[Bucket<'a>; 5]>;
740
741/// Whether our bucket is known to match our full selector, the subject part, or nothing.
742#[derive(Copy, Clone, Debug, PartialEq, MallocSizeOf)]
743pub enum BucketMatches {
744    /// Full selector is known-matching.
745    Full,
746    /// The subject is known-matching.
747    Subject,
748    /// Nothing is known-matching.
749    Unknown,
750}
751
752fn specific_bucket_for<'a>(
753    component: &'a Component<SelectorImpl>,
754    quirks_mode: QuirksMode,
755    disjoint_buckets: &mut DisjointBuckets<'a>,
756    bucket_matches: &mut BucketMatches,
757    nested: bool,
758) -> Bucket<'a> {
759    match *component {
760        Component::Root => Bucket::Root,
761        Component::ID(ref id) => {
762            if quirks_mode == QuirksMode::Quirks {
763                // Lookup is case-insensitive, we still need to match the real thing.
764                *bucket_matches = BucketMatches::Unknown;
765            }
766            Bucket::ID(id)
767        },
768        Component::Class(ref class) => {
769            if quirks_mode == QuirksMode::Quirks {
770                // Lookup is case-insensitive, we still need to match the real thing.
771                *bucket_matches = BucketMatches::Unknown;
772            }
773            Bucket::Class(class)
774        },
775        Component::AttributeInNoNamespace { ref local_name, .. } => {
776            // Depends on the attribute value, or might have namespaced attributes (ugh!).
777            *bucket_matches = BucketMatches::Unknown;
778            Bucket::Attribute {
779                name: local_name,
780                lower_name: local_name,
781            }
782        },
783        Component::AttributeInNoNamespaceExists {
784            ref local_name,
785            ref local_name_lower,
786        } => {
787            // Might have namespaced attributes (ugh!).
788            *bucket_matches = BucketMatches::Unknown;
789            Bucket::Attribute {
790                name: local_name,
791                lower_name: local_name_lower,
792            }
793        },
794        Component::AttributeOther(ref selector) => {
795            // Depends on the attribute value, or might have namespaced attributes (ugh!).
796            *bucket_matches = BucketMatches::Unknown;
797            Bucket::Attribute {
798                name: &selector.local_name,
799                lower_name: &selector.local_name_lower,
800            }
801        },
802        Component::LocalName(ref selector) => {
803            if selector.name != selector.lower_name {
804                *bucket_matches = BucketMatches::Unknown;
805            }
806            Bucket::LocalName {
807                name: &selector.name,
808                lower_name: &selector.lower_name,
809            }
810        },
811        Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
812            Bucket::Namespace(url)
813        },
814        // ::slotted(..) isn't a normal pseudo-element, so we can insert it on
815        // the rule hash normally without much problem. For example, in a
816        // selector like:
817        //
818        //   div::slotted(span)::before
819        //
820        // It looks like:
821        //
822        //  [
823        //    LocalName(div),
824        //    Combinator(SlotAssignment),
825        //    Slotted(span),
826        //    Combinator::PseudoElement,
827        //    PseudoElement(::before),
828        //  ]
829        //
830        // So inserting `span` in the rule hash makes sense since we want to
831        // match the slotted <span>.
832        Component::Slotted(ref selector) => {
833            // We need to set unknown here because <slot> still shouldn't match... We could avoid
834            // looking up slotted rules for <slot> elements instead.
835            *bucket_matches = BucketMatches::Unknown;
836            find_bucket(
837                selector.iter(),
838                quirks_mode,
839                disjoint_buckets,
840                bucket_matches,
841                /* nested = */ true,
842            )
843        },
844        Component::Host(ref selector) => {
845            // Even tho we bucket shadow host rules in shadow trees, this rule could be in the
846            // document.
847            //
848            // TODO(emilio): We could return more state during bucketing and just discard the
849            // selector entirely, probably.
850            *bucket_matches = BucketMatches::Unknown;
851            if let Some(selector) = selector {
852                find_bucket(
853                    selector.iter(),
854                    quirks_mode,
855                    disjoint_buckets,
856                    bucket_matches,
857                    /* nested = */ true,
858                )
859            } else {
860                Bucket::Universal
861            }
862        },
863        Component::Is(ref list) | Component::Where(ref list) => {
864            if list.len() == 1 {
865                find_bucket(
866                    list.slice()[0].iter(),
867                    quirks_mode,
868                    disjoint_buckets,
869                    bucket_matches,
870                    /* nested = */ true,
871                )
872            } else {
873                // TODO: Since the is/where() semantics are effectively OR rather than AND, this is
874                // a bit too conservative, we could keep bucket_matches set for some of the disjoint
875                // buckets or so... But then we also need to deal with other special-cases like
876                // :is(:host, #not-host) or so.
877                *bucket_matches = BucketMatches::Unknown;
878                for selector in list.slice() {
879                    let bucket = find_bucket(
880                        selector.iter(),
881                        quirks_mode,
882                        disjoint_buckets,
883                        bucket_matches,
884                        /* nested = */ true,
885                    );
886                    if disjoint_buckets.last() == Some(&bucket) {
887                        // It's pretty common to have selectors like:
888                        //   input:is([type=foo], [type=bar], ...)
889                        // Try to prevent trivial duplicate entries for the same bucket.
890                        continue;
891                    }
892                    disjoint_buckets.push(bucket);
893                }
894                Bucket::Universal
895            }
896        },
897        Component::NonTSPseudoClass(ref pseudo_class)
898            if pseudo_class
899                .state_flag()
900                .intersects(RARE_PSEUDO_CLASS_STATES) =>
901        {
902            // We bucket a bunch of pseudo-classes together so we still need to do the matching to
903            // figure out if the specific one is covered...
904            *bucket_matches = BucketMatches::Unknown;
905            Bucket::RarePseudoClasses
906        },
907        Component::PseudoElement(ref pseudo) => {
908            // Pseudos are covered by bucketing, unless they are functional in which case they share
909            // a map with the other pseudos of their kind, or if they're nested (due to CSS nesting
910            // or so) in which case they never match and we can't skip the subject part.
911            if pseudo.has_argument() || nested {
912                *bucket_matches = BucketMatches::Unknown;
913            }
914            Bucket::Universal
915        },
916        Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => {
917            // The universal selectors, well, always match, so we can leave bucket_matches as-is...
918            Bucket::Universal
919        },
920        _ => {
921            *bucket_matches = BucketMatches::Unknown;
922            Bucket::Universal
923        },
924    }
925}
926
927/// Searches a compound selector from left to right, and returns the appropriate
928/// bucket for it.
929///
930/// It also populates disjoint_buckets with dependencies from nested selectors
931/// with any semantics like :is() and :where().
932///
933/// If the bucket is not guaranteed to cover the whole selector, it will set bucket_matches to
934/// either Unknown or Subject.
935#[inline(always)]
936fn find_bucket<'a>(
937    mut iter: SelectorIter<'a, SelectorImpl>,
938    quirks_mode: QuirksMode,
939    disjoint_buckets: &mut DisjointBuckets<'a>,
940    bucket_matches: &mut BucketMatches,
941    nested: bool,
942) -> Bucket<'a> {
943    let mut current_bucket = Bucket::Universal;
944
945    loop {
946        for ss in &mut iter {
947            let new_bucket =
948                specific_bucket_for(ss, quirks_mode, disjoint_buckets, bucket_matches, nested);
949            // NOTE: When presented with the choice of multiple specific selectors, use the
950            // rightmost, on the assumption that that's less common, see bug 1829540.
951            if current_bucket != Bucket::Universal {
952                // Selector fits in multiple buckets so need to do selector matching.
953                *bucket_matches = BucketMatches::Unknown;
954            }
955            if new_bucket.more_or_equally_specific_than(&current_bucket) {
956                current_bucket = new_bucket;
957            }
958        }
959
960        // Effectively, pseudo-elements are ignored, given only state
961        // pseudo-classes may appear before them.
962        match iter.next_sequence() {
963            None => break,
964            Some(Combinator::PseudoElement) => continue,
965            Some(..) => {
966                // We need to match the combinator.
967                if *bucket_matches != BucketMatches::Unknown {
968                    if nested {
969                        *bucket_matches = BucketMatches::Unknown;
970                    } else {
971                        *bucket_matches = BucketMatches::Subject;
972                    }
973                }
974                break;
975            },
976        }
977    }
978
979    current_bucket
980}
981
982/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.
983#[derive(Clone, Debug, MallocSizeOf)]
984pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V>(PrecomputedHashMap<K, V>);
985
986impl<V> Default for MaybeCaseInsensitiveHashMap<Atom, V> {
987    #[inline]
988    fn default() -> Self {
989        MaybeCaseInsensitiveHashMap(PrecomputedHashMap::default())
990    }
991}
992
993impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
994    /// Empty map
995    pub fn new() -> Self {
996        Self::default()
997    }
998
999    /// Shrink the capacity of the map if needed.
1000    pub fn shrink_if_needed(&mut self) {
1001        self.0.shrink_if_needed()
1002    }
1003
1004    /// HashMap::try_entry
1005    pub fn try_entry(
1006        &mut self,
1007        mut key: Atom,
1008        quirks_mode: QuirksMode,
1009    ) -> Result<hash_map::Entry<'_, Atom, V>, AllocErr> {
1010        if quirks_mode == QuirksMode::Quirks {
1011            key = key.to_ascii_lowercase()
1012        }
1013        self.0.try_reserve(1)?;
1014        Ok(self.0.entry(key))
1015    }
1016
1017    /// HashMap::is_empty
1018    #[inline]
1019    pub fn is_empty(&self) -> bool {
1020        self.0.is_empty()
1021    }
1022
1023    /// HashMap::iter
1024    pub fn iter(&self) -> hash_map::Iter<'_, Atom, V> {
1025        self.0.iter()
1026    }
1027
1028    /// HashMap::clear
1029    pub fn clear(&mut self) {
1030        self.0.clear()
1031    }
1032
1033    /// HashMap::get
1034    pub fn get(&self, key: &WeakAtom, quirks_mode: QuirksMode) -> Option<&V> {
1035        if quirks_mode == QuirksMode::Quirks {
1036            self.0.get(&key.to_ascii_lowercase())
1037        } else {
1038            self.0.get(key)
1039        }
1040    }
1041}
1042
1043#[test]
1044fn test_precomputed_hash_set() {
1045    let mut set = PrecomputedHashSet::default();
1046    let atom = crate::Atom::from("");
1047    assert!(!set.contains(&atom));
1048    set.insert(atom.clone());
1049    assert!(set.contains(&atom));
1050}