mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
util: Add a new tree
module to avoid duplicating pointer stitching.
Add some basic support to AbstractNode for it.
This commit is contained in:
parent
d374d6561d
commit
58679216b3
5 changed files with 243 additions and 8 deletions
|
@ -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
171
src/servo-util/tree.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue