diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index 9ed2801ac2c..49a04dea1fc 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -337,7 +337,7 @@ impl Arc { } #[inline] - fn is_unique(&self) -> bool { + pub fn is_unique(&self) -> bool { // We can use Relaxed here, but the justification is a bit subtle. // // The reason to use Acquire would be to synchronize with other threads diff --git a/components/style/invalidation/media_queries.rs b/components/style/invalidation/media_queries.rs index 0656027555b..9521e3cdc16 100644 --- a/components/style/invalidation/media_queries.rs +++ b/components/style/invalidation/media_queries.rs @@ -53,7 +53,7 @@ impl ToMediaListKey for MediaRule {} /// A struct that holds the result of a media query evaluation pass for the /// media queries that evaluated successfully. -#[derive(Debug)] +#[derive(Debug, PartialEq)] #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct EffectiveMediaQueryResults { diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs index cc79d03b0f1..0b5079a0751 100644 --- a/components/style/stylesheet_set.rs +++ b/components/style/stylesheet_set.rs @@ -31,11 +31,20 @@ where } /// A iterator over the stylesheets of a list of entries in the StylesheetSet. -#[derive(Clone)] pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry>) where 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> where S: StylesheetInDocument + PartialEq + 'static, @@ -119,11 +128,8 @@ pub struct StylesheetFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { - origins_dirty: OriginSetIterator, - // NB: Bound to the StylesheetSet lifetime when constructed, see - // StylesheetSet::flush. - collections: *mut PerOrigin>, - current: Option<(Origin, slice::IterMut<'a, StylesheetSetEntry>)>, + origins_dirty: OriginSet, + collections: &'a mut PerOrigin>, origin_data_validity: PerOrigin, author_style_disabled: bool, had_invalidations: bool, @@ -153,9 +159,56 @@ where *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. 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 @@ -165,81 +218,44 @@ where } } -#[cfg(debug_assertions)] -impl<'a, S> Drop for StylesheetFlusher<'a, S> +/// A flusher struct for a given origin, that takes care of returning the +/// appropriate stylesheets that need work. +pub struct PerOriginFlusher<'a, S> where - S: StylesheetInDocument + PartialEq + 'static, + S: StylesheetInDocument + PartialEq + 'static { - fn drop(&mut self) { - debug_assert!( - self.origins_dirty.next().is_none(), - "You're supposed to fully consume the flusher" - ); - } + iter: slice::IterMut<'a, StylesheetSetEntry>, + validity: OriginValidity, } -impl<'a, S> Iterator for StylesheetFlusher<'a, S> +impl<'a, S> Iterator for PerOriginFlusher<'a, S> where S: StylesheetInDocument + PartialEq + 'static, { - type Item = (&'a S, Origin, SheetRebuildKind); + type Item = (&'a S, SheetRebuildKind); fn next(&mut self) -> Option { use std::mem; loop { - if self.current.is_none() { - 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() { + let potential_sheet = match self.iter.next() { Some(s) => s, - None => { - self.current = None; - continue; - } + None => return None, }; - let origin = self.current.as_ref().unwrap().0; - let dirty = mem::replace(&mut potential_sheet.dirty, false); - if dirty { // 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) { - continue; - } - - let rebuild_kind = match self.origin_validity(origin) { + let rebuild_kind = match self.validity { OriginValidity::Valid => continue, OriginValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly, 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 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::::default(); - for origin in origins_dirty.clone() { + 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); @@ -516,7 +532,6 @@ where had_invalidations, origins_dirty, origin_data_validity, - current: None, } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 9189a4bba64..841b7483a8e 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -40,6 +40,7 @@ use smallbitvec::SmallBitVec; use smallvec::VecLike; use std::fmt::Debug; use std::ops; +use std::sync::Mutex; use style_traits::viewport::ViewportConstraints; use stylesheet_set::{OriginValidity, SheetRebuildKind, StylesheetSet, StylesheetFlusher}; #[cfg(feature = "gecko")] @@ -61,15 +62,87 @@ pub type StylistSheet = ::stylesheets::DocumentStyleSheet; #[cfg(feature = "gecko")] 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 = + Mutex::new(UserAgentCascadeDataCache::new()); +} + +struct UserAgentCascadeDataCache { + entries: Vec>, +} + +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, FailedAllocationError> + where + I: Iterator + 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()) + } + + fn clear(&mut self) { + self.entries.clear(); + } +} + type PrecomputedPseudoElementDeclarations = PerPseudoElementMap>; -/// All the computed information for a stylesheet. #[derive(Default)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -struct DocumentCascadeData { - /// Common data for all the origins. - per_origin: PerOrigin, +struct UserAgentCascadeData { + cascade_data: CascadeData, /// Applicable declarations for a given non-eagerly cascaded pseudo-element. /// @@ -77,18 +150,98 @@ struct DocumentCascadeData { /// computed values on the fly on layout. /// /// These are only filled from UA stylesheets. - /// - /// FIXME(emilio): Use the rule tree! 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, + 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 { + let (_, origin) = match self.iter.next() { + Some(o) => o, + None => return None, + }; + + Some((self.cascade_data.borrow_for_origin(origin), origin)) + } +} + impl DocumentCascadeData { - fn iter_origins(&self) -> PerOriginIter { - self.per_origin.iter_origins() + fn borrow_for_origin(&self, origin: Origin) -> &CascadeData { + match origin { + Origin::UserAgent => &self.user_agent.cascade_data, + Origin::Author => &self.author, + Origin::User => &self.user, + } } - fn iter_origins_rev(&self) -> PerOriginIter { - self.per_origin.iter_origins_rev() + fn iter_origins(&self) -> DocumentCascadeDataIter { + 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 @@ -98,7 +251,7 @@ impl DocumentCascadeData { &mut self, device: &Device, quirks_mode: QuirksMode, - flusher: StylesheetFlusher<'a, S>, + mut flusher: StylesheetFlusher<'a, S>, guards: &StylesheetGuards, ) -> Result<(), FailedAllocationError> where @@ -106,55 +259,56 @@ impl DocumentCascadeData { { debug_assert!(!flusher.nothing_to_do()); - for (cascade_data, origin) in self.per_origin.iter_mut_origins() { - let validity = flusher.origin_validity(origin); + // First do UA sheets. + { + 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 { - continue; - } - - 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( + let ua_cascade_data = ua_cache.lookup( + origin_sheets, device, quirks_mode, - stylesheet, - guard, - rebuild_kind, - &mut self.precomputed_pseudo_element_decls, + guards.ua_or_user )?; + + 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(()) } /// Measures heap usage. #[cfg(feature = "gecko")] 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.per_origin.user.add_size_of_children(ops, sizes); - self.per_origin.author.add_size_of_children(ops, sizes); + self.user.add_size_of_children(ops, sizes); + self.author.add_size_of_children(ops, sizes); - for elem in self.precomputed_pseudo_element_decls.iter() { - if let Some(ref elem) = *elem { - sizes.mStylistPrecomputedPseudos += elem.shallow_size_of(ops); - } - } + // FIXME(emilio): UA_CASCADE_DATA_CACHE is shared, we should do whatever + // we do for RuleProcessorCache in Gecko. } } @@ -556,7 +710,7 @@ impl Stylist { extra_declarations: Option>, ) -> StrongRuleNode { 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) => { match extra_declarations { Some(mut extra_decls) => { @@ -963,107 +1117,33 @@ impl Stylist { &self, guards: &StylesheetGuards, ) -> OriginSet { - use invalidation::media_queries::PotentiallyEffectiveMediaRules; - debug!("Stylist::media_features_change_changed_style"); let mut origins = OriginSet::empty(); let stylesheets = self.stylesheets.iter(); - 'stylesheets_loop: for (stylesheet, origin) in stylesheets { - let guard = guards.for_origin(origin); - let effective_now = - stylesheet.is_effective_for_device(&self.device, guard); - + for (stylesheet, origin) in stylesheets { if origins.contains(origin.into()) { continue; } + let guard = guards.for_origin(origin); let origin_cascade_data = - self.cascade_data.per_origin.borrow_for_origin(&origin); + self.cascade_data.borrow_for_origin(origin); - let effective_then = - origin_cascade_data - .effective_media_query_results - .was_effective(stylesheet); + let affected_changed = !origin_cascade_data.media_feature_affected_matches( + stylesheet, + guard, + &self.device, + self.quirks_mode + ); - if effective_now != effective_then { - debug!(" > Stylesheet changed -> {}, {}", - effective_then, effective_now); + if affected_changed { origins |= origin; - continue; - } - - if !effective_now { - continue; - } - - let mut iter = - stylesheet.iter_rules::( - &self.device, - guard - ); - - while let Some(rule) = iter.next() { - match *rule { - CssRule::Style(..) | - CssRule::Namespace(..) | - CssRule::FontFace(..) | - CssRule::CounterStyle(..) | - CssRule::Supports(..) | - CssRule::Keyframes(..) | - CssRule::Page(..) | - CssRule::Viewport(..) | - CssRule::Document(..) | - CssRule::FontFeatureValues(..) => { - // Not affected by device changes. - continue; - } - CssRule::Import(ref lock) => { - let import_rule = lock.read_with(guard); - let effective_now = - import_rule.stylesheet - .is_effective_for_device(&self.device, guard); - let effective_then = - origin_cascade_data - .effective_media_query_results - .was_effective(import_rule); - if effective_now != effective_then { - debug!(" > @import rule changed {} -> {}", - effective_then, effective_now); - origins |= origin; - continue 'stylesheets_loop; - } - - if !effective_now { - iter.skip_children(); - } - } - CssRule::Media(ref lock) => { - let media_rule = lock.read_with(guard); - let mq = media_rule.media_queries.read_with(guard); - let effective_now = - mq.evaluate(&self.device, self.quirks_mode); - let effective_then = - origin_cascade_data - .effective_media_query_results - .was_effective(media_rule); - if effective_now != effective_then { - debug!(" > @media rule changed {} -> {}", - effective_then, effective_now); - origins |= origin; - continue 'stylesheets_loop; - } - - if !effective_now { - iter.skip_children(); - } - } - } } } - return origins + origins } /// Returns the viewport constraints that apply to this document because of @@ -1108,7 +1188,7 @@ impl Stylist { // nsXBLPrototypeResources::LoadResources() loads Chrome XBL style // 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( element, &rule_hash_target, @@ -1156,7 +1236,7 @@ impl Stylist { let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly; // 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( element, &rule_hash_target, @@ -1194,7 +1274,7 @@ impl Stylist { // Which may be more what you would probably expect. if rule_hash_target.matches_user_and_author_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( element, &rule_hash_target, @@ -1221,7 +1301,7 @@ impl Stylist { // See nsStyleSet::FileRules(). if !cut_off_inheritance { // 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( element, &rule_hash_target, @@ -1426,6 +1506,11 @@ impl Stylist { // We may measure other fields in the future if DMD says it's worth it. } + + /// Shutdown the static data that this module stores. + pub fn shutdown() { + UA_CASCADE_DATA_CACHE.lock().unwrap().clear() + } } /// This struct holds data which users of Stylist may want to extract @@ -1454,6 +1539,8 @@ pub struct ExtraStyleData { // nsCSSFontFaceRules or nsCSSCounterStyleRules OMT (which we don't). #[cfg(feature = "gecko")] unsafe impl Sync for ExtraStyleData {} +#[cfg(feature = "gecko")] +unsafe impl Send for ExtraStyleData {} #[cfg(feature = "gecko")] impl ExtraStyleData { @@ -1496,7 +1583,7 @@ impl 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> { type Item = (&'a ExtraStyleData, Origin); @@ -1786,6 +1873,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( + 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 fn add_stylesheet( &mut self, @@ -1794,7 +1920,7 @@ impl CascadeData { stylesheet: &S, guard: &SharedRwLockReadGuard, rebuild_kind: SheetRebuildKind, - precomputed_pseudo_element_decls: &mut PrecomputedPseudoElementDeclarations, + mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>, ) -> Result<(), FailedAllocationError> where S: StylesheetInDocument + ToMediaListKey + 'static, @@ -1830,6 +1956,8 @@ impl CascadeData { } precomputed_pseudo_element_decls + .as_mut() + .expect("Expected precomputed declarations for the UA level") .get_or_insert_with(&pseudo.canonical(), Vec::new) .expect("Unexpected tree pseudo-element?") .push(ApplicableDeclarationBlock::new( @@ -1943,6 +2071,93 @@ impl CascadeData { Ok(()) } + /// Returns whether all the media-feature affected values matched before and + /// match now in the given stylesheet. + fn media_feature_affected_matches( + &self, + stylesheet: &S, + guard: &SharedRwLockReadGuard, + device: &Device, + quirks_mode: QuirksMode, + ) -> bool + where + S: StylesheetInDocument + ToMediaListKey + 'static, + { + use invalidation::media_queries::PotentiallyEffectiveMediaRules; + + let effective_now = + stylesheet.is_effective_for_device(device, guard); + + let effective_then = + self.effective_media_query_results.was_effective(stylesheet); + + if effective_now != effective_then { + debug!(" > Stylesheet changed -> {}, {}", + effective_then, effective_now); + return false; + } + + if !effective_now { + return true; + } + + let mut iter = + stylesheet.iter_rules::(device, guard); + + while let Some(rule) = iter.next() { + match *rule { + CssRule::Style(..) | + CssRule::Namespace(..) | + CssRule::FontFace(..) | + CssRule::CounterStyle(..) | + CssRule::Supports(..) | + CssRule::Keyframes(..) | + CssRule::Page(..) | + CssRule::Viewport(..) | + CssRule::Document(..) | + CssRule::FontFeatureValues(..) => { + // Not affected by device changes. + continue; + } + CssRule::Import(ref lock) => { + let import_rule = lock.read_with(guard); + let effective_now = + import_rule.stylesheet + .is_effective_for_device(&device, guard); + let effective_then = + self.effective_media_query_results.was_effective(import_rule); + if effective_now != effective_then { + debug!(" > @import rule changed {} -> {}", + effective_then, effective_now); + return false; + } + + if !effective_now { + iter.skip_children(); + } + } + CssRule::Media(ref lock) => { + let media_rule = lock.read_with(guard); + let mq = media_rule.media_queries.read_with(guard); + let effective_now = mq.evaluate(device, quirks_mode); + let effective_then = + self.effective_media_query_results.was_effective(media_rule); + + if effective_now != effective_then { + debug!(" > @media rule changed {} -> {}", + effective_then, effective_now); + return false; + } + + if !effective_now { + iter.skip_children(); + } + } + } + } + + true + } #[inline] fn borrow_for_pseudo(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap> { diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 452f373b0ff..46efbc51aef 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -130,7 +130,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::RuleInclusion; +use style::stylist::{RuleInclusion, Stylist}; use style::thread_state; use style::timer::Timer; use style::traversal::DomTraversal; @@ -186,6 +186,7 @@ pub extern "C" fn Servo_Shutdown() { // The dummy url will be released after shutdown, so clear the // reference to avoid use-after-free. unsafe { DUMMY_URL_DATA = ptr::null_mut(); } + Stylist::shutdown(); } unsafe fn dummy_url_data() -> &'static RefPtr {