Auto merge of #10934 - emilio:other-gecko-pseudos, r=bholley,mbrubeck

style: Add infrastructure to support lazy pseudo-elements

This builds on top of #10815, so it's really just the last commit the one that should be reviewed.

I tried to apply the new infrastructure to servo, but failed (for now?).

The problem with it is that it'd require `ThreadSafeLayoutElement` to implement `selectors::Element`, which is a lot of work and might be racy (not totally sure about it though). Thus, I prefered to keep selectors eager until knowing that it's safe to do it.

r? @mbrubeck for style changes, @bholley for the geckolib changes (minimal for now, glue + a list of lazy PEs must be added)

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10934)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-05-04 03:42:23 -07:00
commit 29823cb378
13 changed files with 635 additions and 171 deletions

View file

@ -66,12 +66,12 @@ use std::sync::Arc;
use string_cache::{Atom, Namespace};
use style::computed_values::content::ContentItem;
use style::computed_values::{content, display};
use style::dom::{TDocument, TElement, TNode, UnsafeNode};
use style::dom::{PresentationalHintsSynthetizer, TDocument, TElement, TNode, UnsafeNode};
use style::element_state::*;
use style::properties::{ComputedValues, ServoComputedValues};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
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 url::Url;
use util::str::is_whitespace;
@ -81,7 +81,7 @@ pub type NonOpaqueStyleAndLayoutData = *mut RefCell<PrivateLayoutData>;
/// A wrapper so that layout can access only the methods that it should have access to. Layout must
/// only ever see these and must never see instances of `LayoutJS`.
pub trait LayoutNode : TNode {
pub trait LayoutNode: TNode {
type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode;
fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode;
@ -401,6 +401,16 @@ pub struct ServoLayoutElement<'le> {
chain: PhantomData<&'le ()>,
}
impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
{
unsafe {
self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
}
}
}
impl<'le> TElement for ServoLayoutElement<'le> {
type ConcreteNode = ServoLayoutNode<'le>;
type ConcreteDocument = ServoLayoutDocument<'le>;
@ -419,14 +429,6 @@ impl<'le> TElement for ServoLayoutElement<'le> {
self.element.get_state_for_layout()
}
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
{
unsafe {
self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
}
}
#[inline]
fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> {
unsafe {
@ -665,8 +667,10 @@ impl<T> PseudoElementType<T> {
/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<ConcreteThreadSafeLayoutNode = Self>;
pub trait ThreadSafeLayoutNode: Clone + Copy + Sized + PartialEq {
type ConcreteThreadSafeLayoutElement:
ThreadSafeLayoutElement<ConcreteThreadSafeLayoutNode = Self>
+ ::selectors::Element<Impl=ServoSelectorImpl>;
type ChildrenIterator: Iterator<Item = Self> + Sized;
/// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
@ -680,6 +684,18 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
/// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
fn type_id(&self) -> Option<NodeTypeId>;
/// Returns the type ID of this node, without discarding pseudo-elements as
/// `type_id` does.
fn type_id_without_excluding_pseudo_elements(&self) -> NodeTypeId;
#[inline]
fn is_element_or_elements_pseudo(&self) -> bool {
match self.type_id_without_excluding_pseudo_elements() {
NodeTypeId::Element(..) => true,
_ => false,
}
}
fn debug_id(self) -> usize;
fn flow_debug_id(self) -> usize;
@ -774,13 +790,38 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
// Precompute non-eagerly-cascaded pseudo-element styles if not
// cached before.
let style_pseudo = other.style_pseudo_element();
if !style_pseudo.is_eagerly_cascaded() &&
!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
.computed_values_for_pseudo(&style_pseudo,
data.style_data.style.as_ref());
data.style_data.per_pseudo.insert(style_pseudo.clone(), new_style.unwrap());
match style_pseudo.cascade_type() {
// Already computed during the cascade.
PseudoElementCascadeType::Eager => {},
PseudoElementCascadeType::Precomputed => {
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
.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 => {
debug_assert!(self.is_element_or_elements_pseudo());
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| {
@ -904,12 +945,14 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
// This trait is only public so that it can be implemented by the gecko wrapper.
// It can be used to violate thread-safety, so don't use it elsewhere in layout!
pub trait DangerousThreadSafeLayoutNode : ThreadSafeLayoutNode {
pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode {
unsafe fn dangerous_first_child(&self) -> Option<Self>;
unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
}
pub trait ThreadSafeLayoutElement: Clone + Copy + Sized {
pub trait ThreadSafeLayoutElement: Clone + Copy + Sized +
::selectors::Element<Impl=ServoSelectorImpl> +
PresentationalHintsSynthetizer {
type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>;
#[inline]
@ -999,6 +1042,11 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
Some(self.node.type_id())
}
#[inline]
fn type_id_without_excluding_pseudo_elements(&self) -> NodeTypeId {
self.node.type_id()
}
fn debug_id(self) -> usize {
self.node.debug_id()
}
@ -1252,8 +1300,8 @@ impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNod
}
}
/// A wrapper around elements that ensures layout can only ever access safe properties and cannot
/// race on elements.
/// A wrapper around elements that ensures layout can only
/// ever access safe properties and cannot race on elements.
#[derive(Copy, Clone)]
pub struct ServoThreadSafeLayoutElement<'le> {
element: &'le Element,
@ -1292,3 +1340,113 @@ impl TextContent {
}
}
}
/// This implementation of `::selectors::Element` is used for implementing lazy
/// pseudo-elements.
///
/// Lazy pseudo-elements in Servo only allows selectors using safe properties,
/// i.e., local_name, attributes, so they can only be used for **private**
/// pseudo-elements (like `::-servo-details-content`).
///
/// Probably a few more of this functions can be implemented (like `has_class`,
/// `each_class`, etc), but they have no use right now.
///
/// Note that the element implementation is needed only for selector matching,
/// not for inheritance (styles are inherited appropiately).
impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
type Impl = ServoSelectorImpl;
fn parent_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::parent_element called");
None
}
fn first_child_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::first_child_element called");
None
}
// Skips non-element nodes
fn last_child_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::last_child_element called");
None
}
// Skips non-element nodes
fn prev_sibling_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
None
}
// Skips non-element nodes
fn next_sibling_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
None
}
fn is_html_element_in_html_document(&self) -> bool {
debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called");
true
}
#[inline]
fn get_local_name(&self) -> &Atom {
ThreadSafeLayoutElement::get_local_name(self)
}
#[inline]
fn get_namespace(&self) -> &Namespace {
ThreadSafeLayoutElement::get_namespace(self)
}
fn match_non_ts_pseudo_class(&self, _: NonTSPseudoClass) -> bool {
// NB: This could maybe be implemented
warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
false
}
fn get_id(&self) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::get_id called");
None
}
fn has_class(&self, _name: &Atom) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called");
false
}
fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
where F: Fn(&str) -> bool {
match attr.namespace {
NamespaceConstraint::Specific(ref ns) => {
self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr))
},
NamespaceConstraint::Any => {
unsafe {
self.element.get_attr_vals_for_layout(&attr.name).iter()
.any(|attr| test(*attr))
}
}
}
}
fn is_empty(&self) -> bool {
warn!("ServoThreadSafeLayoutElement::is_empty called");
false
}
fn is_root(&self) -> bool {
warn!("ServoThreadSafeLayoutElement::is_root called");
false
}
fn each_class<F>(&self, _callback: F)
where F: FnMut(&Atom) {
warn!("ServoThreadSafeLayoutElement::each_class called");
}
}
impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>> {}
}

