style: Add a StylesheetFlusher abstraction to enable more incremental rebuilds.

MozReview-Commit-ID: 8RBUUErm4bm
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-08-22 15:48:38 +02:00
parent 029ab24671
commit 15594dda10
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
2 changed files with 105 additions and 42 deletions

View file

@ -47,6 +47,77 @@ where
} }
} }
/// A struct to iterate over the different stylesheets to be flushed.
pub struct StylesheetFlusher<'a, 'b, S>
where
'b: 'a,
S: StylesheetInDocument + PartialEq + 'static,
{
iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
guard: &'a SharedRwLockReadGuard<'b>,
origins_dirty: OriginSet,
author_style_disabled: bool,
had_invalidations: bool,
}
/// The type of rebuild that we need to do for a given stylesheet.
pub enum SheetRebuildKind {
/// For now we only support full rebuilds, in the future we'll implement
/// partial rebuilds.
Full,
}
impl<'a, 'b, S> StylesheetFlusher<'a, 'b, S>
where
'b: 'a,
S: StylesheetInDocument + PartialEq + 'static,
{
/// The set of origins to fully rebuild, which need to be cleared
/// beforehand.
pub fn origins_to_fully_rebuild(&self) -> OriginSet {
self.origins_dirty
}
/// 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
/// stylesheet flush.
pub fn had_invalidations(&self) -> bool {
self.had_invalidations
}
}
impl<'a, 'b, S> Iterator for StylesheetFlusher<'a, 'b, S>
where
'b: 'a,
S: StylesheetInDocument + PartialEq + 'static,
{
type Item = (&'a S, SheetRebuildKind);
fn next(&mut self) -> Option<Self::Item> {
loop {
let potential_sheet = match self.iter.next() {
None => return None,
Some(s) => s,
};
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;
}
return Some((&potential_sheet.sheet, SheetRebuildKind::Full))
}
}
}
/// The set of stylesheets effective for a given document. /// The set of stylesheets effective for a given document.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StylesheetSet<S> pub struct StylesheetSet<S>
@ -189,12 +260,13 @@ where
!self.origins_dirty.is_empty() !self.origins_dirty.is_empty()
} }
/// Flush the current set, unmarking it as dirty, and returns the damaged /// Flush the current set, unmarking it as dirty, and returns a
/// origins, and whether any elements were invalidated. /// `StylesheetFlusher` in order to rebuild the stylist.
pub fn flush<E>( pub fn flush<'a, 'b, E>(
&mut self, &'a mut self,
document_element: Option<E>, document_element: Option<E>,
) -> (OriginSet, bool) guard: &'a SharedRwLockReadGuard<'b>,
) -> StylesheetFlusher<'a, 'b, S>
where where
E: TElement, E: TElement,
{ {
@ -202,10 +274,16 @@ where
debug!("StylesheetSet::flush"); debug!("StylesheetSet::flush");
let have_invalidations = self.invalidations.flush(document_element); let had_invalidations = self.invalidations.flush(document_element);
let origins = mem::replace(&mut self.origins_dirty, OriginSet::empty()); let origins_dirty = mem::replace(&mut self.origins_dirty, OriginSet::empty());
(origins, have_invalidations) StylesheetFlusher {
iter: self.entries.iter_mut(),
author_style_disabled: self.author_style_disabled,
had_invalidations,
origins_dirty,
guard,
}
} }
/// Flush stylesheets, but without running any of the invalidation passes. /// Flush stylesheets, but without running any of the invalidation passes.

View file

@ -38,7 +38,7 @@ use smallvec::VecLike;
use std::fmt::Debug; use std::fmt::Debug;
use std::ops; use std::ops;
use style_traits::viewport::ViewportConstraints; use style_traits::viewport::ViewportConstraints;
use stylesheet_set::{StylesheetSet, StylesheetIterator}; use stylesheet_set::{StylesheetSet, StylesheetIterator, StylesheetFlusher};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use stylesheets::{CounterStyleRule, FontFaceRule}; use stylesheets::{CounterStyleRule, FontFaceRule};
use stylesheets::{CssRule, StyleRule}; use stylesheets::{CssRule, StyleRule};
@ -83,22 +83,22 @@ impl DocumentCascadeData {
/// 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. /// optionally with a set of user agent stylesheets.
fn rebuild<'a, I, S>( fn rebuild<'a, 'b, S>(
&mut self, &mut self,
device: &Device, device: &Device,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
doc_stylesheets: I, flusher: StylesheetFlusher<'a, 'b, S>,
guards: &StylesheetGuards, guards: &StylesheetGuards,
ua_stylesheets: Option<&UserAgentStylesheets>, ua_stylesheets: Option<&UserAgentStylesheets>,
author_style_disabled: bool,
extra_data: &mut PerOrigin<ExtraStyleData>, extra_data: &mut PerOrigin<ExtraStyleData>,
origins_to_rebuild: OriginSet,
) )
where where
I: Iterator<Item = &'a S> + Clone, 'b: 'a,
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
{ {
debug_assert!(!origins_to_rebuild.is_empty()); debug_assert!(!flusher.nothing_to_do());
let origins_to_rebuild = flusher.origins_to_fully_rebuild();
for origin in origins_to_rebuild.iter() { for origin in origins_to_rebuild.iter() {
extra_data.borrow_mut_for_origin(&origin).clear(); extra_data.borrow_mut_for_origin(&origin).clear();
@ -146,22 +146,13 @@ impl DocumentCascadeData {
quirks_mode, quirks_mode,
&ua_stylesheets.quirks_mode_stylesheet, &ua_stylesheets.quirks_mode_stylesheet,
guards.ua_or_user, guards.ua_or_user,
extra_data extra_data,
); );
} }
} }
} }
// Only add stylesheets for origins we are updating, and only add for (stylesheet, _rebuild_kind) in flusher {
// Author level sheets if author style is not disabled.
let sheets_to_add = doc_stylesheets.filter(|s| {
let sheet_origin = s.contents(guards.author).origin;
origins_to_rebuild.contains(sheet_origin.into()) &&
(!matches!(sheet_origin, Origin::Author) || !author_style_disabled)
});
for stylesheet in sheets_to_add {
self.add_stylesheet( self.add_stylesheet(
device, device,
quirks_mode, quirks_mode,
@ -489,16 +480,6 @@ impl Stylist {
return false; return false;
} }
let author_style_disabled = self.stylesheets.author_style_disabled();
let (origins_to_rebuild, have_invalidations) =
self.stylesheets.flush(document_element);
if origins_to_rebuild.is_empty() {
return have_invalidations;
}
let doc_stylesheets = self.stylesheets.iter();
self.num_rebuilds += 1; self.num_rebuilds += 1;
// Update viewport_constraints regardless of which origins' // Update viewport_constraints regardless of which origins'
@ -517,7 +498,9 @@ impl Stylist {
// queries defined?) // queries defined?)
let cascaded_rule = ViewportRule { let cascaded_rule = ViewportRule {
declarations: viewport_rule::Cascade::from_stylesheets( declarations: viewport_rule::Cascade::from_stylesheets(
doc_stylesheets.clone(), guards.author, &self.device self.stylesheets.iter(),
guards.author,
&self.device,
).finish() ).finish()
}; };
@ -533,18 +516,20 @@ impl Stylist {
} }
} }
let flusher = self.stylesheets.flush(document_element, &guards.author);
let had_invalidations = flusher.had_invalidations();
self.cascade_data.rebuild( self.cascade_data.rebuild(
&self.device, &self.device,
self.quirks_mode, self.quirks_mode,
doc_stylesheets, flusher,
guards, guards,
ua_sheets, ua_sheets,
author_style_disabled,
extra_data, extra_data,
origins_to_rebuild,
); );
have_invalidations had_invalidations
} }
/// Insert a given stylesheet before another stylesheet in the document. /// Insert a given stylesheet before another stylesheet in the document.