diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2017-09-13 15:48:34 +0200 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2017-09-13 16:01:35 +0200 |
commit | 02d1264047c9aee63ae93c5624c591c42394690c (patch) | |
tree | ebb632a3e785c24ee913805a4ef292e6fe061a43 /components/style/stylesheet_set.rs | |
parent | a373ed3f9def41f9b5cc4f2fd97bcf4eeb893b00 (diff) | |
download | servo-02d1264047c9aee63ae93c5624c591c42394690c.tar.gz servo-02d1264047c9aee63ae93c5624c591c42394690c.zip |
style: Store stylesheets per origin.
This will allow us to cache UA sheets across documents.
MozReview-Commit-ID: 5rYIKTmC6iw
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
Diffstat (limited to 'components/style/stylesheet_set.rs')
-rw-r--r-- | components/style/stylesheet_set.rs | 284 |
1 files changed, 202 insertions, 82 deletions
diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index 01e8bedc133..a05227868a8 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -9,7 +9,7 @@ use invalidation::stylesheets::StylesheetInvalidationSet; use media_queries::Device; use shared_lock::SharedRwLockReadGuard; use std::slice; -use stylesheets::{Origin, OriginSet, PerOrigin, StylesheetInDocument}; +use stylesheets::{Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument}; /// Entry for a StylesheetSet. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -32,11 +32,11 @@ where /// A iterator over the stylesheets of a list of entries in the StylesheetSet. #[derive(Clone)] -pub struct StylesheetIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>) +pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>) where S: StylesheetInDocument + PartialEq + 'static; -impl<'a, S> Iterator for StylesheetIterator<'a, S> +impl<'a, S> Iterator for StylesheetCollectionIterator<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { @@ -47,6 +47,44 @@ where } } +/// An iterator over the flattened view of the stylesheet collections. +#[derive(Clone)] +pub struct StylesheetIterator<'a, S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + origins: OriginSetIterator, + collections: &'a PerOrigin<SheetCollection<S>>, + current: Option<StylesheetCollectionIterator<'a, S>>, +} + +impl<'a, S> Iterator for StylesheetIterator<'a, S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + type Item = &'a S; + + fn next(&mut self) -> Option<Self::Item> { + loop { + if self.current.is_none() { + let next_origin = match self.origins.next() { + Some(o) => o, + None => return None, + }; + + self.current = + Some(self.collections.borrow_for_origin(&next_origin).iter()); + } + + if let Some(s) = self.current.as_mut().unwrap().next() { + return Some(s) + } + + self.current = None; + } + } +} + /// The validity of the data in a given cascade origin. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -75,9 +113,10 @@ where 'b: 'a, S: StylesheetInDocument + PartialEq + 'static, { - iter: slice::IterMut<'a, StylesheetSetEntry<S>>, guard: &'a SharedRwLockReadGuard<'b>, - origins_dirty: OriginSet, + origins_dirty: OriginSetIterator, + collections: *mut PerOrigin<SheetCollection<S>>, + current: Option<slice::IterMut<'a, StylesheetSetEntry<S>>>, origin_data_validity: PerOrigin<OriginValidity>, author_style_disabled: bool, had_invalidations: bool, @@ -110,7 +149,7 @@ where /// Returns whether running the whole flushing process would be a no-op. pub fn nothing_to_do(&self) -> bool { - self.origins_dirty.is_empty() + self.origins_dirty.clone().next().is_none() } /// Returns whether any DOM invalidations were processed as a result of the @@ -128,7 +167,7 @@ where { fn drop(&mut self) { debug_assert!( - self.iter.next().is_none(), + self.origins_dirty.next().is_none(), "You're supposed to fully consume the flusher" ); } @@ -145,9 +184,30 @@ where use std::mem; loop { - let potential_sheet = match self.iter.next() { - None => return None, + if self.current.is_none() { + let next_origin = match self.origins_dirty.next() { + Some(o) => o, + None => return None, + }; + + // Should've been cleared already. + debug_assert_eq!( + unsafe { &*self.collections } + .borrow_for_origin(&next_origin) + .data_validity, + OriginValidity::Valid + ); + + self.current = + Some(unsafe { &mut *self.collections }.borrow_mut_for_origin(&next_origin).entries.iter_mut()); + } + + let potential_sheet = match self.current.as_mut().unwrap().next() { Some(s) => s, + None => { + self.current = None; + continue; + } }; let dirty = mem::replace(&mut potential_sheet.dirty, false); @@ -158,10 +218,6 @@ where } let origin = potential_sheet.sheet.contents(self.guard).origin; - if !self.origins_dirty.contains(origin.into()) { - continue; - } - if self.author_style_disabled && matches!(origin, Origin::Author) { continue; } @@ -177,32 +233,119 @@ where } } -/// The set of stylesheets effective for a given document. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -pub struct StylesheetSet<S> +struct SheetCollection<S> where S: StylesheetInDocument + PartialEq + 'static, { - /// The actual list of all the stylesheets that apply to the given document, - /// each stylesheet associated with a unique ID. + /// The actual list of stylesheets. /// /// This is only a list of top-level stylesheets, and as such it doesn't /// include recursive `@import` rules. entries: Vec<StylesheetSetEntry<S>>, - /// The invalidations for stylesheets added or removed from this document. - invalidations: StylesheetInvalidationSet, - - /// The origins whose stylesheets have changed so far. - origins_dirty: OriginSet, - /// The validity of the data that was already there for a given origin. /// /// Note that an origin may appear on `origins_dirty`, but still have /// `OriginValidity::Valid`, if only sheets have been appended into it (in /// which case the existing data is valid, but the origin needs to be /// rebuilt). - origin_data_validity: PerOrigin<OriginValidity>, + data_validity: OriginValidity, +} + +impl<S> Default for SheetCollection<S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + fn default() -> Self { + Self { + entries: vec![], + data_validity: OriginValidity::Valid, + } + } +} + +impl<S> SheetCollection<S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + /// Returns the number of stylesheets in the set. + fn len(&self) -> usize { + self.entries.len() + } + + /// Returns the `index`th stylesheet in the set if present. + fn get(&self, index: usize) -> Option<&S> { + self.entries.get(index).map(|e| &e.sheet) + } + + fn remove(&mut self, sheet: &S) { + let old_len = self.entries.len(); + self.entries.retain(|entry| entry.sheet != *sheet); + debug_assert!(self.entries.len() != old_len, "Sheet not found?"); + // Removing sheets makes us tear down the whole cascade and invalidation + // data. + self.set_data_validity_at_least(OriginValidity::FullyInvalid); + } + + fn contains(&self, sheet: &S) -> bool { + self.entries.iter().any(|e| e.sheet == *sheet) + } + + /// Appends a given sheet into the collection. + fn append(&mut self, sheet: S) { + debug_assert!(!self.contains(&sheet)); + self.entries.push(StylesheetSetEntry::new(sheet)) + // Appending sheets doesn't alter the validity of the existing data, so + // we don't need to change `data_validity` here. + } + + fn insert_before(&mut self, sheet: S, before_sheet: &S) { + debug_assert!(!self.contains(&sheet)); + + let index = self.entries.iter().position(|entry| { + entry.sheet == *before_sheet + }).expect("`before_sheet` stylesheet not found"); + + // Inserting stylesheets somewhere but at the end changes the validity + // of the cascade data, but not the invalidation data. + self.set_data_validity_at_least(OriginValidity::CascadeInvalid); + self.entries.insert(index, StylesheetSetEntry::new(sheet)); + } + + fn set_data_validity_at_least(&mut self, validity: OriginValidity) { + use std::cmp; + self.data_validity = cmp::max(validity, self.data_validity); + } + + fn prepend(&mut self, sheet: S) { + debug_assert!(!self.contains(&sheet)); + // Inserting stylesheets somewhere but at the end changes the validity + // of the cascade data, but not the invalidation data. + self.set_data_validity_at_least(OriginValidity::CascadeInvalid); + self.entries.insert(0, StylesheetSetEntry::new(sheet)); + } + + /// Returns an iterator over the current list of stylesheets. + fn iter(&self) -> StylesheetCollectionIterator<S> { + StylesheetCollectionIterator(self.entries.iter()) + } +} + +/// The set of stylesheets effective for a given document. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct StylesheetSet<S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + /// The collections of sheets per each origin. + collections: PerOrigin<SheetCollection<S>>, + + /// The invalidations for stylesheets added or removed from this document. + invalidations: StylesheetInvalidationSet, + + /// The origins whose stylesheets have changed so far. + origins_dirty: OriginSet, /// Has author style been disabled? author_style_disabled: bool, @@ -215,22 +358,21 @@ where /// Create a new empty StylesheetSet. pub fn new() -> Self { StylesheetSet { - entries: vec![], + collections: Default::default(), invalidations: StylesheetInvalidationSet::new(), origins_dirty: OriginSet::empty(), - origin_data_validity: Default::default(), author_style_disabled: false, } } /// Returns the number of stylesheets in the set. pub fn len(&self) -> usize { - self.entries.len() + self.collections.iter_origins().fold(0, |s, (item, _)| s + item.len()) } - /// Returns the number of stylesheets in the set. - pub fn get(&self, index: usize) -> Option<&S> { - self.entries.get(index).map(|s| &s.sheet) + /// Returns the `index`th stylesheet in the set for the given origin. + pub fn get(&self, origin: Origin, index: usize) -> Option<&S> { + self.collections.borrow_for_origin(&origin).get(index) } /// Returns whether author styles have been disabled for the current @@ -239,10 +381,6 @@ where 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, device: Option<&Device>, @@ -255,24 +393,6 @@ where self.origins_dirty |= sheet.contents(guard).origin; } - fn set_data_validity_at_least( - &mut self, - origin: Origin, - validity: OriginValidity, - ) { - use std::cmp; - - debug_assert!( - self.origins_dirty.contains(origin.into()), - "data_validity should be a subset of origins_dirty" - ); - - let existing_validity = - self.origin_data_validity.borrow_mut_for_origin(&origin); - - *existing_validity = cmp::max(*existing_validity, validity); - } - /// Appends a new stylesheet to the current set. /// /// No device implies not computing invalidations. @@ -283,11 +403,9 @@ where guard: &SharedRwLockReadGuard ) { debug!("StylesheetSet::append_stylesheet"); - self.remove_stylesheet_if_present(&sheet); self.collect_invalidations_for(device, &sheet, guard); - // Appending sheets doesn't alter the validity of the existing data, so - // we don't need to change `origin_data_validity` here. - self.entries.push(StylesheetSetEntry::new(sheet)); + let origin = sheet.contents(guard).origin; + self.collections.borrow_mut_for_origin(&origin).append(sheet); } /// Prepend a new stylesheet to the current set. @@ -298,14 +416,10 @@ where guard: &SharedRwLockReadGuard ) { debug!("StylesheetSet::prepend_stylesheet"); - self.remove_stylesheet_if_present(&sheet); self.collect_invalidations_for(device, &sheet, guard); - // Inserting stylesheets somewhere but at the end changes the validity - // of the cascade data, but not the invalidation data. - self.set_data_validity_at_least(sheet.contents(guard).origin, OriginValidity::CascadeInvalid); - - self.entries.insert(0, StylesheetSetEntry::new(sheet)); + let origin = sheet.contents(guard).origin; + self.collections.borrow_mut_for_origin(&origin).prepend(sheet) } /// Insert a given stylesheet before another stylesheet in the document. @@ -317,16 +431,12 @@ where 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.collect_invalidations_for(device, &sheet, guard); - // Inserting stylesheets somewhere but at the end changes the validity - // of the cascade data, but not the invalidation data. - self.set_data_validity_at_least(sheet.contents(guard).origin, OriginValidity::CascadeInvalid); - self.entries.insert(index, StylesheetSetEntry::new(sheet)); + let origin = sheet.contents(guard).origin; + self.collections + .borrow_mut_for_origin(&origin) + .insert_before(sheet, &before_sheet) } /// Remove a given stylesheet from the set. @@ -337,13 +447,10 @@ where guard: &SharedRwLockReadGuard, ) { debug!("StylesheetSet::remove_stylesheet"); - self.remove_stylesheet_if_present(&sheet); - self.collect_invalidations_for(device, &sheet, guard); - // Removing sheets makes us tear down the whole cascade and invalidation - // data. - self.set_data_validity_at_least(sheet.contents(guard).origin, OriginValidity::FullyInvalid); + let origin = sheet.contents(guard).origin; + self.collections.borrow_mut_for_origin(&origin).remove(&sheet) } /// Notes that the author style has been disabled for this document. @@ -377,17 +484,24 @@ where debug!("StylesheetSet::flush"); let had_invalidations = self.invalidations.flush(document_element); - let origins_dirty = mem::replace(&mut self.origins_dirty, OriginSet::empty()); - let origin_data_validity = - mem::replace(&mut self.origin_data_validity, Default::default()); + let origins_dirty = + mem::replace(&mut self.origins_dirty, OriginSet::empty()).iter(); + + let mut origin_data_validity = PerOrigin::<OriginValidity>::default(); + for origin in origins_dirty.clone() { + let collection = self.collections.borrow_mut_for_origin(&origin); + *origin_data_validity.borrow_mut_for_origin(&origin) = + mem::replace(&mut collection.data_validity, OriginValidity::Valid); + } StylesheetFlusher { - iter: self.entries.iter_mut(), + collections: &mut self.collections, author_style_disabled: self.author_style_disabled, had_invalidations, origins_dirty, origin_data_validity, guard, + current: None, } } @@ -402,9 +516,13 @@ where mem::replace(&mut self.origins_dirty, OriginSet::empty()) } - /// Returns an iterator over the current list of stylesheets. + /// Return an iterator over the flattened view of all the stylesheets. pub fn iter(&self) -> StylesheetIterator<S> { - StylesheetIterator(self.entries.iter()) + StylesheetIterator { + origins: OriginSet::all().iter(), + collections: &self.collections, + current: None, + } } /// Mark the stylesheets for the specified origin as dirty, because @@ -414,7 +532,9 @@ where self.origins_dirty |= origins; for origin in origins.iter() { // We don't know what happened, assume the worse. - self.set_data_validity_at_least(origin, OriginValidity::FullyInvalid); + self.collections + .borrow_mut_for_origin(&origin) + .set_data_validity_at_least(OriginValidity::FullyInvalid); } } } |