style: Share CascadeData instances across ShadowRoots

This should be both a memory and speed win for pages using a lot of
Shadow DOM.

In order to make the cache properly work we need to start keying media query
results on the actual StyleSheetContents, as that's what we share on Gecko, but
that should all be fine.

Differential Revision: https://phabricator.services.mozilla.com/D107266
This commit is contained in:
Oriol Brufau 2023-05-16 09:51:46 +02:00
parent 11153c63fa
commit 060d74ba3b
11 changed files with 175 additions and 178 deletions

View file

@ -5,16 +5,16 @@
//! A set of author stylesheets and their computed representation, such as the //! A set of author stylesheets and their computed representation, such as the
//! ones used for ShadowRoot. //! ones used for ShadowRoot.
use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::media_queries::ToMediaListKey; use crate::invalidation::media_queries::ToMediaListKey;
use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylist::Stylist;
use crate::stylesheet_set::AuthorStylesheetSet; use crate::stylesheet_set::AuthorStylesheetSet;
use crate::stylesheets::StylesheetInDocument; use crate::stylesheets::StylesheetInDocument;
use crate::stylist::CascadeData; use crate::stylist::CascadeData;
use servo_arc::Arc;
/// A set of author stylesheets and their computed representation, such as the /// A set of author stylesheets and their computed representation, such as the
/// ones used for ShadowRoot. /// ones used for ShadowRoot.
@ -27,7 +27,14 @@ where
/// and all that stuff. /// and all that stuff.
pub stylesheets: AuthorStylesheetSet<S>, pub stylesheets: AuthorStylesheetSet<S>,
/// The actual cascade data computed from the stylesheets. /// The actual cascade data computed from the stylesheets.
pub data: CascadeData, #[ignore_malloc_size_of = "Measured as part of the stylist"]
pub data: Arc<CascadeData>,
}
lazy_static! {
static ref EMPTY_CASCADE_DATA: Arc<CascadeData> = {
Arc::new_leaked(CascadeData::new())
};
} }
impl<S> AuthorStyles<S> impl<S> AuthorStyles<S>
@ -39,7 +46,7 @@ where
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
stylesheets: AuthorStylesheetSet::new(), stylesheets: AuthorStylesheetSet::new(),
data: CascadeData::new(), data: EMPTY_CASCADE_DATA.clone(),
} }
} }
@ -50,8 +57,7 @@ where
#[inline] #[inline]
pub fn flush<E>( pub fn flush<E>(
&mut self, &mut self,
device: &Device, stylist: &mut Stylist,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) where ) where
E: TElement, E: TElement,
@ -61,10 +67,10 @@ where
.stylesheets .stylesheets
.flush::<E>(/* host = */ None, /* snapshot_map = */ None); .flush::<E>(/* host = */ None, /* snapshot_map = */ None);
// Ignore OOM. let result = stylist.rebuild_author_data(&self.data, flusher.sheets, guard);
let _ = self if let Ok(Some(new_data)) = result {
.data self.data = new_data;
.rebuild(device, quirks_mode, flusher.sheets, guard); }
} }
} }

View file

