aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/stylist.rs271
1 files changed, 167 insertions, 104 deletions
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index a488ae461e9..636f0646ce5 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -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