mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
style: Share user agent cascade data across documents.
MozReview-Commit-ID: KcyuTHD0bt9 Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
985c6193eb
commit
cb31e782aa
4 changed files with 326 additions and 118 deletions
|
@ -337,7 +337,7 @@ impl<T: ?Sized> Arc<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_unique(&self) -> bool {
|
pub fn is_unique(&self) -> bool {
|
||||||
// We can use Relaxed here, but the justification is a bit subtle.
|
// We can use Relaxed here, but the justification is a bit subtle.
|
||||||
//
|
//
|
||||||
// The reason to use Acquire would be to synchronize with other threads
|
// The reason to use Acquire would be to synchronize with other threads
|
||||||
|
|
|
@ -53,7 +53,7 @@ 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)]
|
#[derive(Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct EffectiveMediaQueryResults {
|
pub struct EffectiveMediaQueryResults {
|
||||||
|
|
|
@ -31,11 +31,20 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
|
pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static;
|
S: StylesheetInDocument + PartialEq + 'static;
|
||||||
|
|
||||||
|
impl<'a, S> Clone for StylesheetCollectionIterator<'a, S>
|
||||||
|
where
|
||||||
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
StylesheetCollectionIterator(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, S> Iterator for StylesheetCollectionIterator<'a, S>
|
impl<'a, S> Iterator for StylesheetCollectionIterator<'a, S>
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static,
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
|
@ -119,11 +128,8 @@ pub struct StylesheetFlusher<'a, S>
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static,
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
{
|
{
|
||||||
origins_dirty: OriginSetIterator,
|
origins_dirty: OriginSet,
|
||||||
// NB: Bound to the StylesheetSet lifetime when constructed, see
|
collections: &'a mut PerOrigin<SheetCollection<S>>,
|
||||||
// StylesheetSet::flush.
|
|
||||||
collections: *mut PerOrigin<SheetCollection<S>>,
|
|
||||||
current: Option<(Origin, slice::IterMut<'a, StylesheetSetEntry<S>>)>,
|
|
||||||
origin_data_validity: PerOrigin<OriginValidity>,
|
origin_data_validity: PerOrigin<OriginValidity>,
|
||||||
author_style_disabled: bool,
|
author_style_disabled: bool,
|
||||||
had_invalidations: bool,
|
had_invalidations: bool,
|
||||||
|
@ -153,9 +159,56 @@ where
|
||||||
*self.origin_data_validity.borrow_for_origin(&origin)
|
*self.origin_data_validity.borrow_for_origin(&origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the origin data is dirty in any way.
|
||||||
|
pub fn origin_dirty(&self, origin: Origin) -> bool {
|
||||||
|
self.origins_dirty.contains(origin.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 `dirty` member of the Entry is reset to
|
||||||
|
// `false`).
|
||||||
|
//
|
||||||
|
// In practice it doesn't matter for correctness given our use of it
|
||||||
|
// (that this is UA only).
|
||||||
|
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.origin_validity(origin);
|
||||||
|
let origin_dirty = self.origins_dirty.contains(origin.into());
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
origin_dirty || validity == OriginValidity::Valid,
|
||||||
|
"origin_data_validity should be a subset of origins_dirty!"
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.author_style_disabled && origin == Origin::Author {
|
||||||
|
return PerOriginFlusher {
|
||||||
|
iter: [].iter_mut(),
|
||||||
|
validity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.
|
/// Returns whether running the whole flushing process would be a no-op.
|
||||||
pub fn nothing_to_do(&self) -> bool {
|
pub fn nothing_to_do(&self) -> bool {
|
||||||
self.origins_dirty.clone().next().is_none()
|
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
|
||||||
|
@ -165,81 +218,44 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
/// A flusher struct for a given origin, that takes care of returning the
|
||||||
impl<'a, S> Drop for StylesheetFlusher<'a, S>
|
/// appropriate stylesheets that need work.
|
||||||
|
pub struct PerOriginFlusher<'a, S>
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static,
|
S: StylesheetInDocument + PartialEq + 'static
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
|
||||||
debug_assert!(
|
validity: OriginValidity,
|
||||||
self.origins_dirty.next().is_none(),
|
|
||||||
"You're supposed to fully consume the flusher"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S> Iterator for StylesheetFlusher<'a, S>
|
impl<'a, S> Iterator for PerOriginFlusher<'a, S>
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + PartialEq + 'static,
|
S: StylesheetInDocument + PartialEq + 'static,
|
||||||
{
|
{
|
||||||
type Item = (&'a S, Origin, SheetRebuildKind);
|
type Item = (&'a S, SheetRebuildKind);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if self.current.is_none() {
|
let potential_sheet = match self.iter.next() {
|
||||||
let next_origin = match self.origins_dirty.next() {
|
|
||||||
Some(o) => o,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Should've been cleared already.
|
|
||||||
debug_assert_eq!(
|
|
||||||
unsafe { &*self.collections }
|
|
||||||
.borrow_for_origin(&next_origin)
|
|
||||||
.data_validity,
|
|
||||||
OriginValidity::Valid
|
|
||||||
);
|
|
||||||
|
|
||||||
self.current =
|
|
||||||
Some((
|
|
||||||
next_origin,
|
|
||||||
unsafe { &mut *self.collections }
|
|
||||||
.borrow_mut_for_origin(&next_origin)
|
|
||||||
.entries
|
|
||||||
.iter_mut()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let potential_sheet = match self.current.as_mut().unwrap().1.next() {
|
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => {
|
None => return None,
|
||||||
self.current = None;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let origin = self.current.as_ref().unwrap().0;
|
|
||||||
|
|
||||||
let dirty = mem::replace(&mut potential_sheet.dirty, false);
|
let dirty = mem::replace(&mut potential_sheet.dirty, false);
|
||||||
|
|
||||||
if dirty {
|
if dirty {
|
||||||
// If the sheet was dirty, we need to do a full rebuild anyway.
|
// If the sheet was dirty, we need to do a full rebuild anyway.
|
||||||
return Some((&potential_sheet.sheet, origin, SheetRebuildKind::Full))
|
return Some((&potential_sheet.sheet, SheetRebuildKind::Full))
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.author_style_disabled && matches!(origin, Origin::Author) {
|
let rebuild_kind = match self.validity {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rebuild_kind = match self.origin_validity(origin) {
|
|
||||||
OriginValidity::Valid => continue,
|
OriginValidity::Valid => continue,
|
||||||
OriginValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
OriginValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
||||||
OriginValidity::FullyInvalid => SheetRebuildKind::Full,
|
OriginValidity::FullyInvalid => SheetRebuildKind::Full,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Some((&potential_sheet.sheet, origin, rebuild_kind));
|
return Some((&potential_sheet.sheet, rebuild_kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,10 +517,10 @@ where
|
||||||
|
|
||||||
let had_invalidations = self.invalidations.flush(document_element);
|
let had_invalidations = self.invalidations.flush(document_element);
|
||||||
let origins_dirty =
|
let origins_dirty =
|
||||||
mem::replace(&mut self.origins_dirty, OriginSet::empty()).iter();
|
mem::replace(&mut self.origins_dirty, OriginSet::empty());
|
||||||
|
|
||||||
let mut origin_data_validity = PerOrigin::<OriginValidity>::default();
|
let mut origin_data_validity = PerOrigin::<OriginValidity>::default();
|
||||||
for origin in origins_dirty.clone() {
|
for origin in origins_dirty.iter() {
|
||||||
let collection = self.collections.borrow_mut_for_origin(&origin);
|
let collection = self.collections.borrow_mut_for_origin(&origin);
|
||||||
*origin_data_validity.borrow_mut_for_origin(&origin) =
|
*origin_data_validity.borrow_mut_for_origin(&origin) =
|
||||||
mem::replace(&mut collection.data_validity, OriginValidity::Valid);
|
mem::replace(&mut collection.data_validity, OriginValidity::Valid);
|
||||||
|
@ -516,7 +532,6 @@ where
|
||||||
had_invalidations,
|
had_invalidations,
|
||||||
origins_dirty,
|
origins_dirty,
|
||||||
origin_data_validity,
|
origin_data_validity,
|
||||||
current: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ use smallbitvec::SmallBitVec;
|
||||||
use smallvec::VecLike;
|
use smallvec::VecLike;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
use std::sync::Mutex;
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetFlusher};
|
use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetFlusher};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -61,15 +62,83 @@ pub type StylistSheet = ::stylesheets::DocumentStyleSheet;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub type StylistSheet = ::gecko::data::GeckoStyleSheet;
|
pub type StylistSheet = ::gecko::data::GeckoStyleSheet;
|
||||||
|
|
||||||
|
/// A cache of computed user-agent data, to be shared across documents.
|
||||||
|
lazy_static! {
|
||||||
|
static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
|
||||||
|
Mutex::new(UserAgentCascadeDataCache::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UserAgentCascadeDataCache {
|
||||||
|
entries: Vec<Arc<UserAgentCascadeData>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserAgentCascadeDataCache {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
entries: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup<'a, I, S>(
|
||||||
|
&'a mut self,
|
||||||
|
sheets: I,
|
||||||
|
device: &Device,
|
||||||
|
quirks_mode: QuirksMode,
|
||||||
|
guard: &SharedRwLockReadGuard,
|
||||||
|
) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'a S> + Clone,
|
||||||
|
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
|
||||||
|
{
|
||||||
|
let mut key = EffectiveMediaQueryResults::new();
|
||||||
|
for sheet in sheets.clone() {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_data = UserAgentCascadeData {
|
||||||
|
cascade_data: CascadeData::new(),
|
||||||
|
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
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_data = Arc::new(new_data);
|
||||||
|
|
||||||
|
self.entries.push(new_data.clone());
|
||||||
|
Ok(new_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expire_unused(&mut self) {
|
||||||
|
self.entries.retain(|e| !e.is_unique())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PrecomputedPseudoElementDeclarations =
|
type PrecomputedPseudoElementDeclarations =
|
||||||
PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;
|
PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;
|
||||||
|
|
||||||
/// All the computed information for a stylesheet.
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
struct UserAgentCascadeData {
|
||||||
struct DocumentCascadeData {
|
cascade_data: CascadeData,
|
||||||
/// Common data for all the origins.
|
|
||||||
per_origin: PerOrigin<CascadeData>,
|
|
||||||
|
|
||||||
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
||||||
///
|
///
|
||||||
|
@ -77,18 +146,98 @@ struct DocumentCascadeData {
|
||||||
/// computed values on the fly on layout.
|
/// computed values on the fly on layout.
|
||||||
///
|
///
|
||||||
/// These are only filled from UA stylesheets.
|
/// These are only filled from UA stylesheets.
|
||||||
///
|
|
||||||
/// FIXME(emilio): Use the rule tree!
|
|
||||||
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
|
precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All the computed information for a stylesheet.
|
||||||
|
#[derive(Default)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
struct DocumentCascadeData {
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "servo",
|
||||||
|
ignore_heap_size_of = "Arc, owned by UserAgentCascadeDataCache"
|
||||||
|
)]
|
||||||
|
user_agent: Arc<UserAgentCascadeData>,
|
||||||
|
user: CascadeData,
|
||||||
|
author: CascadeData,
|
||||||
|
per_origin: PerOrigin<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DocumentCascadeDataIter<'a> {
|
||||||
|
iter: PerOriginIter<'a, ()>,
|
||||||
|
cascade_data: &'a DocumentCascadeData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for DocumentCascadeDataIter<'a> {
|
||||||
|
type Item = (&'a CascadeData, Origin);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let (_, origin) = match self.iter.next() {
|
||||||
|
Some(o) => o,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((self.cascade_data.borrow_for_origin(origin), origin))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DocumentCascadeData {
|
impl DocumentCascadeData {
|
||||||
fn iter_origins(&self) -> PerOriginIter<CascadeData> {
|
fn borrow_for_origin(&self, origin: Origin) -> &CascadeData {
|
||||||
self.per_origin.iter_origins()
|
match origin {
|
||||||
|
Origin::UserAgent => &self.user_agent.cascade_data,
|
||||||
|
Origin::Author => &self.author,
|
||||||
|
Origin::User => &self.user,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_origins_rev(&self) -> PerOriginIter<CascadeData> {
|
fn iter_origins(&self) -> DocumentCascadeDataIter {
|
||||||
self.per_origin.iter_origins_rev()
|
DocumentCascadeDataIter {
|
||||||
|
iter: self.per_origin.iter_origins(),
|
||||||
|
cascade_data: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter_origins_rev(&self) -> DocumentCascadeDataIter {
|
||||||
|
DocumentCascadeDataIter {
|
||||||
|
iter: self.per_origin.iter_origins_rev(),
|
||||||
|
cascade_data: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
let validity = flusher.origin_validity(origin);
|
||||||
|
|
||||||
|
match validity {
|
||||||
|
OriginValidity::Valid => {},
|
||||||
|
OriginValidity::CascadeInvalid => cascade_data.clear_cascade_data(),
|
||||||
|
OriginValidity::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
|
||||||
|
@ -98,7 +247,7 @@ impl DocumentCascadeData {
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &Device,
|
device: &Device,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
flusher: StylesheetFlusher<'a, S>,
|
mut flusher: StylesheetFlusher<'a, S>,
|
||||||
guards: &StylesheetGuards,
|
guards: &StylesheetGuards,
|
||||||
) -> Result<(), FailedAllocationError>
|
) -> Result<(), FailedAllocationError>
|
||||||
where
|
where
|
||||||
|
@ -106,55 +255,56 @@ impl DocumentCascadeData {
|
||||||
{
|
{
|
||||||
debug_assert!(!flusher.nothing_to_do());
|
debug_assert!(!flusher.nothing_to_do());
|
||||||
|
|
||||||
for (cascade_data, origin) in self.per_origin.iter_mut_origins() {
|
// First do UA sheets.
|
||||||
let validity = flusher.origin_validity(origin);
|
{
|
||||||
|
if flusher.origin_dirty(Origin::UserAgent) {
|
||||||
|
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
|
||||||
|
let origin_sheets =
|
||||||
|
flusher.manual_origin_sheets(Origin::UserAgent);
|
||||||
|
|
||||||
if validity == OriginValidity::Valid {
|
let ua_cascade_data = ua_cache.lookup(
|
||||||
continue;
|
origin_sheets,
|
||||||
}
|
|
||||||
|
|
||||||
if origin == Origin::UserAgent {
|
|
||||||
self.precomputed_pseudo_element_decls.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if validity == OriginValidity::CascadeInvalid {
|
|
||||||
cascade_data.clear_cascade_data()
|
|
||||||
} else {
|
|
||||||
debug_assert_eq!(validity, OriginValidity::FullyInvalid);
|
|
||||||
cascade_data.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (stylesheet, origin, rebuild_kind) in flusher {
|
|
||||||
let guard = guards.for_origin(origin);
|
|
||||||
let origin = stylesheet.origin(guard);
|
|
||||||
self.per_origin
|
|
||||||
.borrow_mut_for_origin(&origin)
|
|
||||||
.add_stylesheet(
|
|
||||||
device,
|
device,
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
stylesheet,
|
guards.ua_or_user
|
||||||
guard,
|
|
||||||
rebuild_kind,
|
|
||||||
&mut self.precomputed_pseudo_element_decls,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
ua_cache.expire_unused();
|
||||||
|
self.user_agent = ua_cascade_data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now do the user sheets.
|
||||||
|
Self::rebuild_origin(
|
||||||
|
device,
|
||||||
|
quirks_mode,
|
||||||
|
&mut flusher,
|
||||||
|
guards,
|
||||||
|
Origin::User,
|
||||||
|
&mut self.user,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// And now the author sheets.
|
||||||
|
Self::rebuild_origin(
|
||||||
|
device,
|
||||||
|
quirks_mode,
|
||||||
|
&mut flusher,
|
||||||
|
guards,
|
||||||
|
Origin::Author,
|
||||||
|
&mut self.author,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Measures heap usage.
|
/// Measures heap usage.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn add_size_of_children(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
pub fn add_size_of_children(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||||
self.per_origin.user_agent.add_size_of_children(ops, sizes);
|
self.user.add_size_of_children(ops, sizes);
|
||||||
self.per_origin.user.add_size_of_children(ops, sizes);
|
self.author.add_size_of_children(ops, sizes);
|
||||||
self.per_origin.author.add_size_of_children(ops, sizes);
|
|
||||||
|
|
||||||
for elem in self.precomputed_pseudo_element_decls.iter() {
|
// FIXME(emilio): UA_CASCADE_DATA_CACHE is shared, we should do whatever
|
||||||
if let Some(ref elem) = *elem {
|
// we do for RuleProcessorCache in Gecko.
|
||||||
sizes.mStylistPrecomputedPseudos += elem.shallow_size_of(ops);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +706,7 @@ impl Stylist {
|
||||||
extra_declarations: Option<Vec<ApplicableDeclarationBlock>>,
|
extra_declarations: Option<Vec<ApplicableDeclarationBlock>>,
|
||||||
) -> StrongRuleNode {
|
) -> StrongRuleNode {
|
||||||
let mut decl;
|
let mut decl;
|
||||||
let declarations = match self.cascade_data.precomputed_pseudo_element_decls.get(pseudo) {
|
let declarations = match self.cascade_data.user_agent.precomputed_pseudo_element_decls.get(pseudo) {
|
||||||
Some(declarations) => {
|
Some(declarations) => {
|
||||||
match extra_declarations {
|
match extra_declarations {
|
||||||
Some(mut extra_decls) => {
|
Some(mut extra_decls) => {
|
||||||
|
@ -975,7 +1125,7 @@ impl Stylist {
|
||||||
|
|
||||||
let guard = guards.for_origin(origin);
|
let guard = guards.for_origin(origin);
|
||||||
let origin_cascade_data =
|
let origin_cascade_data =
|
||||||
self.cascade_data.per_origin.borrow_for_origin(&origin);
|
self.cascade_data.borrow_for_origin(origin);
|
||||||
|
|
||||||
let affected_changed = !origin_cascade_data.media_feature_affected_matches(
|
let affected_changed = !origin_cascade_data.media_feature_affected_matches(
|
||||||
stylesheet,
|
stylesheet,
|
||||||
|
@ -1034,7 +1184,7 @@ impl Stylist {
|
||||||
|
|
||||||
// nsXBLPrototypeResources::LoadResources() loads Chrome XBL style
|
// nsXBLPrototypeResources::LoadResources() loads Chrome XBL style
|
||||||
// sheets under eAuthorSheetFeatures level.
|
// sheets under eAuthorSheetFeatures level.
|
||||||
if let Some(map) = self.cascade_data.per_origin.author.borrow_for_pseudo(pseudo_element) {
|
if let Some(map) = self.cascade_data.author.borrow_for_pseudo(pseudo_element) {
|
||||||
map.get_all_matching_rules(
|
map.get_all_matching_rules(
|
||||||
element,
|
element,
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
|
@ -1082,7 +1232,7 @@ impl Stylist {
|
||||||
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
|
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
|
||||||
|
|
||||||
// Step 1: Normal user-agent rules.
|
// Step 1: Normal user-agent rules.
|
||||||
if let Some(map) = self.cascade_data.per_origin.user_agent.borrow_for_pseudo(pseudo_element) {
|
if let Some(map) = self.cascade_data.user_agent.cascade_data.borrow_for_pseudo(pseudo_element) {
|
||||||
map.get_all_matching_rules(
|
map.get_all_matching_rules(
|
||||||
element,
|
element,
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
|
@ -1120,7 +1270,7 @@ impl Stylist {
|
||||||
// Which may be more what you would probably expect.
|
// Which may be more what you would probably expect.
|
||||||
if rule_hash_target.matches_user_and_author_rules() {
|
if rule_hash_target.matches_user_and_author_rules() {
|
||||||
// Step 3a: User normal rules.
|
// Step 3a: User normal rules.
|
||||||
if let Some(map) = self.cascade_data.per_origin.user.borrow_for_pseudo(pseudo_element) {
|
if let Some(map) = self.cascade_data.user.borrow_for_pseudo(pseudo_element) {
|
||||||
map.get_all_matching_rules(
|
map.get_all_matching_rules(
|
||||||
element,
|
element,
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
|
@ -1147,7 +1297,7 @@ impl Stylist {
|
||||||
// See nsStyleSet::FileRules().
|
// See nsStyleSet::FileRules().
|
||||||
if !cut_off_inheritance {
|
if !cut_off_inheritance {
|
||||||
// Step 3c: Author normal rules.
|
// Step 3c: Author normal rules.
|
||||||
if let Some(map) = self.cascade_data.per_origin.author.borrow_for_pseudo(pseudo_element) {
|
if let Some(map) = self.cascade_data.author.borrow_for_pseudo(pseudo_element) {
|
||||||
map.get_all_matching_rules(
|
map.get_all_matching_rules(
|
||||||
element,
|
element,
|
||||||
&rule_hash_target,
|
&rule_hash_target,
|
||||||
|
@ -1380,6 +1530,8 @@ pub struct ExtraStyleData {
|
||||||
// nsCSSFontFaceRules or nsCSSCounterStyleRules OMT (which we don't).
|
// nsCSSFontFaceRules or nsCSSCounterStyleRules OMT (which we don't).
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
unsafe impl Sync for ExtraStyleData {}
|
unsafe impl Sync for ExtraStyleData {}
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
unsafe impl Send for ExtraStyleData {}
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
impl ExtraStyleData {
|
impl ExtraStyleData {
|
||||||
|
@ -1422,7 +1574,7 @@ impl ExtraStyleData {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over the different ExtraStyleData.
|
/// An iterator over the different ExtraStyleData.
|
||||||
pub struct ExtraStyleDataIterator<'a>(PerOriginIter<'a, CascadeData>);
|
pub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);
|
||||||
|
|
||||||
impl<'a> Iterator for ExtraStyleDataIterator<'a> {
|
impl<'a> Iterator for ExtraStyleDataIterator<'a> {
|
||||||
type Item = (&'a ExtraStyleData, Origin);
|
type Item = (&'a ExtraStyleData, Origin);
|
||||||
|
@ -1712,6 +1864,45 @@ impl CascadeData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collects all the applicable media query results into `results`.
|
||||||
|
///
|
||||||
|
/// This duplicates part of the logic in `add_stylesheet`, which is
|
||||||
|
/// a bit unfortunate.
|
||||||
|
///
|
||||||
|
/// FIXME(emilio): With a bit of smartness in
|
||||||
|
/// `media_feature_affected_matches`, we could convert
|
||||||
|
/// `EffectiveMediaQueryResults` into a vector without too much effort.
|
||||||
|
fn collect_applicable_media_query_results_into<S>(
|
||||||
|
device: &Device,
|
||||||
|
stylesheet: &S,
|
||||||
|
guard: &SharedRwLockReadGuard,
|
||||||
|
results: &mut EffectiveMediaQueryResults,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||||
|
{
|
||||||
|
if !stylesheet.enabled() ||
|
||||||
|
!stylesheet.is_effective_for_device(device, guard) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.saw_effective(stylesheet);
|
||||||
|
|
||||||
|
for rule in stylesheet.effective_rules(device, guard) {
|
||||||
|
match *rule {
|
||||||
|
CssRule::Import(ref lock) => {
|
||||||
|
let import_rule = lock.read_with(guard);
|
||||||
|
results.saw_effective(import_rule);
|
||||||
|
}
|
||||||
|
CssRule::Media(ref lock) => {
|
||||||
|
let media_rule = lock.read_with(guard);
|
||||||
|
results.saw_effective(media_rule);
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns Err(..) to signify OOM
|
// Returns Err(..) to signify OOM
|
||||||
fn add_stylesheet<S>(
|
fn add_stylesheet<S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1720,7 +1911,7 @@ impl CascadeData {
|
||||||
stylesheet: &S,
|
stylesheet: &S,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
rebuild_kind: SheetRebuildKind,
|
rebuild_kind: SheetRebuildKind,
|
||||||
precomputed_pseudo_element_decls: &mut PrecomputedPseudoElementDeclarations,
|
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
|
||||||
) -> Result<(), FailedAllocationError>
|
) -> Result<(), FailedAllocationError>
|
||||||
where
|
where
|
||||||
S: StylesheetInDocument + ToMediaListKey + 'static,
|
S: StylesheetInDocument + ToMediaListKey + 'static,
|
||||||
|
@ -1756,6 +1947,8 @@ impl CascadeData {
|
||||||
}
|
}
|
||||||
|
|
||||||
precomputed_pseudo_element_decls
|
precomputed_pseudo_element_decls
|
||||||
|
.as_mut()
|
||||||
|
.expect("Expected precomputed declarations for the UA level")
|
||||||
.get_or_insert_with(&pseudo.canonical(), Vec::new)
|
.get_or_insert_with(&pseudo.canonical(), Vec::new)
|
||||||
.expect("Unexpected tree pseudo-element?")
|
.expect("Unexpected tree pseudo-element?")
|
||||||
.push(ApplicableDeclarationBlock::new(
|
.push(ApplicableDeclarationBlock::new(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue