Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up stylist rebuilds #16659

Merged
merged 5 commits into from Apr 30, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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)]
@@ -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.
}
}

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

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.