зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #20004 - style: Add infrastructure to have a standalone author stylesheet collection (from emilio:author-sheet-collection); r=nox
Right now Gecko uses a whole `Stylist` for stuff like XBL / Shadow DOM. That's not great, because it has tons of unrelated logic, and also eats up a lot of memory. Also, it prevents us to optimize style changes in shadow hosts the same way we do for the document. These patches mostly rejigger stuff around so that you can define a `DocumentStylesheetSet` and then an `AuthorStylesheetSet`, which would contain just the Shadow DOM sheets / XBL resource sheets. It still doesn't introduce any use for the later, but that will come later. There's a patch in this PR that requires Gecko changes, posted in https://bugzilla.mozilla.org/show_bug.cgi?id=1436798. Source-Repo: https://github.com/servo/servo Source-Revision: 6b1a74672d6b9d9c55b027b147b0a6eb8f727bfd --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 35cd629f88c24439c615e283265dbb78d33279b7
This commit is contained in:
Родитель
60bae57dc0
Коммит
d9b65d628d
|
@ -105,7 +105,7 @@ use style::media_queries::MediaList;
|
|||
use style::properties::PropertyDeclarationBlock;
|
||||
use style::selector_parser::{PseudoElement, Snapshot};
|
||||
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
|
||||
use style::stylesheet_set::StylesheetSet;
|
||||
use style::stylesheet_set::DocumentStylesheetSet;
|
||||
use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule, Stylesheet};
|
||||
use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule, ViewportRule};
|
||||
use style::stylesheets::keyframes_rule::Keyframe;
|
||||
|
@ -659,7 +659,7 @@ unsafe impl JSTraceable for StyleLocked<MediaList> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<S> JSTraceable for StylesheetSet<S>
|
||||
unsafe impl<S> JSTraceable for DocumentStylesheetSet<S>
|
||||
where
|
||||
S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
|
|
|
@ -135,7 +135,7 @@ use style::media_queries::{Device, MediaList, MediaType};
|
|||
use style::selector_parser::{RestyleDamage, Snapshot};
|
||||
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
|
||||
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
|
||||
use style::stylesheet_set::StylesheetSet;
|
||||
use style::stylesheet_set::DocumentStylesheetSet;
|
||||
use style::stylesheets::{Stylesheet, StylesheetContents, Origin, OriginSet};
|
||||
use task_source::TaskSource;
|
||||
use time;
|
||||
|
@ -262,7 +262,7 @@ pub struct Document {
|
|||
/// Can be acquired once for accessing many objects.
|
||||
style_shared_lock: StyleSharedRwLock,
|
||||
/// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed.
|
||||
stylesheets: DomRefCell<StylesheetSet<StyleSheetInDocument>>,
|
||||
stylesheets: DomRefCell<DocumentStylesheetSet<StyleSheetInDocument>>,
|
||||
stylesheet_list: MutNullableDom<StyleSheetList>,
|
||||
ready_state: Cell<DocumentReadyState>,
|
||||
/// Whether the DOMContentLoaded event has already been dispatched.
|
||||
|
@ -1466,7 +1466,7 @@ impl Document {
|
|||
|
||||
// Mark the document element dirty so a reflow will be performed.
|
||||
//
|
||||
// FIXME(emilio): Use the StylesheetSet invalidation stuff.
|
||||
// FIXME(emilio): Use the DocumentStylesheetSet invalidation stuff.
|
||||
if let Some(element) = self.GetDocumentElement() {
|
||||
element.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged);
|
||||
}
|
||||
|
@ -2264,7 +2264,7 @@ impl Document {
|
|||
PER_PROCESS_AUTHOR_SHARED_LOCK.clone()
|
||||
//StyleSharedRwLock::new()
|
||||
},
|
||||
stylesheets: DomRefCell::new(StylesheetSet::new()),
|
||||
stylesheets: DomRefCell::new(DocumentStylesheetSet::new()),
|
||||
stylesheet_list: MutNullableDom::new(None),
|
||||
ready_state: Cell::new(ready_state),
|
||||
domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
|
||||
|
@ -3807,7 +3807,7 @@ impl DocumentMethods for Document {
|
|||
self.scripts.set(None);
|
||||
self.anchors.set(None);
|
||||
self.applets.set(None);
|
||||
*self.stylesheets.borrow_mut() = StylesheetSet::new();
|
||||
*self.stylesheets.borrow_mut() = DocumentStylesheetSet::new();
|
||||
self.animation_frame_ident.set(0);
|
||||
self.animation_frame_list.borrow_mut().clear();
|
||||
self.pending_restyles.borrow_mut().clear();
|
||||
|
|
|
@ -2130,9 +2130,14 @@ extern "C" {
|
|||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_StyleSet_NoteStyleSheetsChanged(
|
||||
pub fn Servo_StyleSet_SetAuthorStyleDisabled(
|
||||
set: RawServoStyleSetBorrowed,
|
||||
author_style_disabled: bool,
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn Servo_StyleSet_NoteStyleSheetsChanged(
|
||||
set: RawServoStyleSetBorrowed,
|
||||
changed_origins: OriginFlags,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use invalidation::stylesheets::StylesheetInvalidationSet;
|
|||
use media_queries::Device;
|
||||
use selector_parser::SnapshotMap;
|
||||
use shared_lock::SharedRwLockReadGuard;
|
||||
use std::slice;
|
||||
use std::{mem, slice};
|
||||
use stylesheets::{Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument};
|
||||
|
||||
/// Entry for a StylesheetSet.
|
||||
|
@ -105,7 +105,7 @@ where
|
|||
/// The validity of the data in a given cascade origin.
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
pub enum OriginValidity {
|
||||
pub enum DataValidity {
|
||||
/// The origin is clean, all the data already there is valid, though we may
|
||||
/// have new sheets at the end.
|
||||
Valid = 0,
|
||||
|
@ -118,21 +118,18 @@ pub enum OriginValidity {
|
|||
FullyInvalid = 2,
|
||||
}
|
||||
|
||||
impl Default for OriginValidity {
|
||||
impl Default for DataValidity {
|
||||
fn default() -> Self {
|
||||
OriginValidity::Valid
|
||||
DataValidity::Valid
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct to iterate over the different stylesheets to be flushed.
|
||||
pub struct StylesheetFlusher<'a, S>
|
||||
pub struct DocumentStylesheetFlusher<'a, S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
origins_dirty: OriginSet,
|
||||
collections: &'a mut PerOrigin<SheetCollection<S>>,
|
||||
origin_data_validity: PerOrigin<OriginValidity>,
|
||||
author_style_disabled: bool,
|
||||
had_invalidations: bool,
|
||||
}
|
||||
|
||||
|
@ -152,85 +149,59 @@ impl SheetRebuildKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S> StylesheetFlusher<'a, S>
|
||||
impl<'a, S> DocumentStylesheetFlusher<'a, S>
|
||||
where
|
||||
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 a flusher for `origin`.
|
||||
pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<S> {
|
||||
self.collections.borrow_mut_for_origin(&origin).flush()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn nothing_to_do(&self) -> bool {
|
||||
self.origins_dirty.is_empty()
|
||||
/// Returns the list of stylesheets for `origin`.
|
||||
///
|
||||
/// Only used for UA sheets.
|
||||
pub fn origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator<S> {
|
||||
self.collections.borrow_mut_for_origin(&origin).iter()
|
||||
}
|
||||
|
||||
/// Returns whether any DOM invalidations were processed as a result of the
|
||||
/// stylesheet flush.
|
||||
#[inline]
|
||||
pub fn had_invalidations(&self) -> bool {
|
||||
self.had_invalidations
|
||||
}
|
||||
}
|
||||
|
||||
/// A flusher struct for a given origin, that takes care of returning the
|
||||
/// A flusher struct for a given collection, that takes care of returning the
|
||||
/// appropriate stylesheets that need work.
|
||||
pub struct PerOriginFlusher<'a, S>
|
||||
pub struct SheetCollectionFlusher<'a, S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static
|
||||
{
|
||||
iter: slice::IterMut<'a, StylesheetSetEntry<S>>,
|
||||
validity: OriginValidity,
|
||||
validity: DataValidity,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl<'a, S> Iterator for PerOriginFlusher<'a, S>
|
||||
impl<'a, S> SheetCollectionFlusher<'a, S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
/// Whether the collection was originally dirty.
|
||||
#[inline]
|
||||
pub fn dirty(&self) -> bool {
|
||||
self.dirty
|
||||
}
|
||||
|
||||
/// What the state of the sheet data is.
|
||||
#[inline]
|
||||
pub fn data_validity(&self) -> DataValidity {
|
||||
self.validity
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Iterator for SheetCollectionFlusher<'a, S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
|
@ -244,14 +215,15 @@ where
|
|||
|
||||
let committed = mem::replace(&mut potential_sheet.committed, true);
|
||||
if !committed {
|
||||
// If the sheet was uncommitted, we need to do a full rebuild anyway.
|
||||
// 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 {
|
||||
OriginValidity::Valid => continue,
|
||||
OriginValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
||||
OriginValidity::FullyInvalid => SheetRebuildKind::Full,
|
||||
DataValidity::Valid => continue,
|
||||
DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
|
||||
DataValidity::FullyInvalid => SheetRebuildKind::Full,
|
||||
};
|
||||
|
||||
return Some((&potential_sheet.sheet, rebuild_kind));
|
||||
|
@ -273,10 +245,15 @@ where
|
|||
/// 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
|
||||
/// `DataValidity::Valid`, if only sheets have been appended into it (in
|
||||
/// which case the existing data is valid, but the origin needs to be
|
||||
/// rebuilt).
|
||||
data_validity: OriginValidity,
|
||||
data_validity: DataValidity,
|
||||
|
||||
/// Whether anything in the collection has changed. Note that this is
|
||||
/// different from `data_validity`, in the sense that after a sheet append,
|
||||
/// the data validity is still `Valid`, but we need to be marked as dirty.
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl<S> Default for SheetCollection<S>
|
||||
|
@ -286,7 +263,8 @@ where
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
entries: vec![],
|
||||
data_validity: OriginValidity::Valid,
|
||||
data_validity: DataValidity::Valid,
|
||||
dirty: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +298,9 @@ where
|
|||
// rebuilding the world when sites quickly append and remove a stylesheet.
|
||||
// See bug 1434756.
|
||||
if sheet.committed {
|
||||
self.set_data_validity_at_least(OriginValidity::FullyInvalid);
|
||||
self.set_data_validity_at_least(DataValidity::FullyInvalid);
|
||||
} else {
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,9 +311,13 @@ where
|
|||
/// Appends a given sheet into the collection.
|
||||
fn append(&mut self, sheet: S) {
|
||||
debug_assert!(!self.contains(&sheet));
|
||||
self.entries.push(StylesheetSetEntry::new(sheet))
|
||||
self.entries.push(StylesheetSetEntry::new(sheet));
|
||||
// Appending sheets doesn't alter the validity of the existing data, so
|
||||
// we don't need to change `data_validity` here.
|
||||
//
|
||||
// But we need to be marked as dirty, otherwise we'll never add the new
|
||||
// sheet!
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
fn insert_before(&mut self, sheet: S, before_sheet: &S) {
|
||||
|
@ -345,12 +329,16 @@ where
|
|||
|
||||
// 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(OriginValidity::CascadeInvalid);
|
||||
self.set_data_validity_at_least(DataValidity::CascadeInvalid);
|
||||
self.entries.insert(index, StylesheetSetEntry::new(sheet));
|
||||
}
|
||||
|
||||
fn set_data_validity_at_least(&mut self, validity: OriginValidity) {
|
||||
fn set_data_validity_at_least(&mut self, validity: DataValidity) {
|
||||
use std::cmp;
|
||||
|
||||
debug_assert_ne!(validity, DataValidity::Valid);
|
||||
|
||||
self.dirty = true;
|
||||
self.data_validity = cmp::max(validity, self.data_validity);
|
||||
}
|
||||
|
||||
|
@ -358,7 +346,7 @@ where
|
|||
debug_assert!(!self.contains(&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(OriginValidity::CascadeInvalid);
|
||||
self.set_data_validity_at_least(DataValidity::CascadeInvalid);
|
||||
self.entries.insert(0, StylesheetSetEntry::new(sheet));
|
||||
}
|
||||
|
||||
|
@ -366,11 +354,22 @@ where
|
|||
fn iter(&self) -> StylesheetCollectionIterator<S> {
|
||||
StylesheetCollectionIterator(self.entries.iter())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> SheetCollectionFlusher<S> {
|
||||
let dirty = mem::replace(&mut self.dirty, false);
|
||||
let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
|
||||
|
||||
SheetCollectionFlusher {
|
||||
iter: self.entries.iter_mut(),
|
||||
dirty,
|
||||
validity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of stylesheets effective for a given document.
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
pub struct StylesheetSet<S>
|
||||
pub struct DocumentStylesheetSet<S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
|
@ -379,28 +378,110 @@ where
|
|||
|
||||
/// The invalidations for stylesheets added or removed from this document.
|
||||
invalidations: StylesheetInvalidationSet,
|
||||
|
||||
/// The origins whose stylesheets have changed so far.
|
||||
origins_dirty: OriginSet,
|
||||
|
||||
/// Has author style been disabled?
|
||||
author_style_disabled: bool,
|
||||
}
|
||||
|
||||
impl<S> StylesheetSet<S>
|
||||
/// This macro defines methods common to DocumentStylesheetSet and
|
||||
/// AuthorStylesheetSet.
|
||||
///
|
||||
/// We could simplify the setup moving invalidations to SheetCollection, but
|
||||
/// that would imply not sharing invalidations across origins of the same
|
||||
/// documents, which is slightly annoying.
|
||||
macro_rules! sheet_set_methods {
|
||||
($set_name:expr) => {
|
||||
fn collect_invalidations_for(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: &S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
if let Some(device) = device {
|
||||
self.invalidations.collect_invalidations_for(device, sheet, guard);
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a new stylesheet to the current set.
|
||||
///
|
||||
/// No device implies not computing invalidations.
|
||||
pub fn append_stylesheet(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!(concat!($set_name, "::append_stylesheet"));
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
let collection = self.collection_for(&sheet, guard);
|
||||
collection.append(sheet);
|
||||
}
|
||||
|
||||
/// Prepend a new stylesheet to the current set.
|
||||
pub fn prepend_stylesheet(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!(concat!($set_name, "::prepend_stylesheet"));
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
|
||||
let collection = self.collection_for(&sheet, guard);
|
||||
collection.prepend(sheet);
|
||||
}
|
||||
|
||||
/// Insert a given stylesheet before another stylesheet in the document.
|
||||
pub fn insert_stylesheet_before(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
before_sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!(concat!($set_name, "::insert_stylesheet_before"));
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
|
||||
let collection = self.collection_for(&sheet, guard);
|
||||
collection.insert_before(sheet, &before_sheet);
|
||||
}
|
||||
|
||||
/// Remove a given stylesheet from the set.
|
||||
pub fn remove_stylesheet(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!(concat!($set_name, "::remove_stylesheet"));
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
|
||||
let collection = self.collection_for(&sheet, guard);
|
||||
collection.remove(&sheet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DocumentStylesheetSet<S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
/// Create a new empty StylesheetSet.
|
||||
/// Create a new empty DocumentStylesheetSet.
|
||||
pub fn new() -> Self {
|
||||
StylesheetSet {
|
||||
Self {
|
||||
collections: Default::default(),
|
||||
invalidations: StylesheetInvalidationSet::new(),
|
||||
origins_dirty: OriginSet::empty(),
|
||||
author_style_disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn collection_for(
|
||||
&mut self,
|
||||
sheet: &S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> &mut SheetCollection<S> {
|
||||
let origin = sheet.contents(guard).origin;
|
||||
self.collections.borrow_mut_for_origin(&origin)
|
||||
}
|
||||
|
||||
sheet_set_methods!("DocumentStylesheetSet");
|
||||
|
||||
/// Returns the number of stylesheets in the set.
|
||||
pub fn len(&self) -> usize {
|
||||
self.collections.iter_origins().fold(0, |s, (item, _)| s + item.len())
|
||||
|
@ -411,145 +492,47 @@ where
|
|||
self.collections.borrow_for_origin(&origin).get(index)
|
||||
}
|
||||
|
||||
/// Returns whether author styles have been disabled for the current
|
||||
/// stylesheet set.
|
||||
pub fn author_style_disabled(&self) -> bool {
|
||||
self.author_style_disabled
|
||||
}
|
||||
|
||||
fn collect_invalidations_for(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: &S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
if let Some(device) = device {
|
||||
self.invalidations.collect_invalidations_for(device, sheet, guard);
|
||||
}
|
||||
self.origins_dirty |= sheet.contents(guard).origin;
|
||||
}
|
||||
|
||||
/// Appends a new stylesheet to the current set.
|
||||
///
|
||||
/// No device implies not computing invalidations.
|
||||
pub fn append_stylesheet(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard
|
||||
) {
|
||||
debug!("StylesheetSet::append_stylesheet");
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
let origin = sheet.contents(guard).origin;
|
||||
self.collections.borrow_mut_for_origin(&origin).append(sheet);
|
||||
}
|
||||
|
||||
/// Prepend a new stylesheet to the current set.
|
||||
pub fn prepend_stylesheet(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard
|
||||
) {
|
||||
debug!("StylesheetSet::prepend_stylesheet");
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
|
||||
let origin = sheet.contents(guard).origin;
|
||||
self.collections.borrow_mut_for_origin(&origin).prepend(sheet)
|
||||
}
|
||||
|
||||
/// Insert a given stylesheet before another stylesheet in the document.
|
||||
pub fn insert_stylesheet_before(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
before_sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!("StylesheetSet::insert_stylesheet_before");
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
|
||||
let origin = sheet.contents(guard).origin;
|
||||
self.collections
|
||||
.borrow_mut_for_origin(&origin)
|
||||
.insert_before(sheet, &before_sheet)
|
||||
}
|
||||
|
||||
/// Remove a given stylesheet from the set.
|
||||
pub fn remove_stylesheet(
|
||||
&mut self,
|
||||
device: Option<&Device>,
|
||||
sheet: S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) {
|
||||
debug!("StylesheetSet::remove_stylesheet");
|
||||
self.collect_invalidations_for(device, &sheet, guard);
|
||||
|
||||
let origin = sheet.contents(guard).origin;
|
||||
self.collections.borrow_mut_for_origin(&origin).remove(&sheet)
|
||||
}
|
||||
|
||||
/// Notes that the author style has been disabled for this document.
|
||||
pub fn set_author_style_disabled(&mut self, disabled: bool) {
|
||||
debug!("StylesheetSet::set_author_style_disabled");
|
||||
if self.author_style_disabled == disabled {
|
||||
return;
|
||||
}
|
||||
self.author_style_disabled = disabled;
|
||||
self.invalidations.invalidate_fully();
|
||||
self.origins_dirty |= Origin::Author;
|
||||
}
|
||||
|
||||
/// Returns whether the given set has changed from the last flush.
|
||||
pub fn has_changed(&self) -> bool {
|
||||
!self.origins_dirty.is_empty()
|
||||
self.collections.iter_origins().any(|(collection, _)| collection.dirty)
|
||||
}
|
||||
|
||||
/// Flush the current set, unmarking it as dirty, and returns a
|
||||
/// `StylesheetFlusher` in order to rebuild the stylist.
|
||||
pub fn flush<'a, E>(
|
||||
&'a mut self,
|
||||
/// `DocumentStylesheetFlusher` in order to rebuild the stylist.
|
||||
pub fn flush<E>(
|
||||
&mut self,
|
||||
document_element: Option<E>,
|
||||
snapshots: Option<&SnapshotMap>,
|
||||
) -> StylesheetFlusher<'a, S>
|
||||
) -> DocumentStylesheetFlusher<S>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
use std::mem;
|
||||
|
||||
debug!("StylesheetSet::flush");
|
||||
debug!("DocumentStylesheetSet::flush");
|
||||
|
||||
let had_invalidations =
|
||||
self.invalidations.flush(document_element, snapshots);
|
||||
|
||||
let origins_dirty =
|
||||
mem::replace(&mut self.origins_dirty, OriginSet::empty());
|
||||
|
||||
let mut origin_data_validity = PerOrigin::<OriginValidity>::default();
|
||||
for origin in origins_dirty.iter() {
|
||||
let collection = self.collections.borrow_mut_for_origin(&origin);
|
||||
*origin_data_validity.borrow_mut_for_origin(&origin) =
|
||||
mem::replace(&mut collection.data_validity, OriginValidity::Valid);
|
||||
}
|
||||
|
||||
StylesheetFlusher {
|
||||
DocumentStylesheetFlusher {
|
||||
collections: &mut self.collections,
|
||||
author_style_disabled: self.author_style_disabled,
|
||||
had_invalidations,
|
||||
origins_dirty,
|
||||
origin_data_validity,
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush stylesheets, but without running any of the invalidation passes.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn flush_without_invalidation(&mut self) -> OriginSet {
|
||||
use std::mem;
|
||||
|
||||
debug!("StylesheetSet::flush_without_invalidation");
|
||||
debug!("DocumentStylesheetSet::flush_without_invalidation");
|
||||
|
||||
let mut origins = OriginSet::empty();
|
||||
self.invalidations.clear();
|
||||
mem::replace(&mut self.origins_dirty, OriginSet::empty())
|
||||
|
||||
for (collection, origin) in self.collections.iter_mut_origins() {
|
||||
if collection.flush().dirty() {
|
||||
origins |= origin;
|
||||
}
|
||||
}
|
||||
|
||||
origins
|
||||
}
|
||||
|
||||
/// Return an iterator over the flattened view of all the stylesheets.
|
||||
|
@ -565,12 +548,67 @@ where
|
|||
/// something external may have invalidated it.
|
||||
pub fn force_dirty(&mut self, origins: OriginSet) {
|
||||
self.invalidations.invalidate_fully();
|
||||
self.origins_dirty |= origins;
|
||||
for origin in origins.iter() {
|
||||
// We don't know what happened, assume the worse.
|
||||
self.collections
|
||||
.borrow_mut_for_origin(&origin)
|
||||
.set_data_validity_at_least(OriginValidity::FullyInvalid);
|
||||
.set_data_validity_at_least(DataValidity::FullyInvalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of stylesheets effective for a given XBL binding or Shadow Root.
|
||||
pub struct AuthorStylesheetSet<S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
/// The actual style sheets.
|
||||
collection: SheetCollection<S>,
|
||||
/// The set of invalidations scheduled for this collection.
|
||||
invalidations: StylesheetInvalidationSet,
|
||||
}
|
||||
|
||||
/// A struct to flush an author style sheet collection.
|
||||
pub struct AuthorStylesheetFlusher<'a, S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
/// The actual flusher for the collection.
|
||||
pub sheets: SheetCollectionFlusher<'a, S>,
|
||||
/// Whether any sheet invalidation matched.
|
||||
pub had_invalidations: bool,
|
||||
}
|
||||
|
||||
impl<S> AuthorStylesheetSet<S>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
fn collection_for(
|
||||
&mut self,
|
||||
_sheet: &S,
|
||||
_guard: &SharedRwLockReadGuard,
|
||||
) -> &mut SheetCollection<S> {
|
||||
&mut self.collection
|
||||
}
|
||||
|
||||
sheet_set_methods!("AuthorStylesheetSet");
|
||||
|
||||
/// Flush the stylesheets for this author set.
|
||||
///
|
||||
/// `host` is the root of the affected subtree, like the shadow host, for
|
||||
/// example.
|
||||
pub fn flush<E>(
|
||||
&mut self,
|
||||
host: Option<E>,
|
||||
snapshots: Option<&SnapshotMap>,
|
||||
) -> AuthorStylesheetFlusher<S>
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
let had_invalidations = self.invalidations.flush(host, snapshots);
|
||||
AuthorStylesheetFlusher {
|
||||
sheets: self.collection.flush(),
|
||||
had_invalidations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ use smallvec::SmallVec;
|
|||
use std::ops;
|
||||
use std::sync::Mutex;
|
||||
use style_traits::viewport::ViewportConstraints;
|
||||
use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetFlusher};
|
||||
use stylesheet_set::{DataValidity, SheetRebuildKind, DocumentStylesheetSet};
|
||||
use stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
|
||||
#[cfg(feature = "gecko")]
|
||||
use stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
|
||||
use stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
|
||||
|
@ -227,42 +228,6 @@ impl DocumentCascadeData {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
/// optionally with a set of user agent stylesheets. Returns Err(..)
|
||||
/// to signify OOM.
|
||||
|
@ -270,21 +235,17 @@ impl DocumentCascadeData {
|
|||
&mut self,
|
||||
device: &Device,
|
||||
quirks_mode: QuirksMode,
|
||||
mut flusher: StylesheetFlusher<'a, S>,
|
||||
mut flusher: DocumentStylesheetFlusher<'a, S>,
|
||||
guards: &StylesheetGuards,
|
||||
) -> Result<(), FailedAllocationError>
|
||||
where
|
||||
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
|
||||
{
|
||||
debug_assert!(!flusher.nothing_to_do());
|
||||
|
||||
// First do UA sheets.
|
||||
{
|
||||
if flusher.origin_dirty(Origin::UserAgent) {
|
||||
if flusher.flush_origin(Origin::UserAgent).dirty() {
|
||||
let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
|
||||
let origin_sheets =
|
||||
flusher.manual_origin_sheets(Origin::UserAgent);
|
||||
|
||||
let origin_sheets = flusher.origin_sheets(Origin::UserAgent);
|
||||
let ua_cascade_data = ua_cache.lookup(
|
||||
origin_sheets,
|
||||
device,
|
||||
|
@ -298,23 +259,19 @@ impl DocumentCascadeData {
|
|||
}
|
||||
|
||||
// Now do the user sheets.
|
||||
Self::rebuild_origin(
|
||||
self.user.rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
&mut flusher,
|
||||
guards,
|
||||
Origin::User,
|
||||
&mut self.user,
|
||||
flusher.flush_origin(Origin::User),
|
||||
guards.ua_or_user,
|
||||
)?;
|
||||
|
||||
// And now the author sheets.
|
||||
Self::rebuild_origin(
|
||||
self.author.rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
&mut flusher,
|
||||
guards,
|
||||
Origin::Author,
|
||||
&mut self.author,
|
||||
flusher.flush_origin(Origin::Author),
|
||||
guards.author,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -328,21 +285,31 @@ impl DocumentCascadeData {
|
|||
}
|
||||
}
|
||||
|
||||
/// A wrapper over a StylesheetSet that can be `Sync`, since it's only used and
|
||||
/// exposed via mutable methods in the `Stylist`.
|
||||
/// Whether author styles are enabled.
|
||||
///
|
||||
/// This is used to support Gecko.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
|
||||
pub enum AuthorStylesEnabled {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
/// A wrapper over a DocumentStylesheetSet that can be `Sync`, since it's only
|
||||
/// used and exposed via mutable methods in the `Stylist`.
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
struct StylistStylesheetSet(StylesheetSet<StylistSheet>);
|
||||
struct StylistStylesheetSet(DocumentStylesheetSet<StylistSheet>);
|
||||
// Read above to see why this is fine.
|
||||
unsafe impl Sync for StylistStylesheetSet {}
|
||||
|
||||
impl StylistStylesheetSet {
|
||||
fn new() -> Self {
|
||||
StylistStylesheetSet(StylesheetSet::new())
|
||||
StylistStylesheetSet(DocumentStylesheetSet::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for StylistStylesheetSet {
|
||||
type Target = StylesheetSet<StylistSheet>;
|
||||
type Target = DocumentStylesheetSet<StylistSheet>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
|
@ -393,6 +360,9 @@ pub struct Stylist {
|
|||
/// cascade level.
|
||||
cascade_data: DocumentCascadeData,
|
||||
|
||||
/// Whether author styles are enabled.
|
||||
author_styles_enabled: AuthorStylesEnabled,
|
||||
|
||||
/// The rule tree, that stores the results of selector matching.
|
||||
rule_tree: RuleTree,
|
||||
|
||||
|
@ -433,6 +403,7 @@ impl Stylist {
|
|||
quirks_mode,
|
||||
stylesheets: StylistStylesheetSet::new(),
|
||||
cascade_data: Default::default(),
|
||||
author_styles_enabled: AuthorStylesEnabled::Yes,
|
||||
rule_tree: RuleTree::new(),
|
||||
num_rebuilds: 0,
|
||||
}
|
||||
|
@ -588,8 +559,8 @@ impl Stylist {
|
|||
}
|
||||
|
||||
/// Sets whether author style is enabled or not.
|
||||
pub fn set_author_style_disabled(&mut self, disabled: bool) {
|
||||
self.stylesheets.set_author_style_disabled(disabled);
|
||||
pub fn set_author_styles_enabled(&mut self, enabled: AuthorStylesEnabled) {
|
||||
self.author_styles_enabled = enabled;
|
||||
}
|
||||
|
||||
/// Returns whether we've recorded any stylesheet change so far.
|
||||
|
@ -1217,8 +1188,11 @@ impl Stylist {
|
|||
|
||||
let only_default_rules =
|
||||
rule_inclusion == RuleInclusion::DefaultOnly;
|
||||
let matches_user_and_author_rules =
|
||||
let matches_user_rules =
|
||||
rule_hash_target.matches_user_and_author_rules();
|
||||
let matches_author_rules =
|
||||
matches_user_rules &&
|
||||
self.author_styles_enabled == AuthorStylesEnabled::Yes;
|
||||
|
||||
// Step 1: Normal user-agent rules.
|
||||
if let Some(map) = self.cascade_data.user_agent.cascade_data.normal_rules(pseudo_element) {
|
||||
|
@ -1256,7 +1230,7 @@ impl Stylist {
|
|||
// rule_hash_target.matches_user_and_author_rules())
|
||||
//
|
||||
// Which may be more what you would probably expect.
|
||||
if matches_user_and_author_rules {
|
||||
if matches_user_rules {
|
||||
// Step 3a: User normal rules.
|
||||
if let Some(map) = self.cascade_data.user.normal_rules(pseudo_element) {
|
||||
map.get_all_matching_rules(
|
||||
|
@ -1276,7 +1250,7 @@ impl Stylist {
|
|||
// particular, normally document rules override ::slotted() rules, but
|
||||
// for !important it should be the other way around. So probably we need
|
||||
// to add some sort of AuthorScoped cascade level or something.
|
||||
if !only_default_rules {
|
||||
if matches_author_rules && !only_default_rules {
|
||||
// Match slotted rules in reverse order, so that the outer slotted
|
||||
// rules come before the inner rules (and thus have less priority).
|
||||
let mut slots = SmallVec::<[_; 3]>::new();
|
||||
|
@ -1304,9 +1278,9 @@ impl Stylist {
|
|||
|
||||
// FIXME(emilio): It looks very wrong to match XBL / Shadow DOM rules
|
||||
// even for getDefaultComputedStyle!
|
||||
//
|
||||
// Also, this doesn't account for the author_styles_enabled stuff.
|
||||
let cut_off_inheritance = element.each_xbl_stylist(|stylist| {
|
||||
// ServoStyleSet::CreateXBLServoStyleSet() loads XBL style sheets
|
||||
// under eAuthorSheetFeatures level.
|
||||
if let Some(map) = stylist.cascade_data.author.normal_rules(pseudo_element) {
|
||||
// NOTE(emilio): This is needed because the XBL stylist may
|
||||
// think it has a different quirks mode than the document.
|
||||
|
@ -1333,9 +1307,7 @@ impl Stylist {
|
|||
}
|
||||
});
|
||||
|
||||
if matches_user_and_author_rules && !only_default_rules &&
|
||||
!cut_off_inheritance
|
||||
{
|
||||
if matches_author_rules && !only_default_rules && !cut_off_inheritance {
|
||||
// Step 3c: Author normal rules.
|
||||
if let Some(map) = self.cascade_data.author.normal_rules(pseudo_element) {
|
||||
map.get_all_matching_rules(
|
||||
|
@ -2029,6 +2001,45 @@ impl CascadeData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Rebuild the cascade data from a given SheetCollection, incrementally if
|
||||
/// possible.
|
||||
fn rebuild<'a, S>(
|
||||
&mut self,
|
||||
device: &Device,
|
||||
quirks_mode: QuirksMode,
|
||||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> Result<(), FailedAllocationError>
|
||||
where
|
||||
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
|
||||
{
|
||||
if !collection.dirty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let validity = collection.data_validity();
|
||||
|
||||
match validity {
|
||||
DataValidity::Valid => {},
|
||||
DataValidity::CascadeInvalid => self.clear_cascade_data(),
|
||||
DataValidity::FullyInvalid => self.clear(),
|
||||
}
|
||||
|
||||
for (stylesheet, rebuild_kind) in collection {
|
||||
self.add_stylesheet(
|
||||
device,
|
||||
quirks_mode,
|
||||
stylesheet,
|
||||
guard,
|
||||
rebuild_kind,
|
||||
/* precomputed_pseudo_element_decls = */ None,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Returns the invalidation map.
|
||||
pub fn invalidation_map(&self) -> &InvalidationMap {
|
||||
&self.invalidation_map
|
||||
|
|
|
@ -142,7 +142,7 @@ use style::stylesheets::{StylesheetContents, SupportsRule};
|
|||
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
||||
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
|
||||
use style::stylesheets::supports_rule::parse_condition_or_declaration;
|
||||
use style::stylist::{add_size_of_ua_cache, RuleInclusion, Stylist};
|
||||
use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
|
||||
use style::thread_state;
|
||||
use style::timer::Timer;
|
||||
use style::traversal::DomTraversal;
|
||||
|
@ -1247,12 +1247,25 @@ pub unsafe extern "C" fn Servo_StyleSet_FlushStyleSheets(
|
|||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleSet_NoteStyleSheetsChanged(
|
||||
raw_data: RawServoStyleSetBorrowed,
|
||||
author_style_disabled: bool,
|
||||
changed_origins: OriginFlags,
|
||||
) {
|
||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||
data.stylist.force_stylesheet_origins_dirty(OriginSet::from(changed_origins));
|
||||
data.stylist.set_author_style_disabled(author_style_disabled);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleSet_SetAuthorStyleDisabled(
|
||||
raw_data: RawServoStyleSetBorrowed,
|
||||
author_style_disabled: bool,
|
||||
) {
|
||||
let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
|
||||
let enabled =
|
||||
if author_style_disabled {
|
||||
AuthorStylesEnabled::No
|
||||
} else {
|
||||
AuthorStylesEnabled::Yes
|
||||
};
|
||||
data.stylist.set_author_styles_enabled(enabled);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
Загрузка…
Ссылка в новой задаче