diff --git a/components/style/gecko/generated/pseudo_element_definition.rs b/components/style/gecko/generated/pseudo_element_definition.rs index 485d4a023f0..00081d8ba8a 100644 --- a/components/style/gecko/generated/pseudo_element_definition.rs +++ b/components/style/gecko/generated/pseudo_element_definition.rs @@ -175,9 +175,14 @@ pub enum PseudoElement { + + /// The number of eager pseudo-elements. pub const EAGER_PSEUDO_COUNT: usize = 4; +/// The number of non-functional pseudo-elements. +pub const SIMPLE_PSEUDO_COUNT: usize = 71; + /// The list of eager pseudos. pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ PseudoElement::Before, @@ -188,88 +193,7 @@ pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ - - - impl PseudoElement { - /// Executes a closure with each simple (not functional) - /// pseudo-element as an argument. - pub fn each_simple(mut fun: F) - where F: FnMut(Self), - { - fun(PseudoElement::After); - fun(PseudoElement::Before); - fun(PseudoElement::Backdrop); - fun(PseudoElement::Cue); - fun(PseudoElement::FirstLetter); - fun(PseudoElement::FirstLine); - fun(PseudoElement::MozSelection); - fun(PseudoElement::MozFocusInner); - fun(PseudoElement::MozFocusOuter); - fun(PseudoElement::MozListBullet); - fun(PseudoElement::MozListNumber); - fun(PseudoElement::MozMathAnonymous); - fun(PseudoElement::MozNumberWrapper); - fun(PseudoElement::MozNumberText); - fun(PseudoElement::MozNumberSpinBox); - fun(PseudoElement::MozNumberSpinUp); - fun(PseudoElement::MozNumberSpinDown); - fun(PseudoElement::MozProgressBar); - fun(PseudoElement::MozRangeTrack); - fun(PseudoElement::MozRangeProgress); - fun(PseudoElement::MozRangeThumb); - fun(PseudoElement::MozMeterBar); - fun(PseudoElement::MozPlaceholder); - fun(PseudoElement::Placeholder); - fun(PseudoElement::MozColorSwatch); - fun(PseudoElement::MozText); - fun(PseudoElement::OofPlaceholder); - fun(PseudoElement::FirstLetterContinuation); - fun(PseudoElement::MozBlockInsideInlineWrapper); - fun(PseudoElement::MozMathMLAnonymousBlock); - fun(PseudoElement::MozXULAnonymousBlock); - fun(PseudoElement::HorizontalFramesetBorder); - fun(PseudoElement::VerticalFramesetBorder); - fun(PseudoElement::MozLineFrame); - fun(PseudoElement::ButtonContent); - fun(PseudoElement::CellContent); - fun(PseudoElement::DropDownList); - fun(PseudoElement::FieldsetContent); - fun(PseudoElement::FramesetBlank); - fun(PseudoElement::MozDisplayComboboxControlFrame); - fun(PseudoElement::HtmlCanvasContent); - fun(PseudoElement::InlineTable); - fun(PseudoElement::Table); - fun(PseudoElement::TableCell); - fun(PseudoElement::TableColGroup); - fun(PseudoElement::TableCol); - fun(PseudoElement::TableWrapper); - fun(PseudoElement::TableRowGroup); - fun(PseudoElement::TableRow); - fun(PseudoElement::Canvas); - fun(PseudoElement::PageBreak); - fun(PseudoElement::Page); - fun(PseudoElement::PageContent); - fun(PseudoElement::PageSequence); - fun(PseudoElement::ScrolledContent); - fun(PseudoElement::ScrolledCanvas); - fun(PseudoElement::ScrolledPageSequence); - fun(PseudoElement::ColumnContent); - fun(PseudoElement::Viewport); - fun(PseudoElement::ViewportScroll); - fun(PseudoElement::AnonymousFlexItem); - fun(PseudoElement::AnonymousGridItem); - fun(PseudoElement::Ruby); - fun(PseudoElement::RubyBase); - fun(PseudoElement::RubyBaseContainer); - fun(PseudoElement::RubyText); - fun(PseudoElement::RubyTextContainer); - fun(PseudoElement::MozSVGMarkerAnonChild); - fun(PseudoElement::MozSVGOuterSVGAnonChild); - fun(PseudoElement::MozSVGForeignContent); - fun(PseudoElement::MozSVGText); - } - /// Get the pseudo-element as an atom. #[inline] pub fn atom(&self) -> Atom { @@ -360,6 +284,165 @@ impl PseudoElement { } } + /// Returns an index if the pseudo-element is a simple (non-functional) + /// pseudo. + #[inline] + pub fn simple_index(&self) -> Option { + match *self { + PseudoElement::After => Some(0), + PseudoElement::Before => Some(1), + PseudoElement::Backdrop => Some(2), + PseudoElement::Cue => Some(3), + PseudoElement::FirstLetter => Some(4), + PseudoElement::FirstLine => Some(5), + PseudoElement::MozSelection => Some(6), + PseudoElement::MozFocusInner => Some(7), + PseudoElement::MozFocusOuter => Some(8), + PseudoElement::MozListBullet => Some(9), + PseudoElement::MozListNumber => Some(10), + PseudoElement::MozMathAnonymous => Some(11), + PseudoElement::MozNumberWrapper => Some(12), + PseudoElement::MozNumberText => Some(13), + PseudoElement::MozNumberSpinBox => Some(14), + PseudoElement::MozNumberSpinUp => Some(15), + PseudoElement::MozNumberSpinDown => Some(16), + PseudoElement::MozProgressBar => Some(17), + PseudoElement::MozRangeTrack => Some(18), + PseudoElement::MozRangeProgress => Some(19), + PseudoElement::MozRangeThumb => Some(20), + PseudoElement::MozMeterBar => Some(21), + PseudoElement::MozPlaceholder => Some(22), + PseudoElement::Placeholder => Some(23), + PseudoElement::MozColorSwatch => Some(24), + PseudoElement::MozText => Some(25), + PseudoElement::OofPlaceholder => Some(26), + PseudoElement::FirstLetterContinuation => Some(27), + PseudoElement::MozBlockInsideInlineWrapper => Some(28), + PseudoElement::MozMathMLAnonymousBlock => Some(29), + PseudoElement::MozXULAnonymousBlock => Some(30), + PseudoElement::HorizontalFramesetBorder => Some(31), + PseudoElement::VerticalFramesetBorder => Some(32), + PseudoElement::MozLineFrame => Some(33), + PseudoElement::ButtonContent => Some(34), + PseudoElement::CellContent => Some(35), + PseudoElement::DropDownList => Some(36), + PseudoElement::FieldsetContent => Some(37), + PseudoElement::FramesetBlank => Some(38), + PseudoElement::MozDisplayComboboxControlFrame => Some(39), + PseudoElement::HtmlCanvasContent => Some(40), + PseudoElement::InlineTable => Some(41), + PseudoElement::Table => Some(42), + PseudoElement::TableCell => Some(43), + PseudoElement::TableColGroup => Some(44), + PseudoElement::TableCol => Some(45), + PseudoElement::TableWrapper => Some(46), + PseudoElement::TableRowGroup => Some(47), + PseudoElement::TableRow => Some(48), + PseudoElement::Canvas => Some(49), + PseudoElement::PageBreak => Some(50), + PseudoElement::Page => Some(51), + PseudoElement::PageContent => Some(52), + PseudoElement::PageSequence => Some(53), + PseudoElement::ScrolledContent => Some(54), + PseudoElement::ScrolledCanvas => Some(55), + PseudoElement::ScrolledPageSequence => Some(56), + PseudoElement::ColumnContent => Some(57), + PseudoElement::Viewport => Some(58), + PseudoElement::ViewportScroll => Some(59), + PseudoElement::AnonymousFlexItem => Some(60), + PseudoElement::AnonymousGridItem => Some(61), + PseudoElement::Ruby => Some(62), + PseudoElement::RubyBase => Some(63), + PseudoElement::RubyBaseContainer => Some(64), + PseudoElement::RubyText => Some(65), + PseudoElement::RubyTextContainer => Some(66), + PseudoElement::MozSVGMarkerAnonChild => Some(67), + PseudoElement::MozSVGOuterSVGAnonChild => Some(68), + PseudoElement::MozSVGForeignContent => Some(69), + PseudoElement::MozSVGText => Some(70), + _ => None, + } + } + + /// Returns an array of `None` values. + /// + /// FIXME(emilio): Integer generics can't come soon enough. + pub fn simple_pseudo_none_array() -> [Option; SIMPLE_PSEUDO_COUNT] { + [ + None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None, +None + ] + } + /// Whether this pseudo-element is an anonymous box. #[inline] pub fn is_anon_box(&self) -> bool { diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index fd11fd6abb4..fd65de440b4 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -16,10 +16,15 @@ pub enum PseudoElement { } <% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %> +<% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %> +<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if not pseudo.is_tree_pseudo_element()] %> /// The number of eager pseudo-elements. pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)}; +/// The number of non-functional pseudo-elements. +pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)}; + /// The list of eager pseudos. pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ % for eager_pseudo_name in EAGER_PSEUDOS: @@ -27,24 +32,11 @@ pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ % endfor ]; -<% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %> -<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if not pseudo.is_tree_pseudo_element()] %> - <%def name="pseudo_element_variant(pseudo, tree_arg='..')">\ PseudoElement::${pseudo.capitalized()}${"({})".format(tree_arg) if pseudo.is_tree_pseudo_element() else ""}\ impl PseudoElement { - /// Executes a closure with each simple (not functional) - /// pseudo-element as an argument. - pub fn each_simple(mut fun: F) - where F: FnMut(Self), - { - % for pseudo in SIMPLE_PSEUDOS: - fun(${pseudo_element_variant(pseudo)}); - % endfor - } - /// Get the pseudo-element as an atom. #[inline] pub fn atom(&self) -> Atom { @@ -55,6 +47,27 @@ impl PseudoElement { } } + /// Returns an index if the pseudo-element is a simple (non-functional) + /// pseudo. + #[inline] + pub fn simple_index(&self) -> Option { + match *self { + % for i, pseudo in enumerate(SIMPLE_PSEUDOS): + ${pseudo_element_variant(pseudo)} => Some(${i}), + % endfor + _ => None, + } + } + + /// Returns an array of `None` values. + /// + /// FIXME(emilio): Integer generics can't come soon enough. + pub fn simple_pseudo_none_array() -> [Option; SIMPLE_PSEUDO_COUNT] { + [ + ${",\n".join(["None" for pseudo in SIMPLE_PSEUDOS])} + ] + } + /// Whether this pseudo-element is an anonymous box. #[inline] pub fn is_anon_box(&self) -> bool { diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 26802333007..8327ae5c05c 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -14,7 +14,7 @@ use std::fmt; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use style_traits::{ParseError, StyleParseError}; -pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT}; +pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, SIMPLE_PSEUDO_COUNT}; pub use gecko::snapshot::SnapshotMap; bitflags! { @@ -413,15 +413,6 @@ impl SelectorImpl { } - #[inline] - /// Executes a function for each simple (not functional) pseudo-element. - pub fn each_simple_pseudo_element(fun: F) - where F: FnMut(PseudoElement), - { - PseudoElement::each_simple(fun) - } - - #[inline] /// Returns the relevant state flag for a given non-tree-structural /// pseudo-class. pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index 8399d5e6778..7443c4534c1 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -9,7 +9,7 @@ use cssparser::{Parser as CssParser, ParserInput}; use selectors::Element; use selectors::parser::SelectorList; -use std::fmt::Debug; +use std::fmt::{self, Debug}; use style_traits::ParseError; use stylesheets::{Origin, Namespaces, UrlExtraData}; @@ -121,20 +121,81 @@ pub trait ElementExt: Element + Debug { fn matches_user_and_author_rules(&self) -> bool; } -impl SelectorImpl { - /// A helper to traverse each precomputed pseudo-element, executing `fun` on - /// it. - /// - /// The optimization comment in `each_eagerly_cascaded_pseudo_element` also - /// applies here. - #[inline] - pub fn each_precomputed_pseudo_element(mut fun: F) - where F: FnMut(PseudoElement), - { - Self::each_simple_pseudo_element(|pseudo| { - if pseudo.is_precomputed() { - fun(pseudo) - } - }) +/// A per-functional-pseudo map, from a given pseudo to a `T`. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct PerPseudoElementMap { + entries: [Option; SIMPLE_PSEUDO_COUNT], +} + +impl Default for PerPseudoElementMap { + fn default() -> Self { + Self { + entries: PseudoElement::simple_pseudo_none_array(), + } + } +} + +impl Debug for PerPseudoElementMap +where + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("[")?; + let mut first = true; + for entry in self.entries.iter() { + if !first { + f.write_str(", ")?; + } + first = false; + entry.fmt(f)?; + } + f.write_str("]") + } +} + +impl PerPseudoElementMap { + /// Get an entry in the map. + pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> { + let index = match pseudo.simple_index() { + Some(i) => i, + None => return None, + }; + self.entries[index].as_ref() + } + + /// Clear this enumerated array. + pub fn clear(&mut self) { + *self = Self::default(); + } + + /// Set an entry value. + /// + /// Returns an error if the element is not a simple pseudo. + pub fn set(&mut self, pseudo: &PseudoElement, value: T) -> Result<(), ()> { + let index = match pseudo.simple_index() { + Some(i) => i, + None => return Err(()), + }; + self.entries[index] = Some(value); + Ok(()) + } + + /// Get an entry for `pseudo`, or create it with calling `f`. + pub fn get_or_insert_with( + &mut self, + pseudo: &PseudoElement, + f: F, + ) -> Result<&mut T, ()> + where + F: FnOnce() -> T, + { + let index = match pseudo.simple_index() { + Some(i) => i, + None => return Err(()), + }; + if self.entries[index].is_none() { + self.entries[index] = Some(f()); + } + Ok(self.entries[index].as_mut().unwrap()) } } diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 784a839c800..4a1170a9a8d 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -64,6 +64,10 @@ pub enum PseudoElement { ServoInlineAbsolute, } +/// The count of simple (non-functional) pseudo-elements (that is, all +/// pseudo-elements for now). +pub const SIMPLE_PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1; + impl ::selectors::parser::PseudoElement for PseudoElement { type Impl = SelectorImpl; @@ -106,6 +110,17 @@ impl PseudoElement { self.clone() as usize } + /// An index for this pseudo-element to be indexed in an enumerated array. + #[inline] + pub fn simple_index(&self) -> Option { + Some(self.clone() as usize) + } + + /// An array of `None`, one per simple pseudo-element. + pub fn simple_pseudo_none_array() -> [Option; SIMPLE_PSEUDO_COUNT] { + Default::default() + } + /// Creates a pseudo-element from an eager index. #[inline] pub fn from_eager_index(i: usize) -> Self { @@ -535,28 +550,6 @@ impl SelectorImpl { } } - /// Executes `fun` for each pseudo-element. - #[inline] - pub fn each_simple_pseudo_element(mut fun: F) - where F: FnMut(PseudoElement), - { - fun(PseudoElement::Before); - fun(PseudoElement::After); - fun(PseudoElement::DetailsContent); - fun(PseudoElement::DetailsSummary); - fun(PseudoElement::Selection); - fun(PseudoElement::ServoText); - fun(PseudoElement::ServoInputText); - fun(PseudoElement::ServoTableWrapper); - fun(PseudoElement::ServoAnonymousTableWrapper); - fun(PseudoElement::ServoAnonymousTable); - fun(PseudoElement::ServoAnonymousTableRow); - fun(PseudoElement::ServoAnonymousTableCell); - fun(PseudoElement::ServoAnonymousBlock); - fun(PseudoElement::ServoInlineBlockWrapper); - fun(PseudoElement::ServoInlineAbsolute); - } - /// Returns the pseudo-class state flag for selector matching. #[inline] pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index cc96b29f226..24435514471 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -23,7 +23,7 @@ use properties::INHERIT_ALL; use properties::IS_LINK; use rule_tree::{CascadeLevel, RuleTree, StyleSource}; use selector_map::{SelectorMap, SelectorMapEntry}; -use selector_parser::{SelectorImpl, PseudoElement}; +use selector_parser::{SelectorImpl, PerPseudoElementMap, PseudoElement}; use selectors::attr::NamespaceConstraint; use selectors::bloom::BloomFilter; use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode}; @@ -105,7 +105,7 @@ pub struct Stylist { /// computed values on the fly on layout. /// /// FIXME(emilio): Use the rule tree! - precomputed_pseudo_element_decls: FnvHashMap>, + precomputed_pseudo_element_decls: PerPseudoElementMap>, /// A monotonically increasing counter to represent the order on which a /// style rule appears in a stylesheet, needed to sort them by source order. @@ -240,7 +240,7 @@ impl Stylist { cascade_data: CascadeData::new(), animations: Default::default(), - precomputed_pseudo_element_decls: Default::default(), + precomputed_pseudo_element_decls: PerPseudoElementMap::default(), rules_source_order: 0, rule_tree: RuleTree::new(), invalidation_map: InvalidationMap::new(), @@ -308,7 +308,7 @@ impl Stylist { // preserve current quirks_mode value self.cascade_data.clear(); self.animations.clear(); // Or set to Default::default()? - self.precomputed_pseudo_element_decls = Default::default(); + self.precomputed_pseudo_element_decls.clear(); self.rules_source_order = 0; // We want to keep rule_tree around across stylist rebuilds. self.invalidation_map.clear(); @@ -474,8 +474,8 @@ impl Stylist { } self.precomputed_pseudo_element_decls - .entry(pseudo.canonical()) - .or_insert_with(Vec::new) + .get_or_insert_with(&pseudo.canonical(), Vec::new) + .expect("Unexpected tree pseudo-element?") .push(ApplicableDeclarationBlock::new( StyleSource::Style(locked.clone()), self.rules_source_order, @@ -489,8 +489,8 @@ impl Stylist { Some(pseudo) => { origin_cascade_data .pseudos_map - .entry(pseudo.canonical()) - .or_insert_with(SelectorMap::new) + .get_or_insert_with(&pseudo.canonical(), SelectorMap::new) + .expect("Unexpected tree pseudo-element?") } }; @@ -1665,14 +1665,14 @@ struct PerOriginCascadeData { /// Rules from stylesheets at this `CascadeData`'s origin that correspond /// to a given pseudo-element. - pseudos_map: FnvHashMap>, + pseudos_map: PerPseudoElementMap>, } impl PerOriginCascadeData { fn new() -> Self { Self { element_map: SelectorMap::new(), - pseudos_map: Default::default(), + pseudos_map: PerPseudoElementMap::default(), } } @@ -1689,9 +1689,7 @@ impl PerOriginCascadeData { } fn has_rules_for_pseudo(&self, pseudo: &PseudoElement) -> bool { - // FIXME(emilio): We should probably make the pseudos map be an - // enumerated array. - self.pseudos_map.contains_key(pseudo) + self.pseudos_map.get(pseudo).is_some() } }