style: Move cleared state into per-origin data. draft
authorCameron McCormack <cam@mcc.id.au>
Sat, 12 Aug 2017 16:19:47 +0800
changeset 645505 11512f2d40b7e2e7a2ed5496f43f4842ce9cabda
parent 645504 4a22de18573814dd70ad97ebeecceb800e37f44c
child 645506 cb1ed81739efa1579cd80e82ff0676abe227c7e6
push id73769
push userbmo:cam@mcc.id.au
push dateSun, 13 Aug 2017 04:04:30 +0000
milestone57.0a1
style: Move cleared state into per-origin data. MozReview-Commit-ID: KTQhBXh72nb
servo/components/style/stylist.rs
--- 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()
     }
 }