Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor flow #416

Merged
merged 12 commits into from May 7, 2013

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

Add some basic support to AbstractNode for it.
  • Loading branch information
pcwalton committed May 7, 2013
commit 58679216b3a4849050b27cf1569434a99310fd51
@@ -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;

@@ -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 {
}
}

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
//
// FIXME: Fold these into util::tree.

/// 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
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.