--- 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;