mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-12 15:02:11 +00:00
servo: Merge #16250 - Don't use a HashMap for pseudo-element styles (from bholley:pseudo_repr); r=emilio
Reviewed in https://bugzilla.mozilla.org/show_bug.cgi?id=1335708 @bors-servo try Source-Repo: https://github.com/servo/servo Source-Revision: eee25e23132b0f5d4cb50e5af4691b6e4bf75978 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : ef691daf9cd4a310e2931d442176203440caa87e
This commit is contained in:
parent
fcb35c70f7
commit
a90fa355f1
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,33 +73,87 @@ 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()
|
||||
}
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PseudoStyles {
|
||||
type Target = PseudoStylesInner;
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl DerefMut for PseudoStyles {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
|
||||
}
|
||||
/// 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.
|
||||
@ -107,8 +161,10 @@ impl DerefMut for PseudoStyles {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
@ -423,13 +498,6 @@ impl SelectorImpl {
|
||||
include!("generated/gecko_pseudo_element_helper.rs")
|
||||
}
|
||||
|
||||
#[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.
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
@ -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.
|
||||
|
@ -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(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user