diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 28d05589bbf..fd41dcb14ed 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -85,6 +85,10 @@ pub struct Stylist { /// 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, + /// The current selector maps, after evaluating media /// rules against the current device. element_map: PerPseudoElementSelectorMap, @@ -163,13 +167,15 @@ impl<'a> ExtraStyleData<'a> { } impl Stylist { - /// Construct a new `Stylist`, using a given `Device`. + /// Construct a new `Stylist`, using a given `Device`. If more members are + /// added here, think about whether they should be reset in clear(). #[inline] pub fn new(device: Device) -> Self { let mut stylist = Stylist { viewport_constraints: None, device: Arc::new(device), is_device_dirty: true, + is_cleared: true, quirks_mode: QuirksMode::NoQuirks, element_map: PerPseudoElementSelectorMap::new(), @@ -219,22 +225,64 @@ impl Stylist { self.selectors_for_cache_revalidation.len() } - /// Update the stylist for the given document stylesheets, and optionally + /// 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 + /// + /// 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.element_map = PerPseudoElementSelectorMap::new(); + self.pseudos_map = Default::default(); + self.animations.clear(); // Or set to Default::default()? + self.precomputed_pseudo_element_decls = Default::default(); + self.rules_source_order = 0; + // We want to keep rule_tree around across stylist rebuilds. + self.dependencies.clear(); + self.selectors_for_cache_revalidation = SelectorMap::new(); + self.num_selectors = 0; + self.num_declarations = 0; + // preserve num_rebuilds value, since it should stay across + // clear()/rebuild() cycles. + } + + /// 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. - pub fn update<'a>(&mut self, - doc_stylesheets: &[Arc], - guards: &StylesheetGuards, - ua_stylesheets: Option<&UserAgentStylesheets>, - stylesheets_changed: bool, - author_style_disabled: bool, - extra_data: &mut ExtraStyleData<'a>) -> bool { + pub fn rebuild<'a>(&mut self, + doc_stylesheets: &[Arc], + guards: &StylesheetGuards, + ua_stylesheets: Option<&UserAgentStylesheets>, + stylesheets_changed: bool, + author_style_disabled: bool, + extra_data: &mut ExtraStyleData<'a>) -> bool { + debug_assert!(!self.is_cleared || self.is_device_dirty); + + self.is_cleared = false; + if !(self.is_device_dirty || stylesheets_changed) { return false; } + self.num_rebuilds += 1; let cascaded_rule = ViewportRule { @@ -251,21 +299,10 @@ impl Stylist { .account_for_viewport_rule(constraints); } - self.element_map = PerPseudoElementSelectorMap::new(); - self.pseudos_map = Default::default(); - self.animations = Default::default(); SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| { self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new()); }); - self.precomputed_pseudo_element_decls = Default::default(); - self.rules_source_order = 0; - self.dependencies.clear(); - self.animations.clear(); - self.selectors_for_cache_revalidation = SelectorMap::new(); - self.num_selectors = 0; - self.num_declarations = 0; - extra_data.clear_font_faces(); if let Some(ua_stylesheets) = ua_stylesheets { @@ -302,6 +339,27 @@ impl Stylist { true } + /// clear the stylist and then rebuild it. Chances are, you want to use + /// either clear() or rebuild(), with the latter done lazily, instead. + pub fn update<'a>(&mut self, + doc_stylesheets: &[Arc], + guards: &StylesheetGuards, + ua_stylesheets: Option<&UserAgentStylesheets>, + stylesheets_changed: bool, + author_style_disabled: bool, + extra_data: &mut ExtraStyleData<'a>) -> bool { + debug_assert!(!self.is_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, + author_style_disabled, extra_data) + } + fn add_stylesheet<'a>(&mut self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard, extra_data: &mut ExtraStyleData<'a>) { if stylesheet.disabled() || !stylesheet.is_effective_for_device(&self.device, guard) {