style: Split style sheet invalidations per-origin. draft
authorCameron McCormack <cam@mcc.id.au>
Sat, 12 Aug 2017 15:34:38 +0800
changeset 645502 54c50d92422855bf9d16156f3e82b4daf3a9b8bf
parent 645501 fb7152a4cffafd5487d341310d90554eba6dc0c6
child 645503 a836c44f16e1943798fb004b574cf9f50dc31809
push id73769
push userbmo:cam@mcc.id.au
push dateSun, 13 Aug 2017 04:04:30 +0000
milestone57.0a1
style: Split style sheet invalidations per-origin. MozReview-Commit-ID: 4I3U02OsJLS
servo/components/style/stylesheet_set.rs
--- a/servo/components/style/stylesheet_set.rs
+++ b/servo/components/style/stylesheet_set.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A centralized set of stylesheets for a document.
 
 use dom::TElement;
 use invalidation::stylesheets::StylesheetInvalidationSet;
 use shared_lock::SharedRwLockReadGuard;
 use std::slice;
-use stylesheets::StylesheetInDocument;
+use stylesheets::{PerOrigin, StylesheetInDocument};
 use stylist::Stylist;
 
 /// Entry for a StylesheetSet. We don't bother creating a constructor, because
 /// there's no sensible defaults for the member variables.
 pub struct StylesheetSetEntry<S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
