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