@ -4,7 +4,6 @@
//! Data needed to style a Gecko document. //! Data needed to style a Gecko document.
use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes}; use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes};
@ -15,7 +14,7 @@ use crate::media_queries::{Device, MediaList};
use crate::properties::ComputedValues; use crate::properties::ComputedValues;
use crate::selector_parser::SnapshotMap; use crate::selector_parser::SnapshotMap;
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheets::{CssRule, Origin, StylesheetContents, StylesheetInDocument}; use crate::stylesheets::{StylesheetContents, StylesheetInDocument};
use crate::stylist::Stylist; use crate::stylist::Stylist;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use malloc_size_of::MallocSizeOfOps; use malloc_size_of::MallocSizeOfOps;
@ -69,16 +68,6 @@ impl GeckoStyleSheet {
fn inner(&self) -> &StyleSheetInfo { fn inner(&self) -> &StyleSheetInfo {
unsafe { &*(self.raw().mInner as *const StyleSheetInfo) } unsafe { &*(self.raw().mInner as *const StyleSheetInfo) }
} }
/// Gets the StylesheetContents for this stylesheet.
pub fn contents(&self) -> &StylesheetContents {
debug_assert!(!self.inner().mContents.mRawPtr.is_null());
unsafe {
let contents =
(&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _;
&*contents
}
}
} }
impl Drop for GeckoStyleSheet { impl Drop for GeckoStyleSheet {
@ -95,14 +84,6 @@ impl Clone for GeckoStyleSheet {
} }
impl StylesheetInDocument for GeckoStyleSheet { impl StylesheetInDocument for GeckoStyleSheet {
fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
self.contents().origin
}
fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
self.contents().quirks_mode
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList; use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList;
use std::mem; use std::mem;
@ -120,13 +101,19 @@ impl StylesheetInDocument for GeckoStyleSheet {
// All the stylesheets Servo knows about are enabled, because that state is // All the stylesheets Servo knows about are enabled, because that state is
// handled externally by Gecko. // handled externally by Gecko.
#[inline]
fn enabled(&self) -> bool { fn enabled(&self) -> bool {
true true
} }
#[inline] #[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.contents().rules(guard) debug_assert!(!self.inner().mContents.mRawPtr.is_null());
unsafe {
let contents =
(&**StylesheetContents::as_arc(&&*self.inner().mContents.mRawPtr)) as *const _;
&*contents
}
} }
} }

View file

@ -185,7 +185,7 @@ pub struct DocumentStateDependency {
/// In particular, we want to lookup as few things as possible to get the fewer /// In particular, we want to lookup as few things as possible to get the fewer
/// selectors the better, so this looks up by id, class, or looks at the list of /// selectors the better, so this looks up by id, class, or looks at the list of
/// state/other attribute affecting selectors. /// state/other attribute affecting selectors.
#[derive(Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct InvalidationMap { pub struct InvalidationMap {
/// A map from a given class name to all the selectors with that class /// A map from a given class name to all the selectors with that class
/// selector. /// selector.

View file

@ -8,7 +8,7 @@ use crate::context::QuirksMode;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::{DocumentRule, ImportRule, MediaRule}; use crate::stylesheets::{DocumentRule, ImportRule, MediaRule};
use crate::stylesheets::{NestedRuleIterationCondition, Stylesheet, SupportsRule}; use crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule};
use fxhash::FxHashSet; use fxhash::FxHashSet;
/// A key for a given media query result. /// A key for a given media query result.
@ -43,13 +43,13 @@ pub trait ToMediaListKey: Sized {
} }
} }
impl ToMediaListKey for Stylesheet {} impl ToMediaListKey for StylesheetContents {}
impl ToMediaListKey for ImportRule {} impl ToMediaListKey for ImportRule {}
impl ToMediaListKey for MediaRule {} impl ToMediaListKey for MediaRule {}
/// A struct that holds the result of a media query evaluation pass for the /// A struct that holds the result of a media query evaluation pass for the
/// media queries that evaluated successfully. /// media queries that evaluated successfully.
#[derive(Debug, MallocSizeOf, PartialEq)] #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub struct EffectiveMediaQueryResults { pub struct EffectiveMediaQueryResults {
/// The set of media lists that matched last time. /// The set of media lists that matched last time.
set: FxHashSet<MediaListKey>, set: FxHashSet<MediaListKey>,

View file

@ -94,7 +94,7 @@ pub trait SelectorMapEntry: Sized + Clone {
/// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755 /// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755
/// ///
/// TODO: Tune the initial capacity of the HashMap /// TODO: Tune the initial capacity of the HashMap
#[derive(Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct SelectorMap<T: 'static> { pub struct SelectorMap<T: 'static> {
/// Rules that have `:root` selectors. /// Rules that have `:root` selectors.
pub root: SmallVec<[T; 1]>, pub root: SmallVec<[T; 1]>,
@ -615,7 +615,7 @@ fn find_bucket<'a>(
} }
/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode. /// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.
#[derive(Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>( pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>(
PrecomputedHashMap<K, V>, PrecomputedHashMap<K, V>,
); );

