layout: Move the LayoutNode wrapper from script into layout.

This commit is contained in:
Patrick Walton 2013-12-17 13:41:45 -08:00
parent c506e52c7c
commit 9e2b63ddd3
13 changed files with 370 additions and 343 deletions

View file

@ -14,14 +14,12 @@ use dom::documenttype::DocumentType;
use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId};
use dom::element::{HTMLAnchorElementTypeId, HTMLStyleElementTypeId};
use dom::eventtarget::{AbstractEventTarget, EventTarget, NodeTypeId};
use dom::nodelist::{NodeList};
use dom::htmlimageelement::HTMLImageElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement;
use dom::nodelist::{NodeList};
use dom::text::Text;
use extra::url::Url;
use js::jsapi::{JSObject, JSContext};
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_util::slot::{MutSlotRef, Slot, SlotRef};
use std::cast::transmute;
use std::cast;
@ -29,198 +27,6 @@ use std::unstable::raw::Box;
use std::util;
use style::TNode;
//
// Layout's immutable, sanitized view of nodes.
//
/// A nonsense constant for layout nodes to point to just to establish a lifetime.
///
/// FIXME(pcwalton): Phantom lifetimes in Rust would be useful...
static HEAVY_IRON_BALL: () = ();
/// 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 `AbstractNode`.
#[deriving(Clone, Eq)]
pub struct LayoutNode<'self> {
/// The wrapped node.
priv node: AbstractNode,
/// Being chained to a `HEAVY_IRON_BALL` prevents `LayoutNode`s from escaping.
priv chain: &'self (),
}
impl<'self> LayoutNode<'self> {
/// NB: Do not make this public.
///
/// FIXME(pcwalton): Probably this should be marked `unsafe`.
/*PRIVATE-FOR-SECURITY-REASONS*/ fn new(node: AbstractNode) -> LayoutNode<'static> {
LayoutNode {
node: node,
chain: &HEAVY_IRON_BALL,
}
}
/// Returns the interior of this node as a `Node`. This is highly unsafe for layout to call
/// and as such is marked `unsafe`.
pub unsafe fn get<'a>(&'a self) -> &'a Node<LayoutView> {
cast::transmute(self.node.node())
}
/// Returns the first child of this node.
pub fn first_child(&self) -> Option<LayoutNode<'self>> {
self.node.first_child().map(|node| LayoutNode::new(node))
}
/// Returns the first child of this node.
pub fn last_child(&self) -> Option<LayoutNode<'self>> {
self.node.last_child().map(|node| LayoutNode::new(node))
}
/// Iterates over this node and all its descendants, in preorder.
///
/// FIXME(pcwalton): Terribly inefficient. We should use parallelism.
pub fn traverse_preorder(&self) -> LayoutTreeIterator<'self> {
let mut nodes = ~[];
gather_layout_nodes(self, &mut nodes, false);
LayoutTreeIterator::new(nodes)
}
/// Returns an iterator over this node's children.
pub fn children(&self) -> LayoutNodeChildrenIterator<'self> {
LayoutNodeChildrenIterator {
current_node: self.first_child(),
}
}
/// Returns the type ID of this node. Fails if this node is borrowed mutably.
pub fn type_id(&self) -> NodeTypeId {
self.node.type_id()
}
/// 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> {
self.with_image_element(|image_element| {
image_element.image.as_ref().map(|url| (*url).clone())
})
}
/// Downcasts this node to an image element and calls the given closure.
///
/// NB: Do not make this public, as layout will be able to do unsafe things with `AbstractNode`
/// otherwise.
///
/// FIXME(pcwalton): RAII.
/*PRIVATE-FOR-SECURITY-REASONS*/ fn with_image_element<R>(
self,
f: &fn(&HTMLImageElement) -> R)
-> R {
if !self.node.is_image_element() {
fail!(~"node is not an image element");
}
self.node.transmute(f)
}
/// If this node is an iframe element, returns its pipeline and subpage IDs. If this node is
/// not an iframe element, fails.
pub fn iframe_pipeline_and_subpage_ids(&self) -> (PipelineId, SubpageId) {
self.with_iframe_element(|iframe_element| {
let size = iframe_element.size.unwrap();
(size.pipeline_id, size.subpage_id)
})
}
/// Downcasts this node to an iframe element and calls the given closure.
///
/// NB: Do not make this public, as layout will be able to do unsafe things with `AbstractNode`
/// otherwise.
///
/// FIXME(pcwalton): RAII.
/*PRIVATE-FOR-SECURITY-REASONS*/ fn with_iframe_element<R>(
self,
f: &fn(&HTMLIFrameElement) -> R)
-> R {
if !self.node.is_iframe_element() {
fail!(~"node is not an iframe element");
}
self.node.transmute(f)
}
/// Returns true if this node is a text node or false otherwise.
#[inline]
pub fn is_text(self) -> bool {
self.node.is_text()
}
/// Returns true if this node consists entirely of ignorable whitespace and false otherwise.
/// Ignorable whitespace is defined as whitespace that would be removed per CSS 2.1 § 16.6.1.
pub fn is_ignorable_whitespace(&self) -> bool {
self.is_text() && self.with_text(|text| text.element.data.is_whitespace())
}
/// If this is a text node, copies out the text. If this is not a text node, fails.
///
/// FIXME(pcwalton): Don't copy text. Atomically reference count instead.
pub fn text(&self) -> ~str {
self.with_text(|text| text.element.data.to_str())
}
/// Downcasts this node to a text node and calls the given closure.
///
/// NB: Do not make this public, as layout will be able to do unsafe things with `AbstractNode`
/// otherwise.
///
/// FIXME(pcwalton): RAII.
/*PRIVATE-FOR-SECURITY-REASONS*/ fn with_text<R>(self, f: &fn(&Text) -> R) -> R {
self.node.with_imm_text(f)
}
/// Dumps this node tree, for debugging.
pub fn dump(&self) {
self.node.dump()
}
/// Returns a string that describes this node, for debugging.
pub fn debug_str(&self) -> ~str {
self.node.debug_str()
}
}
impl<'self> TNode<Element> for LayoutNode<'self> {
fn parent_node(&self) -> Option<LayoutNode<'self>> {
self.node.node().parent_node.map(|node| LayoutNode::new(node))
}
fn prev_sibling(&self) -> Option<LayoutNode<'self>> {
self.node.node().prev_sibling.map(|node| LayoutNode::new(node))
}
fn next_sibling(&self) -> Option<LayoutNode<'self>> {
self.node.node().next_sibling.map(|node| LayoutNode::new(node))
}
fn is_element(&self) -> bool {
match self.node.type_id() {
ElementNodeTypeId(*) => true,
_ => false
}
}
fn is_document(&self) -> bool {
match self.node.type_id() {
DocumentNodeTypeId(*) => true,
_ => false
}
}
/// FIXME(pcwalton): Unsafe!
#[inline]
fn with_element<R>(&self, f: &fn(&Element) -> R) -> R {
self.node.with_imm_element(f)
}
}
//
// The basic Node structure
//
@ -851,20 +657,6 @@ impl Iterator<AbstractNode> for AbstractNodeChildrenIterator {
}
}
pub struct LayoutNodeChildrenIterator<'self> {
priv current_node: Option<LayoutNode<'self>>,
}
impl<'self> Iterator<LayoutNode<'self>> for LayoutNodeChildrenIterator<'self> {
fn next(&mut self) -> Option<LayoutNode<'self>> {
let node = self.current_node;
self.current_node = do self.current_node.and_then |node| {
node.next_sibling()
};
node
}
}
pub struct AncestorIterator {
priv current: Option<AbstractNode>,
}
@ -922,49 +714,6 @@ fn gather_abstract_nodes(cur: &AbstractNode, refs: &mut ~[AbstractNode], postord
}
}
// FIXME: Do this without precomputing a vector of refs.
// Easy for preorder; harder for postorder.
//
// FIXME(pcwalton): Parallelism! Eventually this should just be nuked.
pub struct LayoutTreeIterator<'self> {
priv nodes: ~[LayoutNode<'self>],
priv index: uint,
}
impl<'self> LayoutTreeIterator<'self> {
fn new(nodes: ~[LayoutNode<'self>]) -> LayoutTreeIterator<'self> {
LayoutTreeIterator {
nodes: nodes,
index: 0,
}
}
}
impl<'self> Iterator<LayoutNode<'self>> for LayoutTreeIterator<'self> {
fn next(&mut self) -> Option<LayoutNode<'self>> {
if self.index >= self.nodes.len() {
None
} else {
let v = self.nodes[self.index].clone();
self.index += 1;
Some(v)
}
}
}
/// FIXME(pcwalton): This is super inefficient.
fn gather_layout_nodes<'a>(cur: &LayoutNode<'a>, refs: &mut ~[LayoutNode<'a>], postorder: bool) {
if !postorder {
refs.push(cur.clone());
}
for kid in cur.children() {
gather_layout_nodes(&kid, refs, postorder)
}
if postorder {
refs.push(cur.clone());
}
}
impl AbstractNode {
/// Iterates over all ancestors of this node.
pub fn ancestors(&self) -> AncestorIterator {
@ -1629,80 +1378,3 @@ impl Reflectable for Node<ScriptView> {
}
}
/// A bottom-up, parallelizable traversal.
pub trait PostorderNodeTraversal {
/// The operation to perform. Return true to continue or false to stop.
fn process<'a>(&'a self, node: LayoutNode<'a>) -> bool;
/// Returns true if this node should be pruned. If this returns true, we skip the operation
/// entirely and do not process any descendant nodes. This is called *before* child nodes are
/// visited. The default implementation never prunes any nodes.
fn should_prune<'a>(&'a self, _node: LayoutNode<'a>) -> bool {
false
}
}
/// A bottom-up, parallelizable traversal.
pub trait PostorderNodeMutTraversal {
/// The operation to perform. Return true to continue or false to stop.
fn process<'a>(&'a mut self, node: LayoutNode<'a>) -> bool;
/// Returns true if this node should be pruned. If this returns true, we skip the operation
/// entirely and do not process any descendant nodes. This is called *before* child nodes are
/// visited. The default implementation never prunes any nodes.
fn should_prune<'a>(&'a self, _node: LayoutNode<'a>) -> bool {
false
}
}
impl<'self> LayoutNode<'self> {
/// Traverses the tree in postorder.
///
/// TODO(pcwalton): Offer a parallel version with a compatible API.
pub fn traverse_postorder<T:PostorderNodeTraversal>(self, traversal: &T) -> bool {
if traversal.should_prune(self) {
return true
}
let mut opt_kid = self.first_child();
loop {
match opt_kid {
None => break,
Some(kid) => {
if !kid.traverse_postorder(traversal) {
return false
}
opt_kid = kid.next_sibling()
}
}
}
traversal.process(self)
}
/// Traverses the tree in postorder.
///
/// TODO(pcwalton): Offer a parallel version with a compatible API.
pub fn traverse_postorder_mut<T:PostorderNodeMutTraversal>(mut self, traversal: &mut T)
-> bool {
if traversal.should_prune(self) {
return true
}
let mut opt_kid = self.first_child();
loop {
match opt_kid {
None => break,
Some(kid) => {
if !kid.traverse_postorder_mut(traversal) {
return false
}
opt_kid = kid.next_sibling()
}
}
}
traversal.process(self)
}
}