style: Make XBL / Shadow DOM use something more light-weight than a Stylist.

It's just a struct aggregating stylesheets + CascadeData, with a quirks_mode
parameter because XBL sucks so bad.

Bug: 1436059
Reviewed-by: xidorn
MozReview-Commit-ID: 7q99tSNXo0K
This commit is contained in:
Emilio Cobos Álvarez 2018-02-12 13:57:26 +01:00
parent a6113af873
commit 5e64cb3516
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
9 changed files with 225 additions and 44 deletions

View file

@ -0,0 +1,91 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! A set of author stylesheets and their computed representation, such as the
//! ones used for ShadowRoot and XBL.
use context::QuirksMode;
use dom::TElement;
#[cfg(feature = "gecko")]
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use invalidation::media_queries::ToMediaListKey;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use stylesheet_set::AuthorStylesheetSet;
use stylesheets::StylesheetInDocument;
use stylist::CascadeData;
/// A set of author stylesheets and their computed representation, such as the
/// ones used for ShadowRoot and XBL.
pub struct AuthorStyles<S>
where
S: StylesheetInDocument + PartialEq + 'static,
{
/// The sheet collection, which holds the sheet pointers, the invalidations,
/// and all that stuff.
pub stylesheets: AuthorStylesheetSet<S>,
/// The actual cascade data computed from the stylesheets.
pub data: CascadeData,
/// The quirks mode of the last stylesheet flush, used because XBL sucks and
/// we should really fix it, see bug 1406875.
pub quirks_mode: QuirksMode,
}
impl<S> AuthorStyles<S>
where
S: StylesheetInDocument + PartialEq + 'static,
{
/// Create an empty AuthorStyles.
#[inline]
pub fn new() -> Self {
Self {
stylesheets: AuthorStylesheetSet::new(),
data: CascadeData::new(),
quirks_mode: QuirksMode::NoQuirks,
}
}
/// Flush the pending sheet changes, updating `data` as appropriate.
///
/// TODO(emilio): Need a host element and a snapshot map to do invalidation
/// properly.
#[inline]
pub fn flush<E>(
&mut self,
device: &Device,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard,
)
where
E: TElement,
S: ToMediaListKey,
{
let flusher = self.stylesheets.flush::<E>(
/* host = */ None,
/* snapshot_map = */ None,
);
if flusher.sheets.dirty() {
self.quirks_mode = quirks_mode;
}
// Ignore OOM.
let _ = self.data.rebuild(
device,
quirks_mode,
flusher.sheets,
guard,
);
}
}
#[cfg(feature = "gecko")]
unsafe impl HasFFI for AuthorStyles<::gecko::data::GeckoStyleSheet> {
type FFIType = ::gecko_bindings::bindings::RawServoAuthorStyles;
}
#[cfg(feature = "gecko")]
unsafe impl HasSimpleFFI for AuthorStyles<::gecko::data::GeckoStyleSheet> {}
#[cfg(feature = "gecko")]
unsafe impl HasBoxFFI for AuthorStyles<::gecko::data::GeckoStyleSheet> {}

View file

