aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/stylesheet_set.rs
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2017-09-13 15:48:34 +0200
committerEmilio Cobos Álvarez <emilio@crisal.io>2017-09-13 16:01:35 +0200
commit02d1264047c9aee63ae93c5624c591c42394690c (patch)
treeebb632a3e785c24ee913805a4ef292e6fe061a43 /components/style/stylesheet_set.rs
parenta373ed3f9def41f9b5cc4f2fd97bcf4eeb893b00 (diff)
downloadservo-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.rs284
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);
}
}
}