mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
ID and class selectors are ASCII case-insensitive in quirks mode.
https://bugzilla.mozilla.org/show_bug.cgi?id=1363778
This commit is contained in:
parent
524fcac191
commit
5bccf98aa4
22 changed files with 313 additions and 191 deletions
|
@ -7,6 +7,7 @@
|
|||
|
||||
use {Atom, LocalName};
|
||||
use applicable_declarations::ApplicableDeclarationBlock;
|
||||
use context::QuirksMode;
|
||||
use dom::TElement;
|
||||
use fnv::FnvHashMap;
|
||||
use pdqsort::sort_by;
|
||||
|
@ -16,8 +17,8 @@ use selectors::matching::{matches_selector, MatchingContext, ElementSelectorFlag
|
|||
use selectors::parser::{AncestorHashes, Component, Combinator, SelectorAndHashes, SelectorIter};
|
||||
use selectors::parser::LocalName as LocalNameSelector;
|
||||
use smallvec::VecLike;
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map;
|
||||
use std::hash::Hash;
|
||||
use stylist::Rule;
|
||||
|
||||
|
@ -66,9 +67,9 @@ impl SelectorMapEntry for SelectorAndHashes<SelectorImpl> {
|
|||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct SelectorMap<T: SelectorMapEntry> {
|
||||
/// A hash from an ID to rules which contain that ID selector.
|
||||
pub id_hash: FnvHashMap<Atom, Vec<T>>,
|
||||
pub id_hash: MaybeCaseInsensitiveHashMap<Atom, Vec<T>>,
|
||||
/// A hash from a class name to rules which contain that class selector.
|
||||
pub class_hash: FnvHashMap<Atom, Vec<T>>,
|
||||
pub class_hash: MaybeCaseInsensitiveHashMap<Atom, Vec<T>>,
|
||||
/// A hash from local name to rules which contain that local name selector.
|
||||
pub local_name_hash: FnvHashMap<LocalName, Vec<T>>,
|
||||
/// Rules that don't have ID, class, or element selectors.
|
||||
|
@ -86,8 +87,8 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
/// Trivially constructs an empty `SelectorMap`.
|
||||
pub fn new() -> Self {
|
||||
SelectorMap {
|
||||
id_hash: HashMap::default(),
|
||||
class_hash: HashMap::default(),
|
||||
id_hash: MaybeCaseInsensitiveHashMap::new(),
|
||||
class_hash: MaybeCaseInsensitiveHashMap::new(),
|
||||
local_name_hash: HashMap::default(),
|
||||
other: Vec::new(),
|
||||
count: 0,
|
||||
|
@ -115,6 +116,7 @@ impl SelectorMap<Rule> {
|
|||
rule_hash_target: &E,
|
||||
matching_rules_list: &mut V,
|
||||
context: &mut MatchingContext,
|
||||
quirks_mode: QuirksMode,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel)
|
||||
where E: TElement,
|
||||
|
@ -128,32 +130,35 @@ impl SelectorMap<Rule> {
|
|||
// At the end, we're going to sort the rules that we added, so remember where we began.
|
||||
let init_len = matching_rules_list.len();
|
||||
if let Some(id) = rule_hash_target.get_id() {
|
||||
SelectorMap::get_matching_rules_from_hash(element,
|
||||
&self.id_hash,
|
||||
&id,
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level)
|
||||
if let Some(rules) = self.id_hash.get(&id, quirks_mode) {
|
||||
SelectorMap::get_matching_rules(element,
|
||||
rules,
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level)
|
||||
}
|
||||
}
|
||||
|
||||
rule_hash_target.each_class(|class| {
|
||||
SelectorMap::get_matching_rules_from_hash(element,
|
||||
&self.class_hash,
|
||||
class,
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level);
|
||||
if let Some(rules) = self.class_hash.get(&class, quirks_mode) {
|
||||
SelectorMap::get_matching_rules(element,
|
||||
rules,
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level)
|
||||
}
|
||||
});
|
||||
|
||||
SelectorMap::get_matching_rules_from_hash(element,
|
||||
&self.local_name_hash,
|
||||
rule_hash_target.get_local_name(),
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level);
|
||||
if let Some(rules) = self.local_name_hash.get(rule_hash_target.get_local_name()) {
|
||||
SelectorMap::get_matching_rules(element,
|
||||
rules,
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level)
|
||||
}
|
||||
|
||||
SelectorMap::get_matching_rules(element,
|
||||
&self.other,
|
||||
|
@ -167,12 +172,6 @@ impl SelectorMap<Rule> {
|
|||
|block| (block.specificity, block.source_order()));
|
||||
}
|
||||
|
||||
/// Check whether we have rules for the given id
|
||||
#[inline]
|
||||
pub fn has_rules_for_id(&self, id: &Atom) -> bool {
|
||||
self.id_hash.get(id).is_some()
|
||||
}
|
||||
|
||||
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
|
||||
/// `self` sorted by specificity and source order.
|
||||
pub fn get_universal_rules(&self,
|
||||
|
@ -196,30 +195,6 @@ impl SelectorMap<Rule> {
|
|||
rules_list
|
||||
}
|
||||
|
||||
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
|
||||
element: &E,
|
||||
hash: &FnvHashMap<Str, Vec<Rule>>,
|
||||
key: &BorrowedStr,
|
||||
matching_rules: &mut Vector,
|
||||
context: &mut MatchingContext,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel)
|
||||
where E: TElement,
|
||||
Str: Borrow<BorrowedStr> + Eq + Hash,
|
||||
BorrowedStr: Eq + Hash,
|
||||
Vector: VecLike<ApplicableDeclarationBlock>,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
if let Some(rules) = hash.get(key) {
|
||||
SelectorMap::get_matching_rules(element,
|
||||
rules,
|
||||
matching_rules,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
|
||||
fn get_matching_rules<E, V, F>(element: &E,
|
||||
rules: &[Rule],
|
||||
|
@ -247,16 +222,16 @@ impl SelectorMap<Rule> {
|
|||
|
||||
impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||
/// Inserts into the correct hash, trying id, class, and localname.
|
||||
pub fn insert(&mut self, entry: T) {
|
||||
pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) {
|
||||
self.count += 1;
|
||||
|
||||
if let Some(id_name) = get_id_name(entry.selector()) {
|
||||
find_push(&mut self.id_hash, id_name, entry);
|
||||
self.id_hash.entry(id_name, quirks_mode).or_insert_with(Vec::new).push(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(class_name) = get_class_name(entry.selector()) {
|
||||
find_push(&mut self.class_hash, class_name, entry);
|
||||
self.class_hash.entry(class_name, quirks_mode).or_insert_with(Vec::new).push(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -293,13 +268,13 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
/// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
|
||||
/// but that function is extremely hot and I'd rather not rearrange it.
|
||||
#[inline]
|
||||
pub fn lookup<E, F>(&self, element: E, f: &mut F) -> bool
|
||||
pub fn lookup<E, F>(&self, element: E, quirks_mode: QuirksMode, f: &mut F) -> bool
|
||||
where E: TElement,
|
||||
F: FnMut(&T) -> bool
|
||||
{
|
||||
// Id.
|
||||
if let Some(id) = element.get_id() {
|
||||
if let Some(v) = self.id_hash.get(&id) {
|
||||
if let Some(v) = self.id_hash.get(&id, quirks_mode) {
|
||||
for entry in v.iter() {
|
||||
if !f(&entry) {
|
||||
return false;
|
||||
|
@ -312,7 +287,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
let mut done = false;
|
||||
element.each_class(|class| {
|
||||
if !done {
|
||||
if let Some(v) = self.class_hash.get(class) {
|
||||
if let Some(v) = self.class_hash.get(class, quirks_mode) {
|
||||
for entry in v.iter() {
|
||||
if !f(&entry) {
|
||||
done = true;
|
||||
|
@ -355,6 +330,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
#[inline]
|
||||
pub fn lookup_with_additional<E, F>(&self,
|
||||
element: E,
|
||||
quirks_mode: QuirksMode,
|
||||
additional_id: Option<Atom>,
|
||||
additional_classes: &[Atom],
|
||||
f: &mut F)
|
||||
|
@ -363,13 +339,13 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
F: FnMut(&T) -> bool
|
||||
{
|
||||
// Do the normal lookup.
|
||||
if !self.lookup(element, f) {
|
||||
if !self.lookup(element, quirks_mode, f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the additional id.
|
||||
if let Some(id) = additional_id {
|
||||
if let Some(v) = self.id_hash.get(&id) {
|
||||
if let Some(v) = self.id_hash.get(&id, quirks_mode) {
|
||||
for entry in v.iter() {
|
||||
if !f(&entry) {
|
||||
return false;
|
||||
|
@ -380,7 +356,7 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
|
||||
// Check the additional classes.
|
||||
for class in additional_classes {
|
||||
if let Some(v) = self.class_hash.get(class) {
|
||||
if let Some(v) = self.class_hash.get(class, quirks_mode) {
|
||||
for entry in v.iter() {
|
||||
if !f(&entry) {
|
||||
return false;
|
||||
|
@ -472,3 +448,32 @@ fn find_push<Str: Eq + Hash, V>(map: &mut FnvHashMap<Str, Vec<V>>,
|
|||
value: V) {
|
||||
map.entry(key).or_insert_with(Vec::new).push(value)
|
||||
}
|
||||
|
||||
/// Wrapper for FnvHashMap that does ASCII-case-insensitive lookup in quirks mode.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct MaybeCaseInsensitiveHashMap<K: Hash + Eq, V>(FnvHashMap<K, V>);
|
||||
|
||||
impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
|
||||
/// Empty map
|
||||
pub fn new() -> Self {
|
||||
MaybeCaseInsensitiveHashMap(FnvHashMap::default())
|
||||
}
|
||||
|
||||
/// HashMap::entry
|
||||
pub fn entry(&mut self, mut key: Atom, quirks_mode: QuirksMode) -> hash_map::Entry<Atom, V> {
|
||||
if quirks_mode == QuirksMode::Quirks {
|
||||
key = key.to_ascii_lowercase()
|
||||
}
|
||||
self.0.entry(key)
|
||||
}
|
||||
|
||||
/// HashMap::get
|
||||
pub fn get(&self, key: &Atom, quirks_mode: QuirksMode) -> Option<&V> {
|
||||
if quirks_mode == QuirksMode::Quirks {
|
||||
self.0.get(&key.to_ascii_lowercase())
|
||||
} else {
|
||||
self.0.get(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue