mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #18191 - emilio:incremental-rebuilds, r=SimonSapin,heycam
style: Implement finer-grained stylist rebuilds. This is on top of #18170, and aims to resolve https://bugzilla.mozilla.org/show_bug.cgi?id=1386045. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18191) <!-- Reviewable:end -->
This commit is contained in:
commit
c5cf6dc965
3 changed files with 354 additions and 158 deletions
|
@ -9,16 +9,25 @@ use invalidation::stylesheets::StylesheetInvalidationSet;
|
||||||
use media_queries::Device;
|
use media_queries::Device;
|
||||||
use shared_lock::SharedRwLockReadGuard;
|
use shared_lock::SharedRwLockReadGuard;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use stylesheets::{OriginSet, PerOrigin, StylesheetInDocument};
|
use stylesheets::{Origin, OriginSet, PerOrigin, StylesheetInDocument};
|
||||||
|
|
||||||
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
|
/// Entry for a StylesheetSet.
|
||||||
/// there's no sensible defaults for the member variables.
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct StylesheetSetEntry<S>
|
struct StylesheetSetEntry<S>
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static,
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
{
|
{
|
||||||
sheet: S,
|
sheet: S,
|
||||||
|
dirty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> StylesheetSetEntry<S>
|
||||||
|
where
|
||||||
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
|
{
|
||||||
|
fn new(sheet: S) -> Self {
|
||||||
|
Self { sheet, dirty: true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
|
/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
|
||||||
|
@ -38,6 +47,136 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The validity of the data in a given cascade origin.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum OriginValidity {
|
||||||
|
/// The origin is clean, all the data already there is valid, though we may
|
||||||
|
/// have new sheets at the end.
|
||||||
|
Valid = 0,
|
||||||
|
|
||||||
|
/// The cascade data is invalid, but not the invalidation data (which is
|
||||||
|
/// order-independent), and thus only the cascade data should be inserted.
|
||||||
|
CascadeInvalid = 1,
|
||||||
|
|
||||||
|
/// Everything needs to be rebuilt.
|
||||||
|
FullyInvalid = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OriginValidity {
|
||||||
|
fn default() -> Self {
|
||||||
|
OriginValidity::Valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
origin_data_validity: PerOrigin<OriginValidity>,
|
||||||
|
author_style_disabled: bool,
|
||||||
|
had_invalidations: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of rebuild that we need to do for a given stylesheet.
|
||||||
|
pub enum SheetRebuildKind {
|
||||||
|
/// A full rebuild, of both cascade data and invalidation data.
|
||||||
|
Full,
|
||||||
|
/// A partial rebuild, of only the cascade data.
|
||||||
|
CascadeOnly,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SheetRebuildKind {
|
||||||
|
/// Whether the stylesheet invalidation data should be rebuilt.
|
||||||
|
pub fn should_rebuild_invalidation(&self) -> bool {
|
||||||
|
matches!(*self, SheetRebuildKind::Full)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b, S> StylesheetFlusher<'a, 'b, S>
|
||||||
|
where
|
||||||
|
'b: 'a,
|
||||||
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
|
{
|
||||||
|
/// The data validity for a given origin.
|
||||||
|
pub fn origin_validity(&self, origin: Origin) -> OriginValidity {
|
||||||
|
*self.origin_data_validity.borrow_for_origin(&origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
impl<'a, 'b, S> Drop for StylesheetFlusher<'a, 'b, S>
|
||||||
|
where
|
||||||
|
'b: 'a,
|
||||||
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug_assert!(
|
||||||
|
self.iter.next().is_none(),
|
||||||
|
"You're supposed to fully consume the flusher"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let potential_sheet = match self.iter.next() {
|
||||||
|
None => return None,
|
||||||
|
Some(s) => s,
|
||||||
|
};
|
||||||
|
|
||||||
|
let dirty = mem::replace(&mut potential_sheet.dirty, false);
|
||||||
|
|
||||||
|
if dirty {
|
||||||
|
// If the sheet was dirty, we need to do a full rebuild anyway.
|
||||||
|
return Some((&potential_sheet.sheet, SheetRebuildKind::Full))
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rebuild_kind = match self.origin_validity(origin) {
|
||||||
|
OriginValidity::Valid => continue,
|
||||||
|
OriginValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
||||||
|
OriginValidity::FullyInvalid => SheetRebuildKind::Full,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some((&potential_sheet.sheet, rebuild_kind));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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>
|
||||||
|
@ -51,8 +190,19 @@ where
|
||||||
/// include recursive `@import` rules.
|
/// include recursive `@import` rules.
|
||||||
entries: Vec<StylesheetSetEntry<S>>,
|
entries: Vec<StylesheetSetEntry<S>>,
|
||||||
|
|
||||||
/// Per-origin stylesheet invalidation data.
|
/// The invalidations for stylesheets added or removed from this document.
|
||||||
invalidation_data: PerOrigin<InvalidationData>,
|
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>,
|
||||||
|
|
||||||
/// Has author style been disabled?
|
/// Has author style been disabled?
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
|
@ -66,7 +216,9 @@ where
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
StylesheetSet {
|
StylesheetSet {
|
||||||
entries: vec![],
|
entries: vec![],
|
||||||
invalidation_data: Default::default(),
|
invalidations: StylesheetInvalidationSet::new(),
|
||||||
|
origins_dirty: OriginSet::empty(),
|
||||||
|
origin_data_validity: Default::default(),
|
||||||
author_style_disabled: false,
|
author_style_disabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,12 +249,28 @@ where
|
||||||
sheet: &S,
|
sheet: &S,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
) {
|
) {
|
||||||
let origin = sheet.contents(guard).origin;
|
|
||||||
let data = self.invalidation_data.borrow_mut_for_origin(&origin);
|
|
||||||
if let Some(device) = device {
|
if let Some(device) = device {
|
||||||
data.invalidations.collect_invalidations_for(device, sheet, guard);
|
self.invalidations.collect_invalidations_for(device, sheet, guard);
|
||||||
}
|
}
|
||||||
data.dirty = true;
|
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.
|
/// Appends a new stylesheet to the current set.
|
||||||
|
@ -117,7 +285,9 @@ where
|
||||||
debug!("StylesheetSet::append_stylesheet");
|
debug!("StylesheetSet::append_stylesheet");
|
||||||
self.remove_stylesheet_if_present(&sheet);
|
self.remove_stylesheet_if_present(&sheet);
|
||||||
self.collect_invalidations_for(device, &sheet, guard);
|
self.collect_invalidations_for(device, &sheet, guard);
|
||||||
self.entries.push(StylesheetSetEntry { sheet });
|
// 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepend a new stylesheet to the current set.
|
/// Prepend a new stylesheet to the current set.
|
||||||
|
@ -130,7 +300,12 @@ where
|
||||||
debug!("StylesheetSet::prepend_stylesheet");
|
debug!("StylesheetSet::prepend_stylesheet");
|
||||||
self.remove_stylesheet_if_present(&sheet);
|
self.remove_stylesheet_if_present(&sheet);
|
||||||
self.collect_invalidations_for(device, &sheet, guard);
|
self.collect_invalidations_for(device, &sheet, guard);
|
||||||
self.entries.insert(0, StylesheetSetEntry { 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(sheet.contents(guard).origin, OriginValidity::CascadeInvalid);
|
||||||
|
|
||||||
|
self.entries.insert(0, StylesheetSetEntry::new(sheet));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a given stylesheet before another stylesheet in the document.
|
/// Insert a given stylesheet before another stylesheet in the document.
|
||||||
|
@ -147,7 +322,11 @@ where
|
||||||
entry.sheet == before_sheet
|
entry.sheet == before_sheet
|
||||||
}).expect("`before_sheet` stylesheet not found");
|
}).expect("`before_sheet` stylesheet not found");
|
||||||
self.collect_invalidations_for(device, &sheet, guard);
|
self.collect_invalidations_for(device, &sheet, guard);
|
||||||
self.entries.insert(index, StylesheetSetEntry { 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(sheet.contents(guard).origin, OriginValidity::CascadeInvalid);
|
||||||
|
self.entries.insert(index, StylesheetSetEntry::new(sheet));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a given stylesheet from the set.
|
/// Remove a given stylesheet from the set.
|
||||||
|
@ -159,7 +338,12 @@ where
|
||||||
) {
|
) {
|
||||||
debug!("StylesheetSet::remove_stylesheet");
|
debug!("StylesheetSet::remove_stylesheet");
|
||||||
self.remove_stylesheet_if_present(&sheet);
|
self.remove_stylesheet_if_present(&sheet);
|
||||||
|
|
||||||
self.collect_invalidations_for(device, &sheet, guard);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notes that the author style has been disabled for this document.
|
/// Notes that the author style has been disabled for this document.
|
||||||
|
@ -169,61 +353,53 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.author_style_disabled = disabled;
|
self.author_style_disabled = disabled;
|
||||||
self.invalidation_data.author.invalidations.invalidate_fully();
|
self.invalidations.invalidate_fully();
|
||||||
self.invalidation_data.author.dirty = true;
|
self.origins_dirty |= Origin::Author;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given set has changed from the last flush.
|
/// Returns whether the given set has changed from the last flush.
|
||||||
pub fn has_changed(&self) -> bool {
|
pub fn has_changed(&self) -> bool {
|
||||||
self.invalidation_data
|
!self.origins_dirty.is_empty()
|
||||||
.iter_origins()
|
|
||||||
.any(|(d, _)| d.dirty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush the current set, unmarking it as dirty, and returns an iterator
|
/// Flush the current set, unmarking it as dirty, and returns a
|
||||||
/// over the new stylesheet list.
|
/// `StylesheetFlusher` in order to rebuild the stylist.
|
||||||
///
|
pub fn flush<'a, 'b, E>(
|
||||||
/// Returns true if any elements were invalidated.
|
&'a mut self,
|
||||||
pub fn flush<E>(
|
|
||||||
&mut self,
|
|
||||||
document_element: Option<E>,
|
document_element: Option<E>,
|
||||||
) -> (StylesheetIterator<S>, OriginSet, bool)
|
guard: &'a SharedRwLockReadGuard<'b>,
|
||||||
|
) -> StylesheetFlusher<'a, 'b, S>
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
{
|
{
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
debug!("StylesheetSet::flush");
|
debug!("StylesheetSet::flush");
|
||||||
|
|
||||||
let mut origins = OriginSet::empty();
|
let had_invalidations = self.invalidations.flush(document_element);
|
||||||
let mut have_invalidations = false;
|
let origins_dirty = mem::replace(&mut self.origins_dirty, OriginSet::empty());
|
||||||
for (data, origin) in self.invalidation_data.iter_mut_origins() {
|
let origin_data_validity =
|
||||||
if data.dirty {
|
mem::replace(&mut self.origin_data_validity, Default::default());
|
||||||
have_invalidations |= data.invalidations.flush(document_element);
|
|
||||||
data.dirty = false;
|
|
||||||
origins |= origin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(self.iter(), origins, have_invalidations)
|
StylesheetFlusher {
|
||||||
|
iter: self.entries.iter_mut(),
|
||||||
|
author_style_disabled: self.author_style_disabled,
|
||||||
|
had_invalidations,
|
||||||
|
origins_dirty,
|
||||||
|
origin_data_validity,
|
||||||
|
guard,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush stylesheets, but without running any of the invalidation passes.
|
/// Flush stylesheets, but without running any of the invalidation passes.
|
||||||
///
|
|
||||||
/// FIXME(emilio): This should eventually disappear. Please keep this
|
|
||||||
/// Servo-only.
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
pub fn flush_without_invalidation(&mut self) -> (StylesheetIterator<S>, OriginSet) {
|
pub fn flush_without_invalidation(&mut self) -> OriginSet {
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
debug!("StylesheetSet::flush_without_invalidation");
|
debug!("StylesheetSet::flush_without_invalidation");
|
||||||
|
|
||||||
let mut origins = OriginSet::empty();
|
self.invalidations.clear();
|
||||||
for (data, origin) in self.invalidation_data.iter_mut_origins() {
|
mem::replace(&mut self.origins_dirty, OriginSet::empty())
|
||||||
if data.dirty {
|
|
||||||
data.invalidations.clear();
|
|
||||||
data.dirty = false;
|
|
||||||
origins |= origin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(self.iter(), origins)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the current list of stylesheets.
|
/// Returns an iterator over the current list of stylesheets.
|
||||||
|
@ -234,30 +410,11 @@ where
|
||||||
/// Mark the stylesheets for the specified origin as dirty, because
|
/// Mark the stylesheets for the specified origin as dirty, because
|
||||||
/// something external may have invalidated it.
|
/// something external may have invalidated it.
|
||||||
pub fn force_dirty(&mut self, origins: OriginSet) {
|
pub fn force_dirty(&mut self, origins: OriginSet) {
|
||||||
|
self.invalidations.invalidate_fully();
|
||||||
|
self.origins_dirty |= origins;
|
||||||
for origin in origins.iter() {
|
for origin in origins.iter() {
|
||||||
let data = self.invalidation_data.borrow_mut_for_origin(&origin);
|
// We don't know what happened, assume the worse.
|
||||||
data.invalidations.invalidate_fully();
|
self.set_data_validity_at_least(origin, OriginValidity::FullyInvalid);
|
||||||
data.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
struct InvalidationData {
|
|
||||||
/// The stylesheet invalidations for this origin that we still haven't
|
|
||||||
/// processed.
|
|
||||||
invalidations: StylesheetInvalidationSet,
|
|
||||||
|
|
||||||
/// Whether the sheets for this origin in the `StylesheetSet`'s entry list
|
|
||||||
/// has changed since the last restyle.
|
|
||||||
dirty: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InvalidationData {
|
|
||||||
fn default() -> Self {
|
|
||||||
InvalidationData {
|
|
||||||
invalidations: StylesheetInvalidationSet::new(),
|
|
||||||
dirty: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ impl Origin {
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// A set of origins. This is equivalent to Gecko's OriginFlags.
|
/// A set of origins. This is equivalent to Gecko's OriginFlags.
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub flags OriginSet: u8 {
|
pub flags OriginSet: u8 {
|
||||||
/// https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent
|
/// https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent
|
||||||
const ORIGIN_USER_AGENT = Origin::UserAgent as u8,
|
const ORIGIN_USER_AGENT = Origin::UserAgent as u8,
|
||||||
|
|
|
@ -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::{OriginValidity, SheetRebuildKind, 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,33 +83,44 @@ 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());
|
||||||
|
|
||||||
|
for (cascade_data, origin) in self.per_origin.iter_mut_origins() {
|
||||||
|
let validity = flusher.origin_validity(origin);
|
||||||
|
|
||||||
|
if validity == OriginValidity::Valid {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if origin == Origin::UserAgent {
|
||||||
|
self.precomputed_pseudo_element_decls.clear();
|
||||||
|
}
|
||||||
|
|
||||||
for origin in origins_to_rebuild.iter() {
|
|
||||||
extra_data.borrow_mut_for_origin(&origin).clear();
|
extra_data.borrow_mut_for_origin(&origin).clear();
|
||||||
self.per_origin.borrow_mut_for_origin(&origin).clear();
|
if validity == OriginValidity::CascadeInvalid {
|
||||||
}
|
cascade_data.clear_cascade_data()
|
||||||
|
} else {
|
||||||
if origins_to_rebuild.contains(Origin::UserAgent.into()) {
|
debug_assert_eq!(validity, OriginValidity::FullyInvalid);
|
||||||
self.precomputed_pseudo_element_decls.clear();
|
cascade_data.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ua_stylesheets) = ua_stylesheets {
|
if let Some(ua_stylesheets) = ua_stylesheets {
|
||||||
|
debug_assert!(cfg!(feature = "servo"));
|
||||||
|
|
||||||
for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
|
for stylesheet in &ua_stylesheets.user_or_user_agent_stylesheets {
|
||||||
let sheet_origin =
|
let sheet_origin =
|
||||||
stylesheet.contents(guards.ua_or_user).origin;
|
stylesheet.contents(guards.ua_or_user).origin;
|
||||||
|
@ -119,15 +130,26 @@ impl DocumentCascadeData {
|
||||||
Origin::UserAgent | Origin::User
|
Origin::UserAgent | Origin::User
|
||||||
));
|
));
|
||||||
|
|
||||||
if origins_to_rebuild.contains(sheet_origin.into()) {
|
let validity = flusher.origin_validity(sheet_origin);
|
||||||
self.add_stylesheet(
|
|
||||||
device,
|
// Servo doesn't support to incrementally mutate UA sheets.
|
||||||
quirks_mode,
|
debug_assert!(matches!(
|
||||||
stylesheet,
|
validity,
|
||||||
guards.ua_or_user,
|
OriginValidity::Valid | OriginValidity::FullyInvalid
|
||||||
extra_data,
|
));
|
||||||
);
|
|
||||||
|
if validity == OriginValidity::Valid {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.add_stylesheet(
|
||||||
|
device,
|
||||||
|
quirks_mode,
|
||||||
|
stylesheet,
|
||||||
|
guards.ua_or_user,
|
||||||
|
extra_data,
|
||||||
|
SheetRebuildKind::Full,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if quirks_mode != QuirksMode::NoQuirks {
|
if quirks_mode != QuirksMode::NoQuirks {
|
||||||
|
@ -140,34 +162,35 @@ impl DocumentCascadeData {
|
||||||
Origin::UserAgent | Origin::User
|
Origin::UserAgent | Origin::User
|
||||||
));
|
));
|
||||||
|
|
||||||
if origins_to_rebuild.contains(sheet_origin.into()) {
|
let validity = flusher.origin_validity(sheet_origin);
|
||||||
|
|
||||||
|
// Servo doesn't support to incrementally mutate UA sheets.
|
||||||
|
debug_assert!(matches!(
|
||||||
|
validity,
|
||||||
|
OriginValidity::Valid | OriginValidity::FullyInvalid
|
||||||
|
));
|
||||||
|
|
||||||
|
if validity != OriginValidity::Valid {
|
||||||
self.add_stylesheet(
|
self.add_stylesheet(
|
||||||
device,
|
device,
|
||||||
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,
|
||||||
|
SheetRebuildKind::Full,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
stylesheet,
|
stylesheet,
|
||||||
guards.author,
|
guards.author,
|
||||||
extra_data
|
extra_data,
|
||||||
|
rebuild_kind,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +201,8 @@ impl DocumentCascadeData {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
stylesheet: &S,
|
stylesheet: &S,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
_extra_data: &mut PerOrigin<ExtraStyleData>
|
_extra_data: &mut PerOrigin<ExtraStyleData>,
|
||||||
|
rebuild_kind: SheetRebuildKind,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||||
|
@ -192,9 +216,11 @@ impl DocumentCascadeData {
|
||||||
let origin_cascade_data =
|
let origin_cascade_data =
|
||||||
self.per_origin.borrow_mut_for_origin(&origin);
|
self.per_origin.borrow_mut_for_origin(&origin);
|
||||||
|
|
||||||
origin_cascade_data
|
if rebuild_kind.should_rebuild_invalidation() {
|
||||||
.effective_media_query_results
|
origin_cascade_data
|
||||||
.saw_effective(stylesheet);
|
.effective_media_query_results
|
||||||
|
.saw_effective(stylesheet);
|
||||||
|
}
|
||||||
|
|
||||||
for rule in stylesheet.effective_rules(device, guard) {
|
for rule in stylesheet.effective_rules(device, guard) {
|
||||||
match *rule {
|
match *rule {
|
||||||
|
@ -248,43 +274,49 @@ impl DocumentCascadeData {
|
||||||
|
|
||||||
map.insert(rule, quirks_mode);
|
map.insert(rule, quirks_mode);
|
||||||
|
|
||||||
origin_cascade_data
|
if rebuild_kind.should_rebuild_invalidation() {
|
||||||
.invalidation_map
|
origin_cascade_data
|
||||||
.note_selector(selector, quirks_mode);
|
.invalidation_map
|
||||||
let mut visitor = StylistSelectorVisitor {
|
.note_selector(selector, quirks_mode);
|
||||||
needs_revalidation: false,
|
let mut visitor = StylistSelectorVisitor {
|
||||||
passed_rightmost_selector: false,
|
needs_revalidation: false,
|
||||||
attribute_dependencies: &mut origin_cascade_data.attribute_dependencies,
|
passed_rightmost_selector: false,
|
||||||
style_attribute_dependency: &mut origin_cascade_data.style_attribute_dependency,
|
attribute_dependencies: &mut origin_cascade_data.attribute_dependencies,
|
||||||
state_dependencies: &mut origin_cascade_data.state_dependencies,
|
style_attribute_dependency: &mut origin_cascade_data.style_attribute_dependency,
|
||||||
mapped_ids: &mut origin_cascade_data.mapped_ids,
|
state_dependencies: &mut origin_cascade_data.state_dependencies,
|
||||||
};
|
mapped_ids: &mut origin_cascade_data.mapped_ids,
|
||||||
|
};
|
||||||
|
|
||||||
selector.visit(&mut visitor);
|
selector.visit(&mut visitor);
|
||||||
|
|
||||||
if visitor.needs_revalidation {
|
if visitor.needs_revalidation {
|
||||||
origin_cascade_data.selectors_for_cache_revalidation.insert(
|
origin_cascade_data.selectors_for_cache_revalidation.insert(
|
||||||
RevalidationSelectorAndHashes::new(selector.clone(), hashes),
|
RevalidationSelectorAndHashes::new(selector.clone(), hashes),
|
||||||
quirks_mode
|
quirks_mode
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
origin_cascade_data.rules_source_order += 1;
|
origin_cascade_data.rules_source_order += 1;
|
||||||
}
|
}
|
||||||
CssRule::Import(ref lock) => {
|
CssRule::Import(ref lock) => {
|
||||||
let import_rule = lock.read_with(guard);
|
if rebuild_kind.should_rebuild_invalidation() {
|
||||||
origin_cascade_data
|
let import_rule = lock.read_with(guard);
|
||||||
.effective_media_query_results
|
origin_cascade_data
|
||||||
.saw_effective(import_rule);
|
.effective_media_query_results
|
||||||
|
.saw_effective(import_rule);
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: effective_rules visits the inner stylesheet if
|
// NOTE: effective_rules visits the inner stylesheet if
|
||||||
// appropriate.
|
// appropriate.
|
||||||
}
|
}
|
||||||
CssRule::Media(ref lock) => {
|
CssRule::Media(ref lock) => {
|
||||||
let media_rule = lock.read_with(guard);
|
if rebuild_kind.should_rebuild_invalidation() {
|
||||||
origin_cascade_data
|
let media_rule = lock.read_with(guard);
|
||||||
.effective_media_query_results
|
origin_cascade_data
|
||||||
.saw_effective(media_rule);
|
.effective_media_query_results
|
||||||
|
.saw_effective(media_rule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CssRule::Keyframes(ref keyframes_rule) => {
|
CssRule::Keyframes(ref keyframes_rule) => {
|
||||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||||
|
@ -489,14 +521,6 @@ impl Stylist {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let author_style_disabled = self.stylesheets.author_style_disabled();
|
|
||||||
let (doc_stylesheets, origins_to_rebuild, have_invalidations) =
|
|
||||||
self.stylesheets.flush(document_element);
|
|
||||||
|
|
||||||
if origins_to_rebuild.is_empty() {
|
|
||||||
return have_invalidations;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.num_rebuilds += 1;
|
self.num_rebuilds += 1;
|
||||||
|
|
||||||
// Update viewport_constraints regardless of which origins'
|
// Update viewport_constraints regardless of which origins'
|
||||||
|
@ -515,32 +539,38 @@ 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()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.viewport_constraints =
|
self.viewport_constraints =
|
||||||
ViewportConstraints::maybe_new(&self.device,
|
ViewportConstraints::maybe_new(
|
||||||
&cascaded_rule,
|
&self.device,
|
||||||
self.quirks_mode);
|
&cascaded_rule,
|
||||||
|
self.quirks_mode,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(ref constraints) = self.viewport_constraints {
|
if let Some(ref constraints) = self.viewport_constraints {
|
||||||
self.device.account_for_viewport_rule(constraints);
|
self.device.account_for_viewport_rule(constraints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
@ -1689,6 +1719,9 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
|
||||||
|
|
||||||
/// Data resulting from performing the CSS cascade that is specific to a given
|
/// Data resulting from performing the CSS cascade that is specific to a given
|
||||||
/// origin.
|
/// origin.
|
||||||
|
///
|
||||||
|
/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
|
||||||
|
/// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CascadeData {
|
struct CascadeData {
|
||||||
|
@ -1784,20 +1817,25 @@ impl CascadeData {
|
||||||
self.pseudos_map.get(pseudo).is_some()
|
self.pseudos_map.get(pseudo).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
/// Clears the cascade data, but not the invalidation data.
|
||||||
|
fn clear_cascade_data(&mut self) {
|
||||||
self.element_map.clear();
|
self.element_map.clear();
|
||||||
self.pseudos_map.clear();
|
self.pseudos_map.clear();
|
||||||
self.animations.clear();
|
self.animations.clear();
|
||||||
|
self.rules_source_order = 0;
|
||||||
|
self.num_selectors = 0;
|
||||||
|
self.num_declarations = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.clear_cascade_data();
|
||||||
|
self.effective_media_query_results.clear();
|
||||||
self.invalidation_map.clear();
|
self.invalidation_map.clear();
|
||||||
self.attribute_dependencies.clear();
|
self.attribute_dependencies.clear();
|
||||||
self.style_attribute_dependency = false;
|
self.style_attribute_dependency = false;
|
||||||
self.state_dependencies = ElementState::empty();
|
self.state_dependencies = ElementState::empty();
|
||||||
self.mapped_ids.clear();
|
self.mapped_ids.clear();
|
||||||
self.selectors_for_cache_revalidation.clear();
|
self.selectors_for_cache_revalidation.clear();
|
||||||
self.effective_media_query_results.clear();
|
|
||||||
self.rules_source_order = 0;
|
|
||||||
self.num_selectors = 0;
|
|
||||||
self.num_declarations = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue