mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
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:
commit
29823cb378
13 changed files with 635 additions and 171 deletions
|
@ -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) {
|
||||
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
|
||||
.computed_values_for_pseudo(&style_pseudo,
|
||||
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());
|
||||
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>>> {}
|
||||
}
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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![]));
|
||||
});
|
||||
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,7 +126,7 @@ 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,
|
||||
precomputed_pseudo_element_decls: HashMap<Impl::PseudoElement,
|
||||
Vec<DeclarationBlock>,
|
||||
BuildHasherDefault<::fnv::FnvHasher>>,
|
||||
|
||||
|
@ -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,30 +241,32 @@ 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,
|
||||
/// 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::is_eagerly_cascaded_pseudo_element(pseudo));
|
||||
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, _) =
|
||||
properties::cascade::<Impl::ComputedValues>(Size2D::zero(),
|
||||
properties::cascade(self.device.au_viewport_size(),
|
||||
&declarations, false,
|
||||
parent.map(|p| &**p), None,
|
||||
box StdoutErrorReporter);
|
||||
|
@ -275,6 +276,36 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
|
|||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
|
|
158
docs/components/style.md
Normal file
158
docs/components/style.md
Normal file
|
@ -0,0 +1,158 @@
|
|||
# Servo's style system overview
|
||||
|
||||
This needs to be filled more extensively. Meanwhile, you can also take a look to
|
||||
the [style doc comments][style-doc], or the [Styling
|
||||
Overview][wiki-styling-overview] in the wiki, which is a conversation between
|
||||
Boris Zbarsky and Patrick Walton about how style sharing works.
|
||||
|
||||
<a name="selector-impl"></a>
|
||||
## Selector Implementation
|
||||
|
||||
The style system is generic over quite a few things, in order to be shareable
|
||||
with Servo's layout system, and with [Stylo][stylo], an ambitious project that
|
||||
aims to integrate Servo's style system into Gecko.
|
||||
|
||||
The main generic trait is [selectors' SelectorImpl][selector-impl], that has all
|
||||
the logic related to parsing pseudo-elements and other pseudo-classes appart
|
||||
from [tree-structural ones][tree-structural-pseudo-classes].
|
||||
|
||||
Servo [extends][selector-impl-ext] that trait in order to allow a few more
|
||||
things to be shared between Stylo and Servo.
|
||||
|
||||
The main Servo implementation (the one that is used in regular builds) is
|
||||
[ServoSelectorImpl][servo-selector-impl].
|
||||
|
||||
<a name="dom-glue"></a>
|
||||
## DOM glue
|
||||
|
||||
In order to keep DOM, layout and style in different modules, there are a few
|
||||
traits involved.
|
||||
|
||||
Style's [`dom` traits][style-dom-traits] (`TDocument`, `TElement`, `TNode`,
|
||||
`TRestyleDamage`) are the main "wall" between layout and style.
|
||||
|
||||
Layout's [`wrapper`][layout-wrapper] module is the one that makes sure that
|
||||
layout traits have the required traits implemented.
|
||||
|
||||
<a name="stylist"></a>
|
||||
## The Stylist
|
||||
|
||||
The [`stylist`][stylist] structure is the one that holds all the selectors and
|
||||
device characteristics for a given document.
|
||||
|
||||
The stylesheets' CSS rules are converted into [`Rule`][selectors-rules]s, and
|
||||
introduced in a [`SelectorMap`][selectors-selectormap] depending on the
|
||||
pseudo-element (see [`PerPseudoElementSelectorMap`][per-pseudo-selectormap]),
|
||||
stylesheet origin (see [`PerOriginSelectorMap`][per-origin-selectormap]), and
|
||||
priority (see the `normal` and `important` fields in
|
||||
[`PerOriginSelectorMap`][per-origin-selectormap]).
|
||||
|
||||
This structure is effectively created once per [pipeline][docs-pipeline], in the
|
||||
LayoutThread corresponding to that pipeline.
|
||||
|
||||
<a name="properties"></a>
|
||||
## The `properties` module
|
||||
|
||||
The [properties module][properties-module] is a mako template where all the
|
||||
properties, computed value computation and cascading logic resides.
|
||||
|
||||
It's a complex template with a **lot** of code, but the main function it exposes
|
||||
is the [`cascade` function][properties-cascade-fn], which performs all the
|
||||
computation.
|
||||
|
||||
<a name="pseudo-elements"></a>
|
||||
## Pseudo-Element resolution
|
||||
|
||||
Pseudo-elements are a tricky section of the style system. Not all
|
||||
pseudo-elements are very common, and so some of them might want to skip the
|
||||
cascade.
|
||||
|
||||
Servo has, as of right now, five [pseudo-elements][servo-pseudo-elements]:
|
||||
|
||||
* [`::before`][mdn-pseudo-before] and [`::after`][mdn-pseudo-after].
|
||||
* [`::selection`][mdn-pseudo-selection]: This one is only partially
|
||||
implemented, and only works for text inputs and textareas as of right now.
|
||||
* `::-servo-details-summary`: This pseudo-element represents the `<summary>` of
|
||||
a `<details>` element.
|
||||
* `::-servo-details-content`: This pseudo-element represents the contents of
|
||||
a `<details>` element.
|
||||
|
||||
Both `::-servo-details-*` pseudo-elements are private (i.e. they are only parsed
|
||||
from User-Agent stylesheets).
|
||||
|
||||
Servo has three different ways of cascading a pseudo-element, which are defined
|
||||
in [`PseudoElementCascadeType`][pseudo-cascade-type]:
|
||||
|
||||
<a name="pe-cascading-eager"></a>
|
||||
### "Eager" cascading
|
||||
|
||||
This mode computes the computed values of a given node's pseudo-element over the
|
||||
first pass of the style system.
|
||||
|
||||
This is used for all public pseudo-elements, and is, as of right now, **the only
|
||||
way a public pseudo-element should be cascaded** (the explanation for this is
|
||||
below).
|
||||
|
||||
<a name="pe-cascading-precomputed"></a>
|
||||
### "Precomputed" cascading
|
||||
|
||||
Or, better said, no cascading at all. A pseudo-element marked as such is not
|
||||
cascaded.
|
||||
|
||||
The only rules that apply to the styles of that pseudo-element are universal
|
||||
rules (rules with a `*|*` selector), and they are applied directly over the
|
||||
element's style if present.
|
||||
|
||||
`::-servo-details-content` is an example of this kind of pseudo-element, all the
|
||||
rules in the UA stylesheet with the selector `*|*::-servo-details-content` (and
|
||||
only those) are evaluated over the element's style (except the `display` value,
|
||||
that is overwritten by layout).
|
||||
|
||||
This should be the **preferred type for private pseudo-elements** (although some
|
||||
of them might need selectors, see below).
|
||||
|
||||
<a name="pe-cascading-lazy"></a>
|
||||
### "Lazy" cascading
|
||||
|
||||
Lazy cascading allows to compute pseudo-element styles lazily, that is, just
|
||||
when needed.
|
||||
|
||||
Currently (for Servo, not that much for stylo), **selectors supported for this
|
||||
kind of pseudo-elements are only a subset of selectors that can be matched on
|
||||
the layout tree, which does not hold all data from the DOM tree**.
|
||||
|
||||
This subset includes tags and attribute selectors, enough for making
|
||||
`::-servo-details-summary` a lazy pseudo-element (that only needs to know
|
||||
if it is in an `open` details element or not).
|
||||
|
||||
Since no other selectors would apply to it, **this is (at least for now) not an
|
||||
acceptable type for public pseudo-elements, but should be considered for private
|
||||
pseudo-elements**.
|
||||
|
||||
#### Not found what you were looking for?
|
||||
|
||||
Feel free to ping @SimonSapin, @mbrubeck or @emilio on irc, and please mention
|
||||
that you didn't find it here so it can be added :)
|
||||
|
||||
[style-doc]: http://doc.servo.org/style/index.html
|
||||
[wiki-styling-overview]: https://github.com/servo/servo/wiki/Styling-overview
|
||||
[stylo]: https://public.etherpad-mozilla.org/p/stylo
|
||||
[selector-impl]: http://doc.servo.org/selectors/parser/trait.SelectorImpl.html
|
||||
[selector-impl-ext]: http://doc.servo.org/style/selector_impl/trait.SelectorImplExt.html
|
||||
[servo-selector-impl]: http://doc.servo.org/style/selector_impl/struct.ServoSelectorImpl.html
|
||||
[tree-structural-pseudo-classes]: https://www.w3.org/TR/selectors4/#structural-pseudos
|
||||
[style-dom-traits]: http://doc.servo.org/style/dom/index.html
|
||||
[layout-wrapper]: http://doc.servo.org/layout/wrapper/index.html
|
||||
[pseudo-cascade-type]: http://doc.servo.org/style/selector_impl/enum.PseudoElementCascadeType.html
|
||||
[servo-pseudo-elements]: http://doc.servo.org/style/selector_impl/enum.PseudoElement.html
|
||||
[mdn-pseudo-before]: https://developer.mozilla.org/en/docs/Web/CSS/::before
|
||||
[mdn-pseudo-after]: https://developer.mozilla.org/en/docs/Web/CSS/::after
|
||||
[mdn-pseudo-selection]: https://developer.mozilla.org/en/docs/Web/CSS/::selection
|
||||
[stylist]: http://doc.servo.org/style/selector_matching/struct.Stylist.html
|
||||
[selectors-selectormap]: http://doc.servo.org/selectors/matching/struct.SelectorMap.html
|
||||
[selectors-rule]: http://doc.servo.org/selectors/matching/struct.Rule.html
|
||||
[per-pseudo-selectormap]: http://doc.servo.org/style/selector_matching/struct.PerPseudoElementSelectorMap.html
|
||||
[per-origin-selectormap]: http://doc.servo.org/style/selector_matching/struct.PerOriginSelectorMap.html
|
||||
[docs-pipeline]: https://github.com/servo/servo/blob/master/docs/glossary.md#pipeline
|
||||
[properties-module]: http://doc.servo.org/style/properties/index.html
|
||||
[properties-cascade-fn]: http://doc.servo.org/style/properties/fn.cascade.html
|
|
@ -78,11 +78,11 @@ extern "C" {
|
|||
set: *mut RawServoStyleSet);
|
||||
pub fn Servo_PrependStyleSheet(sheet: *mut RawServoStyleSheet,
|
||||
set: *mut RawServoStyleSet);
|
||||
pub fn Servo_RemoveStyleSheet(sheet: *mut RawServoStyleSheet,
|
||||
set: *mut RawServoStyleSet);
|
||||
pub fn Servo_InsertStyleSheetBefore(sheet: *mut RawServoStyleSheet,
|
||||
reference: *mut RawServoStyleSheet,
|
||||
set: *mut RawServoStyleSet);
|
||||
pub fn Servo_RemoveStyleSheet(sheet: *mut RawServoStyleSheet,
|
||||
set: *mut RawServoStyleSet);
|
||||
pub fn Servo_StyleSheetHasRules(sheet: *mut RawServoStyleSheet) -> bool;
|
||||
pub fn Servo_InitStyleSet() -> *mut RawServoStyleSet;
|
||||
pub fn Servo_DropStyleSet(set: *mut RawServoStyleSet);
|
||||
|
@ -93,6 +93,14 @@ extern "C" {
|
|||
pseudoTag: *mut nsIAtom,
|
||||
set: *mut RawServoStyleSet)
|
||||
-> *mut ServoComputedValues;
|
||||
pub fn Servo_GetComputedValuesForPseudoElement(parent_style:
|
||||
*mut ServoComputedValues,
|
||||
match_element:
|
||||
*mut RawGeckoElement,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
set: *mut RawServoStyleSet,
|
||||
is_probe: bool)
|
||||
-> *mut ServoComputedValues;
|
||||
pub fn Servo_AddRefComputedValues(arg1: *mut ServoComputedValues);
|
||||
pub fn Servo_ReleaseComputedValues(arg1: *mut ServoComputedValues);
|
||||
pub fn Gecko_GetAttrAsUTF8(element: *mut RawGeckoElement, ns: *const u8,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#![allow(unsafe_code)]
|
||||
|
||||
use app_units::Au;
|
||||
use bindings::{RawGeckoDocument, RawGeckoNode};
|
||||
use bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode};
|
||||
use bindings::{RawServoStyleSet, RawServoStyleSheet, ServoComputedValues, ServoNodeData};
|
||||
use bindings::{nsIAtom};
|
||||
use data::PerDocumentStyleData;
|
||||
|
@ -20,15 +20,16 @@ use std::slice;
|
|||
use std::str::from_utf8_unchecked;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use style::context::{ReflowGoal};
|
||||
use style::dom::{TDocument, TNode};
|
||||
use style::dom::{TDocument, TElement, TNode};
|
||||
use style::error_reporting::StdoutErrorReporter;
|
||||
use style::parallel;
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_impl::{SelectorImplExt, PseudoElementCascadeType};
|
||||
use style::stylesheets::Origin;
|
||||
use traversal::RecalcStyleOnly;
|
||||
use url::Url;
|
||||
use util::arc_ptr_eq;
|
||||
use wrapper::{GeckoDocument, GeckoNode, NonOpaqueStyleData};
|
||||
use wrapper::{GeckoDocument, GeckoElement, GeckoNode, NonOpaqueStyleData};
|
||||
|
||||
// TODO: This is ugly and should go away once we get an atom back-end.
|
||||
pub fn pseudo_element_from_atom(pseudo: *mut nsIAtom,
|
||||
|
@ -256,7 +257,7 @@ pub extern "C" fn Servo_GetComputedValuesForAnonymousBox(parent_style_or_null: *
|
|||
-> *mut ServoComputedValues {
|
||||
let data = PerDocumentStyleData::borrow_mut_from_raw(raw_data);
|
||||
|
||||
let pseudo = match pseudo_element_from_atom(pseudo_tag, true) {
|
||||
let pseudo = match pseudo_element_from_atom(pseudo_tag, /* ua_stylesheet = */ true) {
|
||||
Ok(pseudo) => pseudo,
|
||||
Err(pseudo) => {
|
||||
warn!("stylo: Unable to parse anonymous-box pseudo-element: {}", pseudo);
|
||||
|
@ -267,11 +268,67 @@ pub extern "C" fn Servo_GetComputedValuesForAnonymousBox(parent_style_or_null: *
|
|||
type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_GetComputedValuesForPseudoElement(parent_style: *mut ServoComputedValues,
|
||||
match_element: *mut RawGeckoElement,
|
||||
pseudo_tag: *mut nsIAtom,
|
||||
raw_data: *mut RawServoStyleSet,
|
||||
is_probe: bool)
|
||||
-> *mut ServoComputedValues {
|
||||
debug_assert!(!match_element.is_null());
|
||||
|
||||
let parent_or_null = || {
|
||||
if is_probe {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
Servo_AddRefComputedValues(parent_style);
|
||||
parent_style
|
||||
}
|
||||
};
|
||||
|
||||
let pseudo = match pseudo_element_from_atom(pseudo_tag, /* ua_stylesheet = */ true) {
|
||||
Ok(pseudo) => pseudo,
|
||||
Err(pseudo) => {
|
||||
warn!("stylo: Unable to parse anonymous-box pseudo-element: {}", pseudo);
|
||||
return parent_or_null();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let data = PerDocumentStyleData::borrow_mut_from_raw(raw_data);
|
||||
|
||||
let element = unsafe { GeckoElement::from_raw(match_element) };
|
||||
|
||||
type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
|
||||
|
||||
match GeckoSelectorImpl::pseudo_element_cascade_type(&pseudo) {
|
||||
PseudoElementCascadeType::Eager => {
|
||||
let node = element.as_node();
|
||||
let maybe_computed = node.borrow_data()
|
||||
.and_then(|data| {
|
||||
data.per_pseudo.get(&pseudo).map(|c| c.clone())
|
||||
});
|
||||
maybe_computed.map_or_else(parent_or_null, Helpers::from)
|
||||
}
|
||||
PseudoElementCascadeType::Lazy => {
|
||||
Helpers::with(parent_style, |parent| {
|
||||
data.stylist
|
||||
.lazily_compute_pseudo_element_style(&element, &pseudo, parent)
|
||||
.map_or_else(parent_or_null, Helpers::from)
|
||||
})
|
||||
}
|
||||
PseudoElementCascadeType::Precomputed => {
|
||||
unreachable!("Anonymous pseudo found in \
|
||||
Servo_GetComputedValuesForPseudoElement");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_AddRefComputedValues(ptr: *mut ServoComputedValues) -> () {
|
||||
type Helpers = ArcHelpers<ServoComputedValues, GeckoComputedValues>;
|
||||
|
|
|
@ -6,7 +6,7 @@ use properties::GeckoComputedValues;
|
|||
use selectors::parser::{ParserContext, SelectorImpl};
|
||||
use style;
|
||||
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 Stylesheet = style::stylesheets::Stylesheet<GeckoSelectorImpl>;
|
||||
|
@ -22,7 +22,12 @@ pub enum PseudoElement {
|
|||
FirstLine,
|
||||
// TODO: Probably a few more are missing here
|
||||
|
||||
// https://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSAnonBoxList.h
|
||||
AnonBox(AnonBoxPseudoElement),
|
||||
}
|
||||
|
||||
// https://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSAnonBoxList.h
|
||||
#[derive(Clone, Debug, PartialEq, Eq, HeapSizeOf, Hash)]
|
||||
pub enum AnonBoxPseudoElement {
|
||||
MozNonElement,
|
||||
MozAnonymousBlock,
|
||||
MozAnonymousPositionedBlock,
|
||||
|
@ -153,6 +158,7 @@ impl SelectorImpl for GeckoSelectorImpl {
|
|||
|
||||
fn parse_pseudo_element(context: &ParserContext,
|
||||
name: &str) -> Result<PseudoElement, ()> {
|
||||
use self::AnonBoxPseudoElement::*;
|
||||
use self::PseudoElement::*;
|
||||
|
||||
// The braces here are unfortunate, but they're needed for
|
||||
|
@ -168,7 +174,7 @@ impl SelectorImpl for GeckoSelectorImpl {
|
|||
return Err(())
|
||||
}
|
||||
|
||||
Ok(match_ignore_ascii_case! { name,
|
||||
Ok(AnonBox(match_ignore_ascii_case! { name,
|
||||
"-moz-non-element" => MozNonElement,
|
||||
|
||||
"-moz-anonymous-block" => MozAnonymousBlock,
|
||||
|
@ -235,7 +241,7 @@ impl SelectorImpl for GeckoSelectorImpl {
|
|||
"-moz-svg-text" => MozSVGText,
|
||||
|
||||
_ => return Err(())
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,86 +249,89 @@ impl SelectorImplExt for GeckoSelectorImpl {
|
|||
type ComputedValues = GeckoComputedValues;
|
||||
|
||||
#[inline]
|
||||
fn is_eagerly_cascaded_pseudo_element(pseudo: &PseudoElement) -> bool {
|
||||
fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
|
||||
match *pseudo {
|
||||
PseudoElement::Before |
|
||||
PseudoElement::After |
|
||||
PseudoElement::FirstLine => true,
|
||||
_ => false,
|
||||
PseudoElement::After => PseudoElementCascadeType::Eager,
|
||||
PseudoElement::AnonBox(_) => PseudoElementCascadeType::Precomputed,
|
||||
_ => PseudoElementCascadeType::Lazy,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn each_pseudo_element<F>(mut fun: F)
|
||||
where F: FnMut(PseudoElement) {
|
||||
fun(PseudoElement::Before);
|
||||
fun(PseudoElement::After);
|
||||
fun(PseudoElement::FirstLine);
|
||||
use self::AnonBoxPseudoElement::*;
|
||||
use self::PseudoElement::*;
|
||||
|
||||
fun(PseudoElement::MozNonElement);
|
||||
fun(PseudoElement::MozAnonymousBlock);
|
||||
fun(PseudoElement::MozAnonymousPositionedBlock);
|
||||
fun(PseudoElement::MozMathMLAnonymousBlock);
|
||||
fun(PseudoElement::MozXULAnonymousBlock);
|
||||
fun(Before);
|
||||
fun(After);
|
||||
fun(FirstLine);
|
||||
|
||||
fun(PseudoElement::MozHorizontalFramesetBorder);
|
||||
fun(PseudoElement::MozVerticalFramesetBorder);
|
||||
fun(PseudoElement::MozLineFrame);
|
||||
fun(PseudoElement::MozButtonContent);
|
||||
fun(PseudoElement::MozButtonLabel);
|
||||
fun(PseudoElement::MozCellContent);
|
||||
fun(PseudoElement::MozDropdownList);
|
||||
fun(PseudoElement::MozFieldsetContent);
|
||||
fun(PseudoElement::MozFramesetBlank);
|
||||
fun(PseudoElement::MozDisplayComboboxControlFrame);
|
||||
fun(AnonBox(MozNonElement));
|
||||
fun(AnonBox(MozAnonymousBlock));
|
||||
fun(AnonBox(MozAnonymousPositionedBlock));
|
||||
fun(AnonBox(MozMathMLAnonymousBlock));
|
||||
fun(AnonBox(MozXULAnonymousBlock));
|
||||
|
||||
fun(PseudoElement::MozHTMLCanvasContent);
|
||||
fun(PseudoElement::MozInlineTable);
|
||||
fun(PseudoElement::MozTable);
|
||||
fun(PseudoElement::MozTableCell);
|
||||
fun(PseudoElement::MozTableColumnGroup);
|
||||
fun(PseudoElement::MozTableColumn);
|
||||
fun(PseudoElement::MozTableOuter);
|
||||
fun(PseudoElement::MozTableRowGroup);
|
||||
fun(PseudoElement::MozTableRow);
|
||||
fun(AnonBox(MozHorizontalFramesetBorder));
|
||||
fun(AnonBox(MozVerticalFramesetBorder));
|
||||
fun(AnonBox(MozLineFrame));
|
||||
fun(AnonBox(MozButtonContent));
|
||||
fun(AnonBox(MozButtonLabel));
|
||||
fun(AnonBox(MozCellContent));
|
||||
fun(AnonBox(MozDropdownList));
|
||||
fun(AnonBox(MozFieldsetContent));
|
||||
fun(AnonBox(MozFramesetBlank));
|
||||
fun(AnonBox(MozDisplayComboboxControlFrame));
|
||||
|
||||
fun(PseudoElement::MozCanvas);
|
||||
fun(PseudoElement::MozPageBreak);
|
||||
fun(PseudoElement::MozPage);
|
||||
fun(PseudoElement::MozPageContent);
|
||||
fun(PseudoElement::MozPageSequence);
|
||||
fun(PseudoElement::MozScrolledContent);
|
||||
fun(PseudoElement::MozScrolledCanvas);
|
||||
fun(PseudoElement::MozScrolledPageSequence);
|
||||
fun(PseudoElement::MozColumnContent);
|
||||
fun(PseudoElement::MozViewport);
|
||||
fun(PseudoElement::MozViewportScroll);
|
||||
fun(PseudoElement::MozAnonymousFlexItem);
|
||||
fun(PseudoElement::MozAnonymousGridItem);
|
||||
fun(AnonBox(MozHTMLCanvasContent));
|
||||
fun(AnonBox(MozInlineTable));
|
||||
fun(AnonBox(MozTable));
|
||||
fun(AnonBox(MozTableCell));
|
||||
fun(AnonBox(MozTableColumnGroup));
|
||||
fun(AnonBox(MozTableColumn));
|
||||
fun(AnonBox(MozTableOuter));
|
||||
fun(AnonBox(MozTableRowGroup));
|
||||
fun(AnonBox(MozTableRow));
|
||||
|
||||
fun(PseudoElement::MozRuby);
|
||||
fun(PseudoElement::MozRubyBase);
|
||||
fun(PseudoElement::MozRubyBaseContainer);
|
||||
fun(PseudoElement::MozRubyText);
|
||||
fun(PseudoElement::MozRubyTextContainer);
|
||||
fun(AnonBox(MozCanvas));
|
||||
fun(AnonBox(MozPageBreak));
|
||||
fun(AnonBox(MozPage));
|
||||
fun(AnonBox(MozPageContent));
|
||||
fun(AnonBox(MozPageSequence));
|
||||
fun(AnonBox(MozScrolledContent));
|
||||
fun(AnonBox(MozScrolledCanvas));
|
||||
fun(AnonBox(MozScrolledPageSequence));
|
||||
fun(AnonBox(MozColumnContent));
|
||||
fun(AnonBox(MozViewport));
|
||||
fun(AnonBox(MozViewportScroll));
|
||||
fun(AnonBox(MozAnonymousFlexItem));
|
||||
fun(AnonBox(MozAnonymousGridItem));
|
||||
|
||||
fun(PseudoElement::MozTreeColumn);
|
||||
fun(PseudoElement::MozTreeRow);
|
||||
fun(PseudoElement::MozTreeSeparator);
|
||||
fun(PseudoElement::MozTreeCell);
|
||||
fun(PseudoElement::MozTreeIndentation);
|
||||
fun(PseudoElement::MozTreeLine);
|
||||
fun(PseudoElement::MozTreeTwisty);
|
||||
fun(PseudoElement::MozTreeImage);
|
||||
fun(PseudoElement::MozTreeCellText);
|
||||
fun(PseudoElement::MozTreeCheckbox);
|
||||
fun(PseudoElement::MozTreeProgressMeter);
|
||||
fun(PseudoElement::MozTreeDropFeedback);
|
||||
fun(AnonBox(MozRuby));
|
||||
fun(AnonBox(MozRubyBase));
|
||||
fun(AnonBox(MozRubyBaseContainer));
|
||||
fun(AnonBox(MozRubyText));
|
||||
fun(AnonBox(MozRubyTextContainer));
|
||||
|
||||
fun(PseudoElement::MozSVGMarkerAnonChild);
|
||||
fun(PseudoElement::MozSVGOuterSVGAnonChild);
|
||||
fun(PseudoElement::MozSVGForeignContent);
|
||||
fun(PseudoElement::MozSVGText);
|
||||
fun(AnonBox(MozTreeColumn));
|
||||
fun(AnonBox(MozTreeRow));
|
||||
fun(AnonBox(MozTreeSeparator));
|
||||
fun(AnonBox(MozTreeCell));
|
||||
fun(AnonBox(MozTreeIndentation));
|
||||
fun(AnonBox(MozTreeLine));
|
||||
fun(AnonBox(MozTreeTwisty));
|
||||
fun(AnonBox(MozTreeImage));
|
||||
fun(AnonBox(MozTreeCellText));
|
||||
fun(AnonBox(MozTreeCheckbox));
|
||||
fun(AnonBox(MozTreeProgressMeter));
|
||||
fun(AnonBox(MozTreeDropFeedback));
|
||||
|
||||
fun(AnonBox(MozSVGMarkerAnonChild));
|
||||
fun(AnonBox(MozSVGOuterSVGAnonChild));
|
||||
fun(AnonBox(MozSVGForeignContent));
|
||||
fun(AnonBox(MozSVGText));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -32,7 +32,8 @@ use std::slice;
|
|||
use std::str::from_utf8_unchecked;
|
||||
use std::sync::Arc;
|
||||
use string_cache::{Atom, Namespace};
|
||||
use style::dom::{OpaqueNode, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||
use style::dom::{OpaqueNode, PresentationalHintsSynthetizer};
|
||||
use style::dom::{TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||
use style::element_state::ElementState;
|
||||
#[allow(unused_imports)] // Used in commented-out code.
|
||||
use style::error_reporting::StdoutErrorReporter;
|
||||
|
@ -339,12 +340,6 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
}
|
||||
}
|
||||
|
||||
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
|
||||
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
|
||||
{
|
||||
// FIXME(bholley) - Need to implement this.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> {
|
||||
unsafe {
|
||||
|
@ -360,6 +355,14 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> {
|
||||
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
|
||||
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
|
||||
{
|
||||
// FIXME(bholley) - Need to implement this.
|
||||
}
|
||||
}
|
||||
|
||||
impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||
type Impl = GeckoSelectorImpl;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue