diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 9635e0b081d..bf150468dac 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -137,7 +137,7 @@ use style::selector_parser::SnapshotMap; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW}; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; use style::stylesheets::{Origin, Stylesheet, StylesheetInDocument, UserAgentStylesheets}; -use style::stylist::{ExtraStyleData, Stylist}; +use style::stylist::Stylist; use style::thread_state; use style::timer::Timer; use style::traversal::{DomTraversal, TraversalDriver}; @@ -1206,7 +1206,7 @@ impl LayoutThread { author: &author_guard, ua_or_user: &ua_or_user_guard, }; - let mut extra_data = ExtraStyleData; + let mut extra_data = Default::default(); let needs_dirtying = self.stylist.update( StylesheetIterator(data.document_stylesheets.iter()), &guards, diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index 54dab9fe283..45983338ae9 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -17,7 +17,7 @@ use properties::ComputedValues; use servo_arc::Arc; use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard}; use stylesheet_set::StylesheetSet; -use stylesheets::{StylesheetContents, StylesheetInDocument}; +use stylesheets::{PerOrigin, StylesheetContents, StylesheetInDocument}; use stylist::{ExtraStyleData, Stylist}; /// Little wrapper to a Gecko style sheet. @@ -118,7 +118,7 @@ pub struct PerDocumentStyleDataImpl { pub stylesheets: StylesheetSet, /// List of effective @font-face and @counter-style rules. - pub extra_style_data: ExtraStyleData, + pub extra_style_data: PerOrigin, } /// The data itself is an `AtomicRefCell`, which guarantees the proper semantics diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index e5fc73b9064..469fefe3934 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -44,7 +44,7 @@ pub use self::memory::{MallocSizeOf, MallocSizeOfFn, MallocSizeOfWithGuard}; #[cfg(feature = "gecko")] pub use self::memory::{MallocSizeOfWithRepeats, SizeOfState}; pub use self::namespace_rule::NamespaceRule; -pub use self::origin::Origin; +pub use self::origin::{Origin, PerOrigin, PerOriginClear}; pub use self::page_rule::PageRule; pub use self::rule_parser::{State, TopLevelRuleParser}; pub use self::rule_list::{CssRules, CssRulesHelpers}; diff --git a/components/style/stylesheets/origin.rs b/components/style/stylesheets/origin.rs index 312b62cb087..a9a31bfe124 100644 --- a/components/style/stylesheets/origin.rs +++ b/components/style/stylesheets/origin.rs @@ -4,6 +4,9 @@ ///! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins). +use std::marker::PhantomData; +use std::mem::transmute; + /// Each style rule has an origin, which determines where it enters the cascade. /// /// https://drafts.csswg.org/css-cascade/#cascading-origins @@ -19,3 +22,125 @@ pub enum Origin { /// https://drafts.csswg.org/css-cascade/#cascade-origin-user User, } + +/// An object that stores a `T` for each origin of the CSS cascade. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Debug, Default)] +pub struct PerOrigin { + /// Data for `Origin::UserAgent`. + pub user_agent: T, + + /// Data for `Origin::Author`. + pub author: T, + + /// Data for `Origin::User`. + pub user: T, +} + +impl PerOrigin { + /// Returns a reference to the per-origin data for the specified origin. + #[inline] + pub fn borrow_for_origin(&self, origin: &Origin) -> &T { + match *origin { + Origin::UserAgent => &self.user_agent, + Origin::Author => &self.author, + Origin::User => &self.user, + } + } + + /// Returns a mutable reference to the per-origin data for the specified + /// origin. + #[inline] + pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T { + match *origin { + Origin::UserAgent => &mut self.user_agent, + Origin::Author => &mut self.author, + Origin::User => &mut self.user, + } + } + + /// Iterates over references to per-origin extra style data, from highest + /// level (user) to lowest (user agent). + pub fn iter_origins(&self) -> PerOriginIter { + PerOriginIter { + data: &self, + cur: 0, + } + } + + /// Iterates over mutable references to per-origin extra style data, from + /// highest level (user) to lowest (user agent). + pub fn iter_mut_origins(&mut self) -> PerOriginIterMut { + PerOriginIterMut { + data: self, + cur: 0, + _marker: PhantomData, + } + } +} + +/// An object that can be cleared. +pub trait PerOriginClear { + /// Clears the object. + fn clear(&mut self); +} + +impl PerOriginClear for PerOrigin where T: PerOriginClear { + fn clear(&mut self) { + self.user_agent.clear(); + self.author.clear(); + self.user.clear(); + } +} + +/// Iterator over `PerOrigin`, from highest level (user) to lowest +/// (user agent). +/// +/// We rely on this specific order for correctly looking up @font-face, +/// @counter-style and @keyframes rules. +pub struct PerOriginIter<'a, T: 'a> { + data: &'a PerOrigin, + cur: usize, +} + +impl<'a, T> Iterator for PerOriginIter<'a, T> where T: 'a { + type Item = (&'a T, Origin); + + fn next(&mut self) -> Option { + let result = match self.cur { + 0 => (&self.data.user, Origin::User), + 1 => (&self.data.author, Origin::Author), + 2 => (&self.data.user_agent, Origin::UserAgent), + _ => return None, + }; + self.cur += 1; + Some(result) + } +} + +/// Like `PerOriginIter`, but iterates over mutable references to the +/// per-origin data. +/// +/// We must use unsafe code here since it's not possible for the borrow +/// checker to know that we are safely returning a different reference +/// each time from `next()`. +pub struct PerOriginIterMut<'a, T: 'a> { + data: *mut PerOrigin, + cur: usize, + _marker: PhantomData<&'a mut PerOrigin>, +} + +impl<'a, T> Iterator for PerOriginIterMut<'a, T> where T: 'a { + type Item = (&'a mut T, Origin); + + fn next(&mut self) -> Option { + let result = match self.cur { + 0 => (unsafe { transmute(&mut (*self.data).user) }, Origin::User), + 1 => (unsafe { transmute(&mut (*self.data).author) }, Origin::Author), + 2 => (unsafe { transmute(&mut (*self.data).user_agent) }, Origin::UserAgent), + _ => return None, + }; + self.cur += 1; + Some(result) + } +} diff --git a/components/style/stylist.rs b/components/style/stylist.rs index f3d79277a80..51585a7d7f1 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -40,7 +40,8 @@ use style_traits::viewport::ViewportConstraints; #[cfg(feature = "gecko")] use stylesheets::{CounterStyleRule, FontFaceRule}; use stylesheets::{CssRule, StyleRule}; -use stylesheets::{StylesheetInDocument, Origin, UserAgentStylesheets}; +use stylesheets::{StylesheetInDocument, Origin, PerOrigin, PerOriginClear}; +use stylesheets::UserAgentStylesheets; use stylesheets::keyframes_rule::KeyframesAnimation; use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule}; use thread_state; @@ -90,7 +91,7 @@ pub struct Stylist { /// Selector maps for all of the style sheets in the stylist, after /// evalutaing media rules against the current device, split out per /// cascade level. - cascade_data: CascadeData, + cascade_data: PerOrigin, /// The rule tree, that stores the results of selector matching. rule_tree: RuleTree, @@ -110,90 +111,6 @@ pub struct Stylist { num_rebuilds: usize, } -/// This struct holds data which users of Stylist may want to extract -/// from stylesheets which can be done at the same time as updating. -#[cfg(feature = "gecko")] -#[derive(Default)] -pub struct ExtraStyleData { - /// Extra data from user agent stylesheets - user_agent: PerOriginExtraStyleData, - /// Extra data from author stylesheets - author: PerOriginExtraStyleData, - /// Extra data from user stylesheets - user: PerOriginExtraStyleData, -} - -/// This struct holds data which users of Stylist may want to extract -/// from stylesheets which can be done at the same time as updating. -#[cfg(feature = "gecko")] -#[derive(Default)] -pub struct PerOriginExtraStyleData { - /// A list of effective font-face rules and their origin. - pub font_faces: Vec>>, - /// A map of effective counter-style rules. - pub counter_styles: PrecomputedHashMap>>, -} - -#[cfg(feature = "gecko")] -impl ExtraStyleData { - /// Clear the internal data. - pub fn clear(&mut self) { - self.user_agent.clear(); - self.author.clear(); - self.user.clear(); - } - - /// Returns a reference to the per-origin extra style data for - /// the specified origin. - #[inline] - pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginExtraStyleData { - match *origin { - Origin::UserAgent => &mut self.user_agent, - Origin::Author => &mut self.author, - Origin::User => &mut self.user, - } - } - - /// Iterates over the per-origin extra style data, from highest level (user) - /// to lowest (user agent). - pub fn iter_origins(&self) -> ExtraStyleDataIter { - ExtraStyleDataIter { - extra_style_data: &self, - cur: 0, - } - } -} - -#[cfg(feature = "gecko")] -impl PerOriginExtraStyleData { - /// Clears the stored @font-face and @counter-style rules. - fn clear(&mut self) { - self.font_faces.clear(); - self.counter_styles.clear(); - } - - /// Add the given @font-face rule. - fn add_font_face(&mut self, rule: &Arc>) { - self.font_faces.push(rule.clone()); - } - - /// Add the given @counter-style rule. - fn add_counter_style(&mut self, guard: &SharedRwLockReadGuard, - rule: &Arc>) { - let name = rule.read_with(guard).mName.raw::().into(); - self.counter_styles.insert(name, rule.clone()); - } -} - -#[allow(missing_docs)] -#[cfg(feature = "servo")] -pub struct ExtraStyleData; - -#[cfg(feature = "servo")] -impl ExtraStyleData { - fn clear(&mut self) {} -} - /// What cascade levels to include when styling elements. #[derive(Copy, Clone, PartialEq)] pub enum RuleInclusion { @@ -229,7 +146,7 @@ impl Stylist { quirks_mode: quirks_mode, effective_media_query_results: EffectiveMediaQueryResults::new(), - cascade_data: CascadeData::new(), + cascade_data: Default::default(), precomputed_pseudo_element_decls: PerPseudoElementMap::default(), rules_source_order: 0, rule_tree: RuleTree::new(), @@ -241,12 +158,12 @@ impl Stylist { /// Returns the number of selectors. pub fn num_selectors(&self) -> usize { - self.cascade_data.iter_origins().map(|d| d.num_selectors).sum() + self.cascade_data.iter_origins().map(|(d, _)| d.num_selectors).sum() } /// Returns the number of declarations. pub fn num_declarations(&self) -> usize { - self.cascade_data.iter_origins().map(|d| d.num_declarations).sum() + self.cascade_data.iter_origins().map(|(d, _)| d.num_declarations).sum() } /// Returns the number of times the stylist has been rebuilt. @@ -257,13 +174,13 @@ impl Stylist { /// Returns the number of revalidation_selectors. pub fn num_revalidation_selectors(&self) -> usize { self.cascade_data.iter_origins() - .map(|d| d.selectors_for_cache_revalidation.len()).sum() + .map(|(d, _)| d.selectors_for_cache_revalidation.len()).sum() } /// Returns the number of entries in invalidation maps. pub fn num_invalidations(&self) -> usize { self.cascade_data.iter_origins() - .map(|d| d.invalidation_map.len()).sum() + .map(|(d, _)| d.invalidation_map.len()).sum() } /// Invokes `f` with the `InvalidationMap` for each origin. @@ -274,8 +191,8 @@ impl Stylist { pub fn each_invalidation_map(&self, mut f: F) where F: FnMut(&InvalidationMap) { - for origin_cascade_data in self.cascade_data.iter_origins() { - f(&origin_cascade_data.invalidation_map) + for (data, _) in self.cascade_data.iter_origins() { + f(&data.invalidation_map) } } @@ -324,7 +241,7 @@ impl Stylist { ua_stylesheets: Option<&UserAgentStylesheets>, stylesheets_changed: bool, author_style_disabled: bool, - extra_data: &mut ExtraStyleData + extra_data: &mut PerOrigin ) -> bool where I: Iterator + Clone, @@ -404,7 +321,7 @@ impl Stylist { ua_stylesheets: Option<&UserAgentStylesheets>, stylesheets_changed: bool, author_style_disabled: bool, - extra_data: &mut ExtraStyleData + extra_data: &mut PerOrigin ) -> bool where I: Iterator + Clone, @@ -426,7 +343,7 @@ impl Stylist { &mut self, stylesheet: &S, guard: &SharedRwLockReadGuard, - _extra_data: &mut ExtraStyleData + _extra_data: &mut PerOrigin ) where S: StylesheetInDocument + ToMediaListKey + 'static, @@ -574,11 +491,11 @@ impl Stylist { } else if *local_name == local_name!("style") { self.cascade_data .iter_origins() - .any(|d| d.style_attribute_dependency) + .any(|(d, _)| d.style_attribute_dependency) } else { self.cascade_data .iter_origins() - .any(|d| { + .any(|(d, _)| { d.attribute_dependencies .might_contain_hash(local_name.get_hash()) }) @@ -602,7 +519,7 @@ impl Stylist { pub fn has_state_dependency(&self, state: ElementState) -> bool { self.cascade_data .iter_origins() - .any(|d| d.state_dependencies.intersects(state)) + .any(|(d, _)| d.state_dependencies.intersects(state)) } /// Computes the style for a given "precomputed" pseudo-element, taking the @@ -852,6 +769,12 @@ impl Stylist { self.quirks_mode) } + fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool { + self.cascade_data + .iter_origins() + .any(|(d, _)| d.has_rules_for_pseudo(pseudo)) + } + /// Computes the cascade inputs for a lazily-cascaded pseudo-element. /// /// See the documentation on lazy pseudo-elements in @@ -868,7 +791,7 @@ impl Stylist { let pseudo = pseudo.canonical(); debug_assert!(pseudo.is_lazy()); - if !self.cascade_data.has_rules_for_pseudo(&pseudo) { + if !self.has_rules_for_pseudo(&pseudo) { return CascadeInputs::default() } @@ -1324,7 +1247,7 @@ impl Stylist { pub fn may_have_rules_for_id(&self, id: &Atom) -> bool { self.cascade_data .iter_origins() - .any(|d| d.mapped_ids.might_contain_hash(id.get_hash())) + .any(|(d, _)| d.mapped_ids.might_contain_hash(id.get_hash())) } /// Return whether the device is dirty, that is, whether the screen size or @@ -1339,7 +1262,7 @@ impl Stylist { pub fn get_animation(&self, name: &Atom) -> Option<&KeyframesAnimation> { self.cascade_data .iter_origins() - .filter_map(|d| d.animations.get(name)) + .filter_map(|(d, _)| d.animations.get(name)) .next() } @@ -1364,8 +1287,8 @@ impl Stylist { // the lookups, which means that the bitvecs are comparable. We verify // this in the caller by asserting that the bitvecs are same-length. let mut results = BitVec::new(); - for origin_cascade_data in self.cascade_data.iter_origins() { - origin_cascade_data.selectors_for_cache_revalidation.lookup( + for (data, _) in self.cascade_data.iter_origins() { + data.selectors_for_cache_revalidation.lookup( *element, self.quirks_mode, &mut |selector_and_hashes| { results.push(matches_selector(&selector_and_hashes.selector, selector_and_hashes.selector_offset, @@ -1431,6 +1354,44 @@ impl Stylist { } } +/// This struct holds data which users of Stylist may want to extract +/// from stylesheets which can be done at the same time as updating. +#[derive(Default)] +pub struct ExtraStyleData { + /// A list of effective font-face rules and their origin. + #[cfg(feature = "gecko")] + pub font_faces: Vec>>, + + /// A map of effective counter-style rules. + #[cfg(feature = "gecko")] + pub counter_styles: PrecomputedHashMap>>, +} + +#[cfg(feature = "gecko")] +impl ExtraStyleData { + /// Add the given @font-face rule. + fn add_font_face(&mut self, rule: &Arc>) { + self.font_faces.push(rule.clone()); + } + + /// Add the given @counter-style rule. + fn add_counter_style(&mut self, guard: &SharedRwLockReadGuard, + rule: &Arc>) { + let name = rule.read_with(guard).mName.raw::().into(); + self.counter_styles.insert(name, rule.clone()); + } +} + +impl PerOriginClear for ExtraStyleData { + fn clear(&mut self) { + #[cfg(feature = "gecko")] + { + self.font_faces.clear(); + self.counter_styles.clear(); + } + } +} + /// SelectorMapEntry implementation for use in our revalidation selector map. #[derive(Clone, Debug)] struct RevalidationSelectorAndHashes { @@ -1599,111 +1560,11 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { } } -/// Data resulting from performing the CSS cascade. -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Debug)] -struct CascadeData { - /// Rules from user agent stylesheets - user_agent: PerOriginCascadeData, - /// Rules from author stylesheets - author: PerOriginCascadeData, - /// Rules from user stylesheets - user: PerOriginCascadeData, -} - -impl CascadeData { - fn new() -> Self { - CascadeData { - user_agent: PerOriginCascadeData::new(), - author: PerOriginCascadeData::new(), - user: PerOriginCascadeData::new(), - } - } - - #[inline] - fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut PerOriginCascadeData { - match *origin { - Origin::UserAgent => &mut self.user_agent, - Origin::Author => &mut self.author, - Origin::User => &mut self.user, - } - } - - fn clear(&mut self) { - self.user_agent.clear(); - self.author.clear(); - self.user.clear(); - } - - fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool { - self.iter_origins().any(|d| d.has_rules_for_pseudo(pseudo)) - } - - fn iter_origins(&self) -> CascadeDataIter { - CascadeDataIter { - cascade_data: &self, - cur: 0, - } - } -} - -/// Iterator over `PerOriginCascadeData`, from highest level (user) to lowest -/// (user agent). -/// -/// We rely on this specific order for correctly looking up animations -/// (prioritizing rules at higher cascade levels), among other things. -struct CascadeDataIter<'a> { - cascade_data: &'a CascadeData, - cur: usize, -} - -impl<'a> Iterator for CascadeDataIter<'a> { - type Item = &'a PerOriginCascadeData; - - fn next(&mut self) -> Option<&'a PerOriginCascadeData> { - let result = match self.cur { - 0 => &self.cascade_data.user, - 1 => &self.cascade_data.author, - 2 => &self.cascade_data.user_agent, - _ => return None, - }; - self.cur += 1; - Some(result) - } -} - -/// Iterator over `PerOriginExtraStyleData`, from highest level (user) to lowest -/// (user agent). -/// -/// We rely on this specific order for correctly looking up the @font-face -/// and @counter-style rules. -#[cfg(feature = "gecko")] -pub struct ExtraStyleDataIter<'a> { - extra_style_data: &'a ExtraStyleData, - cur: usize, -} - -#[cfg(feature = "gecko")] -impl<'a> Iterator for ExtraStyleDataIter<'a> { - type Item = (&'a PerOriginExtraStyleData, Origin); - - fn next(&mut self) -> Option<(&'a PerOriginExtraStyleData, Origin)> { - let result = match self.cur { - 0 => (&self.extra_style_data.user, Origin::User), - 1 => (&self.extra_style_data.author, Origin::Author), - 2 => (&self.extra_style_data.user_agent, Origin::UserAgent), - _ => return None, - }; - self.cur += 1; - Some(result) - } -} - /// Data resulting from performing the CSS cascade that is specific to a given /// origin. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Debug)] -struct PerOriginCascadeData { +struct CascadeData { /// Rules from stylesheets at this `CascadeData`'s origin. element_map: SelectorMap, @@ -1758,7 +1619,7 @@ struct PerOriginCascadeData { num_declarations: usize, } -impl PerOriginCascadeData { +impl CascadeData { fn new() -> Self { Self { element_map: SelectorMap::new(), @@ -1783,6 +1644,12 @@ impl PerOriginCascadeData { } } + fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool { + self.pseudos_map.get(pseudo).is_some() + } +} + +impl PerOriginClear for CascadeData { fn clear(&mut self) { self.element_map = SelectorMap::new(); self.pseudos_map = Default::default(); @@ -1796,9 +1663,11 @@ impl PerOriginCascadeData { self.num_selectors = 0; self.num_declarations = 0; } +} - fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool { - self.pseudos_map.get(pseudo).is_some() +impl Default for CascadeData { + fn default() -> Self { + CascadeData::new() } }