mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
style: Support lazy pseudo-elements
These can't be supported in Servo as of right now, because I'm not totally sure the accesses that should be done in layout would be thread-safe. It can be revisited later though.
This commit is contained in:
parent
3f2ceeff5d
commit
9caaa6004e
7 changed files with 206 additions and 72 deletions
|
@ -71,7 +71,7 @@ use style::element_state::*;
|
||||||
use style::properties::{ComputedValues, ServoComputedValues};
|
use style::properties::{ComputedValues, ServoComputedValues};
|
||||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
|
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
|
||||||
use style::restyle_hints::ElementSnapshot;
|
use style::restyle_hints::ElementSnapshot;
|
||||||
use style::selector_impl::{NonTSPseudoClass, PseudoElement, ServoSelectorImpl};
|
use style::selector_impl::{NonTSPseudoClass, PseudoElement, PseudoElementCascadeType, ServoSelectorImpl};
|
||||||
use style::servo::{PrivateStyleData, SharedStyleContext};
|
use style::servo::{PrivateStyleData, SharedStyleContext};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::str::is_whitespace;
|
use util::str::is_whitespace;
|
||||||
|
@ -774,13 +774,41 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
|
||||||
// Precompute non-eagerly-cascaded pseudo-element styles if not
|
// Precompute non-eagerly-cascaded pseudo-element styles if not
|
||||||
// cached before.
|
// cached before.
|
||||||
let style_pseudo = other.style_pseudo_element();
|
let style_pseudo = other.style_pseudo_element();
|
||||||
if !style_pseudo.is_eagerly_cascaded() &&
|
match style_pseudo.cascade_type() {
|
||||||
!self.borrow_layout_data().unwrap().style_data.per_pseudo.contains_key(&style_pseudo) {
|
// Already computed during the cascade.
|
||||||
let mut data = self.mutate_layout_data().unwrap();
|
PseudoElementCascadeType::Eager => {},
|
||||||
let new_style = context.stylist
|
PseudoElementCascadeType::Precomputed => {
|
||||||
.computed_values_for_pseudo(&style_pseudo,
|
if !self.borrow_layout_data()
|
||||||
data.style_data.style.as_ref());
|
.unwrap().style_data
|
||||||
data.style_data.per_pseudo.insert(style_pseudo.clone(), new_style.unwrap());
|
.per_pseudo.contains_key(&style_pseudo) {
|
||||||
|
let mut data = self.mutate_layout_data().unwrap();
|
||||||
|
let new_style =
|
||||||
|
context.stylist
|
||||||
|
.precomputed_values_for_pseudo(&style_pseudo,
|
||||||
|
data.style_data.style.as_ref());
|
||||||
|
data.style_data.per_pseudo
|
||||||
|
.insert(style_pseudo.clone(), new_style.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PseudoElementCascadeType::Lazy => {
|
||||||
|
panic!("Lazy pseudo-elements can't be used in Servo \
|
||||||
|
since accessing the DOM tree during layout \
|
||||||
|
could be unsafe.")
|
||||||
|
// debug_assert!(self.is_element());
|
||||||
|
// if !self.borrow_layout_data()
|
||||||
|
// .unwrap().style_data
|
||||||
|
// .per_pseudo.contains_key(&style_pseudo) {
|
||||||
|
// let mut data = self.mutate_layout_data().unwrap();
|
||||||
|
// let new_style =
|
||||||
|
// context.stylist
|
||||||
|
// .lazily_compute_pseudo_element_style(
|
||||||
|
// &self.as_element(),
|
||||||
|
// &style_pseudo,
|
||||||
|
// data.style_data.style.as_ref().unwrap());
|
||||||
|
// data.style_data.per_pseudo
|
||||||
|
// .insert(style_pseudo.clone(), new_style.unwrap())
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref::map(self.borrow_layout_data().unwrap(), |data| {
|
Ref::map(self.borrow_layout_data().unwrap(), |data| {
|
||||||
|
@ -1252,8 +1280,8 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around elements that ensures layout can only ever access safe properties and cannot
|
/// A wrapper around elements that ensures layout can only
|
||||||
/// race on elements.
|
/// ever access safe properties and cannot race on elements.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct ServoThreadSafeLayoutElement<'le> {
|
pub struct ServoThreadSafeLayoutElement<'le> {
|
||||||
element: &'le Element,
|
element: &'le Element,
|
||||||
|
|
|
@ -540,7 +540,7 @@ pub trait ElementMatchMethods : TElement
|
||||||
stylist.push_applicable_declarations(self,
|
stylist.push_applicable_declarations(self,
|
||||||
parent_bf,
|
parent_bf,
|
||||||
None,
|
None,
|
||||||
Some(pseudo.clone()),
|
Some(&pseudo.clone()),
|
||||||
applicable_declarations.per_pseudo.entry(pseudo).or_insert(vec![]));
|
applicable_declarations.per_pseudo.entry(pseudo).or_insert(vec![]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,13 @@ impl Device {
|
||||||
viewport_size: viewport_size,
|
viewport_size: viewport_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn au_viewport_size(&self) -> Size2D<Au> {
|
||||||
|
Size2D::new(Au::from_f32_px(self.viewport_size.width.get()),
|
||||||
|
Au::from_f32_px(self.viewport_size.height.get()))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
|
@ -203,8 +210,7 @@ pub fn parse_media_query_list(input: &mut Parser) -> MediaQueryList {
|
||||||
|
|
||||||
impl MediaQueryList {
|
impl MediaQueryList {
|
||||||
pub fn evaluate(&self, device: &Device) -> bool {
|
pub fn evaluate(&self, device: &Device) -> bool {
|
||||||
let viewport_size = Size2D::new(Au::from_f32_px(device.viewport_size.width.get()),
|
let viewport_size = device.au_viewport_size();
|
||||||
Au::from_f32_px(device.viewport_size.height.get()));
|
|
||||||
|
|
||||||
// Check if any queries match (OR condition)
|
// Check if any queries match (OR condition)
|
||||||
self.media_queries.iter().any(|mq| {
|
self.media_queries.iter().any(|mq| {
|
||||||
|
|
|
@ -8,6 +8,50 @@ use selectors::Element;
|
||||||
use selectors::parser::{ParserContext, SelectorImpl};
|
use selectors::parser::{ParserContext, SelectorImpl};
|
||||||
use stylesheets::Stylesheet;
|
use stylesheets::Stylesheet;
|
||||||
|
|
||||||
|
/// This function determines if a pseudo-element is eagerly cascaded or not.
|
||||||
|
///
|
||||||
|
/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
|
||||||
|
/// `::before` and `::after`). They inherit styles normally as another
|
||||||
|
/// selector would do, and they're part of the cascade.
|
||||||
|
///
|
||||||
|
/// Lazy pseudo-elements are affected by selector matching, but they're only
|
||||||
|
/// computed when needed, and not before. They're useful for general
|
||||||
|
/// pseudo-elements that are not very common.
|
||||||
|
///
|
||||||
|
/// Precomputed ones skip the cascade process entirely, mostly as an
|
||||||
|
/// optimisation since they are private pseudo-elements (like
|
||||||
|
/// `::-servo-details-content`).
|
||||||
|
///
|
||||||
|
/// This pseudo-elements are resolved on the fly using *only* global rules
|
||||||
|
/// (rules of the form `*|*`), and applying them to the parent style.
|
||||||
|
///
|
||||||
|
/// If you're implementing a public selector that the end-user might customize,
|
||||||
|
/// then you probably need doing the whole cascading process and return true in
|
||||||
|
/// this function for that pseudo (either as Eager or Lazy).
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PseudoElementCascadeType {
|
||||||
|
Eager,
|
||||||
|
Lazy,
|
||||||
|
Precomputed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PseudoElementCascadeType {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_eager(&self) -> bool {
|
||||||
|
*self == PseudoElementCascadeType::Eager
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_lazy(&self) -> bool {
|
||||||
|
*self == PseudoElementCascadeType::Lazy
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_precomputed(&self) -> bool {
|
||||||
|
*self == PseudoElementCascadeType::Precomputed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ElementExt: Element {
|
pub trait ElementExt: Element {
|
||||||
fn is_link(&self) -> bool;
|
fn is_link(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -15,49 +59,32 @@ pub trait ElementExt: Element {
|
||||||
pub trait SelectorImplExt : SelectorImpl + Sized {
|
pub trait SelectorImplExt : SelectorImpl + Sized {
|
||||||
type ComputedValues: properties::ComputedValues;
|
type ComputedValues: properties::ComputedValues;
|
||||||
|
|
||||||
fn each_pseudo_element<F>(mut fun: F)
|
fn pseudo_element_cascade_type(pseudo: &Self::PseudoElement) -> PseudoElementCascadeType;
|
||||||
where F: FnMut(<Self as SelectorImpl>::PseudoElement);
|
|
||||||
|
|
||||||
/// This function determines if a pseudo-element is eagerly cascaded or not.
|
fn each_pseudo_element<F>(mut fun: F)
|
||||||
///
|
where F: FnMut(Self::PseudoElement);
|
||||||
/// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
|
|
||||||
/// `::before` and `::after`). They inherit styles normally as another
|
|
||||||
/// selector would do.
|
|
||||||
///
|
|
||||||
/// Non-eagerly cascaded ones skip the cascade process entirely, mostly as
|
|
||||||
/// an optimisation since they are private pseudo-elements (like
|
|
||||||
/// `::-servo-details-content`). This pseudo-elements are resolved on the
|
|
||||||
/// fly using global rules (rules of the form `*|*`), and applying them to
|
|
||||||
/// the parent style.
|
|
||||||
///
|
|
||||||
/// If you're implementing a public selector that the end-user might
|
|
||||||
/// customize, then you probably need doing the whole cascading process and
|
|
||||||
/// return true in this function for that pseudo.
|
|
||||||
///
|
|
||||||
/// But if you are implementing a private pseudo-element, please consider if
|
|
||||||
/// it might be possible to skip the cascade for it.
|
|
||||||
fn is_eagerly_cascaded_pseudo_element(pseudo: &<Self as SelectorImpl>::PseudoElement) -> bool;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
|
fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
|
||||||
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
|
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
|
||||||
Self::each_pseudo_element(|pseudo| {
|
Self::each_pseudo_element(|pseudo| {
|
||||||
if Self::is_eagerly_cascaded_pseudo_element(&pseudo) {
|
if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
|
||||||
fun(pseudo)
|
fun(pseudo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn each_non_eagerly_cascaded_pseudo_element<F>(mut fun: F)
|
fn each_precomputed_pseudo_element<F>(mut fun: F)
|
||||||
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
|
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
|
||||||
Self::each_pseudo_element(|pseudo| {
|
Self::each_pseudo_element(|pseudo| {
|
||||||
if !Self::is_eagerly_cascaded_pseudo_element(&pseudo) {
|
if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
|
||||||
fun(pseudo)
|
fun(pseudo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
|
fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
|
||||||
|
|
||||||
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>];
|
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>];
|
||||||
|
@ -76,13 +103,21 @@ pub enum PseudoElement {
|
||||||
|
|
||||||
impl PseudoElement {
|
impl PseudoElement {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_eagerly_cascaded(&self) -> bool {
|
pub fn cascade_type(&self) -> PseudoElementCascadeType {
|
||||||
|
// TODO: Make PseudoElementCascadeType::Lazy work for Servo.
|
||||||
|
//
|
||||||
|
// This can't be done right now since it would require
|
||||||
|
// ServoThreadSafeLayoutElement to implement ::selectors::Element,
|
||||||
|
// and it might not be thread-safe.
|
||||||
|
//
|
||||||
|
// After that, we'd probably want ::selection and
|
||||||
|
// ::-servo-details-summary to be lazy.
|
||||||
match *self {
|
match *self {
|
||||||
PseudoElement::Before |
|
PseudoElement::Before |
|
||||||
PseudoElement::After |
|
PseudoElement::After |
|
||||||
PseudoElement::Selection |
|
PseudoElement::Selection |
|
||||||
PseudoElement::DetailsSummary => true,
|
PseudoElement::DetailsSummary => PseudoElementCascadeType::Eager,
|
||||||
PseudoElement::DetailsContent => false,
|
PseudoElement::DetailsContent => PseudoElementCascadeType::Precomputed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,8 +226,8 @@ impl SelectorImplExt for ServoSelectorImpl {
|
||||||
type ComputedValues = ServoComputedValues;
|
type ComputedValues = ServoComputedValues;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_eagerly_cascaded_pseudo_element(pseudo: &PseudoElement) -> bool {
|
fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
|
||||||
pseudo.is_eagerly_cascaded()
|
pseudo.cascade_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
|
use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
|
||||||
use euclid::Size2D;
|
|
||||||
use media_queries::{Device, MediaType};
|
use media_queries::{Device, MediaType};
|
||||||
use properties::{self, ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
|
use properties::{self, ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
|
||||||
use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet};
|
use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet};
|
||||||
|
@ -127,9 +126,9 @@ pub struct Stylist<Impl: SelectorImplExt> {
|
||||||
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
/// Applicable declarations for a given non-eagerly cascaded pseudo-element.
|
||||||
/// These are eagerly computed once, and then used to resolve the new
|
/// These are eagerly computed once, and then used to resolve the new
|
||||||
/// computed values on the fly on layout.
|
/// computed values on the fly on layout.
|
||||||
non_eagerly_cascaded_pseudo_element_decls: HashMap<Impl::PseudoElement,
|
precomputed_pseudo_element_decls: HashMap<Impl::PseudoElement,
|
||||||
Vec<DeclarationBlock>,
|
Vec<DeclarationBlock>,
|
||||||
BuildHasherDefault<::fnv::FnvHasher>>,
|
BuildHasherDefault<::fnv::FnvHasher>>,
|
||||||
|
|
||||||
rules_source_order: usize,
|
rules_source_order: usize,
|
||||||
|
|
||||||
|
@ -148,7 +147,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
|
|
||||||
element_map: PerPseudoElementSelectorMap::new(),
|
element_map: PerPseudoElementSelectorMap::new(),
|
||||||
pseudos_map: HashMap::with_hasher(Default::default()),
|
pseudos_map: HashMap::with_hasher(Default::default()),
|
||||||
non_eagerly_cascaded_pseudo_element_decls: HashMap::with_hasher(Default::default()),
|
precomputed_pseudo_element_decls: HashMap::with_hasher(Default::default()),
|
||||||
rules_source_order: 0,
|
rules_source_order: 0,
|
||||||
state_deps: DependencySet::new(),
|
state_deps: DependencySet::new(),
|
||||||
};
|
};
|
||||||
|
@ -175,7 +174,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
|
self.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
|
||||||
});
|
});
|
||||||
|
|
||||||
self.non_eagerly_cascaded_pseudo_element_decls = HashMap::with_hasher(Default::default());
|
self.precomputed_pseudo_element_decls = HashMap::with_hasher(Default::default());
|
||||||
self.rules_source_order = 0;
|
self.rules_source_order = 0;
|
||||||
self.state_deps.clear();
|
self.state_deps.clear();
|
||||||
|
|
||||||
|
@ -242,39 +241,70 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
|
|
||||||
self.rules_source_order = rules_source_order;
|
self.rules_source_order = rules_source_order;
|
||||||
|
|
||||||
Impl::each_non_eagerly_cascaded_pseudo_element(|pseudo| {
|
Impl::each_precomputed_pseudo_element(|pseudo| {
|
||||||
// TODO: Don't precompute this, compute it on demand instead and
|
// TODO: Consider not doing this and just getting the rules on the
|
||||||
// cache it.
|
// fly. It should be a bit slower, but we'd take rid of the
|
||||||
//
|
// extra field, and avoid this precomputation entirely.
|
||||||
// This is actually kind of hard, because the stylist is shared
|
|
||||||
// between threads.
|
|
||||||
if let Some(map) = self.pseudos_map.remove(&pseudo) {
|
if let Some(map) = self.pseudos_map.remove(&pseudo) {
|
||||||
let mut declarations = vec![];
|
let mut declarations = vec![];
|
||||||
|
|
||||||
map.user_agent.normal.get_universal_rules(&mut declarations);
|
map.user_agent.normal.get_universal_rules(&mut declarations);
|
||||||
map.user_agent.important.get_universal_rules(&mut declarations);
|
map.user_agent.important.get_universal_rules(&mut declarations);
|
||||||
|
|
||||||
self.non_eagerly_cascaded_pseudo_element_decls.insert(pseudo, declarations);
|
self.precomputed_pseudo_element_decls.insert(pseudo, declarations);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn computed_values_for_pseudo(&self,
|
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
||||||
pseudo: &Impl::PseudoElement,
|
/// universal rules and applying them.
|
||||||
parent: Option<&Arc<Impl::ComputedValues>>) -> Option<Arc<Impl::ComputedValues>> {
|
pub fn precomputed_values_for_pseudo(&self,
|
||||||
debug_assert!(!Impl::is_eagerly_cascaded_pseudo_element(pseudo));
|
pseudo: &Impl::PseudoElement,
|
||||||
if let Some(declarations) = self.non_eagerly_cascaded_pseudo_element_decls.get(pseudo) {
|
parent: Option<&Arc<Impl::ComputedValues>>)
|
||||||
|
-> Option<Arc<Impl::ComputedValues>> {
|
||||||
|
debug_assert!(Impl::pseudo_element_cascade_type(pseudo).is_precomputed());
|
||||||
|
if let Some(declarations) = self.precomputed_pseudo_element_decls.get(pseudo) {
|
||||||
|
|
||||||
let (computed, _) =
|
let (computed, _) =
|
||||||
properties::cascade::<Impl::ComputedValues>(Size2D::zero(),
|
properties::cascade(self.device.au_viewport_size(),
|
||||||
&declarations, false,
|
&declarations, false,
|
||||||
parent.map(|p| &**p), None,
|
parent.map(|p| &**p), None,
|
||||||
box StdoutErrorReporter);
|
box StdoutErrorReporter);
|
||||||
Some(Arc::new(computed))
|
Some(Arc::new(computed))
|
||||||
} else {
|
} else {
|
||||||
parent.map(|p| p.clone())
|
parent.map(|p| p.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lazily_compute_pseudo_element_style<E>(&self,
|
||||||
|
element: &E,
|
||||||
|
pseudo: &Impl::PseudoElement,
|
||||||
|
parent: &Arc<Impl::ComputedValues>)
|
||||||
|
-> Option<Arc<Impl::ComputedValues>>
|
||||||
|
where E: Element<Impl=Impl> + TElement {
|
||||||
|
debug_assert!(Impl::pseudo_element_cascade_type(pseudo).is_lazy());
|
||||||
|
if self.pseudos_map.get(pseudo).is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut declarations = vec![];
|
||||||
|
|
||||||
|
// NB: This being cached could be worth it, maybe allow an optional
|
||||||
|
// ApplicableDeclarationsCache?.
|
||||||
|
self.push_applicable_declarations(element,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(pseudo),
|
||||||
|
&mut declarations);
|
||||||
|
|
||||||
|
let (computed, _) =
|
||||||
|
properties::cascade(self.device.au_viewport_size(),
|
||||||
|
&declarations, false,
|
||||||
|
Some(&**parent), None,
|
||||||
|
box StdoutErrorReporter);
|
||||||
|
Some(Arc::new(computed))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_restyle_hint<E>(&self, element: &E,
|
pub fn compute_restyle_hint<E>(&self, element: &E,
|
||||||
snapshot: &ElementSnapshot,
|
snapshot: &ElementSnapshot,
|
||||||
// NB: We need to pass current_state as an argument because
|
// NB: We need to pass current_state as an argument because
|
||||||
|
@ -325,7 +355,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
element: &E,
|
element: &E,
|
||||||
parent_bf: Option<&BloomFilter>,
|
parent_bf: Option<&BloomFilter>,
|
||||||
style_attribute: Option<&PropertyDeclarationBlock>,
|
style_attribute: Option<&PropertyDeclarationBlock>,
|
||||||
pseudo_element: Option<Impl::PseudoElement>,
|
pseudo_element: Option<&Impl::PseudoElement>,
|
||||||
applicable_declarations: &mut V)
|
applicable_declarations: &mut V)
|
||||||
-> bool
|
-> bool
|
||||||
where E: Element<Impl=Impl> + TElement,
|
where E: Element<Impl=Impl> + TElement,
|
||||||
|
@ -334,7 +364,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
||||||
assert!(style_attribute.is_none() || pseudo_element.is_none(),
|
assert!(style_attribute.is_none() || pseudo_element.is_none(),
|
||||||
"Style attributes do not apply to pseudo-elements");
|
"Style attributes do not apply to pseudo-elements");
|
||||||
debug_assert!(pseudo_element.is_none() ||
|
debug_assert!(pseudo_element.is_none() ||
|
||||||
Impl::is_eagerly_cascaded_pseudo_element(pseudo_element.as_ref().unwrap()));
|
!Impl::pseudo_element_cascade_type(pseudo_element.as_ref().unwrap()).is_precomputed());
|
||||||
|
|
||||||
let map = match pseudo_element {
|
let map = match pseudo_element {
|
||||||
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),
|
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),
|
||||||
|
|
|
@ -267,7 +267,7 @@ pub extern "C" fn Servo_GetComputedValuesForAnonymousBox(parent_style_or_null: *
|
||||||
type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
|
type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
|
||||||
|
|
||||||
Helpers::maybe_with(parent_style_or_null, |maybe_parent| {
|
Helpers::maybe_with(parent_style_or_null, |maybe_parent| {
|
||||||
let new_computed = data.stylist.computed_values_for_pseudo(&pseudo, maybe_parent);
|
let new_computed = data.stylist.precomputed_values_for_pseudo(&pseudo, maybe_parent);
|
||||||
new_computed.map_or(ptr::null_mut(), |c| Helpers::from(c))
|
new_computed.map_or(ptr::null_mut(), |c| Helpers::from(c))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use properties::GeckoComputedValues;
|
||||||
use selectors::parser::{ParserContext, SelectorImpl};
|
use selectors::parser::{ParserContext, SelectorImpl};
|
||||||
use style;
|
use style;
|
||||||
use style::element_state::ElementState;
|
use style::element_state::ElementState;
|
||||||
use style::selector_impl::SelectorImplExt;
|
use style::selector_impl::{PseudoElementCascadeType, SelectorImplExt};
|
||||||
|
|
||||||
pub type Stylist = style::selector_matching::Stylist<GeckoSelectorImpl>;
|
pub type Stylist = style::selector_matching::Stylist<GeckoSelectorImpl>;
|
||||||
pub type Stylesheet = style::stylesheets::Stylesheet<GeckoSelectorImpl>;
|
pub type Stylesheet = style::stylesheets::Stylesheet<GeckoSelectorImpl>;
|
||||||
|
@ -89,6 +89,36 @@ pub enum PseudoElement {
|
||||||
MozSVGText,
|
MozSVGText,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PseudoElement {
|
||||||
|
fn is_anon_box_pseudo(&self) -> bool {
|
||||||
|
use self::PseudoElement::*;
|
||||||
|
match *self {
|
||||||
|
MozNonElement | MozAnonymousBlock | MozAnonymousPositionedBlock |
|
||||||
|
MozMathMLAnonymousBlock | MozXULAnonymousBlock |
|
||||||
|
MozHorizontalFramesetBorder | MozVerticalFramesetBorder |
|
||||||
|
MozLineFrame | MozButtonContent | MozButtonLabel | MozCellContent |
|
||||||
|
MozDropdownList | MozFieldsetContent | MozFramesetBlank |
|
||||||
|
MozDisplayComboboxControlFrame |
|
||||||
|
MozHTMLCanvasContent | MozInlineTable | MozTable | MozTableCell |
|
||||||
|
MozTableColumnGroup | MozTableColumn | MozTableOuter |
|
||||||
|
MozTableRowGroup | MozTableRow | MozCanvas | MozPageBreak |
|
||||||
|
MozPage | MozPageContent | MozPageSequence | MozScrolledContent |
|
||||||
|
MozScrolledCanvas | MozScrolledPageSequence | MozColumnContent |
|
||||||
|
MozViewport | MozViewportScroll | MozAnonymousFlexItem |
|
||||||
|
MozAnonymousGridItem | MozRuby | MozRubyBase |
|
||||||
|
MozRubyBaseContainer | MozRubyText | MozRubyTextContainer |
|
||||||
|
MozTreeColumn | MozTreeRow | MozTreeSeparator | MozTreeCell |
|
||||||
|
MozTreeIndentation | MozTreeLine | MozTreeTwisty | MozTreeImage |
|
||||||
|
MozTreeCellText | MozTreeCheckbox | MozTreeProgressMeter |
|
||||||
|
MozTreeDropFeedback | MozSVGMarkerAnonChild |
|
||||||
|
MozSVGOuterSVGAnonChild | MozSVGForeignContent |
|
||||||
|
MozSVGText => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, HeapSizeOf, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, HeapSizeOf, Hash)]
|
||||||
pub enum NonTSPseudoClass {
|
pub enum NonTSPseudoClass {
|
||||||
AnyLink,
|
AnyLink,
|
||||||
|
@ -243,12 +273,17 @@ impl SelectorImplExt for GeckoSelectorImpl {
|
||||||
type ComputedValues = GeckoComputedValues;
|
type ComputedValues = GeckoComputedValues;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_eagerly_cascaded_pseudo_element(pseudo: &PseudoElement) -> bool {
|
fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
|
||||||
match *pseudo {
|
match *pseudo {
|
||||||
PseudoElement::Before |
|
PseudoElement::Before |
|
||||||
PseudoElement::After |
|
PseudoElement::After => PseudoElementCascadeType::Eager,
|
||||||
PseudoElement::FirstLine => true,
|
_ => {
|
||||||
_ => false,
|
if pseudo.is_anon_box_pseudo() {
|
||||||
|
PseudoElementCascadeType::Precomputed
|
||||||
|
} else {
|
||||||
|
PseudoElementCascadeType::Lazy
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue