зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
289b4ecd71
Коммит
bbe6006497
|
@ -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 document’s 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче