style: Rework how precomputed pseudo stuff works, to avoid malloc/free churn.

This showed up in a few profiles, and was an easy improvement.

MozReview-Commit-ID: HVqATaSB2Ak
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-08-07 12:16:50 +02:00
parent 48b7e6d27c
commit 64a96ce21c
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
3 changed files with 50 additions and 67 deletions

View file

@ -201,29 +201,6 @@ impl SelectorMap<Rule> {
|block| (block.specificity, block.source_order()));
}
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
/// `self` sorted by specificity and source order.
pub fn get_universal_rules(&self,
cascade_level: CascadeLevel)
-> Vec<ApplicableDeclarationBlock> {
debug_assert!(!cascade_level.is_important());
if self.is_empty() {
return vec![];
}
let mut rules_list = vec![];
for rule in self.other.iter() {
if rule.selector.is_universal() {
rules_list.push(rule.to_applicable_declaration_block(cascade_level))
}
}
sort_by_key(&mut rules_list,
|block| (block.specificity, block.source_order()));
rules_list
}
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V, F>(element: &E,
rules: &[Rule],

View file

@ -403,13 +403,6 @@ impl Stylist {
self.add_stylesheet(stylesheet, guards.author, extra_data);
}
SelectorImpl::each_precomputed_pseudo_element(|pseudo| {
if let Some(map) = self.cascade_data.user_agent.pseudos_map.remove(&pseudo) {
let declarations = map.get_universal_rules(CascadeLevel::UANormal);
self.precomputed_pseudo_element_decls.insert(pseudo, declarations);
}
});
self.is_device_dirty = false;
true
}
@ -470,17 +463,49 @@ impl Stylist {
for selector in &style_rule.selectors.0 {
self.num_selectors += 1;
let map = match selector.pseudo_element() {
None => &mut origin_cascade_data.element_map,
Some(pseudo) => {
if pseudo.is_precomputed() {
if !selector.is_universal() ||
!matches!(origin, Origin::UserAgent) {
// ::-moz-tree selectors may appear in
// non-UA sheets (even though they never
// match).
continue;
}
self.precomputed_pseudo_element_decls
.entry(pseudo.canonical())
.or_insert_with(Vec::new)
.push(ApplicableDeclarationBlock::new(
StyleSource::Style(locked.clone()),
self.rules_source_order,
CascadeLevel::UANormal,
selector.specificity()
));
continue;
} else {
origin_cascade_data
.pseudos_map
.entry(pseudo.canonical())
.or_insert_with(SelectorMap::new)
}
}
};
let hashes =
AncestorHashes::new(&selector, self.quirks_mode);
origin_cascade_data
.borrow_mut_for_pseudo_or_insert(selector.pseudo_element())
.insert(
Rule::new(selector.clone(),
hashes.clone(),
locked.clone(),
self.rules_source_order),
self.quirks_mode);
let rule = Rule::new(
selector.clone(),
hashes.clone(),
locked.clone(),
self.rules_source_order
);
map.insert(rule, self.quirks_mode);
self.invalidation_map.note_selector(selector, self.quirks_mode);
let mut visitor = StylistSelectorVisitor {
@ -1660,18 +1685,6 @@ impl PerOriginCascadeData {
}
}
#[inline]
fn borrow_mut_for_pseudo_or_insert(&mut self, pseudo: Option<&PseudoElement>) -> &mut SelectorMap<Rule> {
match pseudo {
Some(pseudo) => {
self.pseudos_map
.entry(pseudo.canonical())
.or_insert_with(SelectorMap::new)
}
None => &mut self.element_map,
}
}
fn clear(&mut self) {
*self = Self::new();
}
@ -1720,14 +1733,17 @@ impl Rule {
/// Turns this rule into an `ApplicableDeclarationBlock` for the given
/// cascade level.
pub fn to_applicable_declaration_block(&self,
level: CascadeLevel)
-> ApplicableDeclarationBlock {
pub fn to_applicable_declaration_block(
&self,
level: CascadeLevel
) -> ApplicableDeclarationBlock {
let source = StyleSource::Style(self.style_rule.clone());
ApplicableDeclarationBlock::new(source,
self.source_order,
level,
self.specificity())
ApplicableDeclarationBlock::new(
source,
self.source_order,
level,
self.specificity()
)
}
/// Creates a new Rule.

View file

@ -222,16 +222,6 @@ fn test_insert() {
assert!(selector_map.class_hash.get(&Atom::from("foo"), QuirksMode::NoQuirks).is_none());
}
#[test]
fn test_get_universal_rules() {
thread_state::initialize(thread_state::LAYOUT);
let (map, _shared_lock) = get_mock_map(&["*|*", "#foo > *|*", "*|* > *|*", ".klass", "#id"]);
let decls = map.get_universal_rules(CascadeLevel::UserNormal);
assert_eq!(decls.len(), 1, "{:?}", decls);
}
fn mock_stylist() -> Stylist {
let device = Device::new(MediaType::Screen, TypedSize2D::new(0f32, 0f32), ScaleFactor::new(1.0));
Stylist::new(device, QuirksMode::NoQuirks)