aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-04-03 18:04:34 -0500
committerGitHub <noreply@github.com>2017-04-03 18:04:34 -0500
commiteee25e23132b0f5d4cb50e5af4691b6e4bf75978 (patch)
tree21aba7882fbbb3baf700abdaf80459b48695218e /components
parent806584da9ac923feaac1d4c22ca030d75c26c31d (diff)
parent3f0d022ba2658ae25b4ef47f0c2939de79b90d7e (diff)
downloadservo-eee25e23132b0f5d4cb50e5af4691b6e4bf75978.tar.gz
servo-eee25e23132b0f5d4cb50e5af4691b6e4bf75978.zip
Auto merge of #16250 - bholley:pseudo_repr, r=emilio
Don't use a HashMap for pseudo-element styles Reviewed in https://bugzilla.mozilla.org/show_bug.cgi?id=1335708 @bors-servo try <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16250) <!-- Reviewable:end -->
Diffstat (limited to 'components')
-rw-r--r--components/script_layout_interface/wrapper_traits.rs34
-rw-r--r--components/style/data.rs113
-rw-r--r--components/style/gecko/selector_parser.rs84
-rw-r--r--components/style/matching.rs46
-rw-r--r--components/style/selector_parser.rs38
-rw-r--r--components/style/servo/selector_parser.rs72
-rw-r--r--components/style/stylist.rs8
7 files changed, 256 insertions, 139 deletions
diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs
index 08de74f0e21..a1c59d83114 100644
--- a/components/script_layout_interface/wrapper_traits.rs
+++ b/components/script_layout_interface/wrapper_traits.rs
@@ -339,7 +339,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
.unwrap()
.borrow()
.styles().pseudos
- .contains_key(&PseudoElement::Before) {
+ .has(&PseudoElement::Before) {
Some(self.with_pseudo(PseudoElementType::Before(None)))
} else {
None
@@ -352,7 +352,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
.unwrap()
.borrow()
.styles().pseudos
- .contains_key(&PseudoElement::After) {
+ .has(&PseudoElement::After) {
Some(self.with_pseudo(PseudoElementType::After(None)))
} else {
None
@@ -397,31 +397,29 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
// Precompute non-eagerly-cascaded pseudo-element styles if not
// cached before.
let style_pseudo = other.style_pseudo_element();
+ let mut data = self.get_style_data().unwrap().borrow_mut();
match style_pseudo.cascade_type() {
// Already computed during the cascade.
- PseudoElementCascadeType::Eager => {},
+ PseudoElementCascadeType::Eager => {
+ data.styles().pseudos.get(&style_pseudo)
+ .unwrap().values().clone()
+ },
PseudoElementCascadeType::Precomputed => {
- if !self.get_style_data()
- .unwrap()
- .borrow()
- .styles().pseudos.contains_key(&style_pseudo) {
- let mut data = self.get_style_data().unwrap().borrow_mut();
+ if !data.styles().cached_pseudos.contains_key(&style_pseudo) {
let new_style =
context.stylist.precomputed_values_for_pseudo(
&context.guards,
&style_pseudo,
Some(data.styles().primary.values()),
CascadeFlags::empty());
- data.styles_mut().pseudos
+ data.styles_mut().cached_pseudos
.insert(style_pseudo.clone(), new_style);
}
+ data.styles().cached_pseudos.get(&style_pseudo)
+ .unwrap().values().clone()
}
PseudoElementCascadeType::Lazy => {
- if !self.get_style_data()
- .unwrap()
- .borrow()
- .styles().pseudos.contains_key(&style_pseudo) {
- let mut data = self.get_style_data().unwrap().borrow_mut();
+ if !data.styles().cached_pseudos.contains_key(&style_pseudo) {
let new_style =
context.stylist
.lazily_compute_pseudo_element_style(
@@ -429,15 +427,13 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
unsafe { &self.unsafe_get() },
&style_pseudo,
data.styles().primary.values());
- data.styles_mut().pseudos
+ data.styles_mut().cached_pseudos
.insert(style_pseudo.clone(), new_style.unwrap());
}
+ data.styles().cached_pseudos.get(&style_pseudo)
+ .unwrap().values().clone()
}
}
-
- self.get_style_data().unwrap().borrow()
- .styles().pseudos.get(&style_pseudo)
- .unwrap().values().clone()
}
}
}
diff --git a/components/style/data.rs b/components/style/data.rs
index a5a1e1712cb..9eb831e07a1 100644
--- a/components/style/data.rs
+++ b/components/style/data.rs
@@ -11,11 +11,11 @@ use properties::ComputedValues;
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use rule_tree::StrongRuleNode;
-use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
-use std::collections::HashMap;
+use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage, Snapshot};
+#[cfg(feature = "servo")] use std::collections::HashMap;
use std::fmt;
-use std::hash::BuildHasherDefault;
-use std::ops::{Deref, DerefMut};
+#[cfg(feature = "servo")] use std::hash::BuildHasherDefault;
+use std::ops::Deref;
use std::sync::Arc;
use stylist::Stylist;
use thread_state;
@@ -73,42 +73,98 @@ impl fmt::Debug for ComputedStyle {
}
}
-type PseudoStylesInner = HashMap<PseudoElement, ComputedStyle,
- BuildHasherDefault<::fnv::FnvHasher>>;
-
-/// A set of styles for a given element's pseudo-elements.
-///
-/// This is a map from pseudo-element to `ComputedStyle`.
-///
-/// TODO(emilio): This should probably be a small array by default instead of a
-/// full-blown `HashMap`.
+/// A list of styles for eagerly-cascaded pseudo-elements. Lazily-allocated.
#[derive(Clone, Debug)]
-pub struct PseudoStyles(PseudoStylesInner);
+pub struct EagerPseudoStyles(Option<Box<[Option<ComputedStyle>]>>);
-impl PseudoStyles {
- /// Construct an empty set of `PseudoStyles`.
- pub fn empty() -> Self {
- PseudoStyles(HashMap::with_hasher(Default::default()))
+impl EagerPseudoStyles {
+ /// Returns whether there are any pseudo styles.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_some()
}
-}
-impl Deref for PseudoStyles {
- type Target = PseudoStylesInner;
- fn deref(&self) -> &Self::Target { &self.0 }
-}
+ /// Returns a reference to the style for a given eager pseudo, if it exists.
+ pub fn get(&self, pseudo: &PseudoElement) -> Option<&ComputedStyle> {
+ debug_assert!(pseudo.is_eager());
+ self.0.as_ref().and_then(|p| p[pseudo.eager_index()].as_ref())
+ }
+
+ /// Returns a mutable reference to the style for a given eager pseudo, if it exists.
+ pub fn get_mut(&mut self, pseudo: &PseudoElement) -> Option<&mut ComputedStyle> {
+ debug_assert!(pseudo.is_eager());
+ self.0.as_mut().and_then(|p| p[pseudo.eager_index()].as_mut())
+ }
-impl DerefMut for PseudoStyles {
- fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
+ /// Returns true if the EagerPseudoStyles has a ComputedStyle for |pseudo|.
+ pub fn has(&self, pseudo: &PseudoElement) -> bool {
+ self.get(pseudo).is_some()
+ }
+
+ /// Inserts a pseudo-element. The pseudo-element must not already exist.
+ pub fn insert(&mut self, pseudo: &PseudoElement, style: ComputedStyle) {
+ debug_assert!(!self.has(pseudo));
+ if self.0.is_none() {
+ self.0 = Some(vec![None; EAGER_PSEUDO_COUNT].into_boxed_slice());
+ }
+ self.0.as_mut().unwrap()[pseudo.eager_index()] = Some(style);
+ }
+
+ /// Removes a pseudo-element style if it exists, and returns it.
+ pub fn take(&mut self, pseudo: &PseudoElement) -> Option<ComputedStyle> {
+ let result = match self.0.as_mut() {
+ None => return None,
+ Some(arr) => arr[pseudo.eager_index()].take(),
+ };
+ let empty = self.0.as_ref().unwrap().iter().all(|x| x.is_none());
+ if empty {
+ self.0 = None;
+ }
+ result
+ }
+
+ /// Returns a list of the pseudo-elements.
+ pub fn keys(&self) -> Vec<PseudoElement> {
+ let mut v = Vec::new();
+ if let Some(ref arr) = self.0 {
+ for i in 0..EAGER_PSEUDO_COUNT {
+ if arr[i].is_some() {
+ v.push(PseudoElement::from_eager_index(i));
+ }
+ }
+ }
+ v
+ }
+
+ /// Sets the rule node for a given pseudo-element, which must already have an entry.
+ ///
+ /// Returns true if the rule node changed.
+ pub fn set_rules(&mut self, pseudo: &PseudoElement, rules: StrongRuleNode) -> bool {
+ debug_assert!(self.has(pseudo));
+ let mut style = self.get_mut(pseudo).unwrap();
+ let changed = style.rules != rules;
+ style.rules = rules;
+ changed
+ }
}
+/// A cache of precomputed and lazy pseudo-elements, used by servo. This isn't
+/// a very efficient design, but is the result of servo having previously used
+/// the eager pseudo map (when it was a map) for this cache.
+#[cfg(feature = "servo")]
+type PseudoElementCache = HashMap<PseudoElement, ComputedStyle, BuildHasherDefault<::fnv::FnvHasher>>;
+#[cfg(feature = "gecko")]
+type PseudoElementCache = ();
+
/// The styles associated with a node, including the styles for any
/// pseudo-elements.
#[derive(Clone, Debug)]
pub struct ElementStyles {
/// The element's style.
pub primary: ComputedStyle,
- /// The map of styles for the element's pseudos.
- pub pseudos: PseudoStyles,
+ /// A list of the styles for the element's eagerly-cascaded pseudo-elements.
+ pub pseudos: EagerPseudoStyles,
+ /// NB: This is an empty field for gecko.
+ pub cached_pseudos: PseudoElementCache,
}
impl ElementStyles {
@@ -116,7 +172,8 @@ impl ElementStyles {
pub fn new(primary: ComputedStyle) -> Self {
ElementStyles {
primary: primary,
- pseudos: PseudoStyles::empty(),
+ pseudos: EagerPseudoStyles(None),
+ cached_pseudos: PseudoElementCache::default(),
}
}
diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs
index 232087df9ee..c6e4a524142 100644
--- a/components/style/gecko/selector_parser.rs
+++ b/components/style/gecko/selector_parser.rs
@@ -42,7 +42,39 @@ use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElement(Atom, bool);
+/// List of eager pseudos. Keep this in sync with the count below.
+macro_rules! each_eager_pseudo {
+ ($macro_name:ident, $atom_macro:ident) => {
+ $macro_name!($atom_macro!(":after"), 0);
+ $macro_name!($atom_macro!(":before"), 1);
+ }
+}
+
+/// The number of eager pseudo-elements (just ::before and ::after).
+pub const EAGER_PSEUDO_COUNT: usize = 2;
+
+
impl PseudoElement {
+ /// Gets the canonical index of this eagerly-cascaded pseudo-element.
+ #[inline]
+ pub fn eager_index(&self) -> usize {
+ macro_rules! case {
+ ($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return $idx; } }
+ }
+ each_eager_pseudo!(case, atom);
+ panic!("Not eager")
+ }
+
+ /// Creates a pseudo-element from an eager index.
+ #[inline]
+ pub fn from_eager_index(i: usize) -> Self {
+ macro_rules! case {
+ ($atom:expr, $idx:expr) => { if i == $idx { return PseudoElement($atom, false); } }
+ }
+ each_eager_pseudo!(case, atom);
+ panic!("Not eager")
+ }
+
/// Get the pseudo-element as an atom.
#[inline]
pub fn as_atom(&self) -> &Atom {
@@ -55,6 +87,35 @@ impl PseudoElement {
self.1
}
+ /// Whether this pseudo-element is ::before or ::after.
+ #[inline]
+ pub fn is_before_or_after(&self) -> bool {
+ *self.as_atom() == atom!(":before") ||
+ *self.as_atom() == atom!(":after")
+ }
+
+ /// Whether this pseudo-element is eagerly-cascaded.
+ #[inline]
+ pub fn is_eager(&self) -> bool {
+ macro_rules! case {
+ ($atom:expr, $idx:expr) => { if *self.as_atom() == $atom { return true; } }
+ }
+ each_eager_pseudo!(case, atom);
+ return false;
+ }
+
+ /// Whether this pseudo-element is lazily-cascaded.
+ #[inline]
+ pub fn is_lazy(&self) -> bool {
+ !self.is_eager() && !self.is_precomputed()
+ }
+
+ /// Whether this pseudo-element is precomputed.
+ #[inline]
+ pub fn is_precomputed(&self) -> bool {
+ self.is_anon_box()
+ }
+
/// Construct a pseudo-element from an `Atom`, receiving whether it is also
/// an anonymous box, and don't check it on release builds.
///
@@ -398,7 +459,8 @@ impl SelectorImpl {
///
/// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
- if Self::pseudo_is_before_or_after(pseudo) {
+ if pseudo.is_eager() {
+ debug_assert!(!pseudo.is_anon_box());
return PseudoElementCascadeType::Eager
}
@@ -409,6 +471,19 @@ impl SelectorImpl {
PseudoElementCascadeType::Lazy
}
+ /// A helper to traverse each eagerly cascaded pseudo-element, executing
+ /// `fun` on it.
+ #[inline]
+ pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
+ where F: FnMut(PseudoElement),
+ {
+ macro_rules! case {
+ ($atom:expr, $idx:expr) => { fun(PseudoElement($atom, false)); }
+ }
+ each_eager_pseudo!(case, atom);
+ }
+
+
#[inline]
/// Executes a function for each pseudo-element.
pub fn each_pseudo_element<F>(mut fun: F)
@@ -424,13 +499,6 @@ impl SelectorImpl {
}
#[inline]
- /// Returns whether the given pseudo-element is `::before` or `::after`.
- pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
- *pseudo.as_atom() == atom!(":before") ||
- *pseudo.as_atom() == atom!(":after")
- }
-
- #[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/matching.rs b/components/style/matching.rs
index 323561abca9..29865b69e0a 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -20,13 +20,11 @@ use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RestyleHint};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
-use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use servo_config::opts;
use sink::ForgetfulSink;
-use std::collections::hash_map::Entry;
use std::sync::Arc;
use stylist::ApplicableDeclarationBlock;
@@ -903,9 +901,9 @@ pub trait MatchMethods : TElement {
// Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false;
SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
- let mut per_pseudo = &mut data.styles_mut().pseudos;
+ let mut pseudos = &mut data.styles_mut().pseudos;
debug_assert!(applicable_declarations.is_empty());
- let pseudo_animation_rules = if <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo) {
+ let pseudo_animation_rules = if pseudo.is_before_or_after() {
self.get_animation_rules(Some(&pseudo))
} else {
AnimationRules(None, None)
@@ -921,19 +919,13 @@ pub trait MatchMethods : TElement {
if !applicable_declarations.is_empty() {
let new_rules =
compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
- match per_pseudo.entry(pseudo) {
- Entry::Occupied(mut e) => {
- if e.get().rules != new_rules {
- e.get_mut().rules = new_rules;
- rule_nodes_changed = true;
- }
- },
- Entry::Vacant(e) => {
- e.insert(ComputedStyle::new_partial(new_rules));
- matches_different_pseudos = true;
- }
+ if pseudos.has(&pseudo) {
+ rule_nodes_changed = pseudos.set_rules(&pseudo, new_rules);
+ } else {
+ pseudos.insert(&pseudo, ComputedStyle::new_partial(new_rules));
+ matches_different_pseudos = true;
}
- } else if per_pseudo.remove(&pseudo).is_some() {
+ } else if pseudos.take(&pseudo).is_some() {
matches_different_pseudos = true;
}
});
@@ -948,7 +940,7 @@ pub trait MatchMethods : TElement {
}
// If we have any pseudo elements, indicate so in the primary StyleRelations.
- if !data.styles().pseudos.is_empty() {
+ if data.styles().pseudos.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
@@ -995,11 +987,10 @@ pub trait MatchMethods : TElement {
animation_rule.as_ref(),
primary_rules);
- let iter = element_styles.pseudos.iter_mut().filter(|&(p, _)|
- <Self as MatchAttr>::Impl::pseudo_is_before_or_after(p));
- for (pseudo, ref mut computed) in iter {
- let animation_rule = self.get_animation_rule(Some(pseudo));
- let pseudo_rules = &mut computed.rules;
+ let pseudos = &mut element_styles.pseudos;
+ for pseudo in pseudos.keys().iter().filter(|p| p.is_before_or_after()) {
+ let animation_rule = self.get_animation_rule(Some(&pseudo));
+ let pseudo_rules = &mut pseudos.get_mut(&pseudo).unwrap().rules;
replace_rule_node(CascadeLevel::Animations,
animation_rule.as_ref(),
pseudo_rules);
@@ -1198,11 +1189,10 @@ pub trait MatchMethods : TElement {
//
// Note that we've already set up the map of matching pseudo-elements
// in match_element (and handled the damage implications of changing
- // which pseudos match), so now we can just iterate the map. This does
- // mean collecting the keys, so that the borrow checker will let us pass
- // the mutable |data| to the inner cascade function.
- let matched_pseudos: Vec<PseudoElement> =
- data.styles().pseudos.keys().cloned().collect();
+ // which pseudos match), so now we can just iterate what we have. This
+ // does mean collecting owned pseudos, so that the borrow checker will
+ // let us pass the mutable |data| to the inner cascade function.
+ let matched_pseudos = data.styles().pseudos.keys();
for pseudo in matched_pseudos {
// If the new primary style is display:none, we don't need pseudo
// styles, but we still need to clear any stale values.
@@ -1212,7 +1202,7 @@ pub trait MatchMethods : TElement {
}
// Only ::before and ::after are animatable.
- let animate = <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo);
+ let animate = pseudo.is_before_or_after();
self.cascade_primary_or_pseudo(context, data, Some(&pseudo),
&mut possibly_expired_animations,
CascadeBooleans {
diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs
index e8ed76bd01f..52f0a6b6d51 100644
--- a/components/style/selector_parser.rs
+++ b/components/style/selector_parser.rs
@@ -102,26 +102,6 @@ pub enum PseudoElementCascadeType {
Precomputed,
}
-impl PseudoElementCascadeType {
- /// Simple accessor to check whether the cascade type is eager.
- #[inline]
- pub fn is_eager(&self) -> bool {
- *self == PseudoElementCascadeType::Eager
- }
-
- /// Simple accessor to check whether the cascade type is lazy.
- #[inline]
- pub fn is_lazy(&self) -> bool {
- *self == PseudoElementCascadeType::Lazy
- }
-
- /// Simple accessor to check whether the cascade type is precomputed.
- #[inline]
- pub fn is_precomputed(&self) -> bool {
- *self == PseudoElementCascadeType::Precomputed
- }
-}
-
/// An extension to rust-selector's `Element` trait.
pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
/// Whether this element is a `link`.
@@ -134,22 +114,6 @@ pub trait ElementExt: Element<Impl=SelectorImpl> + Debug {
}
impl SelectorImpl {
- /// A helper to traverse each eagerly cascaded pseudo-element, executing
- /// `fun` on it.
- ///
- /// TODO(emilio): We can optimize this for Gecko using the pseudo-element
- /// macro, and we should consider doing that for Servo too.
- #[inline]
- pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
- where F: FnMut(PseudoElement),
- {
- Self::each_pseudo_element(|pseudo| {
- if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
- fun(pseudo)
- }
- })
- }
-
/// A helper to traverse each precomputed pseudo-element, executing `fun` on
/// it.
///
@@ -160,7 +124,7 @@ impl SelectorImpl {
where F: FnMut(PseudoElement),
{
Self::each_pseudo_element(|pseudo| {
- if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
+ if pseudo.is_precomputed() {
fun(pseudo)
}
})
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
index e45e82358dd..a2be7c46415 100644
--- a/components/style/servo/selector_parser.rs
+++ b/components/style/servo/selector_parser.rs
@@ -19,6 +19,7 @@ use selectors::parser::{AttrSelector, SelectorMethods};
use std::borrow::Cow;
use std::fmt;
use std::fmt::Debug;
+use std::mem;
/// A pseudo-element, both public and private.
///
@@ -26,10 +27,13 @@ use std::fmt::Debug;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
+#[repr(usize)]
pub enum PseudoElement {
+ // Eager pseudos. Keep these first so that eager_index() works.
+ After = 0,
Before,
- After,
Selection,
+ // Non-eager pseudos.
DetailsSummary,
DetailsContent,
ServoText,
@@ -48,8 +52,8 @@ impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::PseudoElement::*;
dest.write_str(match *self {
- Before => "::before",
After => "::after",
+ Before => "::before",
Selection => "::selection",
DetailsSummary => "::-servo-details-summary",
DetailsContent => "::-servo-details-content",
@@ -67,25 +71,60 @@ impl ToCss for PseudoElement {
}
}
+/// The number of eager pseudo-elements. Keep this in sync with cascade_type.
+pub const EAGER_PSEUDO_COUNT: usize = 3;
+
impl PseudoElement {
+ /// Gets the canonical index of this eagerly-cascaded pseudo-element.
+ #[inline]
+ pub fn eager_index(&self) -> usize {
+ debug_assert!(self.is_eager());
+ self.clone() as usize
+ }
+
+ /// Creates a pseudo-element from an eager index.
+ #[inline]
+ pub fn from_eager_index(i: usize) -> Self {
+ assert!(i < EAGER_PSEUDO_COUNT);
+ let result: PseudoElement = unsafe { mem::transmute(i) };
+ debug_assert!(result.is_eager());
+ result
+ }
+
/// Whether the current pseudo element is :before or :after.
#[inline]
pub fn is_before_or_after(&self) -> bool {
- match *self {
- PseudoElement::Before |
- PseudoElement::After => true,
- _ => false,
- }
+ matches!(*self, PseudoElement::After | PseudoElement::Before)
+ }
+
+ /// Whether this pseudo-element is eagerly-cascaded.
+ #[inline]
+ pub fn is_eager(&self) -> bool {
+ self.cascade_type() == PseudoElementCascadeType::Eager
+ }
+
+ /// Whether this pseudo-element is lazily-cascaded.
+ #[inline]
+ pub fn is_lazy(&self) -> bool {
+ self.cascade_type() == PseudoElementCascadeType::Lazy
+ }
+
+ /// Whether this pseudo-element is precomputed.
+ #[inline]
+ pub fn is_precomputed(&self) -> bool {
+ self.cascade_type() == PseudoElementCascadeType::Precomputed
}
/// Returns which kind of cascade type has this pseudo.
///
/// For more info on cascade types, see docs/components/style.md
+ ///
+ /// Note: Keep this in sync with EAGER_PSEUDO_COUNT.
#[inline]
pub fn cascade_type(&self) -> PseudoElementCascadeType {
match *self {
- PseudoElement::Before |
PseudoElement::After |
+ PseudoElement::Before |
PseudoElement::Selection => PseudoElementCascadeType::Eager,
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent |
@@ -369,6 +408,17 @@ impl SelectorImpl {
pseudo.cascade_type()
}
+ /// A helper to traverse each eagerly cascaded pseudo-element, executing
+ /// `fun` on it.
+ #[inline]
+ pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
+ where F: FnMut(PseudoElement),
+ {
+ for i in 0..EAGER_PSEUDO_COUNT {
+ fun(PseudoElement::from_eager_index(i));
+ }
+ }
+
/// Executes `fun` for each pseudo-element.
#[inline]
pub fn each_pseudo_element<F>(mut fun: F)
@@ -396,12 +446,6 @@ impl SelectorImpl {
pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
pc.state_flag()
}
-
- /// Returns whether this pseudo is either :before or :after.
- #[inline]
- pub fn pseudo_is_before_or_after(pseudo: &PseudoElement) -> bool {
- pseudo.is_before_or_after()
- }
}
/// Servo's version of an element snapshot.
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index 8b1f83dae26..afd78e1b4d2 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -345,7 +345,7 @@ impl Stylist {
parent: Option<&Arc<ComputedValues>>,
cascade_flags: CascadeFlags)
-> ComputedStyle {
- debug_assert!(SelectorImpl::pseudo_element_cascade_type(pseudo).is_precomputed());
+ debug_assert!(pseudo.is_precomputed());
let rule_node = match self.precomputed_pseudo_element_decls.get(pseudo) {
Some(declarations) => {
@@ -435,7 +435,7 @@ impl Stylist {
fmt::Debug +
PresentationalHintsSynthetizer
{
- debug_assert!(SelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
+ debug_assert!(pseudo.is_lazy());
if self.pseudos_map.get(pseudo).is_none() {
return None;
}
@@ -603,9 +603,7 @@ impl Stylist {
debug_assert!(!self.is_device_dirty);
debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
- debug_assert!(pseudo_element.is_none() ||
- !SelectorImpl::pseudo_element_cascade_type(pseudo_element.as_ref().unwrap())
- .is_precomputed());
+ debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.is_precomputed()));
let map = match pseudo_element {
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),