View file

@ -108,7 +108,7 @@ pub enum PseudoElementCascadeType {
} }
/// A per-pseudo map, from a given pseudo to a `T`. /// A per-pseudo map, from a given pseudo to a `T`.
#[derive(MallocSizeOf)] #[derive(Clone, MallocSizeOf)]
pub struct PerPseudoElementMap<T> { pub struct PerPseudoElementMap<T> {
entries: [Option<T>; PSEUDO_COUNT], entries: [Option<T>; PSEUDO_COUNT],
} }

View file

@ -420,7 +420,7 @@ macro_rules! sheet_set_methods {
) { ) {
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);
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.append(sheet); collection.append(sheet);
} }
@ -435,7 +435,7 @@ macro_rules! sheet_set_methods {
debug!(concat!($set_name, "::insert_stylesheet_before")); debug!(concat!($set_name, "::insert_stylesheet_before"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.insert_before(sheet, &before_sheet); collection.insert_before(sheet, &before_sheet);
} }
@ -449,7 +449,7 @@ macro_rules! sheet_set_methods {
debug!(concat!($set_name, "::remove_stylesheet")); debug!(concat!($set_name, "::remove_stylesheet"));
self.collect_invalidations_for(device, &sheet, guard); self.collect_invalidations_for(device, &sheet, guard);
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.remove(&sheet) collection.remove(&sheet)
} }
@ -499,7 +499,7 @@ macro_rules! sheet_set_methods {
RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid, RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
}; };
let collection = self.collection_for(&sheet, guard); let collection = self.collection_for(&sheet);
collection.set_data_validity_at_least(validity); collection.set_data_validity_at_least(validity);
} }
}; };
@ -517,12 +517,8 @@ where
} }
} }
fn collection_for( fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S> {
&mut self, let origin = sheet.contents().origin;
sheet: &S,
guard: &SharedRwLockReadGuard,
) -> &mut SheetCollection<S> {
let origin = sheet.origin(guard);
self.collections.borrow_mut_for_origin(&origin) self.collections.borrow_mut_for_origin(&origin)
} }
@ -670,11 +666,7 @@ where
self.collection.len() self.collection.len()
} }
fn collection_for( fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S> {
&mut self,
_sheet: &S,
_guard: &SharedRwLockReadGuard,
) -> &mut SheetCollection<S> {
&mut self.collection &mut self.collection
} }

View file

@ -61,6 +61,19 @@ impl ImportSheet {
ImportSheet::Pending(_) => None, ImportSheet::Pending(_) => None,
} }
} }
/// Returns the media list for this import rule.
pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.as_sheet().and_then(|s| s.media(guard))
}
/// Returns the rule list for this import rule.
pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
match self.as_sheet() {
Some(s) => s.rules(guard),
None => &[],
}
}
} }
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -85,69 +98,21 @@ impl DeepCloneWithLock for ImportSheet {
} }
} }
#[cfg(feature = "gecko")]
impl StylesheetInDocument for ImportSheet {
fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
match *self {
ImportSheet::Sheet(ref s) => s.contents().origin,
ImportSheet::Pending(ref p) => p.origin,
}
}
fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
match *self {
ImportSheet::Sheet(ref s) => s.contents().quirks_mode,
ImportSheet::Pending(ref p) => p.quirks_mode,
}
}
fn enabled(&self) -> bool {
match *self {
ImportSheet::Sheet(ref s) => s.enabled(),
ImportSheet::Pending(_) => true,
}
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
match *self {
ImportSheet::Sheet(ref s) => s.media(guard),
ImportSheet::Pending(_) => None,
}
}
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
match *self {
ImportSheet::Sheet(ref s) => s.contents().rules(guard),
ImportSheet::Pending(_) => &[],
}
}
}
/// A sheet that is held from an import rule. /// A sheet that is held from an import rule.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
#[derive(Debug)] #[derive(Debug)]
pub struct ImportSheet(pub ::servo_arc::Arc<crate::stylesheets::Stylesheet>); pub struct ImportSheet(pub ::servo_arc::Arc<crate::stylesheets::Stylesheet>);
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
impl StylesheetInDocument for ImportSheet { impl ImportSheet {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin { /// Returns the media list for this import rule.
self.0.origin(guard) pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.0.quirks_mode(guard)
}
fn enabled(&self) -> bool {
self.0.enabled()
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.0.media(guard) self.0.media(guard)
} }
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { /// Returns the rules for this import rule.
self.0.rules(guard) pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
self.0.rules()
} }
} }

View file

@ -7,7 +7,6 @@
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::StylesheetInDocument;
use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule}; use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::slice; use std::slice;
@ -227,10 +226,13 @@ impl NestedRuleIterationCondition for EffectiveRules {
fn process_import( fn process_import(
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
device: &Device, device: &Device,
_quirks_mode: QuirksMode, quirks_mode: QuirksMode,
rule: &ImportRule, rule: &ImportRule,
) -> bool { ) -> bool {
rule.stylesheet.is_effective_for_device(device, guard) match rule.stylesheet.media(guard) {
Some(m) => m.evaluate(device, quirks_mode),
None => true,
}
} }
fn process_media( fn process_media(

View file

@ -4,7 +4,6 @@
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use crate::media_queries::{Device, MediaList}; use crate::media_queries::{Device, MediaList};
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
@ -102,10 +101,10 @@ impl StylesheetContents {
Self { Self {
rules: CssRules::new(rules, &shared_lock), rules: CssRules::new(rules, &shared_lock),
origin: origin, origin,
url_data: RwLock::new(url_data), url_data: RwLock::new(url_data),
namespaces: namespaces, namespaces,
quirks_mode: quirks_mode, quirks_mode,
source_map_url: RwLock::new(source_map_url), source_map_url: RwLock::new(source_map_url),
source_url: RwLock::new(source_url), source_url: RwLock::new(source_url),
} }
@ -218,12 +217,6 @@ macro_rules! rule_filter {
/// A trait to represent a given stylesheet in a document. /// A trait to represent a given stylesheet in a document.
pub trait StylesheetInDocument: ::std::fmt::Debug { pub trait StylesheetInDocument: ::std::fmt::Debug {
/// Get the stylesheet origin.
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin;
/// Get the stylesheet quirks mode.
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode;
/// Get whether this stylesheet is enabled. /// Get whether this stylesheet is enabled.
fn enabled(&self) -> bool; fn enabled(&self) -> bool;
@ -231,7 +224,12 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>; fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
/// Returns a reference to the list of rules in this stylesheet. /// Returns a reference to the list of rules in this stylesheet.
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule]; fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
self.contents().rules(guard)
}
/// Returns a reference to the contents of the stylesheet.
fn contents(&self) -> &StylesheetContents;
/// Return an iterator using the condition `C`. /// Return an iterator using the condition `C`.
#[inline] #[inline]
@ -243,18 +241,19 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
where where
C: NestedRuleIterationCondition, C: NestedRuleIterationCondition,
{ {
let contents = self.contents();
RulesIterator::new( RulesIterator::new(
device, device,
self.quirks_mode(guard), contents.quirks_mode,
guard, guard,
self.rules(guard).iter(), contents.rules(guard).iter(),
) )
} }
/// Returns whether the style-sheet applies for the current device. /// Returns whether the style-sheet applies for the current device.
fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool { fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
match self.media(guard) { match self.media(guard) {
Some(medialist) => medialist.evaluate(device, self.quirks_mode(guard)), Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
None => true, None => true,
} }
} }
@ -285,14 +284,6 @@ pub trait StylesheetInDocument: ::std::fmt::Debug {
} }
impl StylesheetInDocument for Stylesheet { impl StylesheetInDocument for Stylesheet {
fn origin(&self, _guard: &SharedRwLockReadGuard) -> Origin {
self.contents.origin
}
fn quirks_mode(&self, _guard: &SharedRwLockReadGuard) -> QuirksMode {
self.contents.quirks_mode
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
Some(self.media.read_with(guard)) Some(self.media.read_with(guard))
} }
@ -302,8 +293,8 @@ impl StylesheetInDocument for Stylesheet {
} }
#[inline] #[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.contents.rules(guard) &self.contents
} }
} }
@ -321,21 +312,7 @@ impl PartialEq for DocumentStyleSheet {
} }
} }
impl ToMediaListKey for DocumentStyleSheet {
fn to_media_list_key(&self) -> MediaListKey {
self.0.to_media_list_key()
}
}
impl StylesheetInDocument for DocumentStyleSheet { impl StylesheetInDocument for DocumentStyleSheet {
fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
self.0.origin(guard)
}
fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
self.0.quirks_mode(guard)
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.0.media(guard) self.0.media(guard)
} }
@ -345,8 +322,8 @@ impl StylesheetInDocument for DocumentStyleSheet {
} }
#[inline] #[inline]
fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { fn contents(&self) -> &StylesheetContents {
self.0.rules(guard) self.0.contents()
} }
} }

