util: Add a new tree module to avoid duplicating pointer stitching.

Add some basic support to AbstractNode for it.
This commit is contained in:
Patrick Walton 2013-05-06 18:54:49 -07:00
parent d374d6561d
commit 58679216b3
5 changed files with 243 additions and 8 deletions

View file

@ -13,6 +13,7 @@ extern mod std;
pub mod cache;
pub mod range;
pub mod time;
pub mod tree;
pub mod url;
pub mod vec;

171
src/servo-util/tree.rs Normal file
View file

@ -0,0 +1,171 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Helper functions for garbage collected doubly-linked trees.
/// The basic trait. This function is meant to encapsulate a clonable reference to a tree node.
pub trait TreeNodeRef<N> : Clone {
/// Borrows this node as immutable.
fn with_immutable_node<R>(&self, callback: &fn(&N) -> R) -> R;
/// Borrows this node as mutable.
fn with_mutable_node<R>(&self, callback: &fn(&mut N) -> R) -> R;
}
/// The contents of a tree node.
pub trait TreeNode<NR> {
/// Returns the parent of this node.
fn parent_node(&self) -> Option<NR>;
/// Returns the first child of this node.
fn first_child(&self) -> Option<NR>;
/// Returns the last child of this node.
fn last_child(&self) -> Option<NR>;
/// Returns the previous sibling of this node.
fn prev_sibling(&self) -> Option<NR>;
/// Returns the next sibling of this node.
fn next_sibling(&self) -> Option<NR>;
/// Sets the parent of this node.
fn set_parent_node(&mut self, new_parent: Option<NR>);
/// Sets the first child of this node.
fn set_first_child(&mut self, new_first_child: Option<NR>);
/// Sets the last child of this node.
fn set_last_child(&mut self, new_last_child: Option<NR>);
/// Sets the previous sibling of this node.
fn set_prev_sibling(&mut self, new_prev_sibling: Option<NR>);
/// Sets the next sibling of this node.
fn set_next_sibling(&mut self, new_next_sibling: Option<NR>);
}
/// A set of helper functions useful for operating on trees.
pub trait TreeUtils {
/// Returns true if this node is disconnected from the tree or has no children.
fn is_leaf(&self) -> bool;
/// Adds a new child to the end of this node's list of children.
///
/// Fails unless `new_child` is disconnected from the tree.
fn add_child(&self, new_child: Self);
/// Removes the given child from this node's list of children.
///
/// Fails unless `child` is a child of this node. (FIXME: This is not yet checked.)
fn remove_child(&self, child: Self);
/// Iterates over all children of this node.
fn each_child(&self, callback: &fn(Self) -> bool);
/// Iterates over this node and all its descendants, in preorder.
fn traverse_preorder(&self, callback: &fn(Self) -> bool) -> bool;
/// Iterates over this node and all its descendants, in postorder.
fn traverse_postorder(&self, callback: &fn(Self) -> bool) -> bool;
}
impl<NR:TreeNodeRef<N>,N:TreeNode<NR>> TreeUtils for NR {
fn is_leaf(&self) -> bool {
do self.with_immutable_node |this_node| {
this_node.first_child().is_none()
}
}
fn add_child(&self, new_child: NR) {
do self.with_mutable_node |this_node| {
do new_child.with_mutable_node |new_child_node| {
assert!(new_child_node.parent_node().is_none());
assert!(new_child_node.prev_sibling().is_none());
assert!(new_child_node.next_sibling().is_none());
match this_node.last_child() {
None => this_node.set_first_child(Some(new_child.clone())),
Some(last_child) => {
do last_child.with_mutable_node |last_child_node| {
assert!(last_child_node.next_sibling().is_none());
last_child_node.set_next_sibling(Some(new_child.clone()));
new_child_node.set_prev_sibling(Some(last_child.clone()));
}
}
}
this_node.set_last_child(Some(new_child.clone()));
new_child_node.set_parent_node(Some((*self).clone()));
}
}
}
fn remove_child(&self, child: NR) {
do self.with_mutable_node |this_node| {
do child.with_mutable_node |child_node| {
assert!(child_node.parent_node().is_some());
match child_node.prev_sibling() {
None => this_node.set_first_child(child_node.next_sibling()),
Some(prev_sibling) => {
do prev_sibling.with_mutable_node |prev_sibling_node| {
prev_sibling_node.set_next_sibling(child_node.next_sibling());
}
}
}
match child_node.next_sibling() {
None => this_node.set_last_child(child_node.prev_sibling()),
Some(next_sibling) => {
do next_sibling.with_mutable_node |next_sibling_node| {
next_sibling_node.set_prev_sibling(child_node.prev_sibling());
}
}
}
child_node.set_prev_sibling(None);
child_node.set_next_sibling(None);
child_node.set_parent_node(None);
}
}
}
fn each_child(&self, callback: &fn(NR) -> bool) {
let mut maybe_current = self.with_immutable_node(|n| n.first_child());
while !maybe_current.is_none() {
let current = maybe_current.get_ref().clone();
if !callback(current.clone()) {
break;
}
maybe_current = current.with_immutable_node(|n| n.next_sibling());
}
}
fn traverse_preorder(&self, callback: &fn(NR) -> bool) -> bool {
if !callback((*self).clone()) {
return false;
}
for self.each_child |kid| {
if !kid.traverse_preorder(callback) {
return false;
}
}
true
}
fn traverse_postorder(&self, callback: &fn(NR) -> bool) -> bool {
for self.each_child |kid| {
if !kid.traverse_postorder(callback) {
return false;
}
}
callback((*self).clone())
}
}