View file

@ -2,3 +2,5 @@ servo-style
===========
Style system for Servo, using [rust-cssparser](https://github.com/mozilla-servo/rust-cssparser) for parsing.
* [Documentation](https://github.com/servo/servo/blob/master/docs/components/style.md).

View file

@ -195,7 +195,12 @@ pub trait TDocument : Sized + Copy + Clone {
fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement, ElementSnapshot)>;
}
pub trait TElement : Sized + Copy + Clone + ElementExt {
pub trait PresentationalHintsSynthetizer {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>;
}
pub trait TElement : Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer {
type ConcreteNode: TNode<ConcreteElement = Self, ConcreteDocument = Self::ConcreteDocument>;
type ConcreteDocument: TDocument<ConcreteNode = Self::ConcreteNode, ConcreteElement = Self>;
@ -205,9 +210,6 @@ pub trait TElement : Sized + Copy + Clone + ElementExt {
fn get_state(&self) -> ElementState;
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>;
fn get_attr<'a>(&'a self, namespace: &Namespace, attr: &Atom) -> Option<&'a str>;
fn get_attrs<'a>(&'a self, attr: &Atom) -> Vec<&'a str>;

View file

@ -540,7 +540,7 @@ pub trait ElementMatchMethods : TElement
stylist.push_applicable_declarations(self,
parent_bf,
None,
Some(pseudo.clone()),
Some(&pseudo.clone()),
applicable_declarations.per_pseudo.entry(pseudo).or_insert(vec![]));
});

