diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs new file mode 100644 index 00000000000..7df22ead247 --- /dev/null +++ b/components/style/author_styles.rs @@ -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 +where + S: StylesheetInDocument + PartialEq + 'static, +{ + /// The sheet collection, which holds the sheet pointers, the invalidations, + /// and all that stuff. + pub stylesheets: AuthorStylesheetSet, + /// 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 AuthorStyles +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( + &mut self, + device: &Device, + quirks_mode: QuirksMode, + guard: &SharedRwLockReadGuard, + ) + where + E: TElement, + S: ToMediaListKey, + { + let flusher = self.stylesheets.flush::( + /* 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> {} diff --git a/components/style/dom.rs b/components/style/dom.rs index 81c1e89da0f..6d4cd23a934 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -754,7 +754,7 @@ pub trait TElement fn each_xbl_cascade_data<'a, F>(&self, _: F) -> bool where Self: 'a, - F: FnMut(AtomicRef<'a, CascadeData>, QuirksMode), + F: FnMut(&'a CascadeData, QuirksMode), { false } @@ -766,7 +766,7 @@ pub trait TElement fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool where Self: 'a, - F: FnMut(AtomicRef<'a, CascadeData>, QuirksMode), + F: FnMut(&'a CascadeData, QuirksMode), { let cut_off_inheritance = self.each_xbl_cascade_data(&mut f); diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index b4d4b42ee0a..8dd52eb4541 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -17,13 +17,14 @@ use CaseSensitivityExt; use app_units::Au; 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 data::ElementData; use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode}; use element_state::{ElementState, DocumentState}; use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult}; -use gecko::data::PerDocumentStyleData; +use gecko::data::GeckoStyleSheet; use gecko::global_style_data::GLOBAL_STYLE_DATA; use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement}; use gecko::snapshot_helpers; @@ -431,20 +432,19 @@ impl<'lb> GeckoXBLBinding<'lb> { fn each_xbl_cascade_data(&self, f: &mut F) where - F: FnMut(AtomicRef<'lb, CascadeData>, QuirksMode), + F: FnMut(&'lb CascadeData, QuirksMode), { if let Some(base) = self.base_binding() { base.each_xbl_cascade_data(f); } - let raw_data = unsafe { - bindings::Gecko_XBLBinding_GetRawServoStyleSet(self.0) + let data = unsafe { + bindings::Gecko_XBLBinding_GetRawServoStyles(self.0) }; - if let Some(raw_data) = raw_data { - let data = PerDocumentStyleData::from_ffi(&*raw_data).borrow(); - let quirks_mode = data.stylist.quirks_mode(); - f(AtomicRef::map(data, |d| d.stylist.author_cascade_data()), quirks_mode); + if let Some(data) = data { + let data: &'lb _ = AuthorStyles::::from_ffi(data); + f(&data.data, 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 where 'le: 'a, - F: FnMut(AtomicRef<'a, CascadeData>, QuirksMode), + F: FnMut(&'a CascadeData, QuirksMode), { // 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. diff --git a/components/style/gecko_bindings/sugar/ns_t_array.rs b/components/style/gecko_bindings/sugar/ns_t_array.rs index dadda32a300..632528a1a45 100644 --- a/components/style/gecko_bindings/sugar/ns_t_array.rs +++ b/components/style/gecko_bindings/sugar/ns_t_array.rs @@ -36,6 +36,7 @@ impl nsTArray { debug_assert!(!self.mBuffer.is_null()); unsafe { mem::transmute(self.mBuffer) } } + // unsafe, since header may be in shared static or something unsafe fn header_mut<'a>(&'a mut self) -> &'a mut nsTArrayHeader { debug_assert!(!self.mBuffer.is_null()); diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index a2dc18fb28c..f272d22a71d 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -6,7 +6,6 @@ //! changes. use Atom; -use atomic_refcell::AtomicRef; use context::{QuirksMode, SharedStyleContext}; use data::ElementData; use dom::TElement; @@ -57,7 +56,7 @@ where /// changes. pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { shared_context: &'a SharedStyleContext<'b>, - shadow_rule_datas: &'a [(AtomicRef<'b, CascadeData>, QuirksMode)], + shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode)], cut_off_inheritance: bool, element: E, data: &'a mut ElementData, @@ -68,7 +67,7 @@ impl<'a, 'b: 'a, E: TElement> StateAndAttrInvalidationProcessor<'a, 'b, E> { /// Creates a new StateAndAttrInvalidationProcessor. pub fn new( shared_context: &'a SharedStyleContext<'b>, - shadow_rule_datas: &'a [(AtomicRef<'b, CascadeData>, QuirksMode)], + shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode)], cut_off_inheritance: bool, element: E, data: &'a mut ElementData, diff --git a/components/style/lib.rs b/components/style/lib.rs index b7d062b3bdb..84ccd32574e 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -87,6 +87,7 @@ mod macros; pub mod applicable_declarations; #[allow(missing_docs)] // TODO. #[cfg(feature = "servo")] pub mod attr; +pub mod author_styles; pub mod bezier; pub mod bloom; pub mod context; diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index c170e8d6078..bfe4724f09f 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -583,6 +583,20 @@ impl AuthorStylesheetSet where 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( &mut self, _sheet: &S, @@ -593,6 +607,17 @@ where sheet_set_methods!("AuthorStylesheetSet"); + /// Iterate over the list of stylesheets. + pub fn iter(&self) -> StylesheetCollectionIterator { + 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. /// /// `host` is the root of the affected subtree, like the shadow host, for diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 8c55761733c..db01073a538 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -1983,7 +1983,8 @@ pub struct CascadeData { } impl CascadeData { - fn new() -> Self { + /// Creates an empty `CascadeData`. + pub fn new() -> Self { Self { normal_rules: ElementAndPseudoRules::default(), slotted_rules: None, @@ -2005,7 +2006,7 @@ impl CascadeData { /// Rebuild the cascade data from a given SheetCollection, incrementally if /// possible. - fn rebuild<'a, S>( + pub fn rebuild<'a, S>( &mut self, device: &Device, quirks_mode: QuirksMode, @@ -2281,7 +2282,7 @@ impl CascadeData { /// Returns whether all the media-feature affected values matched before and /// match now in the given stylesheet. - fn media_feature_affected_matches( + pub fn media_feature_affected_matches( &self, stylesheet: &S, guard: &SharedRwLockReadGuard, diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index d7f67dad3e1..1ddb9f386ae 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -17,6 +17,7 @@ use std::iter; use std::mem; use std::ptr; use style::applicable_declarations::ApplicableDeclarationBlock; +use style::author_styles::AuthorStyles; use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext}; use style::context::ThreadLocalStyleContext; 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::{RawServoStyleSheetContentsStrong, ServoStyleContextBorrowed}; 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::{nsACString, nsAString, nsCSSPropertyIDSetBorrowedMut}; use style::gecko_bindings::bindings::Gecko_AddPropertyToSet; @@ -1120,10 +1123,71 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet( data.stylist.append_stylesheet(sheet, &guard); } +#[no_mangle] +pub extern "C" fn Servo_AuthorStyles_Create() -> *mut RawServoAuthorStyles { + Box::into_raw(Box::new(AuthorStyles::::new())) as *mut _ +} + +#[no_mangle] +pub extern "C" fn Servo_AuthorStyles_Drop( + styles: RawServoAuthorStylesOwned, +) { + let _ = styles.into_box::>(); +} + +#[no_mangle] +pub unsafe extern "C" fn Servo_AuthorStyles_AppendStyleSheet( + styles: RawServoAuthorStylesBorrowedMut, + sheet: *const ServoStyleSheet, +) { + let styles = AuthorStyles::::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::::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::::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::( + stylist.device(), + stylist.quirks_mode(), + &guard, + ); +} + #[no_mangle] pub unsafe extern "C" fn Servo_StyleSet_MediumFeaturesChanged( document_set: RawServoStyleSetBorrowed, - non_document_sets: *const nsTArray<*mut structs::ServoStyleSet>, + non_document_styles: *mut nsTArray, may_affect_default_style: bool, ) -> structs::MediumFeaturesChangedResult { 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; - for non_document_style_set in &**non_document_sets { - let non_document_data = &*(**non_document_style_set).mRawSet.mPtr; - let non_document_data = - mem::transmute::<&structs::RawServoStyleSet, &bindings::RawServoStyleSet>(non_document_data); - let mut non_document_data = - PerDocumentStyleData::from_ffi(non_document_data).borrow_mut(); - - let origins_changed = - non_document_data.stylist.media_features_change_changed_style( - &guards, + for author_styles in &mut **non_document_styles { + let author_styles = + AuthorStyles::::from_ffi_mut(&mut *author_styles); + let affected_style = author_styles.stylesheets.iter().any(|sheet| { + !author_styles.data.media_feature_affected_matches( + sheet, + &guards.author, document_data.stylist.device(), - ); - if !origins_changed.is_empty() { + document_data.stylist.quirks_mode(), + ) + }); + if affected_style { affects_non_document_rules = true; - // XBL stylesets are rebuilt entirely, so we need to mark them - // 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; + author_styles.stylesheets.force_dirty(); } } @@ -4930,19 +4987,25 @@ pub extern "C" fn Servo_ParseCounterStyleName( #[no_mangle] pub unsafe extern "C" fn Servo_InvalidateStyleForDocStateChanges( root: RawGeckoElementBorrowed, - raw_style_sets: *const nsTArray, + document_style: RawServoStyleSetBorrowed, + non_document_styles: *const nsTArray, states_changed: u64, ) { use style::invalidation::element::document_state::DocumentStateInvalidationProcessor; use style::invalidation::element::invalidator::TreeStyleInvalidator; - let mut borrows = SmallVec::<[_; 20]>::with_capacity((*raw_style_sets).len()); - for style_set in &**raw_style_sets { - borrows.push(PerDocumentStyleData::from_ffi(*style_set).borrow()); - } + let document_data = PerDocumentStyleData::from_ffi(document_style).borrow(); + + let iter = + document_data.stylist.iter_origins().map(|(data, _origin)| data) + .chain((*non_document_styles).iter().map(|author_styles| { + let styles: &_ = AuthorStyles::::from_ffi(author_styles); + &styles.data + })); + let root = GeckoElement(root); 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), root.as_node().owner_doc().quirks_mode(), );