@@ -44,165 +44,178 @@ where
 {
     /// The actual list of all the stylesheets that apply to the given document,
     /// each stylesheet associated with a unique ID.
     ///
     /// This is only a list of top-level stylesheets, and as such it doesn't
     /// include recursive `@import` rules.
     entries: Vec<StylesheetSetEntry<S>>,
 
-    /// Whether the entries list above has changed since the last restyle.
-    dirty: bool,
+    /// Per-origin stylesheet invalidation data.
+    invalidation_data: PerOrigin<InvalidationData>,
 
     /// Has author style been disabled?
     author_style_disabled: bool,
-
-    /// The style invalidations that we still haven't processed.
-    invalidations: StylesheetInvalidationSet,
 }
 
 impl<S> StylesheetSet<S>
 where
     S: StylesheetInDocument + PartialEq + 'static,
 {
     /// Create a new empty StylesheetSet.
     pub fn new() -> Self {
         StylesheetSet {
             entries: vec![],
-            dirty: false,
+            invalidation_data: Default::default(),
             author_style_disabled: false,
-            invalidations: StylesheetInvalidationSet::new(),
         }
     }
 
     /// Returns whether author styles have been disabled for the current
     /// stylesheet set.
     pub fn author_style_disabled(&self) -> bool {
         self.author_style_disabled
     }
 
     fn remove_stylesheet_if_present(&mut self, sheet: &S) {
         self.entries.retain(|entry| entry.sheet != *sheet);
     }
 
+    fn collect_invalidations_for(
+        &mut self,
+        stylist: &Stylist,
+        sheet: &S,
+        guard: &SharedRwLockReadGuard,
+    ) {
+        let origin = sheet.contents(guard).origin;
+        let data = self.invalidation_data.borrow_mut_for_origin(&origin);
+        data.invalidations.collect_invalidations_for(stylist, sheet, guard);
+        data.dirty = true;
+    }
+
     /// Appends a new stylesheet to the current set.
     pub fn append_stylesheet(
         &mut self,
         stylist: &Stylist,
         sheet: S,
         guard: &SharedRwLockReadGuard
     ) {
         debug!("StylesheetSet::append_stylesheet");
         self.remove_stylesheet_if_present(&sheet);
-        self.invalidations.collect_invalidations_for(
-            stylist,
-            &sheet,
-            guard
-        );
-        self.dirty = true;
+        self.collect_invalidations_for(stylist, &sheet, guard);
         self.entries.push(StylesheetSetEntry { sheet });
     }
 
     /// Prepend a new stylesheet to the current set.
     pub fn prepend_stylesheet(
         &mut self,
         stylist: &Stylist,
         sheet: S,
         guard: &SharedRwLockReadGuard
     ) {
         debug!("StylesheetSet::prepend_stylesheet");
         self.remove_stylesheet_if_present(&sheet);
-        self.invalidations.collect_invalidations_for(
-            stylist,
-            &sheet,
-            guard
-        );
+        self.collect_invalidations_for(stylist, &sheet, guard);
         self.entries.insert(0, StylesheetSetEntry { sheet });
-        self.dirty = true;
     }
 
     /// Insert a given stylesheet before another stylesheet in the document.
     pub fn insert_stylesheet_before(
         &mut self,
         stylist: &Stylist,
         sheet: S,
         before_sheet: S,
         guard: &SharedRwLockReadGuard
     ) {
         debug!("StylesheetSet::insert_stylesheet_before");
         self.remove_stylesheet_if_present(&sheet);
         let index = self.entries.iter().position(|entry| {
             entry.sheet == before_sheet
         }).expect("`before_sheet` stylesheet not found");
-        self.invalidations.collect_invalidations_for(
-            stylist,
-            &sheet,
-            guard
-        );
+        self.collect_invalidations_for(stylist, &sheet, guard);
         self.entries.insert(index, StylesheetSetEntry { sheet });
-        self.dirty = true;
     }
 
     /// Remove a given stylesheet from the set.
     pub fn remove_stylesheet(
         &mut self,
         stylist: &Stylist,
         sheet: S,
         guard: &SharedRwLockReadGuard,
     ) {
         debug!("StylesheetSet::remove_stylesheet");
         self.remove_stylesheet_if_present(&sheet);
-        self.dirty = true;
-        self.invalidations.collect_invalidations_for(
-            stylist,
-            &sheet,
-            guard
-        );
+        self.collect_invalidations_for(stylist, &sheet, guard);
     }
 
     /// Notes that the author style has been disabled for this document.
     pub fn set_author_style_disabled(&mut self, disabled: bool) {
         debug!("StylesheetSet::set_author_style_disabled");
         if self.author_style_disabled == disabled {
             return;
         }
         self.author_style_disabled = disabled;
-        self.dirty = true;
-        self.invalidations.invalidate_fully();
+        self.invalidation_data.author.invalidations.invalidate_fully();
+        self.invalidation_data.author.dirty = true;
     }
 
     /// Returns whether the given set has changed from the last flush.
     pub fn has_changed(&self) -> bool {
-        self.dirty
+        self.invalidation_data
+            .iter_origins()
+            .any(|(d, _)| d.dirty)
     }
 
     /// Flush the current set, unmarking it as dirty, and returns an iterator
     /// over the new stylesheet list.
     pub fn flush<E>(
         &mut self,
         document_element: Option<E>
     ) -> StylesheetIterator<S>
     where
         E: TElement,
     {
         debug!("StylesheetSet::flush");
-        debug_assert!(self.dirty);
+        debug_assert!(self.has_changed());
 
-        self.dirty = false;
-        self.invalidations.flush(document_element);
+        for data in self.invalidation_data.iter_mut_origins() {
+            data.0.invalidations.flush(document_element);
+            data.0.dirty = false;
+        }
 
         self.iter()
     }
 
     /// Returns an iterator over the current list of stylesheets.
     pub fn iter(&self) -> StylesheetIterator<S> {
         StylesheetIterator(self.entries.iter())
     }
 
     /// Mark the stylesheets as dirty, because something external may have
     /// invalidated it.
     ///
     /// FIXME(emilio): Make this more granular.
     pub fn force_dirty(&mut self) {
-        self.dirty = true;
-        self.invalidations.invalidate_fully();
+        for data in self.invalidation_data.iter_mut_origins() {
+            data.0.invalidations.invalidate_fully();
+            data.0.dirty = true;
+        }
     }
 }
+
+struct InvalidationData {
+    /// The stylesheet invalidations for this origin that we still haven't
+    /// processed.
+    invalidations: StylesheetInvalidationSet,
+
+    /// Whether the sheets for this origin in the `StylesheetSet`'s entry list
+    /// has changed since the last restyle.
+    dirty: bool,
+}
+
+impl Default for InvalidationData {
+    fn default() -> Self {
+        InvalidationData {
+            invalidations: StylesheetInvalidationSet::new(),
+            dirty: false,
+        }
+    }
+}