@ -754,7 +754,7 @@ pub trait TElement
fn each_xbl_cascade_data<'a, F>(&self, _: F) -> bool fn each_xbl_cascade_data<'a, F>(&self, _: F) -> bool
where where
Self: 'a, Self: 'a,
F: FnMut(AtomicRef<'a, CascadeData>, QuirksMode), F: FnMut(&'a CascadeData, QuirksMode),
{ {
false false
} }
@ -766,7 +766,7 @@ pub trait TElement
fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool
where where
Self: 'a, Self: 'a,
F: FnMut(AtomicRef<'a, CascadeData>, QuirksMode), F: FnMut(&'a CascadeData, QuirksMode),
{ {
let cut_off_inheritance = self.each_xbl_cascade_data(&mut f); let cut_off_inheritance = self.each_xbl_cascade_data(&mut f);

View file

@ -17,13 +17,14 @@
use CaseSensitivityExt; use CaseSensitivityExt;
use app_units::Au; use app_units::Au;
use applicable_declarations::ApplicableDeclarationBlock; use applicable_declarations::ApplicableDeclarationBlock;
use atomic_refcell::{AtomicRefCell, AtomicRef, AtomicRefMut}; use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use author_styles::AuthorStyles;
use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks}; use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks};
use data::ElementData; use data::ElementData;
use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode}; use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode};
use element_state::{ElementState, DocumentState}; use element_state::{ElementState, DocumentState};
use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult}; use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
use gecko::data::PerDocumentStyleData; use gecko::data::GeckoStyleSheet;
use gecko::global_style_data::GLOBAL_STYLE_DATA; use gecko::global_style_data::GLOBAL_STYLE_DATA;
use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement}; use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement};
use gecko::snapshot_helpers; use gecko::snapshot_helpers;
@ -431,20 +432,19 @@ impl<'lb> GeckoXBLBinding<'lb> {
fn each_xbl_cascade_data<F>(&self, f: &mut F) fn each_xbl_cascade_data<F>(&self, f: &mut F)
where where
F: FnMut(AtomicRef<'lb, CascadeData>, QuirksMode), F: FnMut(&'lb CascadeData, QuirksMode),
{ {
if let Some(base) = self.base_binding() { if let Some(base) = self.base_binding() {
base.each_xbl_cascade_data(f); base.each_xbl_cascade_data(f);
} }
let raw_data = unsafe { let data = unsafe {
bindings::Gecko_XBLBinding_GetRawServoStyleSet(self.0) bindings::Gecko_XBLBinding_GetRawServoStyles(self.0)
}; };
if let Some(raw_data) = raw_data { if let Some(data) = data {
let data = PerDocumentStyleData::from_ffi(&*raw_data).borrow(); let data: &'lb _ = AuthorStyles::<GeckoStyleSheet>::from_ffi(data);
let quirks_mode = data.stylist.quirks_mode(); f(&data.data, data.quirks_mode)
f(AtomicRef::map(data, |d| d.stylist.author_cascade_data()), quirks_mode);
} }
} }
} }
@ -1378,7 +1378,7 @@ impl<'le> TElement for GeckoElement<'le> {
fn each_xbl_cascade_data<'a, F>(&self, mut f: F) -> bool fn each_xbl_cascade_data<'a, F>(&self, mut f: F) -> bool
where where
'le: 'a, 'le: 'a,
F: FnMut(AtomicRef<'a, CascadeData>, QuirksMode), F: FnMut(&'a CascadeData, QuirksMode),
{ {
// Walk the binding scope chain, starting with the binding attached to // Walk the binding scope chain, starting with the binding attached to
// our content, up till we run out of scopes or we get cut off. // our content, up till we run out of scopes or we get cut off.

View file

@ -36,6 +36,7 @@ impl<T> nsTArray<T> {
debug_assert!(!self.mBuffer.is_null()); debug_assert!(!self.mBuffer.is_null());
unsafe { mem::transmute(self.mBuffer) } unsafe { mem::transmute(self.mBuffer) }
} }
// unsafe, since header may be in shared static or something // unsafe, since header may be in shared static or something
unsafe fn header_mut<'a>(&'a mut self) -> &'a mut nsTArrayHeader { unsafe fn header_mut<'a>(&'a mut self) -> &'a mut nsTArrayHeader {
debug_assert!(!self.mBuffer.is_null()); debug_assert!(!self.mBuffer.is_null());

View file

@ -6,7 +6,6 @@
//! changes. //! changes.
use Atom; use Atom;
use atomic_refcell::AtomicRef;
use context::{QuirksMode, SharedStyleContext}; use context::{QuirksMode, SharedStyleContext};
use data::ElementData; use data::ElementData;
use dom::TElement; use dom::TElement;
@ -57,7 +56,7 @@ where
/// changes. /// changes.
pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> {
shared_context: &'a SharedStyleContext<'b>, shared_context: &'a SharedStyleContext<'b>,
shadow_rule_datas: &'a [(AtomicRef<'b, CascadeData>, QuirksMode)], shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode)],
cut_off_inheritance: bool, cut_off_inheritance: bool,
element: E, element: E,
data: &'a mut ElementData, data: &'a mut ElementData,
@ -68,7 +67,7 @@ impl<'a, 'b: 'a, E: TElement> StateAndAttrInvalidationProcessor<'a, 'b, E> {
/// Creates a new StateAndAttrInvalidationProcessor. /// Creates a new StateAndAttrInvalidationProcessor.
pub fn new( pub fn new(
shared_context: &'a SharedStyleContext<'b>, shared_context: &'a SharedStyleContext<'b>,
shadow_rule_datas: &'a [(AtomicRef<'b, CascadeData>, QuirksMode)], shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode)],
cut_off_inheritance: bool, cut_off_inheritance: bool,
element: E, element: E,
data: &'a mut ElementData, data: &'a mut ElementData,

View file

@ -87,6 +87,7 @@ mod macros;
pub mod applicable_declarations; pub mod applicable_declarations;
#[allow(missing_docs)] // TODO. #[allow(missing_docs)] // TODO.
#[cfg(feature = "servo")] pub mod attr; #[cfg(feature = "servo")] pub mod attr;
pub mod author_styles;
pub mod bezier; pub mod bezier;
pub mod bloom; pub mod bloom;
pub mod context; pub mod context;

View file

