mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
style: Make the cascade data cache generic
We're going to use it both for UA sheets and for author styles in Shadow DOM. Differential Revision: https://phabricator.services.mozilla.com/D107265
This commit is contained in:
parent
b38517757f
commit
11153c63fa
2 changed files with 158 additions and 78 deletions
|
@ -184,7 +184,9 @@ pub struct SheetCollectionFlusher<'a, S>
|
|||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
|
||||
// TODO: This can be made an iterator again once
|
||||
// https://github.com/rust-lang/rust/pull/82771 lands on stable.
|
||||
entries: &'a mut [StylesheetSetEntry<S>],
|
||||
validity: DataValidity,
|
||||
dirty: bool,
|
||||
}
|
||||
|
@ -204,32 +206,42 @@ where
|
|||
pub fn data_validity(&self) -> DataValidity {
|
||||
self.validity
|
||||
}
|
||||
|
||||
/// Returns an iterator over the remaining list of sheets to consume.
|
||||
pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
|
||||
self.entries.iter().map(|entry| &entry.sheet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Iterator for SheetCollectionFlusher<'a, S>
|
||||
impl<'a, S> SheetCollectionFlusher<'a, S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
type Item = (&'a S, SheetRebuildKind);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let potential_sheet = self.iter.next()?;
|
||||
|
||||
/// Iterates over all sheets and values that we have to invalidate.
|
||||
///
|
||||
/// TODO(emilio): This would be nicer as an iterator but we can't do that
|
||||
/// until https://github.com/rust-lang/rust/pull/82771 stabilizes.
|
||||
///
|
||||
/// Since we don't have a good use-case for partial iteration, this does the
|
||||
/// trick for now.
|
||||
pub fn each(self, mut callback: impl FnMut(&S, SheetRebuildKind) -> bool) {
|
||||
for potential_sheet in self.entries.iter_mut() {
|
||||
let committed = mem::replace(&mut potential_sheet.committed, true);
|
||||
if !committed {
|
||||
let rebuild_kind = if !committed {
|
||||
// If the sheet was uncommitted, we need to do a full rebuild
|
||||
// anyway.
|
||||
return Some((&potential_sheet.sheet, SheetRebuildKind::Full));
|
||||
}
|
||||
|
||||
let rebuild_kind = match self.validity {
|
||||
DataValidity::Valid => continue,
|
||||
DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
||||
DataValidity::FullyInvalid => SheetRebuildKind::Full,
|
||||
SheetRebuildKind::Full
|
||||
} else {
|
||||
match self.validity {
|
||||
DataValidity::Valid => continue,
|
||||
DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
||||
DataValidity::FullyInvalid => SheetRebuildKind::Full,
|
||||
}
|
||||
};
|
||||
|
||||
return Some((&potential_sheet.sheet, rebuild_kind));
|
||||
if !callback(&potential_sheet.sheet, rebuild_kind) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +369,7 @@ where
|
|||
let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
|
||||
|
||||
SheetCollectionFlusher {
|
||||
iter: self.entries.iter_mut(),
|
||||
entries: &mut self.entries,
|
||||
dirty,
|
||||
validity,
|
||||
}
|
||||
|
|
|
@ -60,17 +60,33 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
|
|||
#[cfg(feature = "gecko")]
|
||||
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
|
||||
|
||||
lazy_static! {
|
||||
/// A cache of computed user-agent data, to be shared across documents.
|
||||
static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
|
||||
Mutex::new(UserAgentCascadeDataCache::new());
|
||||
trait CascadeDataCacheEntry : Sized {
|
||||
/// Returns a reference to the cascade data.
|
||||
fn cascade_data(&self) -> &CascadeData;
|
||||
/// Rebuilds the cascade data for the new stylesheet collection. The
|
||||
/// collection is guaranteed to be dirty.
|
||||
fn rebuild<S>(
|
||||
device: &Device,
|
||||
quirks_mode: QuirksMode,
|
||||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
old_entry: &Self,
|
||||
) -> Result<Arc<Self>, FailedAllocationError>
|
||||
where
|
||||
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static;
|
||||
/// Measures heap memory usage.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes);
|
||||
}
|
||||
|
||||
struct UserAgentCascadeDataCache {
|
||||
entries: Vec<Arc<UserAgentCascadeData>>,
|
||||
struct CascadeDataCache<Entry> {
|
||||
entries: Vec<Arc<Entry>>,
|
||||
}
|
||||
|
||||
impl UserAgentCascadeDataCache {
|
||||
impl<Entry> CascadeDataCache<Entry>
|
||||
where
|
||||
Entry: CascadeDataCacheEntry,
|
||||
{
|
||||
fn new() -> Self {
|
||||
Self { entries: vec![] }
|
||||
}
|
||||
|
@ -79,53 +95,50 @@ impl UserAgentCascadeDataCache {
|
|||
self.entries.len()
|
||||
}
|
||||
|
||||
// FIXME(emilio): This may need to be keyed on quirks-mode too, though there
|
||||
// aren't class / id selectors on those sheets, usually, so it's probably
|
||||
// ok...
|
||||
fn lookup<'a, I, S>(
|
||||
// FIXME(emilio): This may need to be keyed on quirks-mode too, though for
|
||||
// UA sheets there aren't class / id selectors on those sheets, usually, so
|
||||
// it's probably ok... For the other cache the quirks mode shouldn't differ
|
||||
// so also should be fine.
|
||||
fn lookup<'a, S>(
|
||||
&'a mut self,
|
||||
sheets: I,
|
||||
device: &Device,
|
||||
quirks_mode: QuirksMode,
|
||||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError>
|
||||
old_entry: &Entry,
|
||||
) -> Result<Option<Arc<Entry>>, FailedAllocationError>
|
||||
where
|
||||
I: Iterator<Item = &'a S> + Clone,
|
||||
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
|
||||
{
|
||||
debug!("StyleSheetCache::lookup({})", self.len());
|
||||
|
||||
if !collection.dirty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut key = EffectiveMediaQueryResults::new();
|
||||
debug!("UserAgentCascadeDataCache::lookup({:?})", device);
|
||||
for sheet in sheets.clone() {
|
||||
for sheet in collection.sheets() {
|
||||
CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
|
||||
}
|
||||
|
||||
for entry in &self.entries {
|
||||
if entry.cascade_data.effective_media_query_results == key {
|
||||
return Ok(entry.clone());
|
||||
if entry.cascade_data().effective_media_query_results == key {
|
||||
return Ok(Some(entry.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_data = UserAgentCascadeData {
|
||||
cascade_data: CascadeData::new(),
|
||||
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
|
||||
};
|
||||
|
||||
debug!("> Picking the slow path");
|
||||
|
||||
for sheet in sheets {
|
||||
new_data.cascade_data.add_stylesheet(
|
||||
device,
|
||||
quirks_mode,
|
||||
sheet,
|
||||
guard,
|
||||
SheetRebuildKind::Full,
|
||||
Some(&mut new_data.precomputed_pseudo_element_decls),
|
||||
)?;
|
||||
}
|
||||
let new_entry = Entry::rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
collection,
|
||||
guard,
|
||||
old_entry,
|
||||
)?;
|
||||
|
||||
let new_data = Arc::new(new_data);
|
||||
self.entries.push(new_data.clone());
|
||||
Ok(new_data)
|
||||
self.entries.push(new_entry.clone());
|
||||
Ok(Some(new_entry))
|
||||
}
|
||||
|
||||
/// Returns all the cascade datas that are not being used (that is, that are
|
||||
|
@ -135,7 +148,7 @@ impl UserAgentCascadeDataCache {
|
|||
/// keep alive some other documents (like the SVG documents kept alive by
|
||||
/// URL references), and thus we don't want to drop them while locking the
|
||||
/// cache to not deadlock.
|
||||
fn take_unused(&mut self) -> SmallVec<[Arc<UserAgentCascadeData>; 3]> {
|
||||
fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
|
||||
let mut unused = SmallVec::new();
|
||||
for i in (0..self.entries.len()).rev() {
|
||||
// is_unique() returns false for static references, but we never
|
||||
|
@ -148,7 +161,7 @@ impl UserAgentCascadeDataCache {
|
|||
unused
|
||||
}
|
||||
|
||||
fn take_all(&mut self) -> Vec<Arc<UserAgentCascadeData>> {
|
||||
fn take_all(&mut self) -> Vec<Arc<Entry>> {
|
||||
mem::replace(&mut self.entries, Vec::new())
|
||||
}
|
||||
|
||||
|
@ -173,6 +186,58 @@ pub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSet
|
|||
.add_size_of(ops, sizes);
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// A cache of computed user-agent data, to be shared across documents.
|
||||
static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
|
||||
Mutex::new(UserAgentCascadeDataCache::new());
|
||||
}
|
||||
|
||||
impl CascadeDataCacheEntry for UserAgentCascadeData {
|
||||
fn cascade_data(&self) -> &CascadeData {
|
||||
&self.cascade_data
|
||||
}
|
||||
|
||||
fn rebuild<S>(
|
||||
device: &Device,
|
||||
quirks_mode: QuirksMode,
|
||||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
_old: &Self,
|
||||
) -> Result<Arc<Self>, FailedAllocationError>
|
||||
where
|
||||
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static
|
||||
{
|
||||
// TODO: Maybe we should support incremental rebuilds, though they seem
|
||||
// uncommon and rebuild() doesn't deal with
|
||||
// precomputed_pseudo_element_decls for now so...
|
||||
let mut new_data = Self {
|
||||
cascade_data: CascadeData::new(),
|
||||
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
|
||||
};
|
||||
|
||||
for sheet in collection.sheets() {
|
||||
new_data.cascade_data.add_stylesheet(
|
||||
device,
|
||||
quirks_mode,
|
||||
sheet,
|
||||
guard,
|
||||
SheetRebuildKind::Full,
|
||||
Some(&mut new_data.precomputed_pseudo_element_decls),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(Arc::new(new_data))
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||
self.cascade_data.add_size_of(ops, sizes);
|
||||
sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
|
||||
}
|
||||
}
|
||||
|
||||
type UserAgentCascadeDataCache = CascadeDataCache<UserAgentCascadeData>;
|
||||
|
||||
type PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -188,14 +253,6 @@ struct UserAgentCascadeData {
|
|||
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
|
||||
}
|
||||
|
||||
impl UserAgentCascadeData {
|
||||
#[cfg(feature = "gecko")]
|
||||
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||
self.cascade_data.add_size_of(ops, sizes);
|
||||
sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
|
||||
}
|
||||
}
|
||||
|
||||
/// All the computed information for all the stylesheets that apply to the
|
||||
/// document.
|
||||
#[derive(Default)]
|
||||
|
@ -266,15 +323,23 @@ impl DocumentCascadeData {
|
|||
{
|
||||
// First do UA sheets.
|
||||
{
|
||||
if flusher.flush_origin(Origin::UserAgent).dirty() {
|
||||
let origin_sheets = flusher.origin_sheets(Origin::UserAgent);
|
||||
let _unused_cascade_datas = {
|
||||
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
|
||||
self.user_agent =
|
||||
ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?;
|
||||
debug!("User agent data cache size {:?}", ua_cache.len());
|
||||
ua_cache.take_unused()
|
||||
};
|
||||
let origin_flusher = flusher.flush_origin(Origin::UserAgent);
|
||||
if origin_flusher.dirty() {
|
||||
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
|
||||
let new_data = ua_cache.lookup(
|
||||
device,
|
||||
quirks_mode,
|
||||
origin_flusher,
|
||||
guards.ua_or_user,
|
||||
&self.user_agent,
|
||||
)?;
|
||||
if let Some(new_data) = new_data {
|
||||
self.user_agent = new_data;
|
||||
}
|
||||
let _unused_entries = ua_cache.take_unused();
|
||||
// See the comments in take_unused() as for why the following
|
||||
// line.
|
||||
std::mem::drop(ua_cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1836,18 +1901,21 @@ impl CascadeData {
|
|||
DataValidity::FullyInvalid => self.clear(),
|
||||
}
|
||||
|
||||
for (stylesheet, rebuild_kind) in collection {
|
||||
self.add_stylesheet(
|
||||
let mut result = Ok(());
|
||||
|
||||
collection.each(|stylesheet, rebuild_kind| {
|
||||
result = self.add_stylesheet(
|
||||
device,
|
||||
quirks_mode,
|
||||
stylesheet,
|
||||
guard,
|
||||
rebuild_kind,
|
||||
/* precomputed_pseudo_element_decls = */ None,
|
||||
)?;
|
||||
}
|
||||
);
|
||||
result.is_ok()
|
||||
});
|
||||
|
||||
Ok(())
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns the invalidation map.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue