diff options
-rw-r--r-- | components/selectors/parser.rs | 2 | ||||
-rw-r--r-- | components/style/restyle_hints.rs | 99 | ||||
-rw-r--r-- | components/style/stylist.rs | 92 |
3 files changed, 99 insertions, 94 deletions
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index f53380c2b22..66867000fda 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -1457,7 +1457,7 @@ pub mod tests { where V: SelectorVisitor<Impl = Self::Impl> { true } } - #[derive(PartialEq, Debug)] + #[derive(Clone, PartialEq, Debug)] pub struct DummySelectorImpl; #[derive(Default)] diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index 9d3d8814ac7..193b7e82a9d 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -18,7 +18,7 @@ use selectors::{Element, MatchAttr}; use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::matches_selector; use selectors::parser::{AttrSelector, Combinator, Component, Selector}; -use selectors::parser::{SelectorInner, SelectorIter, SelectorMethods}; +use selectors::parser::{SelectorInner, SelectorMethods}; use selectors::visitor::SelectorVisitor; use std::clone::Clone; @@ -482,27 +482,13 @@ struct Dependency { /// of them is sensitive to attribute or state changes. struct SensitivitiesVisitor { sensitivities: Sensitivities, - hint: RestyleHint, } impl SelectorVisitor for SensitivitiesVisitor { type Impl = SelectorImpl; - - fn visit_complex_selector(&mut self, - _: SelectorIter<SelectorImpl>, - combinator: Option<Combinator>) -> bool { - self.hint |= combinator_to_restyle_hint(combinator); - - true - } - fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool { self.sensitivities.states.insert(selector_to_state(s)); - - if !self.sensitivities.attrs { - self.sensitivities.attrs = is_attr_selector(s); - } - + self.sensitivities.attrs |= is_attr_selector(s); true } } @@ -539,56 +525,63 @@ impl DependencySet { /// Adds a selector to this `DependencySet`. pub fn note_selector(&mut self, selector: &Selector<SelectorImpl>) { - let mut is_pseudo_element = selector.pseudo_element.is_some(); - - let mut next = Some(selector.inner.complex.clone()); let mut combinator = None; + let mut iter = selector.inner.complex.iter(); + let mut index = 0; - while let Some(current) = next.take() { - // Set up our visitor. + loop { + let sequence_start = index; let mut visitor = SensitivitiesVisitor { - sensitivities: Sensitivities::new(), - hint: combinator_to_restyle_hint(combinator), + sensitivities: Sensitivities::new() }; - if is_pseudo_element { - // TODO(emilio): use more fancy restyle hints to avoid restyling - // the whole subtree when pseudos change. - // - // We currently need is_pseudo_element to handle eager pseudos - // (so the style the parent stores doesn't become stale), and - // restyle_descendants to handle all of them (::before and - // ::after, because we find them in the subtree, and other lazy - // pseudos for the same reason). - visitor.hint |= RESTYLE_SELF | RESTYLE_DESCENDANTS; - is_pseudo_element = false; + // Visit all the simple selectors in this sequence. + // + // Note that this works because we can't have combinators nested + // inside simple selectors (i.e. in :not() or :-moz-any()). If we + // ever support that we'll need to visit complex selectors as well. + for ss in &mut iter { + ss.visit(&mut visitor); + index += 1; // Account for the simple selector. } - { - // Visit all the simple selectors. - let mut iter = current.iter(); - let mut index = 0usize; - for ss in &mut iter { - ss.visit(&mut visitor); - index += 1; - } - - // Prepare the next sequence of simple selectors. - if let Some(next_combinator) = iter.next_sequence() { - next = Some(current.slice_from(index + 1)); - combinator = Some(next_combinator); + // If we found a sensitivity, add an entry in the dependency set. + if !visitor.sensitivities.is_empty() { + let mut hint = combinator_to_restyle_hint(combinator); + let dep_selector; + if sequence_start == 0 { + if selector.pseudo_element.is_some() { + // TODO(emilio): use more fancy restyle hints to avoid + // restyling the whole subtree when pseudos change. + // + // We currently need is_pseudo_element to handle eager + // pseudos (so the style the parent stores doesn't + // become stale), and restyle_descendants to handle all + // of them (::before and ::after, because we find them + // in the subtree, and other lazy pseudos for the same + // reason). + hint |= RESTYLE_SELF | RESTYLE_DESCENDANTS; + } + + // Reuse the bloom hashes if this is the base selector. + dep_selector = selector.inner.clone(); + } else { + dep_selector = SelectorInner::new(selector.inner.complex.slice_from(sequence_start)); } - } - // Note what we found. - if !visitor.sensitivities.is_empty() { self.add_dependency(Dependency { sensitivities: visitor.sensitivities, - hint: visitor.hint, - selector: SelectorInner::new(current), - }) + hint: hint, + selector: dep_selector, + }); + } + + combinator = iter.next_sequence(); + if combinator.is_none() { + break; } + index += 1; // Account for the combinator. } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index cde51a8f3fb..65d04630fd3 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -313,38 +313,13 @@ impl Stylist { CssRule::Style(ref locked) => { let style_rule = locked.read_with(&guard); self.num_declarations += style_rule.block.read_with(&guard).len(); - for selector in &style_rule.selectors.0 { self.num_selectors += 1; - let map = if let Some(ref pseudo) = selector.pseudo_element { - self.pseudos_map - .entry(pseudo.clone()) - .or_insert_with(PerPseudoElementSelectorMap::new) - .borrow_for_origin(&stylesheet.origin) - } else { - self.element_map.borrow_for_origin(&stylesheet.origin) - }; - - map.insert(Rule::new(guard, - selector.inner.clone(), - locked.clone(), - self.rules_source_order, - selector.specificity)); - } - self.rules_source_order += 1; - - for selector in &style_rule.selectors.0 { + self.add_rule_to_map(guard, selector, locked, stylesheet); self.dependencies.note_selector(selector); - - if needs_revalidation(selector) { - // For revalidation, we can skip everything left of - // the first ancestor combinator. - let revalidation_sel = - selector.inner.slice_to_first_ancestor_combinator(); - - self.selectors_for_cache_revalidation.push(revalidation_sel); - } + self.note_for_revalidation(selector); } + self.rules_source_order += 1; } CssRule::Import(ref import) => { let import = import.read_with(guard); @@ -374,6 +349,38 @@ impl Stylist { }); } + #[inline] + fn add_rule_to_map(&mut self, + guard: &SharedRwLockReadGuard, + selector: &Selector<SelectorImpl>, + rule: &Arc<Locked<StyleRule>>, + stylesheet: &Stylesheet) + { + let map = if let Some(ref pseudo) = selector.pseudo_element { + self.pseudos_map + .entry(pseudo.clone()) + .or_insert_with(PerPseudoElementSelectorMap::new) + .borrow_for_origin(&stylesheet.origin) + } else { + self.element_map.borrow_for_origin(&stylesheet.origin) + }; + + map.insert(Rule::new(guard, + selector.inner.clone(), + rule.clone(), + self.rules_source_order, + selector.specificity)); + } + + #[inline] + fn note_for_revalidation(&mut self, selector: &Selector<SelectorImpl>) { + if needs_revalidation(selector) { + // For revalidation, we can skip everything left of the first ancestor + // combinator. + let revalidation_sel = selector.inner.slice_to_first_ancestor_combinator(); + self.selectors_for_cache_revalidation.push(revalidation_sel); + } + } /// Computes the style for a given "precomputed" pseudo-element, taking the /// universal rules and applying them. @@ -1043,9 +1050,6 @@ pub struct SelectorMap { pub class_hash: FnvHashMap<Atom, Vec<Rule>>, /// A hash from local name to rules which contain that local name selector. pub local_name_hash: FnvHashMap<LocalName, Vec<Rule>>, - /// Same as local_name_hash, but keys are lower-cased. - /// For HTML elements in HTML documents. - pub lower_local_name_hash: FnvHashMap<LocalName, Vec<Rule>>, /// Rules that don't have ID, class, or element selectors. pub other_rules: Vec<Rule>, /// Whether this hash is empty. @@ -1064,7 +1068,6 @@ impl SelectorMap { id_hash: HashMap::default(), class_hash: HashMap::default(), local_name_hash: HashMap::default(), - lower_local_name_hash: HashMap::default(), other_rules: Vec::new(), empty: true, } @@ -1113,14 +1116,9 @@ impl SelectorMap { cascade_level); }); - let local_name_hash = if element.is_html_element_in_html_document() { - &self.lower_local_name_hash - } else { - &self.local_name_hash - }; SelectorMap::get_matching_rules_from_hash(element, parent_bf, - local_name_hash, + &self.local_name_hash, element.get_local_name(), matching_rules_list, relations, @@ -1253,8 +1251,22 @@ impl SelectorMap { } if let Some(LocalNameSelector { name, lower_name }) = SelectorMap::get_local_name(&rule) { - find_push(&mut self.local_name_hash, name, rule.clone()); - find_push(&mut self.lower_local_name_hash, lower_name, rule); + // If the local name in the selector isn't lowercase, insert it into + // the rule hash twice. This means that, during lookup, we can always + // find the rules based on the local name of the element, regardless + // of whether it's an html element in an html document (in which case + // we match against lower_name) or not (in which case we match against + // name). + // + // In the case of a non-html-element-in-html-document with a + // lowercase localname and a non-lowercase selector, the rulehash + // lookup may produce superfluous selectors, but the subsequent + // selector matching work will filter them out. + if name != lower_name { + find_push(&mut self.local_name_hash, lower_name, rule.clone()); + } + find_push(&mut self.local_name_hash, name, rule); + return; } |