diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index 6f41be45fe7..c170e8d6078 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 selector_parser::SnapshotMap; use shared_lock::SharedRwLockReadGuard; -use std::slice; +use std::{mem, slice}; use stylesheets::{Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument}; /// Entry for a StylesheetSet. @@ -125,13 +125,11 @@ impl Default for DataValidity { } /// A struct to iterate over the different stylesheets to be flushed. -pub struct StylesheetFlusher<'a, S> +pub struct DocumentStylesheetFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { - origins_dirty: OriginSet, collections: &'a mut PerOrigin>, - origin_data_validity: PerOrigin, had_invalidations: bool, } @@ -151,82 +149,59 @@ impl SheetRebuildKind { } } -impl<'a, S> StylesheetFlusher<'a, S> +impl<'a, S> DocumentStylesheetFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { - /// The data validity for a given origin. - pub fn data_validity(&self, origin: Origin) -> DataValidity { - *self.origin_data_validity.borrow_for_origin(&origin) + /// Returns a flusher for `origin`. + pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher { + self.collections.borrow_mut_for_origin(&origin).flush() } - /// Whether the origin data is dirty in any way. - pub fn origin_dirty(&self, origin: Origin) -> bool { - self.origins_dirty.contains(origin.into()) - } - - /// Returns an iterator over the stylesheets of a given origin, assuming all - /// of them will be flushed. - pub fn manual_origin_sheets<'b>(&'b mut self, origin: Origin) -> StylesheetCollectionIterator<'b, S> - where - 'a: 'b - { - debug_assert_eq!(origin, Origin::UserAgent); - - // We could iterate over `origin_sheets(origin)` to ensure state is - // consistent (that the `committed` member of the Entry is reset to - // `true`). - // - // In practice it doesn't matter for correctness given our use of it - // (that this is UA only). - // - // FIXME(emilio): I think it does matter and that we effectively don't - // support removing UA sheets since #19927... - self.collections.borrow_for_origin(&origin).iter() - } - - /// Returns a flusher for the dirty origin `origin`. - pub fn origin_sheets<'b>(&'b mut self, origin: Origin) -> PerOriginFlusher<'b, S> - where - 'a: 'b - { - let validity = self.data_validity(origin); - let origin_dirty = self.origins_dirty.contains(origin.into()); - - debug_assert!( - origin_dirty || validity == DataValidity::Valid, - "origin_data_validity should be a subset of origins_dirty!" - ); - - PerOriginFlusher { - iter: self.collections.borrow_mut_for_origin(&origin).entries.iter_mut(), - validity, - } - } - - /// Returns whether running the whole flushing process would be a no-op. - pub fn nothing_to_do(&self) -> bool { - self.origins_dirty.is_empty() + /// Returns the list of stylesheets for `origin`. + /// + /// Only used for UA sheets. + pub fn origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator { + self.collections.borrow_mut_for_origin(&origin).iter() } /// Returns whether any DOM invalidations were processed as a result of the /// stylesheet flush. + #[inline] pub fn had_invalidations(&self) -> bool { self.had_invalidations } } -/// A flusher struct for a given origin, that takes care of returning the +/// A flusher struct for a given collection, that takes care of returning the /// appropriate stylesheets that need work. -pub struct PerOriginFlusher<'a, S> +pub struct SheetCollectionFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static { iter: slice::IterMut<'a, StylesheetSetEntry>, validity: DataValidity, + dirty: bool, } -impl<'a, S> Iterator for PerOriginFlusher<'a, S> +impl<'a, S> SheetCollectionFlusher<'a, S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + /// Whether the collection was originally dirty. + #[inline] + pub fn dirty(&self) -> bool { + self.dirty + } + + /// What the state of the sheet data is. + #[inline] + pub fn data_validity(&self) -> DataValidity { + self.validity + } +} + +impl<'a, S> Iterator for SheetCollectionFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { @@ -379,6 +354,17 @@ where fn iter(&self) -> StylesheetCollectionIterator { StylesheetCollectionIterator(self.entries.iter()) } + + fn flush(&mut self) -> SheetCollectionFlusher { + let dirty = mem::replace(&mut self.dirty, false); + let validity = mem::replace(&mut self.data_validity, DataValidity::Valid); + + SheetCollectionFlusher { + iter: self.entries.iter_mut(), + dirty, + validity, + } + } } /// The set of stylesheets effective for a given document. @@ -420,7 +406,7 @@ macro_rules! sheet_set_methods { &mut self, device: Option<&Device>, sheet: S, - guard: &SharedRwLockReadGuard + guard: &SharedRwLockReadGuard, ) { debug!(concat!($set_name, "::append_stylesheet")); self.collect_invalidations_for(device, &sheet, guard); @@ -433,7 +419,7 @@ macro_rules! sheet_set_methods { &mut self, device: Option<&Device>, sheet: S, - guard: &SharedRwLockReadGuard + guard: &SharedRwLockReadGuard, ) { debug!(concat!($set_name, "::prepend_stylesheet")); self.collect_invalidations_for(device, &sheet, guard); @@ -477,7 +463,7 @@ impl DocumentStylesheetSet where S: StylesheetInDocument + PartialEq + 'static, { - /// Create a new empty StylesheetSet. + /// Create a new empty DocumentStylesheetSet. pub fn new() -> Self { Self { collections: Default::default(), @@ -512,58 +498,41 @@ where } /// Flush the current set, unmarking it as dirty, and returns a - /// `StylesheetFlusher` in order to rebuild the stylist. - pub fn flush<'a, E>( - &'a mut self, + /// `DocumentStylesheetFlusher` in order to rebuild the stylist. + pub fn flush( + &mut self, document_element: Option, snapshots: Option<&SnapshotMap>, - ) -> StylesheetFlusher<'a, S> + ) -> DocumentStylesheetFlusher where E: TElement, { - use std::mem; - debug!("DocumentStylesheetSet::flush"); let had_invalidations = self.invalidations.flush(document_element, snapshots); - let mut origins_dirty = OriginSet::empty(); - let mut origin_data_validity = PerOrigin::::default(); - for (collection, origin) in self.collections.iter_mut_origins() { - let was_dirty = mem::replace(&mut collection.dirty, false); - if !was_dirty { - debug_assert_eq!(collection.data_validity, DataValidity::Valid); - continue; - } - - origins_dirty |= origin; - *origin_data_validity.borrow_mut_for_origin(&origin) = - mem::replace(&mut collection.data_validity, DataValidity::Valid); - } - - StylesheetFlusher { + DocumentStylesheetFlusher { collections: &mut self.collections, had_invalidations, - origins_dirty, - origin_data_validity, } } /// Flush stylesheets, but without running any of the invalidation passes. #[cfg(feature = "servo")] pub fn flush_without_invalidation(&mut self) -> OriginSet { - use std::mem; - debug!("DocumentStylesheetSet::flush_without_invalidation"); + let mut origins = OriginSet::empty(); self.invalidations.clear(); + for (collection, origin) in self.collections.iter_mut_origins() { - collection.dirty = false; - // NOTE(emilio): I think this should also poke at the data validity - // and such, but it doesn't really matter given we don't use that - // collection for style resolution anyway. + if collection.flush().dirty() { + origins |= origin; + } } + + origins } /// Return an iterator over the flattened view of all the stylesheets. @@ -599,6 +568,17 @@ where invalidations: StylesheetInvalidationSet, } +/// A struct to flush an author style sheet collection. +pub struct AuthorStylesheetFlusher<'a, S> +where + S: StylesheetInDocument + PartialEq + 'static, +{ + /// The actual flusher for the collection. + pub sheets: SheetCollectionFlusher<'a, S>, + /// Whether any sheet invalidation matched. + pub had_invalidations: bool, +} + impl AuthorStylesheetSet where S: StylesheetInDocument + PartialEq + 'static, @@ -612,4 +592,23 @@ where } sheet_set_methods!("AuthorStylesheetSet"); + + /// Flush the stylesheets for this author set. + /// + /// `host` is the root of the affected subtree, like the shadow host, for + /// example. + pub fn flush( + &mut self, + host: Option, + snapshots: Option<&SnapshotMap>, + ) -> AuthorStylesheetFlusher + where + E: TElement, + { + let had_invalidations = self.invalidations.flush(host, snapshots); + AuthorStylesheetFlusher { + sheets: self.collection.flush(), + had_invalidations, + } + } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index f656f77476b..029adf11fb9 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -41,7 +41,8 @@ use smallvec::SmallVec; use std::ops; use std::sync::Mutex; use style_traits::viewport::ViewportConstraints; -use stylesheet_set::{DataValidity, SheetRebuildKind, DocumentStylesheetSet, StylesheetFlusher}; +use stylesheet_set::{DataValidity, SheetRebuildKind, DocumentStylesheetSet}; +use stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher}; #[cfg(feature = "gecko")] use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule}; use stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter}; @@ -227,46 +228,6 @@ impl DocumentCascadeData { } } - fn rebuild_origin<'a, S>( - device: &Device, - quirks_mode: QuirksMode, - flusher: &mut StylesheetFlusher<'a, S>, - guards: &StylesheetGuards, - origin: Origin, - cascade_data: &mut CascadeData, - ) -> Result<(), FailedAllocationError> - where - S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, - { - debug_assert_ne!(origin, Origin::UserAgent); - - if !flusher.origin_dirty(origin) { - return Ok(()); - } - - let validity = flusher.data_validity(origin); - - match validity { - DataValidity::Valid => {}, - DataValidity::CascadeInvalid => cascade_data.clear_cascade_data(), - DataValidity::FullyInvalid => cascade_data.clear(), - } - - let guard = guards.for_origin(origin); - for (stylesheet, rebuild_kind) in flusher.origin_sheets(origin) { - cascade_data.add_stylesheet( - device, - quirks_mode, - stylesheet, - guard, - rebuild_kind, - /* precomputed_pseudo_element_decls = */ None, - )?; - } - - Ok(()) - } - /// Rebuild the cascade data for the given document stylesheets, and /// optionally with a set of user agent stylesheets. Returns Err(..) /// to signify OOM. @@ -274,21 +235,17 @@ impl DocumentCascadeData { &mut self, device: &Device, quirks_mode: QuirksMode, - mut flusher: StylesheetFlusher<'a, S>, + mut flusher: DocumentStylesheetFlusher<'a, S>, guards: &StylesheetGuards, ) -> Result<(), FailedAllocationError> where S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, { - debug_assert!(!flusher.nothing_to_do()); - // First do UA sheets. { - if flusher.origin_dirty(Origin::UserAgent) { + if flusher.flush_origin(Origin::UserAgent).dirty() { let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); - let origin_sheets = - flusher.manual_origin_sheets(Origin::UserAgent); - + let origin_sheets = flusher.origin_sheets(Origin::UserAgent); let ua_cascade_data = ua_cache.lookup( origin_sheets, device, @@ -302,23 +259,19 @@ impl DocumentCascadeData { } // Now do the user sheets. - Self::rebuild_origin( + self.user.rebuild( device, quirks_mode, - &mut flusher, - guards, - Origin::User, - &mut self.user, + flusher.flush_origin(Origin::User), + guards.ua_or_user, )?; // And now the author sheets. - Self::rebuild_origin( + self.author.rebuild( device, quirks_mode, - &mut flusher, - guards, - Origin::Author, - &mut self.author, + flusher.flush_origin(Origin::Author), + guards.author, )?; Ok(()) @@ -2048,6 +2001,45 @@ impl CascadeData { } } + /// Rebuild the cascade data from a given SheetCollection, incrementally if + /// possible. + fn rebuild<'a, S>( + &mut self, + device: &Device, + quirks_mode: QuirksMode, + collection: SheetCollectionFlusher, + guard: &SharedRwLockReadGuard, + ) -> Result<(), FailedAllocationError> + where + S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, + { + if !collection.dirty() { + return Ok(()); + } + + let validity = collection.data_validity(); + + match validity { + DataValidity::Valid => {}, + DataValidity::CascadeInvalid => self.clear_cascade_data(), + DataValidity::FullyInvalid => self.clear(), + } + + for (stylesheet, rebuild_kind) in collection { + self.add_stylesheet( + device, + quirks_mode, + stylesheet, + guard, + rebuild_kind, + /* precomputed_pseudo_element_decls = */ None, + )?; + } + + Ok(()) + } + + /// Returns the invalidation map. pub fn invalidation_map(&self) -> &InvalidationMap { &self.invalidation_map