mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Auto merge of #8639 - bholley:generalize_wrappers2, r=pcwalton
Use associated types for layout wrapper trait, and generalized ThreadSafeLayoutFoo <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8639) <!-- Reviewable:end -->
This commit is contained in:
commit
711f516d80
8 changed files with 396 additions and 322 deletions
|
@ -74,14 +74,10 @@ use util::str::{is_whitespace, search_index};
|
|||
/// 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 WrapperTypes<'a> : Sized {
|
||||
type Node: LayoutNode<'a, Self>;
|
||||
type Document: LayoutDocument<'a, Self>;
|
||||
type Element: LayoutElement<'a, Self>;
|
||||
}
|
||||
pub trait LayoutNode<'ln> : Sized + Copy + Clone {
|
||||
type ConcreteLayoutElement: LayoutElement<'ln>;
|
||||
type ConcreteLayoutDocument: LayoutDocument<'ln>;
|
||||
|
||||
pub trait LayoutNode<'ln, Wrappers> : Sized + Copy + Clone
|
||||
where Wrappers: WrapperTypes<'ln> {
|
||||
/// Returns the type ID of this node.
|
||||
fn type_id(&self) -> NodeTypeId;
|
||||
|
||||
|
@ -89,12 +85,12 @@ pub trait LayoutNode<'ln, Wrappers> : Sized + Copy + Clone
|
|||
|
||||
fn dump(self);
|
||||
|
||||
fn traverse_preorder(self) -> LayoutTreeIterator<'ln, Wrappers>;
|
||||
fn traverse_preorder(self) -> LayoutTreeIterator<'ln, Self>;
|
||||
|
||||
/// Returns an iterator over this node's children.
|
||||
fn children(self) -> LayoutNodeChildrenIterator<'ln, Wrappers>;
|
||||
fn children(self) -> LayoutNodeChildrenIterator<'ln, Self>;
|
||||
|
||||
fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, Wrappers>;
|
||||
fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, Self>;
|
||||
|
||||
/// Converts self into an `OpaqueNode`.
|
||||
fn opaque(&self) -> OpaqueNode;
|
||||
|
@ -110,9 +106,9 @@ pub trait LayoutNode<'ln, Wrappers> : Sized + Copy + Clone
|
|||
|
||||
fn debug_id(self) -> usize;
|
||||
|
||||
fn as_element(&self) -> Option<Wrappers::Element>;
|
||||
fn as_element(&self) -> Option<Self::ConcreteLayoutElement>;
|
||||
|
||||
fn as_document(&self) -> Option<Wrappers::Document>;
|
||||
fn as_document(&self) -> Option<Self::ConcreteLayoutDocument>;
|
||||
|
||||
fn children_count(&self) -> u32;
|
||||
|
||||
|
@ -154,29 +150,33 @@ pub trait LayoutNode<'ln, Wrappers> : Sized + Copy + Clone
|
|||
#[inline(always)]
|
||||
fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>>;
|
||||
|
||||
fn parent_node(&self) -> Option<Wrappers::Node>;
|
||||
fn parent_node(&self) -> Option<Self>;
|
||||
|
||||
fn first_child(&self) -> Option<Wrappers::Node>;
|
||||
fn first_child(&self) -> Option<Self>;
|
||||
|
||||
fn last_child(&self) -> Option<Wrappers::Node>;
|
||||
fn last_child(&self) -> Option<Self>;
|
||||
|
||||
fn prev_sibling(&self) -> Option<Wrappers::Node>;
|
||||
fn prev_sibling(&self) -> Option<Self>;
|
||||
|
||||
fn next_sibling(&self) -> Option<Wrappers::Node>;
|
||||
fn next_sibling(&self) -> Option<Self>;
|
||||
}
|
||||
|
||||
pub trait LayoutDocument<'ld, Wrappers> : Sized + Copy + Clone
|
||||
where Wrappers: WrapperTypes<'ld> {
|
||||
fn as_node(&self) -> Wrappers::Node;
|
||||
pub trait LayoutDocument<'ld> : Sized + Copy + Clone {
|
||||
type ConcreteLayoutNode: LayoutNode<'ld>;
|
||||
type ConcreteLayoutElement: LayoutElement<'ld>;
|
||||
|
||||
fn root_node(&self) -> Option<Wrappers::Node>;
|
||||
fn as_node(&self) -> Self::ConcreteLayoutNode;
|
||||
|
||||
fn drain_modified_elements(&self) -> Vec<(Wrappers::Element, ElementSnapshot)>;
|
||||
fn root_node(&self) -> Option<Self::ConcreteLayoutNode>;
|
||||
|
||||
fn drain_modified_elements(&self) -> Vec<(Self::ConcreteLayoutElement, ElementSnapshot)>;
|
||||
}
|
||||
|
||||
pub trait LayoutElement<'le, Wrappers> : Sized + Copy + Clone + ::selectors::Element
|
||||
where Wrappers: WrapperTypes<'le> {
|
||||
fn as_node(&self) -> Wrappers::Node;
|
||||
pub trait LayoutElement<'le> : Sized + Copy + Clone + ::selectors::Element + TElementAttributes {
|
||||
type ConcreteLayoutNode: LayoutNode<'le>;
|
||||
type ConcreteLayoutDocument: LayoutDocument<'le>;
|
||||
|
||||
fn as_node(&self) -> Self::ConcreteLayoutNode;
|
||||
|
||||
fn style_attribute(&self) -> &'le Option<PropertyDeclarationBlock>;
|
||||
|
||||
|
@ -224,17 +224,6 @@ pub trait LayoutElement<'le, Wrappers> : Sized + Copy + Clone + ::selectors::Ele
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ServoWrapperTypes<'a> {
|
||||
// Satisfy the compiler about the unused lifetime.
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> WrapperTypes<'a> for ServoWrapperTypes<'a> {
|
||||
type Node = ServoLayoutNode<'a>;
|
||||
type Element = ServoLayoutElement<'a>;
|
||||
type Document = ServoLayoutDocument<'a>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ServoLayoutNode<'a> {
|
||||
/// The wrapped node.
|
||||
|
@ -272,7 +261,10 @@ impl<'ln> ServoLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ln> LayoutNode<'ln, ServoWrapperTypes<'ln>> for ServoLayoutNode<'ln> {
|
||||
impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> {
|
||||
type ConcreteLayoutElement = ServoLayoutElement<'ln>;
|
||||
type ConcreteLayoutDocument = ServoLayoutDocument<'ln>;
|
||||
|
||||
fn type_id(&self) -> NodeTypeId {
|
||||
unsafe {
|
||||
self.node.type_id_for_layout()
|
||||
|
@ -289,19 +281,21 @@ impl<'ln> LayoutNode<'ln, ServoWrapperTypes<'ln>> for ServoLayoutNode<'ln> {
|
|||
self.dump_indent(0);
|
||||
}
|
||||
|
||||
fn traverse_preorder(self) -> LayoutTreeIterator<'ln, ServoWrapperTypes<'ln>> {
|
||||
fn traverse_preorder(self) -> LayoutTreeIterator<'ln, Self> {
|
||||
LayoutTreeIterator::new(self)
|
||||
}
|
||||
|
||||
fn children(self) -> LayoutNodeChildrenIterator<'ln, ServoWrapperTypes<'ln>> {
|
||||
fn children(self) -> LayoutNodeChildrenIterator<'ln, Self> {
|
||||
LayoutNodeChildrenIterator {
|
||||
current: self.first_child(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, ServoWrapperTypes<'ln>> {
|
||||
fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, Self> {
|
||||
LayoutNodeReverseChildrenIterator {
|
||||
current: self.last_child()
|
||||
current: self.last_child(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,49 +446,59 @@ impl<'ln> ServoLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct LayoutNodeChildrenIterator<'a, Wrappers: WrapperTypes<'a>> {
|
||||
current: Option<Wrappers::Node>,
|
||||
pub struct LayoutNodeChildrenIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
current: Option<ConcreteLayoutNode>,
|
||||
// Satisfy the compiler about the unused lifetime.
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, Wrappers: WrapperTypes<'a>> Iterator for LayoutNodeChildrenIterator<'a, Wrappers> {
|
||||
type Item = Wrappers::Node;
|
||||
fn next(&mut self) -> Option<Wrappers::Node> {
|
||||
impl<'a, ConcreteLayoutNode> Iterator for LayoutNodeChildrenIterator<'a, ConcreteLayoutNode>
|
||||
where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
type Item = ConcreteLayoutNode;
|
||||
fn next(&mut self) -> Option<ConcreteLayoutNode> {
|
||||
let node = self.current;
|
||||
self.current = node.and_then(|node| node.next_sibling());
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayoutNodeReverseChildrenIterator<'a, Wrappers: WrapperTypes<'a>> {
|
||||
current: Option<Wrappers::Node>,
|
||||
pub struct LayoutNodeReverseChildrenIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
current: Option<ConcreteLayoutNode>,
|
||||
// Satisfy the compiler about the unused lifetime.
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, Wrappers: WrapperTypes<'a>> Iterator for LayoutNodeReverseChildrenIterator<'a, Wrappers> {
|
||||
type Item = Wrappers::Node;
|
||||
fn next(&mut self) -> Option<Wrappers::Node> {
|
||||
impl<'a, ConcreteLayoutNode> Iterator for LayoutNodeReverseChildrenIterator<'a, ConcreteLayoutNode>
|
||||
where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
type Item = ConcreteLayoutNode;
|
||||
fn next(&mut self) -> Option<ConcreteLayoutNode> {
|
||||
let node = self.current;
|
||||
self.current = node.and_then(|node| node.prev_sibling());
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayoutTreeIterator<'a, Wrappers: WrapperTypes<'a>> {
|
||||
stack: Vec<Wrappers::Node>,
|
||||
pub struct LayoutTreeIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
stack: Vec<ConcreteLayoutNode>,
|
||||
// Satisfy the compiler about the unused lifetime.
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, Wrappers> LayoutTreeIterator<'a, Wrappers> where Wrappers: WrapperTypes<'a> {
|
||||
fn new(root: Wrappers::Node) -> LayoutTreeIterator<'a, Wrappers> {
|
||||
impl<'a, ConcreteLayoutNode> LayoutTreeIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
fn new(root: ConcreteLayoutNode) -> LayoutTreeIterator<'a, ConcreteLayoutNode> {
|
||||
let mut stack = vec!();
|
||||
stack.push(root);
|
||||
LayoutTreeIterator {
|
||||
stack: stack
|
||||
stack: stack,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Wrappers> Iterator for LayoutTreeIterator<'a, Wrappers> where Wrappers: WrapperTypes<'a> {
|
||||
type Item = Wrappers::Node;
|
||||
fn next(&mut self) -> Option<Wrappers::Node> {
|
||||
impl<'a, ConcreteLayoutNode> Iterator for LayoutTreeIterator<'a, ConcreteLayoutNode>
|
||||
where ConcreteLayoutNode: LayoutNode<'a> {
|
||||
type Item = ConcreteLayoutNode;
|
||||
fn next(&mut self) -> Option<ConcreteLayoutNode> {
|
||||
let ret = self.stack.pop();
|
||||
ret.map(|node| self.stack.extend(node.rev_children()));
|
||||
ret
|
||||
|
@ -508,7 +512,10 @@ pub struct ServoLayoutDocument<'ld> {
|
|||
chain: PhantomData<&'ld ()>,
|
||||
}
|
||||
|
||||
impl<'ld> LayoutDocument<'ld, ServoWrapperTypes<'ld>> for ServoLayoutDocument<'ld> {
|
||||
impl<'ld> LayoutDocument<'ld> for ServoLayoutDocument<'ld> {
|
||||
type ConcreteLayoutNode = ServoLayoutNode<'ld>;
|
||||
type ConcreteLayoutElement = ServoLayoutElement<'ld>;
|
||||
|
||||
fn as_node(&self) -> ServoLayoutNode<'ld> {
|
||||
ServoLayoutNode::from_layout_js(self.document.upcast())
|
||||
}
|
||||
|
@ -539,7 +546,10 @@ pub struct ServoLayoutElement<'le> {
|
|||
chain: PhantomData<&'le ()>,
|
||||
}
|
||||
|
||||
impl<'le> LayoutElement<'le, ServoWrapperTypes<'le>> for ServoLayoutElement<'le> {
|
||||
impl<'le> LayoutElement<'le> for ServoLayoutElement<'le> {
|
||||
type ConcreteLayoutNode = ServoLayoutNode<'le>;
|
||||
type ConcreteLayoutDocument = ServoLayoutDocument<'le>;
|
||||
|
||||
fn as_node(&self) -> ServoLayoutNode<'le> {
|
||||
ServoLayoutNode::from_layout_js(self.element.upcast())
|
||||
}
|
||||
|
@ -782,141 +792,53 @@ 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.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ThreadSafeLayoutNode<'ln> {
|
||||
/// The wrapped node.
|
||||
node: ServoLayoutNode<'ln>,
|
||||
|
||||
pseudo: PseudoElementType<display::T>,
|
||||
}
|
||||
|
||||
impl<'ln> ThreadSafeLayoutNode<'ln> {
|
||||
/// Creates a new layout node with the same lifetime as this layout node.
|
||||
pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ThreadSafeLayoutNode<'ln> {
|
||||
ThreadSafeLayoutNode {
|
||||
node: self.node.new_with_this_lifetime(node),
|
||||
pseudo: PseudoElementType::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
|
||||
pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ThreadSafeLayoutNode<'a> {
|
||||
ThreadSafeLayoutNode {
|
||||
node: node.clone(),
|
||||
pseudo: PseudoElementType::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
|
||||
/// with a different pseudo-element type.
|
||||
fn with_pseudo(&self, pseudo: PseudoElementType<display::T>) -> ThreadSafeLayoutNode<'ln> {
|
||||
ThreadSafeLayoutNode {
|
||||
node: self.node.clone(),
|
||||
pseudo: pseudo,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
|
||||
/// call and as such is marked `unsafe`.
|
||||
unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
|
||||
self.node.get_jsmanaged()
|
||||
}
|
||||
pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized {
|
||||
type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<'ln>;
|
||||
|
||||
/// Converts self into an `OpaqueNode`.
|
||||
pub fn opaque(&self) -> OpaqueNode {
|
||||
OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() })
|
||||
}
|
||||
fn opaque(&self) -> OpaqueNode;
|
||||
|
||||
/// Returns the type ID of this node.
|
||||
/// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
|
||||
pub fn type_id(&self) -> Option<NodeTypeId> {
|
||||
if self.pseudo != PseudoElementType::Normal {
|
||||
return None
|
||||
}
|
||||
fn type_id(&self) -> Option<NodeTypeId>;
|
||||
|
||||
Some(self.node.type_id())
|
||||
}
|
||||
fn debug_id(self) -> usize;
|
||||
|
||||
pub fn debug_id(self) -> usize {
|
||||
self.node.debug_id()
|
||||
}
|
||||
|
||||
pub fn flow_debug_id(self) -> usize {
|
||||
self.node.flow_debug_id()
|
||||
}
|
||||
fn flow_debug_id(self) -> usize;
|
||||
|
||||
/// Returns an iterator over this node's children.
|
||||
pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> {
|
||||
ThreadSafeLayoutNodeChildrenIterator::new(*self)
|
||||
}
|
||||
fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln, Self>;
|
||||
|
||||
/// If this is an element, accesses the element data. Fails if this is not an element node.
|
||||
#[inline]
|
||||
pub fn as_element(&self) -> ThreadSafeLayoutElement<'ln> {
|
||||
unsafe {
|
||||
let element = match self.get_jsmanaged().downcast() {
|
||||
Some(e) => e.unsafe_get(),
|
||||
None => panic!("not an element")
|
||||
};
|
||||
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
|
||||
// implementations.
|
||||
ThreadSafeLayoutElement {
|
||||
element: &*element,
|
||||
}
|
||||
}
|
||||
}
|
||||
fn as_element(&self) -> Self::ConcreteThreadSafeLayoutElement;
|
||||
|
||||
#[inline]
|
||||
pub fn get_pseudo_element_type(&self) -> PseudoElementType<display::T> {
|
||||
self.pseudo
|
||||
}
|
||||
fn get_pseudo_element_type(&self) -> PseudoElementType<display::T>;
|
||||
|
||||
#[inline]
|
||||
pub fn get_before_pseudo(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap();
|
||||
node_layout_data_wrapper.data.before_style.as_ref().map(|style| {
|
||||
self.with_pseudo(PseudoElementType::Before(style.get_box().display))
|
||||
})
|
||||
}
|
||||
fn get_before_pseudo(&self) -> Option<Self>;
|
||||
|
||||
#[inline]
|
||||
pub fn get_after_pseudo(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap();
|
||||
node_layout_data_wrapper.data.after_style.as_ref().map(|style| {
|
||||
self.with_pseudo(PseudoElementType::After(style.get_box().display))
|
||||
})
|
||||
}
|
||||
|
||||
/// Borrows the layout data without checking.
|
||||
#[inline(always)]
|
||||
fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> {
|
||||
unsafe {
|
||||
self.node.borrow_layout_data_unchecked()
|
||||
}
|
||||
}
|
||||
fn get_after_pseudo(&self) -> Option<Self>;
|
||||
|
||||
/// Borrows the layout data immutably. Fails on a conflicting borrow.
|
||||
///
|
||||
/// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
|
||||
#[inline(always)]
|
||||
pub fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>> {
|
||||
self.node.borrow_layout_data()
|
||||
}
|
||||
fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>>;
|
||||
|
||||
/// Borrows the layout data mutably. Fails on a conflicting borrow.
|
||||
///
|
||||
/// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases.
|
||||
#[inline(always)]
|
||||
pub fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>> {
|
||||
self.node.mutate_layout_data()
|
||||
}
|
||||
fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>>;
|
||||
|
||||
/// Returns the style results for the given node. If CSS selector matching
|
||||
/// has not yet been performed, fails.
|
||||
#[inline]
|
||||
pub fn style(&self) -> Ref<Arc<ComputedValues>> {
|
||||
fn style(&self) -> Ref<Arc<ComputedValues>> {
|
||||
Ref::map(self.borrow_layout_data(), |layout_data_ref| {
|
||||
let layout_data = layout_data_ref.as_ref().expect("no layout data");
|
||||
let style = match self.get_pseudo_element_type() {
|
||||
|
@ -929,7 +851,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
|
||||
/// Removes the style from this node.
|
||||
pub fn unstyle(self) {
|
||||
fn unstyle(self) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
let layout_data = layout_data_ref.as_mut().expect("no layout data");
|
||||
|
||||
|
@ -943,7 +865,220 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
*style = None;
|
||||
}
|
||||
|
||||
pub fn is_ignorable_whitespace(&self) -> bool {
|
||||
fn is_ignorable_whitespace(&self) -> bool;
|
||||
|
||||
/// Get the description of how to account for recent style changes.
|
||||
/// This is a simple bitfield and fine to copy by value.
|
||||
fn restyle_damage(self) -> RestyleDamage {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
layout_data_ref.as_ref().unwrap().data.restyle_damage
|
||||
}
|
||||
|
||||
/// Set the restyle damage field.
|
||||
fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref {
|
||||
Some(ref mut layout_data) => layout_data.data.restyle_damage = damage,
|
||||
_ => panic!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the layout data flags for this node.
|
||||
fn flags(self) -> LayoutDataFlags;
|
||||
|
||||
/// Adds the given flags to this node.
|
||||
fn insert_flags(self, new_flags: LayoutDataFlags) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref {
|
||||
Some(ref mut layout_data) => layout_data.data.flags.insert(new_flags),
|
||||
_ => panic!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the given flags from this node.
|
||||
fn remove_flags(self, flags: LayoutDataFlags) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref {
|
||||
Some(ref mut layout_data) => layout_data.data.flags.remove(flags),
|
||||
_ => panic!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this node contributes content. This is used in the implementation of
|
||||
/// `empty_cells` per CSS 2.1 § 17.6.1.1.
|
||||
fn is_content(&self) -> bool {
|
||||
match self.type_id() {
|
||||
Some(NodeTypeId::Element(..)) | Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text(..))) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is a text node, generated content, or a form element, copies out
|
||||
/// its content. Otherwise, panics.
|
||||
///
|
||||
/// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this.
|
||||
fn text_content(&self) -> TextContent;
|
||||
|
||||
/// If the insertion point is within this node, returns it. Otherwise, returns `None`.
|
||||
fn insertion_point(&self) -> Option<CharIndex>;
|
||||
|
||||
/// If this is an image element, returns its URL. If this is not an image element, fails.
|
||||
///
|
||||
/// FIXME(pcwalton): Don't copy URLs.
|
||||
fn image_url(&self) -> Option<Url>;
|
||||
|
||||
fn canvas_data(&self) -> Option<HTMLCanvasData>;
|
||||
|
||||
/// If this node is an iframe element, returns its pipeline ID. If this node is
|
||||
/// not an iframe element, fails.
|
||||
fn iframe_pipeline_id(&self) -> PipelineId;
|
||||
|
||||
fn get_colspan(&self) -> u32;
|
||||
}
|
||||
|
||||
// These can violate the thread-safety and therefore are not public.
|
||||
trait DangerousThreadSafeLayoutNode<'ln> : ThreadSafeLayoutNode<'ln> {
|
||||
unsafe fn dangerous_first_child(&self) -> Option<Self>;
|
||||
unsafe fn dangerous_next_sibling(&self) -> Option<Self>;
|
||||
}
|
||||
|
||||
pub trait ThreadSafeLayoutElement<'le> {
|
||||
type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'le>;
|
||||
|
||||
#[inline]
|
||||
fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&'le str>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ServoThreadSafeLayoutNode<'ln> {
|
||||
/// The wrapped node.
|
||||
node: ServoLayoutNode<'ln>,
|
||||
|
||||
pseudo: PseudoElementType<display::T>,
|
||||
}
|
||||
|
||||
impl<'ln> DangerousThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> {
|
||||
unsafe fn dangerous_first_child(&self) -> Option<Self> {
|
||||
self.get_jsmanaged().first_child_ref()
|
||||
.map(|node| self.new_with_this_lifetime(&node))
|
||||
}
|
||||
unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
|
||||
self.get_jsmanaged().next_sibling_ref()
|
||||
.map(|node| self.new_with_this_lifetime(&node))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ln> ServoThreadSafeLayoutNode<'ln> {
|
||||
/// Creates a new layout node with the same lifetime as this layout node.
|
||||
pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoThreadSafeLayoutNode<'ln> {
|
||||
ServoThreadSafeLayoutNode {
|
||||
node: self.node.new_with_this_lifetime(node),
|
||||
pseudo: PseudoElementType::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
|
||||
pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> {
|
||||
ServoThreadSafeLayoutNode {
|
||||
node: node.clone(),
|
||||
pseudo: PseudoElementType::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `ServoThreadSafeLayoutNode` for the same `LayoutNode`
|
||||
/// with a different pseudo-element type.
|
||||
fn with_pseudo(&self, pseudo: PseudoElementType<display::T>) -> ServoThreadSafeLayoutNode<'ln> {
|
||||
ServoThreadSafeLayoutNode {
|
||||
node: self.node.clone(),
|
||||
pseudo: pseudo,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
|
||||
/// call and as such is marked `unsafe`.
|
||||
unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
|
||||
self.node.get_jsmanaged()
|
||||
}
|
||||
|
||||
/// Borrows the layout data without checking.
|
||||
#[inline(always)]
|
||||
fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> {
|
||||
unsafe {
|
||||
self.node.borrow_layout_data_unchecked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> {
|
||||
type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>;
|
||||
|
||||
fn opaque(&self) -> OpaqueNode {
|
||||
OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() })
|
||||
}
|
||||
|
||||
fn type_id(&self) -> Option<NodeTypeId> {
|
||||
if self.pseudo != PseudoElementType::Normal {
|
||||
return None
|
||||
}
|
||||
|
||||
Some(self.node.type_id())
|
||||
}
|
||||
|
||||
fn debug_id(self) -> usize {
|
||||
self.node.debug_id()
|
||||
}
|
||||
|
||||
fn flow_debug_id(self) -> usize {
|
||||
self.node.flow_debug_id()
|
||||
}
|
||||
|
||||
fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln, Self> {
|
||||
ThreadSafeLayoutNodeChildrenIterator::new(*self)
|
||||
}
|
||||
|
||||
fn as_element(&self) -> ServoThreadSafeLayoutElement<'ln> {
|
||||
unsafe {
|
||||
let element = match self.get_jsmanaged().downcast() {
|
||||
Some(e) => e.unsafe_get(),
|
||||
None => panic!("not an element")
|
||||
};
|
||||
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
|
||||
// implementations.
|
||||
ServoThreadSafeLayoutElement {
|
||||
element: &*element,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pseudo_element_type(&self) -> PseudoElementType<display::T> {
|
||||
self.pseudo
|
||||
}
|
||||
|
||||
fn get_before_pseudo(&self) -> Option<ServoThreadSafeLayoutNode<'ln>> {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap();
|
||||
node_layout_data_wrapper.data.before_style.as_ref().map(|style| {
|
||||
self.with_pseudo(PseudoElementType::Before(style.get_box().display))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_after_pseudo(&self) -> Option<ServoThreadSafeLayoutNode<'ln>> {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap();
|
||||
node_layout_data_wrapper.data.after_style.as_ref().map(|style| {
|
||||
self.with_pseudo(PseudoElementType::After(style.get_box().display))
|
||||
})
|
||||
}
|
||||
|
||||
fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>> {
|
||||
self.node.borrow_layout_data()
|
||||
}
|
||||
|
||||
fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>> {
|
||||
self.node.mutate_layout_data()
|
||||
}
|
||||
|
||||
fn is_ignorable_whitespace(&self) -> bool {
|
||||
unsafe {
|
||||
let text: LayoutJS<Text> = match self.get_jsmanaged().downcast() {
|
||||
Some(text) => text,
|
||||
|
@ -964,24 +1099,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the description of how to account for recent style changes.
|
||||
/// This is a simple bitfield and fine to copy by value.
|
||||
pub fn restyle_damage(self) -> RestyleDamage {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
layout_data_ref.as_ref().unwrap().data.restyle_damage
|
||||
}
|
||||
|
||||
/// Set the restyle damage field.
|
||||
pub fn set_restyle_damage(self, damage: RestyleDamage) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref {
|
||||
Some(ref mut layout_data) => layout_data.data.restyle_damage = damage,
|
||||
_ => panic!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the layout data flags for this node.
|
||||
pub fn flags(self) -> LayoutDataFlags {
|
||||
fn flags(self) -> LayoutDataFlags {
|
||||
unsafe {
|
||||
match *self.borrow_layout_data_unchecked() {
|
||||
None => panic!(),
|
||||
|
@ -990,38 +1108,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds the given flags to this node.
|
||||
pub fn insert_flags(self, new_flags: LayoutDataFlags) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref {
|
||||
Some(ref mut layout_data) => layout_data.data.flags.insert(new_flags),
|
||||
_ => panic!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the given flags from this node.
|
||||
pub fn remove_flags(self, flags: LayoutDataFlags) {
|
||||
let mut layout_data_ref = self.mutate_layout_data();
|
||||
match *layout_data_ref {
|
||||
Some(ref mut layout_data) => layout_data.data.flags.remove(flags),
|
||||
_ => panic!("no layout data for this node"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this node contributes content. This is used in the implementation of
|
||||
/// `empty_cells` per CSS 2.1 § 17.6.1.1.
|
||||
pub fn is_content(&self) -> bool {
|
||||
match self.type_id() {
|
||||
Some(NodeTypeId::Element(..)) | Some(NodeTypeId::CharacterData(CharacterDataTypeId::Text(..))) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// If this is a text node, generated content, or a form element, copies out
|
||||
/// its content. Otherwise, panics.
|
||||
///
|
||||
/// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this.
|
||||
pub fn text_content(&self) -> TextContent {
|
||||
fn text_content(&self) -> TextContent {
|
||||
if self.pseudo != PseudoElementType::Normal {
|
||||
let layout_data_ref = self.borrow_layout_data();
|
||||
let data = &layout_data_ref.as_ref().unwrap().data;
|
||||
|
@ -1058,8 +1145,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
panic!("not text!")
|
||||
}
|
||||
|
||||
/// If the insertion point is within this node, returns it. Otherwise, returns `None`.
|
||||
pub fn insertion_point(&self) -> Option<CharIndex> {
|
||||
fn insertion_point(&self) -> Option<CharIndex> {
|
||||
let this = unsafe {
|
||||
self.get_jsmanaged()
|
||||
};
|
||||
|
@ -1078,10 +1164,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
None
|
||||
}
|
||||
|
||||
/// If this is an image element, returns its URL. If this is not an image element, fails.
|
||||
///
|
||||
/// FIXME(pcwalton): Don't copy URLs.
|
||||
pub fn image_url(&self) -> Option<Url> {
|
||||
fn image_url(&self) -> Option<Url> {
|
||||
unsafe {
|
||||
self.get_jsmanaged().downcast()
|
||||
.expect("not an image!")
|
||||
|
@ -1089,16 +1172,14 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn canvas_data(&self) -> Option<HTMLCanvasData> {
|
||||
fn canvas_data(&self) -> Option<HTMLCanvasData> {
|
||||
unsafe {
|
||||
let canvas_element = self.get_jsmanaged().downcast();
|
||||
canvas_element.map(|canvas| canvas.data())
|
||||
}
|
||||
}
|
||||
|
||||
/// If this node is an iframe element, returns its pipeline ID. If this node is
|
||||
/// not an iframe element, fails.
|
||||
pub fn iframe_pipeline_id(&self) -> PipelineId {
|
||||
fn iframe_pipeline_id(&self) -> PipelineId {
|
||||
use script::dom::htmliframeelement::HTMLIFrameElementLayoutMethods;
|
||||
unsafe {
|
||||
let iframe_element = self.get_jsmanaged().downcast::<HTMLIFrameElement>()
|
||||
|
@ -1107,65 +1188,56 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_colspan(&self) -> u32 {
|
||||
fn get_colspan(&self) -> u32 {
|
||||
unsafe {
|
||||
self.get_jsmanaged().downcast::<Element>().unwrap().get_colspan()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||
current_node: Option<ThreadSafeLayoutNode<'a>>,
|
||||
parent_node: ThreadSafeLayoutNode<'a>,
|
||||
pub struct ThreadSafeLayoutNodeChildrenIterator<'ln, ConcreteNode: ThreadSafeLayoutNode<'ln>> {
|
||||
current_node: Option<ConcreteNode>,
|
||||
parent_node: ConcreteNode,
|
||||
// Satisfy the compiler about the unused lifetime.
|
||||
phantom: PhantomData<&'ln ()>,
|
||||
}
|
||||
|
||||
impl<'a> ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||
fn new(parent: ThreadSafeLayoutNode<'a>) -> ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||
fn first_child(parent: ThreadSafeLayoutNode)
|
||||
-> Option<ThreadSafeLayoutNode> {
|
||||
if parent.pseudo != PseudoElementType::Normal {
|
||||
return None
|
||||
}
|
||||
|
||||
parent.get_before_pseudo().or_else(|| {
|
||||
unsafe {
|
||||
parent.get_jsmanaged().first_child_ref()
|
||||
.map(|node| parent.new_with_this_lifetime(&node))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl<'ln, ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<'ln, ConcreteNode>
|
||||
where ConcreteNode: DangerousThreadSafeLayoutNode<'ln> {
|
||||
fn new(parent: ConcreteNode) -> Self {
|
||||
let first_child: Option<ConcreteNode> = match parent.get_pseudo_element_type() {
|
||||
PseudoElementType::Normal => {
|
||||
parent.get_before_pseudo().or_else(|| {
|
||||
unsafe { parent.dangerous_first_child() }
|
||||
})
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
ThreadSafeLayoutNodeChildrenIterator {
|
||||
current_node: first_child(parent),
|
||||
current_node: first_child,
|
||||
parent_node: parent,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ThreadSafeLayoutNodeChildrenIterator<'a> {
|
||||
type Item = ThreadSafeLayoutNode<'a>;
|
||||
fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> {
|
||||
impl<'ln, ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<'ln, ConcreteNode>
|
||||
where ConcreteNode: DangerousThreadSafeLayoutNode<'ln> {
|
||||
type Item = ConcreteNode;
|
||||
fn next(&mut self) -> Option<ConcreteNode> {
|
||||
let node = self.current_node.clone();
|
||||
|
||||
if let Some(ref node) = node {
|
||||
self.current_node = match node.pseudo {
|
||||
self.current_node = match node.get_pseudo_element_type() {
|
||||
PseudoElementType::Before(_) => {
|
||||
match unsafe { self.parent_node.get_jsmanaged().first_child_ref() } {
|
||||
Some(first) => {
|
||||
Some(unsafe {
|
||||
self.parent_node.new_with_this_lifetime(&first)
|
||||
})
|
||||
},
|
||||
match unsafe { self.parent_node.dangerous_first_child() } {
|
||||
Some(first) => Some(first),
|
||||
None => self.parent_node.get_after_pseudo(),
|
||||
}
|
||||
},
|
||||
PseudoElementType::Normal => {
|
||||
match unsafe { node.get_jsmanaged().next_sibling_ref() } {
|
||||
Some(next) => {
|
||||
Some(unsafe {
|
||||
self.parent_node.new_with_this_lifetime(&next)
|
||||
})
|
||||
},
|
||||
match unsafe { node.dangerous_next_sibling() } {
|
||||
Some(next) => Some(next),
|
||||
None => self.parent_node.get_after_pseudo(),
|
||||
}
|
||||
},
|
||||
|
@ -1181,13 +1253,14 @@ impl<'a> Iterator for ThreadSafeLayoutNodeChildrenIterator<'a> {
|
|||
|
||||
/// A wrapper around elements that ensures layout can only ever access safe properties and cannot
|
||||
/// race on elements.
|
||||
pub struct ThreadSafeLayoutElement<'le> {
|
||||
pub struct ServoThreadSafeLayoutElement<'le> {
|
||||
element: &'le Element,
|
||||
}
|
||||
|
||||
impl<'le> ThreadSafeLayoutElement<'le> {
|
||||
#[inline]
|
||||
pub fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&'le str> {
|
||||
impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> {
|
||||
type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>;
|
||||
|
||||
fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&'le str> {
|
||||
unsafe {
|
||||
self.element.get_attr_val_for_layout(namespace, name)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue