mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +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 cache;
|
||||||
pub mod range;
|
pub mod range;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
pub mod tree;
|
||||||
pub mod url;
|
pub mod url;
|
||||||
pub mod vec;
|
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::complete::CompleteSelectResults;
|
||||||
use newcss::select::{SelectCtx, SelectResults};
|
use newcss::select::{SelectCtx, SelectResults};
|
||||||
|
|
||||||
|
use servo_util::tree::TreeUtils;
|
||||||
|
|
||||||
pub trait MatchMethods {
|
pub trait MatchMethods {
|
||||||
fn restyle_subtree(&self, select_ctx: &SelectCtx);
|
fn restyle_subtree(&self, select_ctx: &SelectCtx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use dom::node::AbstractNode;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
|
||||||
use js::jsapi::bindgen::{JS_AddObjectRoot, JS_RemoveObjectRoot};
|
use js::jsapi::bindgen::{JS_AddObjectRoot, JS_RemoveObjectRoot};
|
||||||
|
use servo_util::tree::TreeUtils;
|
||||||
|
|
||||||
pub struct Document {
|
pub struct Document {
|
||||||
root: AbstractNode,
|
root: AbstractNode,
|
||||||
|
@ -69,4 +70,4 @@ pub impl Document {
|
||||||
chan.send(ReflowEvent)
|
chan.send(ReflowEvent)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,20 +7,21 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
use content::content_task::global_content;
|
use content::content_task::global_content;
|
||||||
use dom::bindings;
|
|
||||||
use dom::bindings::codegen;
|
use dom::bindings::codegen;
|
||||||
use dom::bindings::node;
|
use dom::bindings::node;
|
||||||
use dom::bindings::utils::WrapperCache;
|
use dom::bindings::utils::WrapperCache;
|
||||||
|
use dom::bindings;
|
||||||
use dom::characterdata::CharacterData;
|
use dom::characterdata::CharacterData;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId};
|
use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId};
|
||||||
use dom::element::{HTMLStyleElementTypeId};
|
use dom::element::{HTMLStyleElementTypeId};
|
||||||
|
use js::rust::Compartment;
|
||||||
use layout::debug::DebugMethods;
|
use layout::debug::DebugMethods;
|
||||||
use layout::flow::FlowContext;
|
use layout::flow::FlowContext;
|
||||||
use newcss::complete::CompleteSelectResults;
|
use newcss::complete::CompleteSelectResults;
|
||||||
use js::rust::Compartment;
|
|
||||||
|
|
||||||
use core::cast::transmute;
|
use core::cast::transmute;
|
||||||
|
use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils};
|
||||||
|
|
||||||
//
|
//
|
||||||
// The basic Node structure
|
// The basic Node structure
|
||||||
|
@ -135,17 +136,76 @@ impl Text {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl AbstractNode {
|
impl Clone for AbstractNode {
|
||||||
//
|
fn clone(&self) -> AbstractNode {
|
||||||
// Convenience accessors
|
*self
|
||||||
//
|
}
|
||||||
// FIXME: Fold these into util::tree.
|
}
|
||||||
|
|
||||||
|
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) }
|
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) }
|
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) }
|
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) }
|
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) }
|
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) }
|
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
|
// 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