diff --git a/src/servo-util/servo_util.rc b/src/servo-util/servo_util.rc index 0f67d299cb3..5779542af3a 100644 --- a/src/servo-util/servo_util.rc +++ b/src/servo-util/servo_util.rc @@ -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; diff --git a/src/servo-util/tree.rs b/src/servo-util/tree.rs new file mode 100644 index 00000000000..2e46ee541fa --- /dev/null +++ b/src/servo-util/tree.rs @@ -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 : Clone { + /// Borrows this node as immutable. + fn with_immutable_node(&self, callback: &fn(&N) -> R) -> R; + + /// Borrows this node as mutable. + fn with_mutable_node(&self, callback: &fn(&mut N) -> R) -> R; +} + +/// The contents of a tree node. +pub trait TreeNode { + /// Returns the parent of this node. + fn parent_node(&self) -> Option; + + /// Returns the first child of this node. + fn first_child(&self) -> Option; + + /// Returns the last child of this node. + fn last_child(&self) -> Option; + + /// Returns the previous sibling of this node. + fn prev_sibling(&self) -> Option; + + /// Returns the next sibling of this node. + fn next_sibling(&self) -> Option; + + /// Sets the parent of this node. + fn set_parent_node(&mut self, new_parent: Option); + + /// Sets the first child of this node. + fn set_first_child(&mut self, new_first_child: Option); + + /// Sets the last child of this node. + fn set_last_child(&mut self, new_last_child: Option); + + /// Sets the previous sibling of this node. + fn set_prev_sibling(&mut self, new_prev_sibling: Option); + + /// Sets the next sibling of this node. + fn set_next_sibling(&mut self, new_next_sibling: Option); +} + +/// 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,N:TreeNode> 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()) + } +} + diff --git a/src/servo/css/matching.rs b/src/servo/css/matching.rs index 4739c4928a1..3a95aceac07 100644 --- a/src/servo/css/matching.rs +++ b/src/servo/css/matching.rs @@ -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); } diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index 11fc5de0457..fc841534718 100644 --- a/src/servo/dom/document.rs +++ b/src/servo/dom/document.rs @@ -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) }; } -} \ No newline at end of file +} diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index d8745ae00fb..8f8ffe45ed2 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -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 for Node { + fn parent_node(&self) -> Option { + self.parent_node + } + fn first_child(&self) -> Option { + self.first_child + } + fn last_child(&self) -> Option { + self.last_child + } + fn prev_sibling(&self) -> Option { + self.prev_sibling + } + fn next_sibling(&self) -> Option { + self.next_sibling + } + + fn set_parent_node(&mut self, new_parent_node: Option) { + self.parent_node = new_parent_node + } + fn set_first_child(&mut self, new_first_child: Option) { + self.first_child = new_first_child + } + fn set_last_child(&mut self, new_last_child: Option) { + self.last_child = new_last_child + } + fn set_prev_sibling(&mut self, new_prev_sibling: Option) { + self.prev_sibling = new_prev_sibling + } + fn set_next_sibling(&mut self, new_next_sibling: Option) { + self.next_sibling = new_next_sibling + } +} + +impl TreeNodeRef for AbstractNode { + // FIXME: The duplication between `with_imm_node` and `with_immutable_node` is ugly. + fn with_immutable_node(&self, callback: &fn(&Node) -> R) -> R { + self.with_imm_node(callback) + } + + fn with_mutable_node(&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 { self.with_imm_node(|n| n.parent_node) } + + /// Returns the first child of this node. fn first_child(self) -> Option { self.with_imm_node(|n| n.first_child) } + + /// Returns the last child of this node. fn last_child(self) -> Option { self.with_imm_node(|n| n.last_child) } + + /// Returns the previous sibling of this node. fn prev_sibling(self) -> Option { self.with_imm_node(|n| n.prev_sibling) } + + /// Returns the next sibling of this node. fn next_sibling(self) -> Option { 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