@ -583,6 +583,20 @@ impl<S> AuthorStylesheetSet<S>
where where
S: StylesheetInDocument + PartialEq + 'static, S: StylesheetInDocument + PartialEq + 'static,
{ {
/// Create a new empty AuthorStylesheetSet.
#[inline]
pub fn new() -> Self {
Self {
collection: Default::default(),
invalidations: StylesheetInvalidationSet::new(),
}
}
/// Whether anything has changed since the last time this was flushed.
pub fn dirty(&self) -> bool {
self.collection.dirty
}
fn collection_for( fn collection_for(
&mut self, &mut self,
_sheet: &S, _sheet: &S,
@ -593,6 +607,17 @@ where
sheet_set_methods!("AuthorStylesheetSet"); sheet_set_methods!("AuthorStylesheetSet");
/// Iterate over the list of stylesheets.
pub fn iter(&self) -> StylesheetCollectionIterator<S> {
self.collection.iter()
}
/// Mark the sheet set dirty, as appropriate.
pub fn force_dirty(&mut self) {
self.invalidations.invalidate_fully();
self.collection.set_data_validity_at_least(DataValidity::FullyInvalid);
}
/// Flush the stylesheets for this author set. /// Flush the stylesheets for this author set.
/// ///
/// `host` is the root of the affected subtree, like the shadow host, for /// `host` is the root of the affected subtree, like the shadow host, for

View file

@ -1983,7 +1983,8 @@ pub struct CascadeData {
} }
impl CascadeData { impl CascadeData {
fn new() -> Self { /// Creates an empty `CascadeData`.
pub fn new() -> Self {
Self { Self {
normal_rules: ElementAndPseudoRules::default(), normal_rules: ElementAndPseudoRules::default(),
slotted_rules: None, slotted_rules: None,
@ -2005,7 +2006,7 @@ impl CascadeData {
/// Rebuild the cascade data from a given SheetCollection, incrementally if /// Rebuild the cascade data from a given SheetCollection, incrementally if
/// possible. /// possible.
fn rebuild<'a, S>( pub fn rebuild<'a, S>(
&mut self, &mut self,
device: &Device, device: &Device,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
@ -2281,7 +2282,7 @@ impl CascadeData {
/// Returns whether all the media-feature affected values matched before and /// Returns whether all the media-feature affected values matched before and
/// match now in the given stylesheet. /// match now in the given stylesheet.
fn media_feature_affected_matches<S>( pub fn media_feature_affected_matches<S>(
&self, &self,
stylesheet: &S, stylesheet: &S,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,

View file

@ -17,6 +17,7 @@ use std::iter;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use style::applicable_declarations::ApplicableDeclarationBlock; use style::applicable_declarations::ApplicableDeclarationBlock;
use style::author_styles::AuthorStyles;
use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext}; use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
use style::context::ThreadLocalStyleContext; use style::context::ThreadLocalStyleContext;
use style::counter_style; use style::counter_style;
@ -51,6 +52,8 @@ use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSet
use style::gecko_bindings::bindings::{RawServoStyleSheetContentsBorrowed, ServoComputedDataBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetContentsBorrowed, ServoComputedDataBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSheetContentsStrong, ServoStyleContextBorrowed}; use style::gecko_bindings::bindings::{RawServoStyleSheetContentsStrong, ServoStyleContextBorrowed};
use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoSupportsRule, RawServoSupportsRuleBorrowed};
use style::gecko_bindings::bindings::{RawServoAuthorStyles, RawServoAuthorStylesBorrowed};
use style::gecko_bindings::bindings::{RawServoAuthorStylesBorrowedMut, RawServoAuthorStylesOwned};
use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong}; use style::gecko_bindings::bindings::{ServoCssRulesBorrowed, ServoCssRulesStrong};
use style::gecko_bindings::bindings::{nsACString, nsAString, nsCSSPropertyIDSetBorrowedMut}; use style::gecko_bindings::bindings::{nsACString, nsAString, nsCSSPropertyIDSetBorrowedMut};
use style::gecko_bindings::bindings::Gecko_AddPropertyToSet; use style::gecko_bindings::bindings::Gecko_AddPropertyToSet;
@ -1120,10 +1123,71 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
data.stylist.append_stylesheet(sheet, &guard); data.stylist.append_stylesheet(sheet, &guard);
} }
#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_Create() -> *mut RawServoAuthorStyles {
Box::into_raw(Box::new(AuthorStyles::<GeckoStyleSheet>::new())) as *mut _
}
#[no_mangle]
pub extern "C" fn Servo_AuthorStyles_Drop(
styles: RawServoAuthorStylesOwned,
) {
let _ = styles.into_box::<AuthorStyles<_>>();
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet(
styles: RawServoAuthorStylesBorrowedMut,
sheet: *const ServoStyleSheet,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let sheet = GeckoStyleSheet::new(sheet);
styles.stylesheets.append_stylesheet(None, sheet, &guard);
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_ForceDirty(
styles: RawServoAuthorStylesBorrowedMut,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
styles.stylesheets.force_dirty();
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AuthorStyles_Flush(
styles: RawServoAuthorStylesBorrowedMut,
document_set: RawServoStyleSetBorrowed,
) {
let styles = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(styles);
// Try to avoid the atomic borrow below if possible.
if !styles.stylesheets.dirty() {
return;
}
let global_style_data = &*GLOBAL_STYLE_DATA;
let guard = global_style_data.shared_lock.read();
let document_data =
PerDocumentStyleData::from_ffi(document_set).borrow();
let stylist = &document_data.stylist;
// TODO(emilio): This is going to need an element or something to do proper
// invalidation in Shadow roots.
styles.flush::<GeckoElement>(
stylist.device(),
stylist.quirks_mode(),
&guard,
);
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged( pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
document_set: RawServoStyleSetBorrowed, document_set: RawServoStyleSetBorrowed,
non_document_sets: *const nsTArray<*mut structs::ServoStyleSet>, non_document_styles: *mut nsTArray<RawServoAuthorStylesBorrowedMut>,
may_affect_default_style: bool, may_affect_default_style: bool,
) -> structs::MediumFeaturesChangedResult { ) -> structs::MediumFeaturesChangedResult {
let global_style_data = &*GLOBAL_STYLE_DATA; let global_style_data = &*GLOBAL_STYLE_DATA;
@ -1156,27 +1220,20 @@ pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged(
} }
let mut affects_non_document_rules = false; let mut affects_non_document_rules = false;
for non_document_style_set in &**non_document_sets { for author_styles in &mut **non_document_styles {
let non_document_data = &*(**non_document_style_set).mRawSet.mPtr; let author_styles =
let non_document_data = AuthorStyles::<GeckoStyleSheet>::from_ffi_mut(&mut *author_styles);
mem::transmute::<&structs::RawServoStyleSet, &bindings::RawServoStyleSet>(non_document_data); let affected_style = author_styles.stylesheets.iter().any(|sheet| {
let mut non_document_data = !author_styles.data.media_feature_affected_matches(
PerDocumentStyleData::from_ffi(non_document_data).borrow_mut(); sheet,
&guards.author,
let origins_changed =
non_document_data.stylist.media_features_change_changed_style(
&guards,
document_data.stylist.device(), document_data.stylist.device(),
); document_data.stylist.quirks_mode(),
if !origins_changed.is_empty() { )
});
if affected_style {
affects_non_document_rules = true; affects_non_document_rules = true;
// XBL stylesets are rebuilt entirely, so we need to mark them author_styles.stylesheets.force_dirty();
// dirty from here instead of going through the stylist
// force_origin_dirty stuff, which would be useless.
//
// FIXME(emilio, bug 1436059): This is super-hacky, make XBL /
// Shadow DOM not use a style set at all.
(**non_document_style_set).mStylistState = structs::StylistState_StyleSheetsDirty;
} }
} }
@ -4930,19 +4987,25 @@ pub extern "C" fn Servo_ParseCounterStyleName(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges( pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges(
root: RawGeckoElementBorrowed, root: RawGeckoElementBorrowed,
raw_style_sets: *const nsTArray<RawServoStyleSetBorrowed>, document_style: RawServoStyleSetBorrowed,
non_document_styles: *const nsTArray<RawServoAuthorStylesBorrowed>,
states_changed: u64, states_changed: u64,
) { ) {
use style::invalidation::element::document_state::DocumentStateInvalidationProcessor; use style::invalidation::element::document_state::DocumentStateInvalidationProcessor;
use style::invalidation::element::invalidator::TreeStyleInvalidator; use style::invalidation::element::invalidator::TreeStyleInvalidator;
let mut borrows = SmallVec::<[_; 20]>::with_capacity((*raw_style_sets).len()); let document_data = PerDocumentStyleData::from_ffi(document_style).borrow();
for style_set in &**raw_style_sets {
borrows.push(PerDocumentStyleData::from_ffi(*style_set).borrow()); let iter =
} document_data.stylist.iter_origins().map(|(data, _origin)| data)
.chain((*non_document_styles).iter().map(|author_styles| {
let styles: &_ = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles);
&styles.data
}));
let root = GeckoElement(root); let root = GeckoElement(root);
let mut processor = DocumentStateInvalidationProcessor::new( let mut processor = DocumentStateInvalidationProcessor::new(
borrows.iter().flat_map(|b| b.stylist.iter_origins().map(|(data, _origin)| data)), iter,
DocumentState::from_bits_truncate(states_changed), DocumentState::from_bits_truncate(states_changed),
root.as_node().owner_doc().quirks_mode(), root.as_node().owner_doc().quirks_mode(),
); );