style: Move more stuff to the place it belongs to.

In particular, `rebuild` is now done entirely by CascadeData, which simplifies
more stuff.

The eventual final state for this is that the data structure we use to store the
XBL / Shadow DOM stuff will be something like:

  struct AuthorStyles { CascadeData, AuthorSheetCollection }

MozReview-Commit-ID: 8TExtP58L4X
This commit is contained in:
Emilio Cobos Álvarez 2018-02-09 12:21:14 +01:00
parent 7edd25890d
commit 88cc2798c4
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
2 changed files with 139 additions and 148 deletions

View file

@ -9,7 +9,7 @@ use invalidation::stylesheets::StylesheetInvalidationSet;
use media_queries::Device; use media_queries::Device;
use selector_parser::SnapshotMap; use selector_parser::SnapshotMap;
use shared_lock::SharedRwLockReadGuard; use shared_lock::SharedRwLockReadGuard;
use std::slice; use std::{mem, slice};
use stylesheets::{Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument}; use stylesheets::{Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument};
/// Entry for a StylesheetSet. /// Entry for a StylesheetSet.
@ -125,13 +125,11 @@ impl Default for DataValidity {
} }
/// A struct to iterate over the different stylesheets to be flushed. /// A struct to iterate over the different stylesheets to be flushed.
pub struct StylesheetFlusher<'a, S> pub struct DocumentStylesheetFlusher<'a, S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
origins_dirty: OriginSet,
collections: &'a mut PerOrigin<SheetCollection<S>>, collections: &'a mut PerOrigin<SheetCollection<S>>,
origin_data_validity: PerOrigin<DataValidity>,
had_invalidations: bool, had_invalidations: bool,
} }
@ -151,82 +149,59 @@ impl SheetRebuildKind {
} }
} }
impl<'a, S> StylesheetFlusher<'a, S> impl<'a, S> DocumentStylesheetFlusher<'a, S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
/// The data validity for a given origin. /// Returns a flusher for `origin`.
pub fn data_validity(&self, origin: Origin) -> DataValidity { pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<S> {
*self.origin_data_validity.borrow_for_origin(&origin) self.collections.borrow_mut_for_origin(&origin).flush()
} }
/// Whether the origin data is dirty in any way. /// Returns the list of stylesheets for `origin`.
pub fn origin_dirty(&self, origin: Origin) -> bool { ///
self.origins_dirty.contains(origin.into()) /// Only used for UA sheets.
} pub fn origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator<S> {
self.collections.borrow_mut_for_origin(&origin).iter()
/// 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 whether any DOM invalidations were processed as a result of the /// Returns whether any DOM invalidations were processed as a result of the
/// stylesheet flush. /// stylesheet flush.
#[inline]
pub fn had_invalidations(&self) -> bool { pub fn had_invalidations(&self) -> bool {
self.had_invalidations 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. /// appropriate stylesheets that need work.
pub struct PerOriginFlusher<'a, S> pub struct SheetCollectionFlusher<'a, S>
where where
S: StylesheetInDocument + PartialEq + 'static S: StylesheetInDocument + PartialEq + 'static
{ {
iter: slice::IterMut<'a, StylesheetSetEntry<S>>, iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
validity: DataValidity, 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 where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
@ -379,6 +354,17 @@ where
fn iter(&self) -> StylesheetCollectionIterator<S> { fn iter(&self) -> StylesheetCollectionIterator<S> {
StylesheetCollectionIterator(self.entries.iter()) StylesheetCollectionIterator(self.entries.iter())
} }
fn flush(&mut self) -> SheetCollectionFlusher<S> {
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. /// The set of stylesheets effective for a given document.
@ -420,7 +406,7 @@ macro_rules! sheet_set_methods {
&mut self, &mut self,
device: Option<&Device>, device: Option<&Device>,
sheet: S, sheet: S,
guard: &SharedRwLockReadGuard guard: &SharedRwLockReadGuard,
) { ) {
debug!(concat!($set_name, "::append_stylesheet")); debug!(concat!($set_name, "::append_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
@ -433,7 +419,7 @@ macro_rules! sheet_set_methods {
&mut self, &mut self,
device: Option<&Device>, device: Option<&Device>,
sheet: S, sheet: S,
guard: &SharedRwLockReadGuard guard: &SharedRwLockReadGuard,
) { ) {
debug!(concat!($set_name, "::prepend_stylesheet")); debug!(concat!($set_name, "::prepend_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
@ -477,7 +463,7 @@ impl<S> DocumentStylesheetSet<S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
/// Create a new empty StylesheetSet. /// Create a new empty DocumentStylesheetSet.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
collections: Default::default(), collections: Default::default(),
@ -512,60 +498,43 @@ where
} }
/// Flush the current set, unmarking it as dirty, and returns a /// Flush the current set, unmarking it as dirty, and returns a
/// `StylesheetFlusher` in order to rebuild the stylist. /// `DocumentStylesheetFlusher` in order to rebuild the stylist.
pub fn flush<'a, E>( pub fn flush<E>(
&'a mut self, &mut self,
document_element: Option<E>, document_element: Option<E>,
snapshots: Option<&SnapshotMap>, snapshots: Option<&SnapshotMap>,
) -> StylesheetFlusher<'a, S> ) -> DocumentStylesheetFlusher<S>
where where
E: TElement, E: TElement,
{ {
use std::mem;
debug!("DocumentStylesheetSet::flush"); debug!("DocumentStylesheetSet::flush");
let had_invalidations = let had_invalidations =
self.invalidations.flush(document_element, snapshots); self.invalidations.flush(document_element, snapshots);
let mut origins_dirty = OriginSet::empty(); DocumentStylesheetFlusher {
let mut origin_data_validity = PerOrigin::<DataValidity>::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 {
collections: &mut self.collections, collections: &mut self.collections,
had_invalidations, had_invalidations,
origins_dirty,
origin_data_validity,
} }
} }
/// Flush stylesheets, but without running any of the invalidation passes. /// Flush stylesheets, but without running any of the invalidation passes.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub fn flush_without_invalidation(&mut self) -> OriginSet { pub fn flush_without_invalidation(&mut self) -> OriginSet {
use std::mem;
debug!("DocumentStylesheetSet::flush_without_invalidation"); debug!("DocumentStylesheetSet::flush_without_invalidation");
let mut origins = OriginSet::empty();
self.invalidations.clear(); self.invalidations.clear();
for (collection, origin) in self.collections.iter_mut_origins() { for (collection, origin) in self.collections.iter_mut_origins() {
collection.dirty = false; if collection.flush().dirty() {
// NOTE(emilio): I think this should also poke at the data validity origins |= origin;
// and such, but it doesn't really matter given we don't use that
// collection for style resolution anyway.
} }
} }
origins
}
/// Return an iterator over the flattened view of all the stylesheets. /// Return an iterator over the flattened view of all the stylesheets.
pub fn iter(&self) -> StylesheetIterator<S> { pub fn iter(&self) -> StylesheetIterator<S> {
StylesheetIterator { StylesheetIterator {
@ -599,6 +568,17 @@ where
invalidations: StylesheetInvalidationSet, 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<S> AuthorStylesheetSet<S> impl<S> AuthorStylesheetSet<S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
@ -612,4 +592,23 @@ where
} }
sheet_set_methods!("AuthorStylesheetSet"); 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<E>(
&mut self,
host: Option<E>,
snapshots: Option<&SnapshotMap>,
) -> AuthorStylesheetFlusher<S>
where
E: TElement,
{
let had_invalidations = self.invalidations.flush(host, snapshots);
AuthorStylesheetFlusher {
sheets: self.collection.flush(),
had_invalidations,
}
}
} }

View file

@ -41,7 +41,8 @@ use smallvec::SmallVec;
use std::ops; use std::ops;
use std::sync::Mutex; use std::sync::Mutex;
use style_traits::viewport::ViewportConstraints; 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")] #[cfg(feature = "gecko")]
use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule}; use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
use stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter}; 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 /// Rebuild the cascade data for the given document stylesheets, and
/// optionally with a set of user agent stylesheets. Returns Err(..) /// optionally with a set of user agent stylesheets. Returns Err(..)
/// to signify OOM. /// to signify OOM.
@ -274,21 +235,17 @@ impl DocumentCascadeData {
&mut self, &mut self,
device: &Device, device: &Device,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
mut flusher: StylesheetFlusher<'a, S>, mut flusher: DocumentStylesheetFlusher<'a, S>,
guards: &StylesheetGuards, guards: &StylesheetGuards,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
{ {
debug_assert!(!flusher.nothing_to_do());
// First do UA sheets. // 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 mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
let origin_sheets = let origin_sheets = flusher.origin_sheets(Origin::UserAgent);
flusher.manual_origin_sheets(Origin::UserAgent);
let ua_cascade_data = ua_cache.lookup( let ua_cascade_data = ua_cache.lookup(
origin_sheets, origin_sheets,
device, device,
@ -302,23 +259,19 @@ impl DocumentCascadeData {
} }
// Now do the user sheets. // Now do the user sheets.
Self::rebuild_origin( self.user.rebuild(
device, device,
quirks_mode, quirks_mode,
&mut flusher, flusher.flush_origin(Origin::User),
guards, guards.ua_or_user,
Origin::User,
&mut self.user,
)?; )?;
// And now the author sheets. // And now the author sheets.
Self::rebuild_origin( self.author.rebuild(
device, device,
quirks_mode, quirks_mode,
&mut flusher, flusher.flush_origin(Origin::Author),
guards, guards.author,
Origin::Author,
&mut self.author,
)?; )?;
Ok(()) 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<S>,
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. /// Returns the invalidation map.
pub fn invalidation_map(&self) -> &InvalidationMap { pub fn invalidation_map(&self) -> &InvalidationMap {
&self.invalidation_map &self.invalidation_map