Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: Add infrastructure to support lazy pseudo-elements #10934

Merged
merged 8 commits into from May 4, 2016

style: layout: Allow a lazy pseudo-element implementation in Servo.

  • Loading branch information
emilio committed May 3, 2016
commit 028f9b6cd26f0cd2835166d96ffd30d69a39e7cb
@@ -66,7 +66,7 @@ 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};
@@ -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;
@@ -791,23 +807,20 @@ pub trait ThreadSafeLayoutNode : Clone + Copy + Sized + PartialEq {
}
}
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())
// }
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());
}
}

This comment has been minimized.

@mbrubeck

mbrubeck Apr 29, 2016

Contributor

Please remove the commented-out code before merging.

}

@@ -932,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]
@@ -1027,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()
}
@@ -1320,3 +1340,101 @@ impl TextContent {
}
}
}

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 {
warn!("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> {
warn!("ServoThreadSafeLayoutElement::get_id called");
None
}

fn has_class(&self, _name: &Atom) -> bool {
warn!("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>>> {}
}
@@ -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>;

@@ -104,19 +104,11 @@ pub enum PseudoElement {
impl PseudoElement {
#[inline]
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 {
PseudoElement::Before |
PseudoElement::After |
PseudoElement::Selection |
PseudoElement::DetailsSummary => PseudoElementCascadeType::Eager,
PseudoElement::Selection => PseudoElementCascadeType::Eager,
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent => PseudoElementCascadeType::Precomputed,
}
}
@@ -5,7 +5,7 @@
// For lazy_static
#![allow(unsafe_code)]

use dom::TElement;
use dom::PresentationalHintsSynthetizer;
use element_state::*;
use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
use media_queries::{Device, MediaType};
@@ -281,7 +281,8 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
pseudo: &Impl::PseudoElement,
parent: &Arc<Impl::ComputedValues>)
-> Option<Arc<Impl::ComputedValues>>
where E: Element<Impl=Impl> + TElement {
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;
@@ -358,7 +359,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> {
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(),
@@ -23,7 +23,6 @@ pub enum PseudoElement {
// TODO: Probably a few more are missing here

AnonBox(AnonBoxPseudoElement),

}

// https://mxr.mozilla.org/mozilla-central/source/layout/style/nsCSSAnonBoxList.h
@@ -262,8 +261,8 @@ impl SelectorImplExt for GeckoSelectorImpl {
#[inline]
fn each_pseudo_element<F>(mut fun: F)
where F: FnMut(PseudoElement) {
use self::PseudoElement::*;
use self::AnonBoxPseudoElement::*;
use self::PseudoElement::*;

fun(Before);
fun(After);
@@ -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;

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.