diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index e286217ad12..f6ab2db39c7 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -158,6 +158,8 @@ impl NodeScope : tree::WriteMethods { tree::add_child(&self, node, child) } + pure fn eq(a: &Node, b: &Node) -> bool { a == b } + fn with_tree_fields(node: &Node, f: fn(&tree::Tree) -> R) -> R { self.write(node, |n| f(&n.tree)) } diff --git a/src/servo/layout/flow.rs b/src/servo/layout/flow.rs index 9fa8c5d4dbc..4fd954d532e 100644 --- a/src/servo/layout/flow.rs +++ b/src/servo/layout/flow.rs @@ -326,10 +326,11 @@ impl FlowTree : tree::ReadMethods<@FlowContext> { impl FlowTree : tree::WriteMethods<@FlowContext> { fn add_child(parent: @FlowContext, child: @FlowContext) { - assert !core::box::ptr_eq(parent, child); tree::add_child(&self, parent, child) } + pure fn eq(a: &@FlowContext, b: &@FlowContext) -> bool { core::box::ptr_eq(*a, *b) } + fn with_tree_fields(box: &@FlowContext, f: fn(&tree::Tree<@FlowContext>) -> R) -> R { f(&box.d().tree) } diff --git a/src/servo/util/tree.rs b/src/servo/util/tree.rs index 55b251f061e..1436a92707e 100644 --- a/src/servo/util/tree.rs +++ b/src/servo/util/tree.rs @@ -18,6 +18,7 @@ pub trait ReadMethods { pub trait WriteMethods { fn with_tree_fields(&T, f: fn(&Tree) -> R) -> R; + pure fn eq(&T, &T) -> bool; } pub fn each_child>(ops: &O, node: &T, f: fn(&T) -> bool) { @@ -42,6 +43,7 @@ pub fn empty() -> Tree { } pub fn add_child>(ops: &O, parent: T, child: T) { + assert !ops.eq(&parent, &child); ops.with_tree_fields(&child, |child_tf| { match child_tf.parent { @@ -58,7 +60,6 @@ pub fn add_child>(ops: &O, parent: T, child: T) { parent_tf.first_child = Some(child); } Some(lc) => { - let lc = lc; // satisfy alias checker ops.with_tree_fields(&lc, |lc_tf| { assert lc_tf.next_sibling.is_none(); lc_tf.next_sibling = Some(child); @@ -72,36 +73,90 @@ pub fn add_child>(ops: &O, parent: T, child: T) { }); } +pub fn remove_child>(ops: &O, parent: T, child: T) { + do ops.with_tree_fields(&child) |child_tf| { + match copy child_tf.parent { + None => { fail ~"Not a child"; } + Some(parent_n) => { + assert ops.eq(&parent, &parent_n); + + // adjust parent fields + do ops.with_tree_fields(&parent) |parent_tf| { + match copy parent_tf.first_child { + None => { fail ~"parent had no first child??" }, + Some(first_child) if ops.eq(&child, &first_child) => { + parent_tf.first_child = child_tf.next_sibling; + }, + Some(_) => {} + }; + + match copy parent_tf.last_child { + None => { fail ~"parent had no last child??" }, + Some(last_child) if ops.eq(&child, &last_child) => { + parent_tf.last_child = child_tf.prev_sibling; + }, + Some(_) => {} + } + } + } + } + + // adjust siblings + match child_tf.prev_sibling { + None => {}, + Some(_) => { + do ops.with_tree_fields(&child_tf.prev_sibling.get()) |prev_tf| { + prev_tf.next_sibling = child_tf.next_sibling; + } + } + } + match child_tf.next_sibling { + None => {}, + Some(_) => { + do ops.with_tree_fields(&child_tf.next_sibling.get()) |next_tf| { + next_tf.prev_sibling = child_tf.prev_sibling; + } + } + } + + // clear child + child_tf.parent = None; + child_tf.next_sibling = None; + child_tf.prev_sibling = None; + } +} + pub fn get_parent>(ops: &O, node: &T) -> Option { ops.with_tree_fields(node, |tf| tf.parent) } #[cfg(test)] mod test { - enum dummy = @{ - fields: Tree, + enum dummy = { + fields: Tree<@dummy>, value: uint }; enum dtree { dtree } - impl dtree : ReadMethods { - fn with_tree_fields(d: &dummy, f: fn(&Tree) -> R) -> R { + impl dtree : ReadMethods<@dummy> { + fn with_tree_fields(d: &@dummy, f: fn(&Tree<@dummy>) -> R) -> R { f(&d.fields) } } - impl dtree : WriteMethods { - fn with_tree_fields(d: &dummy, f: fn(&Tree) -> R) -> R { + impl dtree : WriteMethods<@dummy> { + fn with_tree_fields(d: &@dummy, f: fn(&Tree<@dummy>) -> R) -> R { f(&d.fields) } + pure fn eq(a: &@dummy, b: &@dummy) -> bool { core::box::ptr_eq(*a, *b) } } - fn new_dummy(v: uint) -> dummy { - dummy(@{fields: empty(), value: v}) + fn new_dummy(v: uint) -> @dummy { + @dummy({fields: empty(), value: v}) } - fn parent_with_3_children() -> {p: dummy, children: ~[dummy]} { + fn parent_with_3_children() -> {p: @dummy, children: ~[@dummy]} { let children = ~[new_dummy(0u), new_dummy(1u), new_dummy(2u)]; @@ -135,4 +190,54 @@ mod test { } assert i == 1u; } + + #[test] + fn remove_first_child() { + let {p, children} = parent_with_3_children(); + remove_child(&dtree, p, children[0]); + + let mut i = 0; + for each_child(&dtree, &p) |_c| { + i += 1; + } + assert i == 2; + } + + #[test] + fn remove_last_child() { + let {p, children} = parent_with_3_children(); + remove_child(&dtree, p, children[2]); + + let mut i = 0; + for each_child(&dtree, &p) |_c| { + i += 1; + } + assert i == 2; + } + + #[test] + fn remove_middle_child() { + let {p, children} = parent_with_3_children(); + remove_child(&dtree, p, children[1]); + + let mut i = 0; + for each_child(&dtree, &p) |_c| { + i += 1; + } + assert i == 2; + } + + #[test] + fn remove_all_child() { + let {p, children} = parent_with_3_children(); + remove_child(&dtree, p, children[0]); + remove_child(&dtree, p, children[1]); + remove_child(&dtree, p, children[2]); + + let mut i = 0; + for each_child(&dtree, &p) |_c| { + i += 1; + } + assert i == 0; + } }