servo: Merge #17990 - style: Invert storage of selector maps to key off origin first (from heycam:invert-selector-maps); r=emilio

<!-- Please describe your changes on the following line: -->

This will make it easier to avoid rebuilding all cascade levels when only a style sheet at a particular level changes.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [ ] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: cc1aab35ea11415d8c5fb3e33c745434b8a5ec2d

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : abc4bde3d17e73a101c2d17824384f752fbc3392
This commit is contained in:
Cameron McCormack 2017-08-07 04:03:24 -05:00
Родитель 289b4ecd71
Коммит bbe6006497
1 изменённых файлов: 167 добавлений и 104 удалений

Просмотреть файл

@ -51,9 +51,8 @@ pub use ::fnv::FnvHashMap;
/// This structure holds all the selectors and device characteristics
/// for a given document. The selectors are converted into `Rule`s
/// (defined in rust-selectors), and introduced in a `SelectorMap`
/// depending on the pseudo-element (see `PerPseudoElementSelectorMap`),
/// and stylesheet origin (see the fields of `PerPseudoElementSelectorMap`).
/// (defined in rust-selectors), and sorted into `SelectorMap`s keyed
/// off stylesheet origin and pseudo-element (see `CascadeData`).
///
/// This structure is effectively created once per pipeline, in the
/// LayoutThread corresponding to that pipeline.
@ -90,17 +89,14 @@ pub struct Stylist {
/// had clear() called on it with no following rebuild()).
is_cleared: bool,
/// The current selector maps, after evaluating media
/// rules against the current device.
element_map: PerPseudoElementSelectorMap,
/// Selector maps for all of the style sheets in the stylist, after
/// evalutaing media rules against the current device, split out per
/// cascade level.
cascade_data: CascadeData,
/// The rule tree, that stores the results of selector matching.
rule_tree: RuleTree,
/// The selector maps corresponding to a given pseudo-element
/// (depending on the implementation)
pseudos_map: FnvHashMap<PseudoElement, PerPseudoElementSelectorMap>,
/// A map with all the animations indexed by name.
animations: FnvHashMap<Atom, KeyframesAnimation>,
@ -234,7 +230,7 @@ impl Stylist {
/// be reset in clear().
#[inline]
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
let mut stylist = Stylist {
Stylist {
viewport_constraints: None,
device: device,
is_device_dirty: true,
@ -242,8 +238,7 @@ impl Stylist {
quirks_mode: quirks_mode,
effective_media_query_results: EffectiveMediaQueryResults::new(),
element_map: PerPseudoElementSelectorMap::new(),
pseudos_map: Default::default(),
cascade_data: CascadeData::new(),
animations: Default::default(),
precomputed_pseudo_element_decls: Default::default(),
rules_source_order: 0,
@ -257,15 +252,9 @@ impl Stylist {
num_selectors: 0,
num_declarations: 0,
num_rebuilds: 0,
};
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
stylist.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
});
}
// FIXME: Add iso-8859-9.css when the documents encoding is ISO-8859-8.
stylist
}
/// Returns the number of selectors.
@ -317,8 +306,7 @@ impl Stylist {
// preserve current device
self.is_device_dirty = true;
// preserve current quirks_mode value
self.element_map = PerPseudoElementSelectorMap::new();
self.pseudos_map = Default::default();
self.cascade_data.clear();
self.animations.clear(); // Or set to Default::default()?
self.precomputed_pseudo_element_decls = Default::default();
self.rules_source_order = 0;
@ -393,10 +381,6 @@ impl Stylist {
self.device.account_for_viewport_rule(constraints);
}
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
});
extra_data.clear();
if let Some(ua_stylesheets) = ua_stylesheets {
@ -420,8 +404,8 @@ impl Stylist {
}
SelectorImpl::each_precomputed_pseudo_element(|pseudo| {
if let Some(map) = self.pseudos_map.remove(&pseudo) {
let declarations = map.user_agent.get_universal_rules(CascadeLevel::UANormal);
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);
}
});
@ -482,24 +466,18 @@ impl Stylist {
for selector in &style_rule.selectors.0 {
self.num_selectors += 1;
let map = if let Some(pseudo) = selector.pseudo_element() {
self.pseudos_map
.entry(pseudo.canonical())
.or_insert_with(PerPseudoElementSelectorMap::new)
.borrow_for_origin(&origin)
} else {
self.element_map.borrow_for_origin(&origin)
};
let hashes =
AncestorHashes::new(&selector, self.quirks_mode);
map.insert(
Rule::new(selector.clone(),
hashes.clone(),
locked.clone(),
self.rules_source_order),
self.quirks_mode);
self.cascade_data
.borrow_mut_for_origin(&origin)
.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);
self.invalidation_map.note_selector(selector, self.quirks_mode);
let mut visitor = StylistSelectorVisitor {
@ -857,7 +835,8 @@ impl Stylist {
{
let pseudo = pseudo.canonical();
debug_assert!(pseudo.is_lazy());
if self.pseudos_map.get(&pseudo).is_none() {
if !self.cascade_data.has_rules_for_pseudo(&pseudo) {
return CascadeInputs::default()
}
@ -1117,17 +1096,6 @@ impl Stylist {
self.quirks_mode = quirks_mode;
}
/// Returns the correspond PerPseudoElementSelectorMap given PseudoElement.
fn get_map(&self,
pseudo_element: Option<&PseudoElement>) -> Option<&PerPseudoElementSelectorMap>
{
match pseudo_element {
Some(pseudo) => self.pseudos_map.get(pseudo),
None => Some(&self.element_map),
}
}
/// Returns the applicable CSS declarations for the given element by
/// treating us as an XBL stylesheet-only stylist.
pub fn push_applicable_declarations_as_xbl_only_stylist<E, V>(&self,
@ -1141,21 +1109,19 @@ impl Stylist {
MatchingContext::new(MatchingMode::Normal, None, self.quirks_mode);
let mut dummy_flag_setter = |_: &E, _: ElementSelectorFlags| {};
let map = match self.get_map(pseudo_element) {
Some(map) => map,
None => return,
};
let rule_hash_target = element.rule_hash_target();
// nsXBLPrototypeResources::LoadResources() loads Chrome XBL style
// sheets under eAuthorSheetFeatures level.
map.author.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
&mut matching_context,
self.quirks_mode,
&mut dummy_flag_setter,
CascadeLevel::XBL);
if let Some(map) = self.cascade_data.author.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
&mut matching_context,
self.quirks_mode,
&mut dummy_flag_setter,
CascadeLevel::XBL);
}
}
/// Returns the applicable CSS declarations for the given element.
@ -1187,10 +1153,6 @@ impl Stylist {
"Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed()));
let map = match self.get_map(pseudo_element) {
Some(map) => map,
None => return,
};
let rule_hash_target = element.rule_hash_target();
debug!("Determining if style is shareable: pseudo: {}",
@ -1199,13 +1161,15 @@ impl Stylist {
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
// Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
self.quirks_mode,
flags_setter,
CascadeLevel::UANormal);
if let Some(map) = self.cascade_data.user_agent.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
self.quirks_mode,
flags_setter,
CascadeLevel::UANormal);
}
if pseudo_element.is_none() && !only_default_rules {
// Step 2: Presentational hints.
@ -1233,13 +1197,15 @@ impl Stylist {
// Which may be more what you would probably expect.
if rule_hash_target.matches_user_and_author_rules() {
// Step 3a: User normal rules.
map.user.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
self.quirks_mode,
flags_setter,
CascadeLevel::UserNormal);
if let Some(map) = self.cascade_data.user.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
self.quirks_mode,
flags_setter,
CascadeLevel::UserNormal);
}
} else {
debug!("skipping user rules");
}
@ -1254,13 +1220,15 @@ impl Stylist {
// See nsStyleSet::FileRules().
if !cut_off_inheritance {
// Step 3c: Author normal rules.
map.author.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
self.quirks_mode,
flags_setter,
CascadeLevel::AuthorNormal);
if let Some(map) = self.cascade_data.author.borrow_for_pseudo(pseudo_element) {
map.get_all_matching_rules(element,
&rule_hash_target,
applicable_declarations,
context,
self.quirks_mode,
flags_setter,
CascadeLevel::AuthorNormal);
}
} else {
debug!("skipping author normal rules due to cut off inheritance");
}
@ -1592,37 +1560,132 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
}
}
/// Map that contains the CSS rules for a specific PseudoElement
/// (or lack of PseudoElement).
/// Data resulting from performing the CSS cascade.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug)]
struct PerPseudoElementSelectorMap {
struct CascadeData {
/// Rules from user agent stylesheets
user_agent: SelectorMap<Rule>,
user_agent: PerOriginCascadeData,
/// Rules from author stylesheets
author: SelectorMap<Rule>,
author: PerOriginCascadeData,
/// Rules from user stylesheets
user: SelectorMap<Rule>,
user: PerOriginCascadeData,
}
impl PerPseudoElementSelectorMap {
#[inline]
impl CascadeData {
fn new() -> Self {
PerPseudoElementSelectorMap {
user_agent: SelectorMap::new(),
author: SelectorMap::new(),
user: SelectorMap::new(),
CascadeData {
user_agent: PerOriginCascadeData::new(),
author: PerOriginCascadeData::new(),
user: PerOriginCascadeData::new(),
}
}
#[inline]
fn borrow_for_origin(&mut self, origin: &Origin) -> &mut SelectorMap<Rule> {
fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginCascadeData {
match *origin {
Origin::UserAgent => &mut self.user_agent,
Origin::Author => &mut self.author,
Origin::User => &mut self.user,
}
}
fn clear(&mut self) {
self.user_agent.clear();
self.author.clear();
self.user.clear();
}
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
self.iter_origins().any(|d| d.has_rules_for_pseudo(pseudo))
}
fn iter_origins(&self) -> CascadeDataIter {
CascadeDataIter {
cascade_data: &self,
cur: 0,
}
}
}
struct CascadeDataIter<'a> {
cascade_data: &'a CascadeData,
cur: usize,
}
impl<'a> Iterator for CascadeDataIter<'a> {
type Item = &'a PerOriginCascadeData;
fn next(&mut self) -> Option<&'a PerOriginCascadeData> {
let result = match self.cur {
0 => &self.cascade_data.user_agent,
1 => &self.cascade_data.author,
2 => &self.cascade_data.user,
_ => return None,
};
self.cur += 1;
Some(result)
}
}
/// Data resulting from performing the CSS cascade that is specific to a given
/// origin.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug)]
struct PerOriginCascadeData {
/// Rules from stylesheets at this `CascadeData`'s origin.
element_map: SelectorMap<Rule>,
/// Rules from stylesheets at this `CascadeData`'s origin that correspond
/// to a given pseudo-element.
pseudos_map: FnvHashMap<PseudoElement, SelectorMap<Rule>>,
}
impl PerOriginCascadeData {
fn new() -> Self {
let mut data = PerOriginCascadeData {
element_map: SelectorMap::new(),
pseudos_map: Default::default(),
};
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
data.pseudos_map.insert(pseudo, SelectorMap::new());
});
data
}
#[inline]
fn borrow_for_pseudo(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
match pseudo {
Some(pseudo) => self.pseudos_map.get(&pseudo.canonical()),
None => Some(&self.element_map),
}
}
#[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.element_map = SelectorMap::new();
self.pseudos_map = Default::default();
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
self.pseudos_map.insert(pseudo, SelectorMap::new());
});
}
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
// FIXME(emilio): We should probably make the pseudos map be an
// enumerated array.
self.pseudos_map.contains_key(pseudo)
}
}
/// A rule, that wraps a style rule, but represents a single selector of the