View file

@ -11,7 +11,7 @@ use crate::element_state::{DocumentState, ElementState};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion}; use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
use crate::invalidation::element::invalidation_map::InvalidationMap; use crate::invalidation::element::invalidation_map::InvalidationMap;
use crate::invalidation::media_queries::{EffectiveMediaQueryResults, ToMediaListKey}; use crate::invalidation::media_queries::EffectiveMediaQueryResults;
use crate::invalidation::stylesheets::RuleChangeKind; use crate::invalidation::stylesheets::RuleChangeKind;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::properties::{self, CascadeMode, ComputedValues}; use crate::properties::{self, CascadeMode, ComputedValues};
@ -73,7 +73,7 @@ trait CascadeDataCacheEntry : Sized {
old_entry: &Self, old_entry: &Self,
) -> Result<Arc<Self>, FailedAllocationError> ) -> Result<Arc<Self>, FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static; S: StylesheetInDocument + PartialEq + 'static;
/// Measures heap memory usage. /// Measures heap memory usage.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes); fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes);
@ -108,7 +108,7 @@ where
old_entry: &Entry, old_entry: &Entry,
) -> Result<Option<Arc<Entry>>, FailedAllocationError> ) -> Result<Option<Arc<Entry>>, FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
debug!("StyleSheetCache::lookup({})", self.len()); debug!("StyleSheetCache::lookup({})", self.len());
@ -122,9 +122,23 @@ where
} }
for entry in &self.entries { for entry in &self.entries {
if entry.cascade_data().effective_media_query_results == key { if std::ptr::eq(&**entry, old_entry) {
return Ok(Some(entry.clone())); // Avoid reusing our old entry (this can happen if we get
// invalidated due to CSSOM mutations and our old stylesheet
// contents were already unique, for example). This old entry
// will be pruned from the cache with take_unused() afterwards.
continue;
} }
if entry.cascade_data().effective_media_query_results != key {
continue;
}
if log_enabled!(log::Level::Debug) {
debug!("cache hit for:");
for sheet in collection.sheets() {
debug!(" > {:?}", sheet);
}
}
return Ok(Some(entry.clone()));
} }
debug!("> Picking the slow path"); debug!("> Picking the slow path");
@ -205,7 +219,7 @@ impl CascadeDataCacheEntry for UserAgentCascadeData {
_old: &Self, _old: &Self,
) -> Result<Arc<Self>, FailedAllocationError> ) -> Result<Arc<Self>, FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static S: StylesheetInDocument + PartialEq + 'static
{ {
// TODO: Maybe we should support incremental rebuilds, though they seem // TODO: Maybe we should support incremental rebuilds, though they seem
// uncommon and rebuild() doesn't deal with // uncommon and rebuild() doesn't deal with
@ -319,11 +333,13 @@ impl DocumentCascadeData {
guards: &StylesheetGuards, guards: &StylesheetGuards,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
// First do UA sheets. // First do UA sheets.
{ {
let origin_flusher = flusher.flush_origin(Origin::UserAgent); let origin_flusher = flusher.flush_origin(Origin::UserAgent);
// Dirty check is just a minor optimization (no need to grab the
// lock if nothing has changed).
if origin_flusher.dirty() { if origin_flusher.dirty() {
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap(); let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
let new_data = ua_cache.lookup( let new_data = ua_cache.lookup(
@ -436,6 +452,9 @@ pub struct Stylist {
/// The list of stylesheets. /// The list of stylesheets.
stylesheets: StylistStylesheetSet, stylesheets: StylistStylesheetSet,
/// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM).
author_data_cache: CascadeDataCache<CascadeData>,
/// If true, the quirks-mode stylesheet is applied. /// If true, the quirks-mode stylesheet is applied.
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")] #[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")]
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
@ -487,6 +506,7 @@ impl Stylist {
device, device,
quirks_mode, quirks_mode,
stylesheets: StylistStylesheetSet::new(), stylesheets: StylistStylesheetSet::new(),
author_data_cache: CascadeDataCache::new(),
cascade_data: Default::default(), cascade_data: Default::default(),
author_styles_enabled: AuthorStylesEnabled::Yes, author_styles_enabled: AuthorStylesEnabled::Yes,
rule_tree: RuleTree::new(), rule_tree: RuleTree::new(),
@ -512,6 +532,31 @@ impl Stylist {
self.cascade_data.iter_origins() self.cascade_data.iter_origins()
} }
/// Does what the name says, to prevent author_data_cache to grow without
/// bound.
pub fn remove_unique_author_data_cache_entries(&mut self) {
self.author_data_cache.take_unused();
}
/// Rebuilds (if needed) the CascadeData given a sheet collection.
pub fn rebuild_author_data<S>(
&mut self,
old_data: &CascadeData,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
) -> Result<Option<Arc<CascadeData>>, FailedAllocationError>
where
S: StylesheetInDocument + PartialEq + 'static,
{
self.author_data_cache.lookup(
&self.device,
self.quirks_mode,
collection,
guard,
old_data,
)
}
/// Iterate over the extra data in origin order. /// Iterate over the extra data in origin order.
#[inline] #[inline]
pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator { pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
@ -1420,6 +1465,7 @@ impl Stylist {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.cascade_data.add_size_of(ops, sizes); self.cascade_data.add_size_of(ops, sizes);
self.author_data_cache.add_size_of(ops, sizes);
sizes.mRuleTree += self.rule_tree.size_of(ops); sizes.mRuleTree += self.rule_tree.size_of(ops);
// We may measure other fields in the future if DMD says it's worth it. // We may measure other fields in the future if DMD says it's worth it.
@ -1433,7 +1479,7 @@ impl Stylist {
/// This struct holds data which users of Stylist may want to extract /// This struct holds data which users of Stylist may want to extract
/// from stylesheets which can be done at the same time as updating. /// from stylesheets which can be done at the same time as updating.
#[derive(Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))] #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct ExtraStyleData { pub struct ExtraStyleData {
/// A list of effective font-face rules and their origin. /// A list of effective font-face rules and their origin.
@ -1453,11 +1499,6 @@ pub struct ExtraStyleData {
pub pages: Vec<Arc<Locked<PageRule>>>, pub pages: Vec<Arc<Locked<PageRule>>>,
} }
#[cfg(feature = "gecko")]
unsafe impl Sync for ExtraStyleData {}
#[cfg(feature = "gecko")]
unsafe impl Send for ExtraStyleData {}
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl ExtraStyleData { impl ExtraStyleData {
/// Add the given @font-face rule. /// Add the given @font-face rule.
@ -1698,7 +1739,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
} }
/// A set of rules for element and pseudo-elements. /// A set of rules for element and pseudo-elements.
#[derive(Debug, Default, MallocSizeOf)] #[derive(Clone, Debug, Default, MallocSizeOf)]
struct GenericElementAndPseudoRules<Map> { struct GenericElementAndPseudoRules<Map> {
/// Rules from stylesheets at this `CascadeData`'s origin. /// Rules from stylesheets at this `CascadeData`'s origin.
element_map: Map, element_map: Map,
@ -1777,7 +1818,7 @@ impl PartElementAndPseudoRules {
/// ///
/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and /// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
/// `InvalidationData`? That'd make `clear_cascade_data()` clearer. /// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
#[derive(Debug, MallocSizeOf)] #[derive(Debug, Clone, MallocSizeOf)]
pub struct CascadeData { pub struct CascadeData {
/// The data coming from normal style rules that apply to elements at this /// The data coming from normal style rules that apply to elements at this
/// cascade level. /// cascade level.
@ -1887,7 +1928,7 @@ impl CascadeData {
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
if !collection.dirty() { if !collection.dirty() {
return Ok(()); return Ok(());
@ -1990,14 +2031,14 @@ impl CascadeData {
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
results: &mut EffectiveMediaQueryResults, results: &mut EffectiveMediaQueryResults,
) where ) where
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + 'static,
{ {
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
return; return;
} }
debug!(" + {:?}", stylesheet); debug!(" + {:?}", stylesheet);
results.saw_effective(stylesheet); results.saw_effective(stylesheet.contents());
for rule in stylesheet.effective_rules(device, guard) { for rule in stylesheet.effective_rules(device, guard) {
match *rule { match *rule {
@ -2027,16 +2068,17 @@ impl CascadeData {
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>, mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), FailedAllocationError> ) -> Result<(), FailedAllocationError>
where where
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + 'static,
{ {
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
return Ok(()); return Ok(());
} }
let origin = stylesheet.origin(guard); let contents = stylesheet.contents();
let origin = contents.origin;
if rebuild_kind.should_rebuild_invalidation() { if rebuild_kind.should_rebuild_invalidation() {
self.effective_media_query_results.saw_effective(stylesheet); self.effective_media_query_results.saw_effective(contents);
} }
for rule in stylesheet.effective_rules(device, guard) { for rule in stylesheet.effective_rules(device, guard) {
@ -2211,13 +2253,13 @@ impl CascadeData {
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
) -> bool ) -> bool
where where
S: StylesheetInDocument + ToMediaListKey + 'static, S: StylesheetInDocument + 'static,
{ {
use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules; use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules;
let effective_now = stylesheet.is_effective_for_device(device, guard); let effective_now = stylesheet.is_effective_for_device(device, guard);
let effective_then = self.effective_media_query_results.was_effective(stylesheet); let effective_then = self.effective_media_query_results.was_effective(stylesheet.contents());
if effective_now != effective_then { if effective_now != effective_then {
debug!( debug!(
@ -2252,9 +2294,10 @@ impl CascadeData {
}, },
CssRule::Import(ref lock) => { CssRule::Import(ref lock) => {
let import_rule = lock.read_with(guard); let import_rule = lock.read_with(guard);
let effective_now = import_rule let effective_now = match import_rule.stylesheet.media(guard) {
.stylesheet Some(m) => m.evaluate(device, quirks_mode),
.is_effective_for_device(&device, guard); None => true,
};
let effective_then = self let effective_then = self
.effective_media_query_results .effective_media_query_results
.was_effective(import_rule); .was_effective(import_rule);
@ -2326,8 +2369,33 @@ impl CascadeData {
self.selectors_for_cache_revalidation.clear(); self.selectors_for_cache_revalidation.clear();
self.effective_media_query_results.clear(); self.effective_media_query_results.clear();
} }
}
impl CascadeDataCacheEntry for CascadeData {
fn cascade_data(&self) -> &CascadeData {
self
}
fn rebuild<S>(
device: &Device,
quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
old: &Self,
) -> Result<Arc<Self>, FailedAllocationError>
where
S: StylesheetInDocument + PartialEq + 'static
{
debug_assert!(collection.dirty(), "We surely need to do something?");
// If we're doing a full rebuild anyways, don't bother cloning the data.
let mut updatable_entry = match collection.data_validity() {
DataValidity::Valid | DataValidity::CascadeInvalid => old.clone(),
DataValidity::FullyInvalid => Self::new(),
};
updatable_entry.rebuild(device, quirks_mode, collection, guard)?;
Ok(Arc::new(updatable_entry))
}
/// Measures heap usage.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
self.normal_rules.add_size_of(ops, sizes); self.normal_rules.add_size_of(ops, sizes);