Bug 1374017: Add namespace bucket for the selector map. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 24 Jun 2018 04:06:49 +0200
changeset 809949 f810947dea57814efb937ba5e0ca030851d67836
parent 809948 6055a3c32a4263a9dd4a2aa52d1d144353b4b116
push id113843
push userbmo:emilio@crisal.io
push dateSun, 24 Jun 2018 05:10:47 +0000
reviewersheycam
bugs1374017, 1470163
milestone62.0a1
Bug 1374017: Add namespace bucket for the selector map. r?heycam After bug 1470163 we have some nasty selectors from mathml.css in every page. We only want to match them against MathML elements. This patch brings the global revalidation selectors from 14 to 2 in about:blank. Also halves the ones from XUL documents. MozReview-Commit-ID: nOVyknNcVm
servo/components/style/selector_map.rs
--- a/servo/components/style/selector_map.rs
+++ b/servo/components/style/selector_map.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A data structure to efficiently index structs containing selectors by local
 //! name, ids and hash.
 
-use {Atom, LocalName, WeakAtom};
+use {Atom, LocalName, Namespace, WeakAtom};
 use applicable_declarations::ApplicableDeclarationList;
 use context::QuirksMode;
 use dom::TElement;
 use fallible::FallibleVec;
 use hash::{HashMap, HashSet};
 use hash::map as hash_map;
 use hashglobe::FailedAllocationError;
 use precomputed_hash::PrecomputedHash;
@@ -97,16 +97,18 @@ pub trait SelectorMapEntry: Sized + Clon
 #[derive(Debug, MallocSizeOf)]
 pub struct SelectorMap<T: 'static> {
     /// A hash from an ID to rules which contain that ID selector.
     pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
     /// A hash from a class name to rules which contain that class selector.
     pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
     /// A hash from local name to rules which contain that local name selector.
     pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
+    /// A hash from namespace to rules which contain that namespace selector.
+    pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,
     /// Rules that don't have ID, class, or element selectors.
     pub other: SmallVec<[T; 1]>,
     /// The number of entries in this map.
     pub count: usize,
 }
 
 impl<T: 'static> Default for SelectorMap<T> {
     #[inline]
@@ -120,26 +122,28 @@ impl<T: 'static> Default for SelectorMap
 // or when stdlib gets fallible collections
 impl<T: 'static> SelectorMap<T> {
     /// Trivially constructs an empty `SelectorMap`.
     pub fn new() -> Self {
         SelectorMap {
             id_hash: MaybeCaseInsensitiveHashMap::new(),
             class_hash: MaybeCaseInsensitiveHashMap::new(),
             local_name_hash: HashMap::default(),
+            namespace_hash: HashMap::default(),
             other: SmallVec::new(),
             count: 0,
         }
     }
 
     /// Clears the hashmap retaining storage.
     pub fn clear(&mut self) {
         self.id_hash.clear();
         self.class_hash.clear();
         self.local_name_hash.clear();
+        self.namespace_hash.clear();
         self.other.clear();
         self.count = 0;
     }
 
     /// Returns whether there are any entries in the map.
     pub fn is_empty(&self) -> bool {
         self.count == 0
     }
@@ -212,16 +216,28 @@ impl SelectorMap<Rule> {
                 matching_rules_list,
                 context,
                 flags_setter,
                 cascade_level,
                 shadow_cascade_order,
             )
         }
 
+        if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
+            SelectorMap::get_matching_rules(
+                element,
+                rules,
+                matching_rules_list,
+                context,
+                flags_setter,
+                cascade_level,
+                shadow_cascade_order,
+            )
+        }
+
         SelectorMap::get_matching_rules(
             element,
             &self.other,
             matching_rules_list,
             context,
             flags_setter,
             cascade_level,
             shadow_cascade_order,
@@ -293,16 +309,19 @@ impl<T: SelectorMapEntry> SelectorMap<T>
                         .try_entry(lower_name.clone())?
                         .or_insert_with(SmallVec::new)
                         .try_push(entry.clone())?;
                 }
                 self.local_name_hash
                     .try_entry(name.clone())?
                     .or_insert_with(SmallVec::new)
             },
+            Bucket::Namespace(url) => self.namespace_hash
+                .try_entry(url.clone())?
+                .or_insert_with(SmallVec::new),
             Bucket::Universal => &mut self.other,
         };
 
         vector.try_push(entry)
     }
 
     /// Looks up entries by id, class, local name, and other (in order).
     ///
@@ -352,16 +371,24 @@ impl<T: SelectorMapEntry> SelectorMap<T>
         if let Some(v) = self.local_name_hash.get(element.local_name()) {
             for entry in v.iter() {
                 if !f(&entry) {
                     return false;
                 }
             }
         }
 
+        if let Some(v) = self.namespace_hash.get(element.namespace()) {
+            for entry in v.iter() {
+                if !f(&entry) {
+                    return false;
+                }
+            }
+        }
+
         // Other.
         for entry in self.other.iter() {
             if !f(&entry) {
                 return false;
             }
         }
 
         true
@@ -420,27 +447,30 @@ impl<T: SelectorMapEntry> SelectorMap<T>
 
 enum Bucket<'a> {
     ID(&'a Atom),
     Class(&'a Atom),
     LocalName {
         name: &'a LocalName,
         lower_name: &'a LocalName,
     },
+    Namespace(&'a Namespace),
     Universal,
 }
 
 fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a> {
     match *component {
         Component::ID(ref id) => Bucket::ID(id),
         Component::Class(ref class) => Bucket::Class(class),
         Component::LocalName(ref selector) => Bucket::LocalName {
             name: &selector.name,
             lower_name: &selector.lower_name,
         },
+        Component::Namespace(_, ref url) |
+        Component::DefaultNamespace(ref url) => Bucket::Namespace(url),
         // ::slotted(..) isn't a normal pseudo-element, so we can insert it on
         // the rule hash normally without much problem. For example, in a
         // selector like:
         //
         //   div::slotted(span)::before
         //
         // It looks like:
         //
@@ -465,30 +495,35 @@ fn specific_bucket_for<'a>(component: &'
 #[inline(always)]
 fn find_bucket<'a>(mut iter: SelectorIter<'a, SelectorImpl>) -> Bucket<'a> {
     let mut current_bucket = Bucket::Universal;
 
     loop {
         // We basically want to find the most specific bucket,
         // where:
         //
-        //   id > class > local name > universal.
+        //   id > class > local name > namespace > universal.
         //
         for ss in &mut iter {
             let new_bucket = specific_bucket_for(ss);
             match new_bucket {
                 Bucket::ID(..) => return new_bucket,
                 Bucket::Class(..) => {
                     current_bucket = new_bucket;
                 },
                 Bucket::LocalName { .. } => {
+                    if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) {
+                        current_bucket = new_bucket;
+                    }
+                },
+                Bucket::Namespace(..) => {
                     if matches!(current_bucket, Bucket::Universal) {
                         current_bucket = new_bucket;
                     }
-                },
+                }
                 Bucket::Universal => {},
             }
         }
 
         // Effectively, pseudo-elements are ignored, given only state
         // pseudo-classes may appear before them.
         if iter.next_sequence() != Some(Combinator::PseudoElement) {
             break;