--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -76,20 +76,16 @@ pub struct Stylist {
/// If true, the quirks-mode stylesheet is applied.
#[cfg_attr(feature = "servo", ignore_heap_size_of = "defined in selectors")]
quirks_mode: QuirksMode,
/// If true, the device has changed, and the stylist needs to be updated.
is_device_dirty: bool,
- /// If true, the stylist is in a cleared state (e.g. just-constructed, or
- /// had clear() called on it with no following rebuild()).
- is_cleared: bool,
-
/// 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: PerOrigin<CascadeData>,
/// The rule tree, that stores the results of selector matching.
rule_tree: RuleTree,
@@ -130,17 +126,16 @@ impl Stylist {
/// If more members are added here, think about whether they should
/// be reset in clear().
#[inline]
pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
Stylist {
viewport_constraints: None,
device: device,
is_device_dirty: true,
- is_cleared: true,
quirks_mode: quirks_mode,
cascade_data: Default::default(),
precomputed_pseudo_element_decls: PerPseudoElementMap::default(),
rule_tree: RuleTree::new(),
num_rebuilds: 0,
}
@@ -189,37 +184,36 @@ impl Stylist {
/// Clear the stylist's state, effectively resetting it to more or less
/// the state Stylist::new creates.
///
/// We preserve the state of the following members:
/// device: Someone might have set this on us.
/// quirks_mode: Again, someone might have set this on us.
/// num_rebuilds: clear() followed by rebuild() should just increment this
+ /// rule_tree: So we can re-use rule nodes across rebuilds.
///
/// We don't just use struct update syntax with Stylist::new(self.device)
/// beause for some of our members we can clear them instead of creating new
/// objects. This does cause unfortunate code duplication with
/// Stylist::new.
pub fn clear(&mut self) {
- if self.is_cleared {
- return
- }
-
- self.is_cleared = true;
-
- self.viewport_constraints = None;
- // preserve current device
- self.is_device_dirty = true;
- // preserve current quirks_mode value
self.cascade_data.clear();
self.precomputed_pseudo_element_decls.clear();
- // We want to keep rule_tree around across stylist rebuilds.
- // preserve num_rebuilds value, since it should stay across
- // clear()/rebuild() cycles.
+ self.viewport_constraints = None;
+
+ // XXX(heycam) Why do this, if we are preserving the Device?
+ self.is_device_dirty = true;
+ }
+
+ /// Returns whether any origin's `CascadeData` has been cleared.
+ fn any_origin_cleared(&self) -> bool {
+ self.cascade_data
+ .iter_origins()
+ .any(|(d, _)| d.is_cleared)
}
/// rebuild the stylist for the given document stylesheets, and optionally
/// with a set of user agent stylesheets.
///
/// This method resets all the style data each time the stylesheets change
/// (which is indicated by the `stylesheets_changed` parameter), or the
/// device is dirty, which means we need to re-evaluate media queries.
@@ -231,19 +225,21 @@ impl Stylist {
stylesheets_changed: bool,
author_style_disabled: bool,
extra_data: &mut PerOrigin<ExtraStyleData>
) -> bool
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
- debug_assert!(!self.is_cleared || self.is_device_dirty);
+ debug_assert!(!self.any_origin_cleared() || self.is_device_dirty);
- self.is_cleared = false;
+ for (data, _) in self.cascade_data.iter_mut_origins() {
+ data.is_cleared = false;
+ }
if !(self.is_device_dirty || stylesheets_changed) {
return false;
}
self.num_rebuilds += 1;
self.viewport_constraints = None;
@@ -311,17 +307,17 @@ impl Stylist {
stylesheets_changed: bool,
author_style_disabled: bool,
extra_data: &mut PerOrigin<ExtraStyleData>
) -> bool
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
- debug_assert!(!self.is_cleared || self.is_device_dirty);
+ debug_assert!(!self.any_origin_cleared() || self.is_device_dirty);
// We have to do a dirtiness check before clearing, because if
// we're not actually dirty we need to no-op here.
if !(self.is_device_dirty || stylesheets_changed) {
return false;
}
self.clear();
self.rebuild(doc_stylesheets, guards, ua_stylesheets, stylesheets_changed,
@@ -473,17 +469,17 @@ impl Stylist {
}
}
/// Returns whether the given attribute might appear in an attribute
/// selector of some rule in the stylist.
pub fn might_have_attribute_dependency(&self,
local_name: &LocalName)
-> bool {
- if self.is_cleared || self.is_device_dirty {
+ if self.any_origin_cleared() || self.is_device_dirty {
// We can't tell what attributes are in our style rules until
// we rebuild.
true
} else if *local_name == local_name!("style") {
self.cascade_data
.iter_origins()
.any(|(d, _)| d.style_attribute_dependency)
} else {
@@ -494,19 +490,19 @@ impl Stylist {
.might_contain_hash(local_name.get_hash())
})
}
}
/// Returns whether the given ElementState bit might be relied upon by a
/// selector of some rule in the stylist.
pub fn might_have_state_dependency(&self, state: ElementState) -> bool {
- if self.is_cleared || self.is_device_dirty {
- // If self.is_cleared is true, we can't tell what states our style
- // rules rely on until we rebuild.
+ if self.any_origin_cleared() || self.is_device_dirty {
+ // We can't tell what states our style rules rely on until
+ // we rebuild.
true
} else {
self.has_state_dependency(state)
}
}
/// Returns whether the given ElementState bit is relied upon by a selector
/// of some rule in the stylist.
@@ -1623,16 +1619,21 @@ struct CascadeData {
/// style rule appears in a stylesheet, needed to sort them by source order.
rules_source_order: u32,
/// The total number of selectors.
num_selectors: usize,
/// The total number of declarations.
num_declarations: usize,
+
+ /// If true, the `CascadeData` is in a cleared state (e.g. just-constructed,
+ /// or had `clear()` called on it with no following `rebuild()` on the
+ /// `Stylist`).
+ is_cleared: bool,
}
impl CascadeData {
fn new() -> Self {
Self {
element_map: SelectorMap::new(),
pseudos_map: PerPseudoElementMap::default(),
animations: Default::default(),
@@ -1641,16 +1642,17 @@ impl CascadeData {
style_attribute_dependency: false,
state_dependencies: ElementState::empty(),
mapped_ids: NonCountingBloomFilter::new(),
selectors_for_cache_revalidation: SelectorMap::new(),
effective_media_query_results: EffectiveMediaQueryResults::new(),
rules_source_order: 0,
num_selectors: 0,
num_declarations: 0,
+ is_cleared: true,
}
}
#[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),
@@ -1659,29 +1661,34 @@ impl CascadeData {
fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool {
self.pseudos_map.get(pseudo).is_some()
}
}
impl PerOriginClear for CascadeData {
fn clear(&mut self) {
+ if self.is_cleared {
+ return;
+ }
+
self.element_map = SelectorMap::new();
self.pseudos_map = Default::default();
self.animations = Default::default();
self.invalidation_map.clear();
self.attribute_dependencies.clear();
self.style_attribute_dependency = false;
self.state_dependencies = ElementState::empty();
self.mapped_ids.clear();
self.selectors_for_cache_revalidation = SelectorMap::new();
self.effective_media_query_results.clear();
self.rules_source_order = 0;
self.num_selectors = 0;
self.num_declarations = 0;
+ self.is_cleared = true;
}
}
impl Default for CascadeData {
fn default() -> Self {
CascadeData::new()
}
}