View file

@ -117,6 +117,13 @@ impl Device {
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 {
@ -203,8 +210,7 @@ pub fn parse_media_query_list(input: &mut Parser) -> MediaQueryList {
impl MediaQueryList {
pub fn evaluate(&self, device: &Device) -> bool {
let viewport_size = Size2D::new(Au::from_f32_px(device.viewport_size.width.get()),
Au::from_f32_px(device.viewport_size.height.get()));
let viewport_size = device.au_viewport_size();
// Check if any queries match (OR condition)
self.media_queries.iter().any(|mq| {

View file

@ -8,6 +8,53 @@ use selectors::Element;
use selectors::parser::{ParserContext, SelectorImpl};
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.
///
/// Note that in Servo lazy pseudo-elements are restricted to a subset of
/// selectors, so you can't use it for public pseudo-elements. This is not the
/// case with Gecko though.
///
/// 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 to make it eager.
#[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 {
fn is_link(&self) -> bool;
}
@ -15,49 +62,32 @@ pub trait ElementExt: Element {
pub trait SelectorImplExt : SelectorImpl + Sized {
type ComputedValues: properties::ComputedValues;
fn each_pseudo_element<F>(mut fun: F)
where F: FnMut(<Self as SelectorImpl>::PseudoElement);
fn pseudo_element_cascade_type(pseudo: &Self::PseudoElement) -> PseudoElementCascadeType;
/// 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.
///
/// 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;
fn each_pseudo_element<F>(mut fun: F)
where F: FnMut(Self::PseudoElement);
#[inline]
fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where F: FnMut(<Self as SelectorImpl>::PseudoElement) {
Self::each_pseudo_element(|pseudo| {
if Self::is_eagerly_cascaded_pseudo_element(&pseudo) {
if Self::pseudo_element_cascade_type(&pseudo).is_eager() {
fun(pseudo)
}
})
}
#[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) {
Self::each_pseudo_element(|pseudo| {
if !Self::is_eagerly_cascaded_pseudo_element(&pseudo) {
if Self::pseudo_element_cascade_type(&pseudo).is_precomputed() {
fun(pseudo)
}
})
}
fn pseudo_class_state_flag(pc: &Self::NonTSPseudoClass) -> ElementState;
fn get_user_or_user_agent_stylesheets() -> &'static [Stylesheet<Self>];
@ -76,13 +106,13 @@ pub enum PseudoElement {
impl PseudoElement {
#[inline]
pub fn is_eagerly_cascaded(&self) -> bool {
pub fn cascade_type(&self) -> PseudoElementCascadeType {
match *self {
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Selection |
PseudoElement::DetailsSummary => true,
PseudoElement::DetailsContent => false,
PseudoElement::Selection => PseudoElementCascadeType::Eager,
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent => PseudoElementCascadeType::Precomputed,
}
}
}
@ -191,8 +221,8 @@ impl SelectorImplExt for ServoSelectorImpl {
type ComputedValues = ServoComputedValues;
#[inline]
fn is_eagerly_cascaded_pseudo_element(pseudo: &PseudoElement) -> bool {
pseudo.is_eagerly_cascaded()
fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
pseudo.cascade_type()
}
#[inline]

View file

@ -5,10 +5,9 @@
// For lazy_static
#![allow(unsafe_code)]
use dom::TElement;
use dom::PresentationalHintsSynthetizer;
use element_state::*;
use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
use euclid::Size2D;
use media_queries::{Device, MediaType};
use properties::{self, ComputedValues, PropertyDeclaration, PropertyDeclarationBlock};
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.
/// These are eagerly computed once, and then used to resolve the new
/// computed values on the fly on layout.
non_eagerly_cascaded_pseudo_element_decls: HashMap<Impl::PseudoElement,
Vec<DeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
precomputed_pseudo_element_decls: HashMap<Impl::PseudoElement,
Vec<DeclarationBlock>,
BuildHasherDefault<::fnv::FnvHasher>>,
rules_source_order: usize,
@ -148,7 +147,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
element_map: PerPseudoElementSelectorMap::new(),
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,
state_deps: DependencySet::new(),
};
@ -175,7 +174,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
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.state_deps.clear();
@ -242,39 +241,71 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
self.rules_source_order = rules_source_order;
Impl::each_non_eagerly_cascaded_pseudo_element(|pseudo| {
// TODO: Don't precompute this, compute it on demand instead and
// cache it.
//
// This is actually kind of hard, because the stylist is shared
// between threads.
Impl::each_precomputed_pseudo_element(|pseudo| {
// TODO: Consider not doing this and just getting the rules on the
// fly. It should be a bit slower, but we'd take rid of the
// extra field, and avoid this precomputation entirely.
if let Some(map) = self.pseudos_map.remove(&pseudo) {
let mut declarations = vec![];
map.user_agent.normal.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,
pseudo: &Impl::PseudoElement,
parent: Option<&Arc<Impl::ComputedValues>>) -> Option<Arc<Impl::ComputedValues>> {
debug_assert!(!Impl::is_eagerly_cascaded_pseudo_element(pseudo));
if let Some(declarations) = self.non_eagerly_cascaded_pseudo_element_decls.get(pseudo) {
/// Computes the style for a given "precomputed" pseudo-element, taking the
/// universal rules and applying them.
pub fn precomputed_values_for_pseudo(&self,
pseudo: &Impl::PseudoElement,
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, _) =
properties::cascade::<Impl::ComputedValues>(Size2D::zero(),
&declarations, false,
parent.map(|p| &**p), None,
box StdoutErrorReporter);
properties::cascade(self.device.au_viewport_size(),
&declarations, false,
parent.map(|p| &**p), None,
box StdoutErrorReporter);
Some(Arc::new(computed))
} else {
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> +
PresentationalHintsSynthetizer {
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,
snapshot: &ElementSnapshot,
// NB: We need to pass current_state as an argument because
@ -325,16 +356,16 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
element: &E,
parent_bf: Option<&BloomFilter>,
style_attribute: Option<&PropertyDeclarationBlock>,
pseudo_element: Option<Impl::PseudoElement>,
pseudo_element: Option<&Impl::PseudoElement>,
applicable_declarations: &mut V)
-> bool
where E: Element<Impl=Impl> + TElement,
where E: Element<Impl=Impl> + PresentationalHintsSynthetizer,
V: VecLike<DeclarationBlock> {
assert!(!self.is_device_dirty);
assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
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 {
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(),