View file

@ -10,6 +10,8 @@ use dom::node::AbstractNode;
use newcss::complete::CompleteSelectResults;
use newcss::select::{SelectCtx, SelectResults};
use servo_util::tree::TreeUtils;
pub trait MatchMethods {
fn restyle_subtree(&self, select_ctx: &SelectCtx);
}

View file

@ -11,6 +11,7 @@ use dom::node::AbstractNode;
use dom::window::Window;
use js::jsapi::bindgen::{JS_AddObjectRoot, JS_RemoveObjectRoot};
use servo_util::tree::TreeUtils;
pub struct Document {
root: AbstractNode,
@ -69,4 +70,4 @@ pub impl Document {
chan.send(ReflowEvent)
};
}
}
}

View file

@ -7,20 +7,21 @@
//
use content::content_task::global_content;
use dom::bindings;
use dom::bindings::codegen;
use dom::bindings::node;
use dom::bindings::utils::WrapperCache;
use dom::bindings;
use dom::characterdata::CharacterData;
use dom::document::Document;
use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId};
use dom::element::{HTMLStyleElementTypeId};
use js::rust::Compartment;
use layout::debug::DebugMethods;
use layout::flow::FlowContext;
use newcss::complete::CompleteSelectResults;
use js::rust::Compartment;
use core::cast::transmute;
use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils};
//
// The basic Node structure
@ -135,17 +136,76 @@ impl Text {
}
}
pub impl AbstractNode {
//
// Convenience accessors
//
// FIXME: Fold these into util::tree.
impl Clone for AbstractNode {
fn clone(&self) -> AbstractNode {
*self
}
}
impl TreeNode<AbstractNode> for Node {
fn parent_node(&self) -> Option<AbstractNode> {
self.parent_node
}
fn first_child(&self) -> Option<AbstractNode> {
self.first_child
}
fn last_child(&self) -> Option<AbstractNode> {
self.last_child
}
fn prev_sibling(&self) -> Option<AbstractNode> {
self.prev_sibling
}
fn next_sibling(&self) -> Option<AbstractNode> {
self.next_sibling
}
fn set_parent_node(&mut self, new_parent_node: Option<AbstractNode>) {
self.parent_node = new_parent_node
}
fn set_first_child(&mut self, new_first_child: Option<AbstractNode>) {
self.first_child = new_first_child
}
fn set_last_child(&mut self, new_last_child: Option<AbstractNode>) {
self.last_child = new_last_child
}
fn set_prev_sibling(&mut self, new_prev_sibling: Option<AbstractNode>) {
self.prev_sibling = new_prev_sibling
}
fn set_next_sibling(&mut self, new_next_sibling: Option<AbstractNode>) {
self.next_sibling = new_next_sibling
}
}
impl TreeNodeRef<Node> for AbstractNode {
// FIXME: The duplication between `with_imm_node` and `with_immutable_node` is ugly.
fn with_immutable_node<R>(&self, callback: &fn(&Node) -> R) -> R {
self.with_imm_node(callback)
}
fn with_mutable_node<R>(&self, callback: &fn(&mut Node) -> R) -> R {
self.with_mut_node(callback)
}
}
pub impl AbstractNode {
// Convenience accessors
/// Returns the type ID of this node.
fn type_id(self) -> NodeTypeId { self.with_imm_node(|n| n.type_id) }
/// Returns the parent node of this node.
fn parent_node(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.parent_node) }
/// Returns the first child of this node.
fn first_child(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.first_child) }
/// Returns the last child of this node.
fn last_child(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.last_child) }
/// Returns the previous sibling of this node.
fn prev_sibling(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.prev_sibling) }
/// Returns the next sibling of this node.
fn next_sibling(self) -> Option<AbstractNode> { self.with_imm_node(|n| n.next_sibling) }
// NB: You must not call these if you are not layout. We should do something with scoping to