servo: Start work on new DOM representation
authorPatrick Walton <pcwalton@mimiga.net>
Thu, 14 Feb 2013 19:13:56 -0800
changeset 380002 3bf8e6ea833ec5bf4abcbd0f92ca01af8920c897
parent 380001 77b7ea9bc4b1372f677f44626d51aedeb454969d
child 380003 66cf21ddfa73149771a6e341b9c601f08528ea5d
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
servo: Start work on new DOM representation Source-Repo: https://github.com/servo/servo Source-Revision: 1b030480abdf9c7a36a5dc0bea59e6f6aae63082
servo/src/servo/content/content_task.rs
servo/src/servo/css/matching.rs
servo/src/servo/css/node_style.rs
servo/src/servo/css/node_util.rs
servo/src/servo/css/node_void_ptr.rs
servo/src/servo/css/select_handler.rs
servo/src/servo/dom/bindings/document.rs
servo/src/servo/dom/bindings/element.rs
servo/src/servo/dom/bindings/node.rs
servo/src/servo/dom/cow.rs
servo/src/servo/dom/document.rs
servo/src/servo/dom/element.rs
servo/src/servo/dom/node.rs
servo/src/servo/html/hubbub_html_parser.rs
servo/src/servo/layout/aux.rs
servo/src/servo/layout/box.rs
servo/src/servo/layout/box_builder.rs
servo/src/servo/layout/display_list_builder.rs
servo/src/servo/layout/flow.rs
servo/src/servo/layout/inline.rs
servo/src/servo/layout/layout_task.rs
servo/src/servo/servo.rc
--- a/servo/src/servo/content/content_task.rs
+++ b/servo/src/servo/content/content_task.rs
@@ -1,16 +1,16 @@
 /*!
 The content task is the main task that runs JavaScript and spawns layout
 tasks.
 */
 
 use dom::bindings::utils::rust_box;
 use dom::document::Document;
-use dom::node::{Node, NodeScope, define_bindings};
+use dom::node::define_bindings;
 use dom::event::{Event, ResizeEvent, ReflowEvent};
 use dom::window::Window;
 use layout::layout_task;
 use layout::layout_task::{AddStylesheet, BuildData, BuildMsg, Damage, LayoutTask};
 use layout::layout_task::{MatchSelectorsDamage, NoDamage, ReflowDamage};
 use util::task::spawn_listener;
 
 use core::comm::{Port, Chan, SharedChan};
@@ -85,17 +85,16 @@ pub struct Content {
     mut layout_join_port: Option<comm::Port<()>>,
 
     image_cache_task: ImageCacheTask,
     control_port: comm::Port<ControlMsg>,
     control_chan: comm::SharedChan<ControlMsg>,
     event_port: comm::Port<Event>,
     event_chan: comm::SharedChan<Event>,
 
-    scope: NodeScope,
     jsrt: jsrt,
     cx: @Cx,
 
     mut document: Option<@Document>,
     mut window:   Option<@Window>,
     mut doc_url: Option<Url>,
     mut window_size: Size2D<uint>,
 
@@ -130,29 +129,28 @@ pub fn Content(layout_task: LayoutTask,
         layout_task: layout_task,
         layout_join_port: None,
         image_cache_task: img_cache_task,
         control_port: control_port,
         control_chan: control_chan,
         event_port: event_port,
         event_chan: event_chan,
 
-        scope: NodeScope(),
-        jsrt: jsrt,
-        cx: cx,
+        jsrt : jsrt,
+        cx : cx,
 
-        document:    None,
-        window:      None,
-        doc_url:     None,
-        window_size: Size2D(800u, 600u),
+        document    : None,
+        window      : None,
+        doc_url     : None,
+        window_size : Size2D(800u, 600u),
 
-        resource_task: resource_task,
-        compartment:   compartment,
+        resource_task : resource_task,
+        compartment : compartment,
 
-        damage: MatchSelectorsDamage,
+        damage : MatchSelectorsDamage,
     };
 
     cx.set_cx_private(ptr::to_unsafe_ptr(&*content) as *());
 
     content
 }
 
 pub fn task_from_context(cx: *JSContext) -> *Content {
@@ -179,18 +177,17 @@ impl Content {
     fn handle_control_msg(control_msg: ControlMsg) -> bool {
         match control_msg {
           ParseMsg(url) => {
             debug!("content: Received url `%s` to parse", url_to_str(&url));
 
             // Note: we can parse the next document in parallel
             // with any previous documents.
 
-            let result = html::hubbub_html_parser::parse_html(self.scope,
-                                                              copy url,
+            let result = html::hubbub_html_parser::parse_html(copy url,
                                                               self.resource_task.clone(),
                                                               self.image_cache_task.clone());
 
             let root = result.root;
 
               // Send stylesheets over to layout
               // FIXME: Need these should be streamed to layout as they are parsed
               // and do not need to stop here in the content task
@@ -201,17 +198,17 @@ impl Content {
                       }
                       None => break
                   }
               }
 
             let js_scripts = result.js_port.recv();
             debug!("js_scripts: %?", js_scripts);
 
-            let document = Document(root, self.scope);
+            let document = Document(root);
             let window   = Window(self.control_chan.clone());
 
             self.damage.add(MatchSelectorsDamage);
             self.relayout(&document, &url);
 
             self.document = Some(@document);
             self.window   = Some(@window);
             self.doc_url = Some(url);
@@ -267,35 +264,29 @@ impl Content {
           }
         }
     }
 
     /**
        Sends a ping to layout and waits for the response (i.e., it has finished any
        pending layout request messages).
     */
-    fn join_layout() {
-        assert self.scope.is_reader_forked() == self.layout_join_port.is_some();
-
-        if self.scope.is_reader_forked() {
-
+    fn join_layout(&self) {
+        if self.layout_join_port.is_some() {
             let join_port = replace(&mut self.layout_join_port, None);
-
             match join_port {
                 Some(ref join_port) => {
                     if !join_port.peek() {
                         warn!("content: waiting on layout");
                     }
                     join_port.recv();
                     debug!("content: layout joined");
                 }
                 None => fail!(~"reader forked but no join port?")
             }
-
-            self.scope.reader_joined();
         }
     }
 
     /**
        This method will wait until the layout task has completed its current action,
        join the layout task, and then request a new layout run. It won't wait for the
        new layout computation to finish.
     */
@@ -307,31 +298,27 @@ impl Content {
         self.join_layout();
 
         // Layout will let us know when it's done
         let (join_port, join_chan) = comm::stream();
         self.layout_join_port = Some(join_port);
 
         // Send new document and relevant styles to layout
 
-        let data = BuildData {
+        let data = ~BuildData {
             node: document.root,
             url: copy *doc_url,
             dom_event_chan: self.event_chan.clone(),
             window_size: self.window_size,
             content_join_chan: join_chan,
             damage: replace(&mut self.damage, NoDamage),
         };
 
         self.layout_task.send(BuildMsg(data));
 
-        // Indicate that reader was forked so any further
-        // changes will be isolated.
-        self.scope.reader_forked();
-
         debug!("content: layout forked");
     }
 
      fn query_layout(query: layout_task::LayoutQuery) -> layout_task::LayoutQueryResponse {
          self.relayout(self.document.get(), &(copy self.doc_url).get());
          self.join_layout();
 
          let (response_port, response_chan) = comm::stream();
--- a/servo/src/servo/css/matching.rs
+++ b/servo/src/servo/css/matching.rs
@@ -1,69 +1,57 @@
 // High-level interface to CSS selector matching.
 
 use css::node_util::NodeUtil;
 use css::select_handler::NodeSelectHandler;
-use dom::node::{Node, NodeTree};
+use dom::node::AbstractNode;
 use layout::context::LayoutContext;
 use newcss::complete::CompleteSelectResults;
 use newcss::select::{SelectCtx, SelectResults};
 
 use std::arc::{ARC, get, clone};
 
 pub trait MatchMethods {
-    fn restyle_subtree(select_ctx: &SelectCtx);
+    fn restyle_subtree(&self, select_ctx: &SelectCtx);
 }
 
-impl MatchMethods for Node {
+impl MatchMethods for AbstractNode {
     /**
      * Performs CSS selector matching on a subtree.
-
+     *
      * This is, importantly, the function that updates the layout data for
      * the node (the reader-auxiliary box in the COW model) with the
      * computed style.
      */
-    fn restyle_subtree(select_ctx: &SelectCtx) {
-
+    fn restyle_subtree(&self, select_ctx: &SelectCtx) {
         // Only elements have styles
         if self.is_element() {
-            let select_handler = NodeSelectHandler {
-                node: self
-            };
-            let incomplete_results = select_ctx.select_style(&self, &select_handler);
+            let select_handler = NodeSelectHandler { node: *self };
+            let incomplete_results = select_ctx.select_style(self, &select_handler);
             // Combine this node's results with its parent's to resolve all inherited values
-            let complete_results = compose_results(&self, incomplete_results);
+            let complete_results = compose_results(*self, incomplete_results);
             self.set_css_select_results(complete_results);
         }
 
-        let mut i = 0u;
-        
-        for NodeTree.each_child(&self) |kid| {
-            i = i + 1u;
+        for self.each_child |kid| {
             kid.restyle_subtree(select_ctx); 
         }
     }
 }
 
-fn compose_results(node: &Node, results: SelectResults) -> CompleteSelectResults {
+fn compose_results(node: AbstractNode, results: SelectResults) -> CompleteSelectResults {
     match find_parent_element_node(node) {
         None => CompleteSelectResults::new_root(results),
         Some(parent_node) => {
             let parent_results = parent_node.get_css_select_results();
             CompleteSelectResults::new_from_parent(parent_results, results)
         }
     }    
 }
 
-fn find_parent_element_node(node: &Node) -> Option<Node> {
-    use util::tree::parent;
-
-    match parent(&NodeTree, node) {
-        Some(ref parent) => {
-            if parent.is_element() {
-                Some(*parent)
-            } else {
-                find_parent_element_node(parent)
-            }
-        }
-        None => None
+fn find_parent_element_node(node: AbstractNode) -> Option<AbstractNode> {
+    match node.parent_node() {
+        Some(parent) if parent.is_element() => Some(parent),
+        Some(parent) => find_parent_element_node(parent),
+        None => None,
     }
 }
+
--- a/servo/src/servo/css/node_style.rs
+++ b/servo/src/servo/css/node_style.rs
@@ -1,18 +1,18 @@
 // Style retrieval from DOM elements.
 
 use css::node_util::NodeUtil;
-use dom::node::Node;
+use dom::node::AbstractNode;
 use newcss::complete::CompleteStyle;
 
 /// Node mixin providing `style` method that returns a `NodeStyle`
 pub trait StyledNode {
     fn style(&self) -> CompleteStyle/&self;
 }
 
-impl StyledNode for Node {
+impl StyledNode for AbstractNode {
     fn style(&self) -> CompleteStyle/&self {
         assert self.is_element(); // Only elements can have styles
         let results = self.get_css_select_results();
         results.computed_style()
     }
 }
--- a/servo/src/servo/css/node_util.rs
+++ b/servo/src/servo/css/node_util.rs
@@ -1,39 +1,38 @@
-use dom::node::Node;
+use dom::node::AbstractNode;
 use newcss::complete::CompleteSelectResults;
-use std::cell::Cell;
+
+use core::cast::transmute;
 
 pub trait NodeUtil {
-    fn get_css_select_results() -> &self/CompleteSelectResults;
-    fn set_css_select_results(decl : CompleteSelectResults);
+    fn get_css_select_results(self) -> &self/CompleteSelectResults;
+    fn set_css_select_results(self, decl: CompleteSelectResults);
 }
 
-impl NodeUtil for Node {
+impl NodeUtil for AbstractNode {
     /** 
      * Provides the computed style for the given node. If CSS selector
      * Returns the style results for the given node. If CSS selector
      * matching has not yet been performed, fails.
      * FIXME: This isn't completely memory safe since the style is
      * stored in a box that can be overwritten
      */
-    fn get_css_select_results() -> &self/CompleteSelectResults {
-        if !self.has_aux() {
+    fn get_css_select_results(self) -> &self/CompleteSelectResults {
+        if !self.has_layout_data() {
             fail!(~"style() called on a node without aux data!");
         }
-        unsafe { &*self.aux( |x| {
-            match x.style {
-                Some(ref style) => ptr::to_unsafe_ptr(style),
-                None => fail!(~"style() called on node without a style!")
-            }
-        })}
+
+        match self.layout_data().style {
+            None => fail!(~"style() called on node without a style!"),
+            Some(ref style) => unsafe { transmute(style) }
+        }
     }
 
-    /**
-    Update the computed style of an HTML element with a style specified by CSS.
-    */
-    fn set_css_select_results(decl : CompleteSelectResults) {
-        let decl = Cell(decl);
-        do self.aux |data| {
-            data.style = Some(decl.take())
+    /// Update the computed style of an HTML element with a style specified by CSS.
+    fn set_css_select_results(self, decl: CompleteSelectResults) {
+        if !self.has_layout_data() {
+            fail!(~"set_css_select_results() called on a node without aux data!");
         }
+
+        self.layout_data().style = Some(decl);
     }
 }
--- a/servo/src/servo/css/node_void_ptr.rs
+++ b/servo/src/servo/css/node_void_ptr.rs
@@ -1,19 +1,24 @@
 //! CSS library requires that DOM nodes be convertable to *c_void through this trait
 
-use dom::node::Node;
+use dom::node::AbstractNode;
+
+use core::cast;
 
 // FIXME: Rust #3908. rust-css can't reexport VoidPtrLike
 extern mod netsurfcss;
 use css::node_void_ptr::netsurfcss::util::VoidPtrLike;
 
-impl VoidPtrLike for Node {
-    static fn from_void_ptr(node: *libc::c_void) -> Node {
+impl VoidPtrLike for AbstractNode {
+    static fn from_void_ptr(node: *libc::c_void) -> AbstractNode {
         assert node.is_not_null();
-        unsafe { cast::reinterpret_cast(&node) }
+        unsafe {
+            cast::transmute(node)
+        }
     }
 
     fn to_void_ptr(&self) -> *libc::c_void {
-        let node: *libc::c_void = unsafe { cast::reinterpret_cast(self) };
-        node
+        unsafe {
+            cast::transmute(*self)
+        }
     }
 }
--- a/servo/src/servo/css/select_handler.rs
+++ b/servo/src/servo/css/select_handler.rs
@@ -1,98 +1,94 @@
-use dom::node::{Node, NodeData, NodeTree, Doctype, Comment, Element, Text};
+///
+/// Implementation of the callbacks that the CSS selector engine uses to query the DOM.
+///
+
+use dom::node::AbstractNode;
 use newcss::select::SelectHandler;
-use util::tree;
+
+use core::str::eq_slice;
 
 pub struct NodeSelectHandler {
-    node: Node
+    node: AbstractNode
 }
 
-fn with_node_name<R>(data: &NodeData, f: &fn(&str) -> R) -> R {
-    match *data.kind {
-        Element(ref data) => f(data.tag_name),
-        _ => fail!(~"attempting to style non-element node")
+fn with_node_name<R>(node: AbstractNode, f: &fn(&str) -> R) -> R {
+    if !node.is_element() {
+        fail!(~"attempting to style non-element node");
+    }
+    do node.with_imm_element |element_n| {
+        f(element_n.tag_name)
     }
 }
 
-impl SelectHandler<Node> for NodeSelectHandler {
-    fn with_node_name<R>(node: &Node, f: &fn(&str) -> R) -> R {
-        do node.read |data| {
-            with_node_name(data, f)
-        }
+impl SelectHandler<AbstractNode> for NodeSelectHandler {
+    fn with_node_name<R>(node: &AbstractNode, f: &fn(&str) -> R) -> R {
+        with_node_name(*node, f)
     }
 
-    fn named_parent_node(node: &Node, name: &str) -> Option<Node> {
-        let parent = tree::parent(&NodeTree, node);
-        match parent {
+    fn named_parent_node(node: &AbstractNode, name: &str) -> Option<AbstractNode> {
+        match node.parent_node() {
             Some(parent) => {
-                do parent.read |data| {
-                    do with_node_name(data) |node_name| {
-                        if name == node_name {
-                            Some(parent)
-                        } else {
-                            None
-                        }
+                do with_node_name(parent) |node_name| {
+                    if eq_slice(name, node_name) {
+                        Some(parent)
+                    } else {
+                        None
                     }
                 }
             }
             None => None
         }
     }
 
-    fn parent_node(node: &Node) -> Option<Node> {
-        tree::parent(&NodeTree, node)
+    fn parent_node(node: &AbstractNode) -> Option<AbstractNode> {
+        node.parent_node()
     }
 
     // TODO: Use a Bloom filter.
-    fn named_ancestor_node(node: &Node, name: &str) -> Option<Node> {
+    fn named_ancestor_node(node: &AbstractNode, name: &str) -> Option<AbstractNode> {
         let mut node = *node;
         loop {
-            let parent = tree::parent(&NodeTree, &node);
+            let parent = node.parent_node();
             match parent {
                 Some(parent) => {
                     let mut found = false;
-                    do parent.read |data| {
-                        do with_node_name(data) |node_name| {
-                            if name == node_name {
-                                found = true;
-                            }
+                    do with_node_name(parent) |node_name| {
+                        if eq_slice(name, node_name) {
+                            found = true;
                         }
                     }
                     if found {
                         return Some(parent);
                     }
                     node = parent;
                 }
                 None => return None
             }
         }
     }
 
-    fn node_is_root(node: &Node) -> bool {
+    fn node_is_root(node: &AbstractNode) -> bool {
         self.parent_node(node).is_none()
     }
 
-    fn with_node_id<R>(node: &Node, f: &fn(Option<&str>) -> R) -> R {
-        do node.read |data| {
-            match *data.kind {
-                Element(ref data) => data.with_attr("id", f),
-                _ => fail!(~"attempting to style non-element node")
-            }
+    fn with_node_id<R>(node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R {
+        if !node.is_element() {
+            fail!(~"attempting to style non-element node");
+        }
+        do node.with_imm_element() |element_n| {
+            f(element_n.get_attr("id"))
         }
     }
 
-    fn node_has_id(node: &Node, id: &str) -> bool {
-        do node.read |data| {
-            match *data.kind {
-                Element(ref data) => {
-                    do data.with_attr("id") |existing_id_opt| {
-                        match existing_id_opt {
-                            None => false,
-                            Some(existing_id) => str::eq_slice(id, existing_id)
-                        }
-                    }
-                }
-                _ => fail!(~"attempting to style non-element node")
+    fn node_has_id(node: &AbstractNode, id: &str) -> bool {
+        if !node.is_element() {
+            fail!(~"attempting to style non-element node");
+        }
+        do node.with_imm_element |element_n| {
+            match element_n.get_attr("id") {
+                None => false,
+                Some(existing_id) => id == existing_id
             }
         }
     }
 }
--- a/servo/src/servo/dom/bindings/document.rs
+++ b/servo/src/servo/dom/bindings/document.rs
@@ -59,45 +59,50 @@ enum Element = int;
     unsafe {
         let uri = (*unwrap(JS_THIS_OBJECT(cx, vp))).payload.getDocumentURI();
         JS_SET_RVAL(cx, vp, domstring_to_jsval(cx, uri));
     }
     return 1;
 }*/
 
 extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+    /*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let box = unwrap(obj);
         let node = (*box).payload.root;
         let scope = (*box).payload.scope;
         *vp = RUST_OBJECT_TO_JSVAL(node::create(cx, node, scope).ptr);
         return 1;
-    }
+    }*/
+    return 1;
 }
 
 unsafe fn unwrap(obj: *JSObject) -> *rust_box<Document> {
     //TODO: some kind of check if this is a Document object
     let val = JS_GetReservedSlot(obj, 0);
     cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val))
 }
 
 extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
+    /*
     debug!("document finalize!");
     unsafe {
         let val = JS_GetReservedSlot(obj, 0);
         let _doc: @Document = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
     }
+    */
 }
 
 pub fn init(compartment: @mut Compartment, doc: @Document) {
+    /*
     let obj = utils::define_empty_prototype(~"Document", None, compartment);
 
     let attrs = @~[
         JSPropertySpec {
          name: compartment.add_name(~"documentElement"),
          tinyid: 0,
          flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
          getter: JSPropertyOpWrapper {op: getDocumentElement, info: null()},
@@ -123,9 +128,10 @@ pub fn init(compartment: @mut Compartmen
         let raw_ptr: *libc::c_void = cast::reinterpret_cast(&squirrel_away(doc));
         JS_SetReservedSlot(instance.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr));
     }
 
     compartment.define_property(~"document", RUST_OBJECT_TO_JSVAL(instance.ptr),
                                 GetJSClassHookStubPointer(PROPERTY_STUB) as *u8,
                                 GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8,
                                 JSPROP_ENUMERATE);
+    */
 }
--- a/servo/src/servo/dom/bindings/element.rs
+++ b/servo/src/servo/dom/bindings/element.rs
@@ -1,41 +1,40 @@
-use js::rust::{Compartment, jsobj};
-use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL,
-            JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS};
-use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec,
-                JSPropertyOpWrapper, JSStrictPropertyOpWrapper};
-use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError,
-                            JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN,
-                            JS_DefineFunctions, JS_DefineProperty};
-use js::jsapi::bindgen::*;
-use js::glue::bindgen::*;
-use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
-
 use content::content_task::{Content, task_from_context};
-use layout::layout_task;
-use dom::node::{Node, NodeScope, Element};
-use dom::element::*;
-use dom::bindings::node::NodeBundle;
+use dom::bindings::node::{NodeBundle, unwrap};
 use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval};
 use dom::bindings::utils::{str};
-use libc::c_uint;
-use ptr::null;
-use dom::bindings::node::unwrap;
+use dom::element::*;
+use dom::node::{Node, Element};
+use layout::layout_task;
 use super::utils;
 
+use core::libc::c_uint;
+use core::ptr::null;
+use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
+use js::glue::bindgen::*;
+use js::jsapi::bindgen::*;
+use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec};
+use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper};
+use js::rust::{Compartment, jsobj};
+use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL};
+use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS};
+
 extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
+/*
     debug!("element finalize!");
     unsafe {
         let val = JS_GetReservedSlot(obj, 0);
         let _node: ~NodeBundle = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
     }
+    */
 }
 
 pub fn init(compartment: @mut Compartment) {
+/*
     let obj = utils::define_empty_prototype(~"Element", Some(~"Node"), compartment);
     let attrs = @~[
         JSPropertySpec {
          name: compartment.add_name(~"tagName"),
          tinyid: 0,
          flags: (JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS) as u8,
          getter: JSPropertyOpWrapper {op: getTagName, info: null()},
          setter: JSStrictPropertyOpWrapper {op: null(), info: null()}},
@@ -69,21 +68,22 @@ pub fn init(compartment: @mut Compartmen
          tinyid: 0,
          flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
          getter: JSPropertyOpWrapper {op: null(), info: null()},
          setter: JSStrictPropertyOpWrapper {op: null(), info: null()}}];
     vec::push(&mut compartment.global_props, attrs);
     vec::as_imm_buf(*attrs, |specs, _len| {
         JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
     });
+    */
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal)
-    -> JSBool {
+extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+    /*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let bundle = unwrap(obj);
         let node = (*bundle).payload.node;
@@ -104,22 +104,23 @@ extern fn HTMLImageElement_getWidth(cx: 
                     }
                 }
                 _ => fail!(~"why is this not an element?")
             }
         });
         *vp = RUST_INT_TO_JSVAL(
                 (width & (i32::max_value as int)) as libc::c_int);
         return 1;
-    }
+    }*/
+    return 0;
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal)
-    -> JSBool {
+extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+    /*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let bundle = unwrap(obj);
         do (*bundle).payload.scope.write(&(*bundle).payload.node) |nd| {
@@ -132,22 +133,23 @@ extern fn HTMLImageElement_setWidth(cx: 
                         }
                         _ => fail!(~"why is this not an image element?")
                     }
                 }
                 _ => fail!(~"why is this not an element?")
             }
         };
         return 1;
-    }
+    }*/
+    return 0;
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal)
-    -> JSBool {
+extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+/*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let bundle = unwrap(obj);
         do (*bundle).payload.scope.write(&(*bundle).payload.node) |nd| {
@@ -157,22 +159,23 @@ extern fn getTagName(cx: *JSContext, _ar
                 *vp = domstring_to_jsval(cx, &s);
               }
               _ => {
                 //XXXjdm should probably read the spec to figure out what to do here
                 *vp = JSVAL_NULL;
               }
             }
         };
-    }
+    }*/
     return 1;
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj {
+pub fn create(cx: *JSContext, node: Node) -> jsobj {
+    /*
     let proto = scope.write(&node, |nd| {
         match &nd.kind {
           &~Element(ref ed) => {
             match &ed.kind {
               &~HTMLDivElement(*) => ~"HTMLDivElement",
               &~HTMLHeadElement(*) => ~"HTMLHeadElement",
               &~HTMLImageElement(*) => ~"HTMLImageElement",
               &~HTMLScriptElement(*) => ~"HTMLScriptElement",
@@ -190,10 +193,12 @@ pub fn create(cx: *JSContext, node: Node
                                                                proto,
                                                                compartment.global_obj.ptr));
  
     unsafe {
         let raw_ptr: *libc::c_void =
             cast::reinterpret_cast(&squirrel_away_unique(~NodeBundle(node, scope)));
         JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr));
     }
-    return obj;
+    
+    return obj;*/
+    fail!(~"stub");
 }
--- a/servo/src/servo/dom/bindings/node.rs
+++ b/servo/src/servo/dom/bindings/node.rs
@@ -1,30 +1,32 @@
-use js::rust::{Compartment, jsobj};
-use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL,
-            JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS};
-use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec,
-                JSPropertyOpWrapper, JSStrictPropertyOpWrapper};
-use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError,
-                            JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN,
-                            JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate};
-use js::jsval::{INT_TO_JSVAL, JSVAL_TO_PRIVATE};
-use js::jsapi::bindgen::*;
-use js::glue::bindgen::*;
-
-use dom::node::{Node, NodeScope, Text, Doctype, Comment, Element};
 use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval};
 use dom::bindings::utils::{str};
-use libc::c_uint;
-use ptr::null;
+use dom::node::{AbstractNode, Node};
+use super::element;
 use super::utils;
-use super::element;
+
+use core::cast::transmute;
+use core::libc::c_uint;
+use core::ptr::null;
+use js::glue::bindgen::*;
+use js::jsapi::bindgen::*;
+use js::jsapi::bindgen::{JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate};
+use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN};
+use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError};
+use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec};
+use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper};
+use js::jsval::{INT_TO_JSVAL, JSVAL_TO_PRIVATE};
+use js::rust::{Compartment, jsobj};
+use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL};
+use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS};
 use js;
 
 pub fn init(compartment: @mut Compartment) {
+/*
     let obj = utils::define_empty_prototype(~"Node", None, compartment);
 
     let attrs = @~[
         JSPropertySpec {
          name: compartment.add_name(~"firstChild"),
          tinyid: 0,
          flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
          getter: JSPropertyOpWrapper {op: getFirstChild, info: null()},
@@ -48,50 +50,54 @@ pub fn init(compartment: @mut Compartmen
          name: null(),
          tinyid: 0,
          flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
          getter: JSPropertyOpWrapper {op: null(), info: null()},
          setter: JSStrictPropertyOpWrapper {op: null(), info: null()}}];
     vec::push(&mut compartment.global_props, attrs);
     vec::as_imm_buf(*attrs, |specs, _len| {
         JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
-    });
+    });*/
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-pub fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj {
+pub fn create(cx: *JSContext, node: Node) -> jsobj {
+    /*
     do scope.write(&node) |nd| {
         match nd.kind {
             ~Element(*) => element::create(cx, node, scope),
             ~Text(*) => fail!(~"no text node bindings yet"),
             ~Comment(*) => fail!(~"no comment node bindings yet"),
             ~Doctype(*) => fail!(~"no doctype node bindings yet")
         }
     }
+    */
+    unsafe {
+        transmute(0)
+    }
 }
 
 pub struct NodeBundle {
-    node: Node,
-    scope: NodeScope,
+    node: AbstractNode,
 }
 
-pub fn NodeBundle(n: Node, s: NodeScope) -> NodeBundle {
+pub fn NodeBundle(n: AbstractNode) -> NodeBundle {
     NodeBundle {
-        node : n,
-        scope : s
+        node: n,
     }
 }
 
 pub unsafe fn unwrap(obj: *JSObject) -> *rust_box<NodeBundle> {
     let val = js::GetReservedSlot(obj, 0);
     cast::reinterpret_cast(&JSVAL_TO_PRIVATE(val))
 }
 
 #[allow(non_implicitly_copyable_typarams)]
 extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+    /*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let bundle = unwrap(obj);
         do (*bundle).payload.scope.write(&(*bundle).payload.node) |nd| {
@@ -100,22 +106,23 @@ extern fn getFirstChild(cx: *JSContext, 
                 let obj = create(cx, n, (*bundle).payload.scope).ptr;
                 *vp = RUST_OBJECT_TO_JSVAL(obj);
               }
               None => {
                 *vp = JSVAL_NULL;
               }
             }
         };
-    }
+    }*/
     return 1;
 }
 
 #[allow(non_implicitly_copyable_typarams)]
 extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+    /*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let bundle = unwrap(obj);
         do (*bundle).payload.scope.write(&(*bundle).payload.node) |nd| {
@@ -124,38 +131,41 @@ extern fn getNextSibling(cx: *JSContext,
                 let obj = create(cx, n, (*bundle).payload.scope).ptr;
                 *vp = RUST_OBJECT_TO_JSVAL(obj);
               }
               None => {
                 *vp = JSVAL_NULL;
               }
             }
         };
-    }
+    }*/
     return 1;
 }
 
 impl NodeBundle {
     fn getNodeType() -> i32 {
+        /*
         do self.node.read |nd| {
             match nd.kind {
               ~Element(*) => 1,
               ~Text(*)    => 3,
               ~Comment(*) => 8,
               ~Doctype(*) => 10
             }
-        }
+        }*/
+        0
     }
 }
 
 extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
+    /*
     unsafe {
         let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp));
         if obj.is_null() {
             return 0;
         }
 
         let bundle = unwrap(obj);
         let nodeType = (*bundle).payload.getNodeType();
         *vp = INT_TO_JSVAL(nodeType);
-    }
+    }*/
     return 1;
 }
deleted file mode 100644
--- a/servo/src/servo/dom/cow.rs
+++ /dev/null
@@ -1,366 +0,0 @@
-/*!
-
-Implements the copy-on-write DOM-sharing model.  This model allows for
-a single writer and any number of readers, but the writer must be able
-to control and manage the lifetimes of the reader(s).  For simplicity
-I will describe the implementation as though there were a single
-reader.
-
-The basic idea is that every object in the COW pool has both a reader
-view and a writer view.  The writer always sees the writer view, which
-contains the most up-to-date values.  The reader uses the reader view,
-which contains the values as of the point where the reader was forked.
-When the writer joins the reader, the reader view will be synchronized
-with the writer view.
-
-Internally, the way this works is using a copy-on-write scheme.  Each
-COW node maintains two pointers (`read_ptr` and `write_ptr`).
-Assuming that readers are active, when a writer wants to modify a
-node, it first copies the reader's data into a new pointer.  Any
-writes that occur after that point (but before the reader is joined)
-will operate on this same copy.  When the reader is joined, any nodes
-which the writer modified will free the stale reader data and update
-the reader pointer to be the same as the writer pointer.
-
-# Using the COW APIs as a writer
-
-You must first create a `scope` object.  The scope object manages the
-memory and the COW operations.  COW'd objects of some sendable type
-`T` are not referenced directly but rather through a `handle<T>`.  To
-create a new COW object, you use `scope.handle(t)` where `t` is some
-initial value of type `T`.  To write to an COW object, use
-`scope.write()` and to read from it use `scope.read()`. Be sure not to
-use the various `ReaderMethods`.
-
-Handles can be freely sent between tasks but the COW scope cannot.  It must stay with the writer
-task.  You are responsible for correctly invoking `reader_forked()` and `reader_joined()` to keep
-the COW scope abreast of when the reader is active.  Failure to do so will lead to race conditions
-or worse.
-
-# Using the COW APIs as a reader
-
-Import the `ReaderMethods` impl.  When you receive a handle, you can
-invoke `h.read { |v| ... }` and so forth.  There is also a piece of
-auxiliary data that can be optionally associated with each handle.
-
-Note: if the type `T` contains mutable fields, then there is nothing
-to stop the reader from mutating those fields in the `read()` method.
-Do not do this.  It will lead to race conditions.
-
-FIXME: We can enforce that this is not done by ensuring that the type
-`T` contains no mutable fields.
-
-# Auxiliary data
-
-Readers can associate a piece of auxiliary data of type `A` along with
-main nodes.  This is convenient but dangerous: it is the reader's job
-to ensure that this data remains live independent of the COW nodes
-themselves.
-
-*/
-
-use core::libc::types::os::arch::c95::size_t;
-use ptr::Ptr;
-use vec::push;
-
-struct ScopeData<T,A> {
-    mut layout_active: bool,
-    mut free_list: ~[Handle<T,A>],
-    mut first_dirty: Handle<T,A>
-}
-
-struct ScopeResource<T,A> {
-    d : ScopeData<T,A>,
-
-    drop {
-        unsafe {
-            for self.d.free_list.each |h| { free_handle(*h); }
-        }
-    }
-}
-
-fn ScopeResource<T:Owned,A>(d : ScopeData<T,A>) -> ScopeResource<T,A> {
-    ScopeResource { d: d }
-}
-
-pub type Scope<T,A> = @ScopeResource<T,A>;
-
-type HandleData<T,A> = {mut read_ptr: *T,
-                        mut write_ptr: *mut T,
-                        mut read_aux: *A,
-                        mut next_dirty: Handle<T,A>};
-pub enum Handle<T,A> {
-    _Handle(*HandleData<T,A>)
-}
-
-// Private methods
-impl<T,A> Handle<T,A> {
-    fn read_ptr() -> *T            { unsafe { (**self).read_ptr }   }
-    fn write_ptr() -> *mut T       { unsafe { (**self).write_ptr }  }
-    fn read_aux() -> *A            { unsafe { (**self).read_aux }   }
-    fn next_dirty() -> Handle<T,A> { unsafe { (**self).next_dirty } }
-
-    fn set_read_ptr(t: *T)            { unsafe { (**self).read_ptr = t; }   }
-    fn set_write_ptr(t: *mut T)       { unsafe { (**self).write_ptr = t; }  }
-    fn set_read_aux(t: *A)            { unsafe { (**self).read_aux = t; }   }
-    fn set_next_dirty(h: Handle<T,A>) { unsafe { (**self).next_dirty = h; } }
-
-    pure fn is_null() -> bool { (*self).is_null() }
-    fn is_not_null() -> bool { (*self).is_not_null() }
-}
-
-impl<T:Owned,A> Handle<T,A> {
-    /// Access the reader's view of the handle's data
-    fn read<U>(f: fn(&T) -> U) -> U {
-        unsafe {
-            f(&*self.read_ptr())
-        }
-    }
-
-    /// True if auxiliary data is associated with this handle
-    fn has_aux() -> bool {
-        unsafe {
-            self.read_aux().is_not_null()
-        }
-    }
-
-    /** Set the auxiliary data associated with this handle.
-
-    **Warning:** the reader is responsible for keeping this data live!
-    */
-    fn set_aux(p: @A) {
-        unsafe {
-            (**self).read_aux = ptr::to_unsafe_ptr(&*p);
-        }
-    }
-
-    /// Access the auxiliary data associated with this handle
-    fn aux<U>(f: fn(&A) -> U) -> U {
-        unsafe {
-            assert self.has_aux();
-            f(&*self.read_aux())
-        }
-    }
-}
-
-impl<T:Owned,A> cmp::Eq for Handle<T,A> {
-    pure fn eq(&self, other: &Handle<T,A>) -> bool { **self == **other }
-    pure fn ne(&self, other: &Handle<T,A>) -> bool { **self != **other }
-}
-
-// Private methods
-impl<T: Copy + Owned,A> Scope<T,A> {
-    fn clone(v: *T) -> *T {
-        unsafe {
-            let n: *mut T =
-                cast::reinterpret_cast(&libc::calloc(sys::size_of::<T>() as size_t, 1u as size_t));
-
-            // n.b.: this assignment will run the drop glue for <T,A>. *Hopefully* the fact that
-            // everything is initialized to NULL by calloc will make this ok.  We may have to make the
-            // take glue be tolerant of this.
-            *n = unsafe{*v};
-
-            return cast::reinterpret_cast(&n);
-        }
-    }
-}
-
-unsafe fn free<T>(t: *T) {
-    let _x = *cast::reinterpret_cast::<*T,*mut T>(&t);
-    libc::free(cast::reinterpret_cast(&t));
-}
-
-unsafe fn free_handle<T,A>(h: Handle<T,A>) {
-    free(h.read_ptr());
-    if h.write_ptr() != cast::reinterpret_cast(&h.read_ptr()) {
-        free(cast::reinterpret_cast::<*mut T,*T>(&h.write_ptr()));
-    }
-}
-
-pub unsafe fn unwrap<T:Owned, A>(handle: Handle<T,A>) -> *HandleData<T,A> {
-    *handle
-}
-
-pub unsafe fn wrap<T:Owned, A>(data: *HandleData<T,A>) -> Handle<T,A> {
-    _Handle(data)
-}
-
-fn null_handle<T:Owned,A>() -> Handle<T,A> {
-    _Handle(ptr::null())
-}
-
-pub fn Scope<T:Owned,A>() -> Scope<T,A> {
-    @ScopeResource(
-        ScopeData {
-            mut layout_active: false,
-            mut free_list: ~[],
-            mut first_dirty: null_handle()
-        })
-}
-
-// Writer methods
-impl<T:Copy + Owned,A> Scope<T,A> {
-    fn is_reader_forked() -> bool {
-        self.d.layout_active
-    }
-
-    fn reader_forked() {
-        assert !self.d.layout_active;
-        assert self.d.first_dirty.is_null();
-        self.d.layout_active = true;
-    }
-
-    fn reader_joined() {
-        assert self.d.layout_active;
-
-        if (/*bad*/copy self.d.first_dirty).is_not_null() {
-            let mut handle = self.d.first_dirty;
-            while (*handle).is_not_null() {
-                unsafe {
-                    free(handle.read_ptr());
-
-                    handle.set_read_ptr(cast::reinterpret_cast(&handle.write_ptr()));
-                    let next_handle = handle.next_dirty();
-                    handle.set_next_dirty(null_handle());
-                    handle = next_handle;
-                }
-            }
-            self.d.first_dirty = null_handle();
-        }
-
-        assert self.d.first_dirty.is_null();
-        self.d.layout_active = false;
-    }
-
-    fn read<U>(h: &Handle<T,A>, f: fn(&T) -> U) -> U {
-        // Use the write_ptr, which may be more up to date than the read_ptr or may not
-        unsafe {
-            f(&*h.write_ptr())
-        }
-    }
-
-    fn write<U>(h: &Handle<T,A>, f: fn(&T) -> U) -> U {
-        unsafe {
-            let const_read_ptr = ptr::const_offset(h.read_ptr(), 0);
-            let const_write_ptr = ptr::const_offset(h.write_ptr(), 0);
-            if self.d.layout_active && const_read_ptr == const_write_ptr {
-                debug!("marking handle %? as dirty", h);
-                h.set_write_ptr(cast::reinterpret_cast(&self.clone(h.read_ptr())));
-                h.set_next_dirty(self.d.first_dirty);
-                self.d.first_dirty = *h;
-            }
-            f(&*h.write_ptr())
-        }
-    }
-
-    // FIXME: This could avoid a deep copy by taking ownership of `v`
-    #[allow(non_implicitly_copyable_typarams)]
-    fn handle(v: &T) -> Handle<T,A> {
-        unsafe {
-            debug!("vv: %?", *v);
-            let d: *HandleData<T,A> =
-                cast::reinterpret_cast(
-                    &libc::malloc(sys::size_of::<HandleData<T,A>>() as size_t));
-            (*d).read_ptr = self.clone(ptr::to_unsafe_ptr(v));
-            (*d).write_ptr = cast::reinterpret_cast(&(*d).read_ptr);
-            (*d).read_aux = ptr::null();
-            (*d).next_dirty = null_handle();
-            let h = _Handle(d);
-            push(&mut self.d.free_list, h);
-            do self.read(&h) |v| {
-                debug!("vv: %?", *v);
-            }
-            return h;
-        }
-    }
-}
-
-#[cfg(test)]
-#[allow(non_implicitly_copyable_typarams)]
-mod test {
-    use pipes::{SharedChan, stream};
-    use super::Scope;
-
-    type animal = {name: ~str, species: species};
-    enum species {
-        chicken(~chicken),
-        bull(~bull)
-    }
-    type chicken = {mut eggs_per_day:uint};
-    type bull = {mut horns:uint};
-
-    type processed = {flag: bool};
-
-    type animal_scope = Scope<animal, processed>;
-
-    #[test]
-    fn handles_get_freed() {
-        let s: animal_scope = Scope();
-        s.handle(&{name:~"henrietta", species:chicken(~{mut eggs_per_day:22u})});
-        s.handle(&{name:~"ferdinand", species:bull(~{mut horns:3u})});
-    }
-
-    fn mutate(a: &animal) {
-        match &a.species {
-          &chicken(ref c) => c.eggs_per_day += 1u,
-          &bull(ref c) => c.horns += 1u
-        }
-    }
-
-    fn read_characteristic(a: &animal) -> uint {
-        match &a.species {
-          &chicken(ref c) => c.eggs_per_day,
-          &bull(ref c) => c.horns
-        }
-    }
-
-    #[test]
-    fn interspersed_execution() {
-        let s: animal_scope = Scope();
-        let henrietta =
-            s.handle(&{name:~"henrietta",
-                      species:chicken(~{mut eggs_per_day:0u})});
-        let ferdinand =
-            s.handle(&{name:~"ferdinand",
-                      species:bull(~{mut horns:0u})});
-
-        let iter1 = 3u;
-        let iter2 = 22u;
-        let (read_port, read_chan) = stream();
-        let read_chan = SharedChan(read_chan);
-
-        // fire up a reader task
-        for uint::range(0u, iter1) |i| {
-            s.reader_forked();
-            let (wait_port, wait_chan) = stream();
-
-            let read_chan = read_chan.clone();
-            do task::spawn {
-                let read_chan = read_chan.clone();
-                for uint::range(0u, iter2) |_i| {
-                    read_chan.send(henrietta.read(read_characteristic));
-                    read_chan.send(ferdinand.read(read_characteristic));
-                    wait_port.recv();
-                }
-            }
-
-            let hrc = henrietta.read(read_characteristic);
-            assert hrc == (i * iter2);
-
-            let frc = ferdinand.read(read_characteristic);
-            assert frc == i * iter2;
-
-            for uint::range(0u, iter2) |_i| {
-                assert hrc == read_port.recv();
-                s.write(&henrietta, mutate);
-                assert frc == read_port.recv();
-                s.write(&ferdinand, mutate);
-                wait_chan.send(());
-            }
-            s.reader_joined();
-        }
-
-        assert henrietta.read(read_characteristic) == iter1 * iter2;
-        assert ferdinand.read(read_characteristic) == iter1 * iter2;
-    }
-}
--- a/servo/src/servo/dom/document.rs
+++ b/servo/src/servo/dom/document.rs
@@ -1,15 +1,14 @@
+use dom::node::AbstractNode;
 use newcss::stylesheet::Stylesheet;
-use dom::node::{NodeScope, Node};
+
 use std::arc::ARC;
 
 pub struct Document {
-    root: Node,
-    scope: NodeScope,
+    root: AbstractNode,
 }
 
-pub fn Document(root: Node, scope: NodeScope) -> Document {
+pub fn Document(root: AbstractNode) -> Document {
     Document {
-        root : root,
-        scope : scope,
+        root: root,
     }
 }
--- a/servo/src/servo/dom/element.rs
+++ b/servo/src/servo/dom/element.rs
@@ -1,118 +1,165 @@
-use core::dvec::DVec;
-use geom::size::Size2D;
+//
+// Element nodes.
+//
+
+use dom::node::{ElementNodeTypeId, Node};
+
+use core::str::eq_slice;
+use std::cell::Cell;
 use std::net::url::Url;
 
-pub struct ElementData {
-    tag_name: ~str,
-    kind: ~ElementKind,
-    attrs: DVec<~Attr>,
+pub struct Element {
+    parent: Node,
+    tag_name: ~str,     // TODO: This should be an atom, not a ~str.
+    attrs: ~[Attr],
+}
+
+#[deriving_eq]
+pub enum ElementTypeId {
+    HTMLAnchorElementTypeId,
+    HTMLAsideElementTypeId,
+    HTMLBRElementTypeId,
+    HTMLBodyElementTypeId,
+    HTMLBoldElementTypeId,
+    HTMLDivElementTypeId,
+    HTMLFontElementTypeId,
+    HTMLFormElementTypeId,
+    HTMLHRElementTypeId,
+    HTMLHeadElementTypeId,
+    HTMLHeadingElementTypeId,
+    HTMLHtmlElementTypeId,
+    HTMLImageElementTypeId,
+    HTMLInputElementTypeId,
+    HTMLItalicElementTypeId,
+    HTMLLinkElementTypeId,
+    HTMLListItemElementTypeId,
+    HTMLMetaElementTypeId,
+    HTMLOListElementTypeId,
+    HTMLOptionElementTypeId,
+    HTMLParagraphElementTypeId,
+    HTMLScriptElementTypeId,
+    HTMLSectionElementTypeId,
+    HTMLSelectElementTypeId,
+    HTMLSmallElementTypeId,
+    HTMLSpanElementTypeId,
+    HTMLStyleElementTypeId,
+    HTMLTableBodyElementTypeId,
+    HTMLTableCellElementTypeId,
+    HTMLTableElementTypeId,
+    HTMLTableRowElementTypeId,
+    HTMLTitleElementTypeId,
+    HTMLUListElementTypeId,
+    UnknownElementTypeId,
 }
 
-#[allow(non_implicitly_copyable_typarams)]
-impl ElementData {
-    fn get_attr(name: &str) -> Option<~str> {
-        let found = do self.attrs.find |attr| { name == attr.name };
-        match found {
-            Some(attr) => Some(copy attr.value),
-            None => None
+//
+// Regular old elements
+//
+
+pub struct HTMLAnchorElement    { parent: Element }
+pub struct HTMLAsideElement     { parent: Element }
+pub struct HTMLBRElement        { parent: Element }
+pub struct HTMLBodyElement      { parent: Element }
+pub struct HTMLBoldElement      { parent: Element }
+pub struct HTMLDivElement       { parent: Element }
+pub struct HTMLFontElement      { parent: Element }
+pub struct HTMLFormElement      { parent: Element }
+pub struct HTMLHRElement        { parent: Element }
+pub struct HTMLHeadElement      { parent: Element }
+pub struct HTMLHtmlElement      { parent: Element }
+pub struct HTMLInputElement     { parent: Element }
+pub struct HTMLItalicElement    { parent: Element }
+pub struct HTMLLinkElement      { parent: Element }
+pub struct HTMLListItemElement  { parent: Element }
+pub struct HTMLMetaElement      { parent: Element }
+pub struct HTMLOListElement     { parent: Element }
+pub struct HTMLOptionElement    { parent: Element }
+pub struct HTMLParagraphElement { parent: Element }
+pub struct HTMLScriptElement    { parent: Element }
+pub struct HTMLSectionElement   { parent: Element }
+pub struct HTMLSelectElement    { parent: Element }
+pub struct HTMLSmallElement     { parent: Element }
+pub struct HTMLSpanElement      { parent: Element }
+pub struct HTMLStyleElement     { parent: Element }
+pub struct HTMLTableBodyElement { parent: Element }
+pub struct HTMLTableCellElement { parent: Element }
+pub struct HTMLTableElement     { parent: Element }
+pub struct HTMLTableRowElement  { parent: Element }
+pub struct HTMLTitleElement     { parent: Element }
+pub struct HTMLUListElement     { parent: Element }
+pub struct UnknownElement       { parent: Element }
+
+//
+// Fancier elements
+//
+
+pub struct HTMLHeadingElement {
+    parent: Element,
+    level: HeadingLevel,
+}
+
+pub struct HTMLImageElement {
+    parent: Element,
+    image: Option<Url>,
+}
+
+//
+// Element methods
+//
+
+impl Element {
+    static pub fn new(type_id: ElementTypeId, tag_name: ~str) -> Element {
+        Element {
+            parent: Node::new(ElementNodeTypeId(type_id)),
+            tag_name: tag_name,
+            attrs: ~[]
         }
     }
 
-    // Gets an attribute without copying.
-    //
-    // FIXME: Should not take a closure, but we need covariant type parameters for
-    // that.
-    fn with_attr<R>(name: &str, f: &fn(Option<&str>) -> R) -> R {
-        for self.attrs.each |attr| {
-            if name == attr.name {
-                let value: &str = attr.value;
-                return f(Some(value));
+    fn get_attr(&self, name: &str) -> Option<&self/str> {
+        // FIXME: Need an each() that links lifetimes in Rust.
+        for uint::range(0, self.attrs.len()) |i| {
+            if eq_slice(self.attrs[i].name, name) {
+                let val: &str = self.attrs[i].value;
+                return Some(val);
             }
         }
-        f(None)
+        return None;
     }
 
-    fn set_attr(name: &str, value: ~str) {
-        let idx = do self.attrs.position |attr| { name == attr.name };
-        match idx {
-            Some(idx) => self.attrs.set_elt(idx, ~Attr(name.to_str(), value)),
-            None => {}
+    fn set_attr(&mut self, name: &str, value: ~str) {
+        // FIXME: We need a better each_mut in Rust; this is ugly.
+        let value_cell = Cell(value);
+        for uint::range(0, self.attrs.len()) |i| {
+            if eq_slice(self.attrs[i].name, name) {
+                self.attrs[i].value = value_cell.take();
+                return;
+            }
         }
-    }
-}
-
-pub fn ElementData(tag_name: ~str, kind: ~ElementKind) -> ElementData {
-    ElementData {
-        tag_name : tag_name,
-        kind : kind,
-        attrs : DVec(),
+        self.attrs.push(Attr::new(name.to_str(), value_cell.take()));
     }
 }
 
 pub struct Attr {
     name: ~str,
     value: ~str,
 }
 
-pub fn Attr(name: ~str, value: ~str) -> Attr {
-    Attr {
-        name : name,
-        value : value,
+impl Attr {
+    static pub fn new(name: ~str, value: ~str) -> Attr {
+        Attr {
+            name: name,
+            value: value
+        }
     }
 }
 
-pub fn HTMLImageData() -> HTMLImageData {
-    HTMLImageData {
-        image: None
-    }
-}
-
-pub struct HTMLImageData {
-    mut image: Option<Url>
-}
-
 pub enum HeadingLevel {
     Heading1,
     Heading2,
     Heading3,
     Heading4,
     Heading5,
     Heading6,
 }
 
-pub enum ElementKind {
-    HTMLAnchorElement,
-    HTMLAsideElement,
-    HTMLBRElement,
-    HTMLBodyElement,
-    HTMLBoldElement,
-    HTMLDivElement,
-    HTMLFontElement,
-    HTMLFormElement,
-    HTMLHRElement,
-    HTMLHeadElement,
-    HTMLHeadingElement(HeadingLevel),
-    HTMLHtmlElement,
-    HTMLImageElement(HTMLImageData),
-    HTMLInputElement,
-    HTMLItalicElement,
-    HTMLLinkElement,
-    HTMLListItemElement,
-    HTMLMetaElement,
-    HTMLOListElement,
-    HTMLOptionElement,
-    HTMLParagraphElement,
-    HTMLScriptElement,
-    HTMLSectionElement,
-    HTMLSelectElement,
-    HTMLSmallElement,
-    HTMLSpanElement,
-    HTMLStyleElement,
-    HTMLTableBodyElement,
-    HTMLTableCellElement,
-    HTMLTableElement,
-    HTMLTableRowElement,
-    HTMLTitleElement,
-    HTMLUListElement,
-    UnknownElement,
-}
--- a/servo/src/servo/dom/node.rs
+++ b/servo/src/servo/dom/node.rs
@@ -1,185 +1,373 @@
-/* The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. */
-use newcss::complete::CompleteSelectResults;
+//
+// The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
+//
+
 use dom::bindings;
 use dom::document::Document;
-use dom::element::{Attr, ElementData};
+use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId};
+use dom::element::{HTMLStyleElementTypeId};
 use dom::window::Window;
-use geom::size::Size2D;
-use js::crust::*;
-use js::glue::bindgen::RUST_OBJECT_TO_JSVAL;
-use js::jsapi::{JSClass, JSObject, JSPropertySpec, JSContext, jsid, JSVal, JSBool};
-use js::rust::Compartment;
-use js::{JSPROP_ENUMERATE, JSPROP_SHARED};
 use layout::debug::DebugMethods;
 use layout::flow::FlowContext;
-use ptr::null;
+use newcss::complete::CompleteSelectResults;
+
+use core::cast::transmute;
+use core::ptr::null;
+use geom::size::Size2D;
+use js::crust::*;
+use js::rust::Compartment;
 use std::arc::ARC;
-use util::tree;
-use super::cow;
+
+//
+// The basic Node structure
+//
+
+/// This is what a Node looks like if you do not know what kind of node it is. To unpack it, use
+/// downcast().
+///
+/// FIXME: This should be replaced with a trait once they can inherit from structs.
+pub struct AbstractNode {
+    priv obj: *mut Node
+}
+
+impl Eq for AbstractNode {
+    pure fn eq(&self, other: &AbstractNode) -> bool { self.obj == other.obj }
+    pure fn ne(&self, other: &AbstractNode) -> bool { self.obj != other.obj }
+}
+
+pub struct Node {
+    type_id: NodeTypeId,
 
-pub struct NodeData {
-    tree: tree::Tree<Node>,
-    kind: ~NodeKind,
+    parent_node: Option<AbstractNode>,
+    first_child: Option<AbstractNode>,
+    last_child: Option<AbstractNode>,
+    next_sibling: Option<AbstractNode>,
+    prev_sibling: Option<AbstractNode>,
+
+    // You must not touch this if you are not layout.
+    priv layout_data: Option<@mut LayoutData>
+}
+
+#[deriving_eq]
+pub enum NodeTypeId {
+    DoctypeNodeTypeId,
+    CommentNodeTypeId,
+    ElementNodeTypeId(ElementTypeId),
+    TextNodeTypeId,
+}
+
+//
+// Auxiliary layout data
+//
+
+pub struct LayoutData {
+    style: Option<CompleteSelectResults>,
+    flow: Option<@FlowContext>,
 }
 
-/* The tree holding Nodes (read-only) */
-pub enum NodeTree { NodeTree }
+impl LayoutData {
+    static pub fn new() -> LayoutData {
+        LayoutData {
+            style: None,
+            flow: None,
+        }
+    }
+}
+
+//
+// Basic node types
+//
+
+pub struct Doctype {
+    parent: Node,
+    name: ~str,
+    public_id: Option<~str>,
+    system_id: Option<~str>,
+    force_quirks: bool
+}
 
-impl NodeTree {
-    fn each_child(node: &Node, f: fn(&Node) -> bool) {
-        tree::each_child(&self, node, f)
+impl Doctype {
+    static pub fn new(name: ~str,
+                      public_id: Option<~str>,
+                      system_id: Option<~str>,
+                      force_quirks: bool)
+                   -> Doctype {
+        Doctype {
+            parent: Node::new(DoctypeNodeTypeId),
+            name: name,
+            public_id: public_id,
+            system_id: system_id,
+            force_quirks: force_quirks,
+        }
     }
+}
 
-    fn get_parent(node: &Node) -> Option<Node> {
-        tree::get_parent(&self, node)
+pub struct Comment {
+    parent: Node,
+    text: ~str,
+}
+
+impl Comment {
+    static pub fn new(text: ~str) -> Comment {
+        Comment {
+            parent: Node::new(CommentNodeTypeId),
+            text: text
+        }
+    }
+}
+
+pub struct Text {
+    parent: Node,
+    text: ~str,
+}
+
+impl Text {
+    static pub fn new(text: ~str) -> Text {
+        Text {
+            parent: Node::new(CommentNodeTypeId),
+            text: text
+        }
     }
 }
 
-impl tree::ReadMethods<Node> for NodeTree {
-    fn with_tree_fields<R>(n: &Node, f: fn(&tree::Tree<Node>) -> R) -> R {
-        n.read(|n| f(&n.tree))
+impl AbstractNode {
+    //
+    // Convenience accessors
+    //
+    // FIXME: Fold these into util::tree.
+
+    fn type_id(self)         -> NodeTypeId           { self.with_imm_node(|n| n.type_id)      }
+    fn parent_node(self)     -> Option<AbstractNode> { self.with_imm_node(|n| n.parent_node)  }
+    fn first_child(self)     -> Option<AbstractNode> { self.with_imm_node(|n| n.first_child)  }
+    fn last_child(self)      -> Option<AbstractNode> { self.with_imm_node(|n| n.last_child)   }
+    fn prev_sibling(self)    -> Option<AbstractNode> { self.with_imm_node(|n| n.prev_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
+    // ensure this.
+    fn layout_data(self)     -> @mut LayoutData {
+        self.with_imm_node(|n| n.layout_data.get())
+    }
+    fn has_layout_data(self) -> bool {
+        self.with_imm_node(|n| n.layout_data.is_some())
+    }
+    fn set_layout_data(self, data: @mut LayoutData) {
+        self.with_mut_node(|n| n.layout_data = Some(data))
+    }
+
+    //
+    // Tree operations
+    //
+    // FIXME: Fold this into util::tree.
+    //
+
+    fn is_leaf(self) -> bool { self.first_child().is_none() }
+
+    // Invariant: `child` is disconnected from the document.
+    fn append_child(self, child: AbstractNode) {
+        assert self != child;
+
+        do self.with_mut_node |parent_n| {
+            do child.with_mut_node |child_n| {
+                assert child_n.parent_node.is_none();
+                assert child_n.prev_sibling.is_none();
+                assert child_n.next_sibling.is_none();
+
+                child_n.parent_node = Some(self);
+
+                match parent_n.last_child {
+                    None => parent_n.first_child = Some(child),
+                    Some(last_child) => {
+                        do last_child.with_mut_node |last_child_n| {
+                            assert last_child_n.next_sibling.is_none();
+                            last_child_n.next_sibling = Some(child);
+                        }
+                    }
+                }
+
+                child_n.prev_sibling = parent_n.last_child;
+            }
+        }
+    }
+
+    //
+    // Tree traversal
+    //
+    // FIXME: Fold this into util::tree.
+    //
+
+    fn each_child(self, f: &fn(AbstractNode) -> bool) {
+        let mut current_opt = self.first_child();
+        while !current_opt.is_none() {
+            let current = current_opt.get();
+            if !f(current) {
+                break;
+            }
+            current_opt = current.next_sibling();
+        }
+    }
+
+    fn traverse_preorder(self, f: &fn(AbstractNode) -> bool) -> bool {
+        if !f(self) {
+            return false;
+        }
+        for self.each_child |kid| {
+            if !f(kid) {
+                return false;
+            }
+        }
+        true
+    }
+
+    fn traverse_postorder(self, f: &fn(AbstractNode) -> bool) -> bool {
+        for self.each_child |kid| {
+            if !f(kid) {
+                return false;
+            }
+        }
+        f(self)
+    }
+
+    //
+    // Downcasting borrows
+    //
+
+    fn with_imm_node<R>(self, f: &fn(&Node) -> R) -> R {
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    fn with_mut_node<R>(self, f: &fn(&mut Node) -> R) -> R {
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    fn is_text(self) -> bool { self.type_id() == TextNodeTypeId }
+
+    // FIXME: This should be doing dynamic borrow checking for safety.
+    fn with_imm_text<R>(self, f: &fn(&Text) -> R) -> R {
+        if !self.is_text() {
+            fail!(~"node is not text");
+        }
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    fn is_element(self) -> bool {
+        match self.type_id() {
+            ElementNodeTypeId(*) => true,
+            _ => false
+        }
+    }
+
+    // FIXME: This should be doing dynamic borrow checking for safety.
+    fn with_imm_element<R>(self, f: &fn(&Element) -> R) -> R {
+        if !self.is_element() {
+            fail!(~"node is not an element");
+        }
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    // FIXME: This should be doing dynamic borrow checking for safety.
+    fn as_mut_element<R>(self, f: &fn(&mut Element) -> R) -> R {
+        if !self.is_element() {
+            fail!(~"node is not an element");
+        }
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    fn is_image_element(self) -> bool {
+        self.type_id() == ElementNodeTypeId(HTMLImageElementTypeId)
+    }
+
+    fn with_imm_image_element<R>(self, f: &fn(&HTMLImageElement) -> R) -> R {
+        if !self.is_image_element() {
+            fail!(~"node is not an image element");
+        }
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    fn with_mut_image_element<R>(self, f: &fn(&mut HTMLImageElement) -> R) -> R {
+        if !self.is_image_element() {
+            fail!(~"node is not an image element");
+        }
+        unsafe {
+            f(transmute(self.obj))
+        }
+    }
+
+    fn is_style_element(self) -> bool {
+        self.type_id() == ElementNodeTypeId(HTMLStyleElementTypeId)
     }
 }
 
-impl Node {
-    fn traverse_preorder(preorder_cb: &fn(Node)) {
-        preorder_cb(self);
-        do NodeTree.each_child(&self) |child| { child.traverse_preorder(preorder_cb); true }
+impl DebugMethods for AbstractNode {
+    // Dumps the subtree rooted at this node, for debugging.
+    pure fn dump(&self) {
+        self.dump_indent(0);
     }
 
-    fn traverse_postorder(postorder_cb: &fn(Node)) {
-        do NodeTree.each_child(&self) |child| { child.traverse_postorder(postorder_cb); true }
-        postorder_cb(self);
-    }
-}
-
-impl DebugMethods for Node {
-    /* Dumps the subtree rooted at this node, for debugging. */
-    pure fn dump(&self) {
-        self.dump_indent(0u);
-    }
-    /* Dumps the node tree, for debugging, with indentation. */
+    // Dumps the node tree, for debugging, with indentation.
     pure fn dump_indent(&self, indent: uint) {
         let mut s = ~"";
         for uint::range(0u, indent) |_i| {
             s += ~"    ";
         }
 
         s += self.debug_str();
         debug!("%s", s);
 
         // FIXME: this should have a pure version?
         unsafe {
-            for NodeTree.each_child(self) |kid| {
+            for self.each_child() |kid| {
                 kid.dump_indent(indent + 1u) 
             }
         }
     }
 
     pure fn debug_str(&self) -> ~str {
+        // Unsafe due to the call to type_id().
         unsafe {
-            do self.read |n| { fmt!("%?", n.kind) }
+            fmt!("%?", self.type_id())
         }
     }
 }
 
 impl Node {
-    fn is_element(&self) -> bool {
-        self.read(|n| match *n.kind { Element(*) => true, _ => false } )
+    static pub unsafe fn as_abstract_node<N>(node: ~N) -> AbstractNode {
+        // This surrenders memory management of the node!
+        AbstractNode {
+            obj: transmute(node)
+        }
+    }
+
+    static pub fn new(type_id: NodeTypeId) -> Node {
+        Node {
+            type_id: type_id,
+
+            parent_node: None,
+            first_child: None,
+            last_child: None,
+            next_sibling: None,
+            prev_sibling: None,
+
+            layout_data: None,
+        }
     }
 }
 
-pub enum NodeKind {
-    Doctype(DoctypeData),
-    Comment(~str),
-    Element(ElementData),
-    Text(~str)
-}
-
-pub struct DoctypeData {
-    name: ~str,
-    public_id: Option<~str>,
-    system_id: Option<~str>,
-    force_quirks: bool
-}
-
-pub fn DoctypeData(name: ~str, public_id: Option<~str>,
-               system_id: Option<~str>, force_quirks: bool) -> DoctypeData {
-    DoctypeData {
-        name : name,
-        public_id : public_id,
-        system_id : system_id,
-        force_quirks : force_quirks,
-    }
-}
-
-
-
 pub fn define_bindings(compartment: @mut Compartment, doc: @Document, win: @Window) {
     bindings::window::init(compartment, win);
     bindings::document::init(compartment, doc);
     bindings::node::init(compartment);
     bindings::element::init(compartment);
 }
-
-
-/** The COW rd_aux data is a (weak) pointer to the layout data,
-   defined by this `LayoutData` struct. It contains the CSS style object
-   as well as the primary `RenderBox`.
-
-   Note that there may be multiple boxes per DOM node. */
-pub struct LayoutData {
-    mut style: Option<CompleteSelectResults>,
-    mut flow:  Option<@FlowContext>
-}
-
-pub type Node = cow::Handle<NodeData, LayoutData>;
-
-pub type NodeScope = cow::Scope<NodeData, LayoutData>;
-
-pub fn NodeScope() -> NodeScope {
-    cow::Scope()
-}
-
-pub trait NodeScopeExtensions {
-    fn new_node(+k: NodeKind) -> Node;
-}
-
-#[allow(non_implicitly_copyable_typarams)]
-impl NodeScopeExtensions for NodeScope {
-    fn new_node(k: NodeKind) -> Node {
-        self.handle(&NodeData {tree: tree::empty(), kind: ~k})
-    }
-}
-
-impl NodeScope {
-    fn each_child(node: &Node, f: fn(&Node) -> bool) {
-        tree::each_child(&self, node, f)
-    }
-
-    fn get_parent(node: &Node) -> Option<Node> {
-        tree::get_parent(&self, node)
-    }
-}
-
-#[allow(non_implicitly_copyable_typarams)]
-impl tree::ReadMethods<Node> for NodeScope {
-    fn with_tree_fields<R>(node: &Node, f: fn(&tree::Tree<Node>) -> R) -> R {
-        self.read(node, |n| f(&n.tree))
-    }
-}
-
-impl NodeScope {
-    fn add_child(node: Node, child: Node) {
-        tree::add_child(&self, node, child)
-    }
-}
-
-#[allow(non_implicitly_copyable_typarams)]
-impl tree::WriteMethods<Node> for NodeScope {
-    pure fn tree_eq(a: &Node, b: &Node) -> bool { a == b }
-
-    fn with_tree_fields<R>(node: &Node, f: fn(&tree::Tree<Node>) -> R) -> R {
-        self.write(node, |n| f(&n.tree))
-    }
-}
--- a/servo/src/servo/html/hubbub_html_parser.rs
+++ b/servo/src/servo/html/hubbub_html_parser.rs
@@ -1,47 +1,87 @@
-use gfx::util::url::make_url;
-use au = gfx::geometry;
 use content::content_task::ContentTask;
-use dom::cow;
 use dom::element::*;
 use dom::event::{Event, ReflowEvent};
-use dom::node::{Comment, Doctype, DoctypeData, Element, Node, NodeScope, NodeScopeExtensions};
-use dom::node::{Text};
+use dom::node::{AbstractNode, Comment, Doctype, Element, ElementNodeTypeId, Node, Text};
+use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
+use newcss::stylesheet::Stylesheet;
 use resource::image_cache_task::ImageCacheTask;
 use resource::image_cache_task;
 use resource::resource_task::{Done, Load, Payload, ResourceTask};
 use util::task::{spawn_listener, spawn_conversation};
 
 use core::comm::{Chan, Port, SharedChan};
-use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
+use core::str::eq_slice;
+use gfx::util::url::make_url;
 use hubbub::hubbub::Attribute;
 use hubbub::hubbub;
-use newcss::stylesheet::Stylesheet;
+use std::cell::Cell;
 use std::net::url::Url;
 use std::net::url;
 
+macro_rules! handle_element(
+    ($tag:expr, $string:expr, $ctor:ident, $type_id:expr) => (
+        if eq_slice($tag, $string) {
+            let element = ~$ctor {
+                parent: Element::new($type_id, ($tag).to_str())
+            };
+            unsafe {
+                return Node::as_abstract_node(element);
+            }
+        }
+    )
+)
+
+macro_rules! handle_heading_element(
+    ($tag:expr, $string:expr, $ctor:ident, $type_id:expr, $level:expr) => (
+        if eq_slice($tag, $string) {
+            let element = ~HTMLHeadingElement {
+                parent: Element::new($type_id, ($tag).to_str()),
+                level: $level
+            };
+            unsafe {
+                return Node::as_abstract_node(element);
+            }
+        }
+    )
+)
+
 type JSResult = ~[~[u8]];
 
 enum CSSMessage {
     CSSTaskNewFile(StylesheetProvenance),
     CSSTaskExit   
 }
 
 enum JSMessage {
     JSTaskNewFile(Url),
     JSTaskExit
 }
 
 struct HtmlParserResult {
-    root: Node,
+    root: AbstractNode,
     style_port: Port<Option<Stylesheet>>,
     js_port: Port<JSResult>,
 }
 
+trait NodeWrapping {
+    unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr;
+    static unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> Self;
+}
+
+impl NodeWrapping for AbstractNode {
+    unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr {
+        cast::transmute(self)
+    }
+    static unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> AbstractNode {
+        cast::transmute(n)
+    }
+}
+
 /**
 Runs a task that coordinates parsing links to css stylesheets.
 
 This function should be spawned in a separate task and spins waiting
 for the html builder to find links to css stylesheets and sends off
 tasks to parse each link.  When the html process finishes, it notifies
 the listener, who then collects the css rules from each task it
 spawned, collates them, and sends them to the given result channel.
@@ -114,62 +154,67 @@ fn js_script_listener(to_parent: Chan<~[
             }
         }
     }
 
     let js_scripts = vec::map(result_vec, |result_port| result_port.recv());
     to_parent.send(js_scripts);
 }
 
-fn build_element_kind(tag: &str) -> ~ElementKind {
+// Silly macros to handle constructing DOM nodes. This produces bad code and should be optimized
+// via atomization (issue #85).
+
+fn build_element_from_tag(tag: &str) -> AbstractNode {
     // TODO (Issue #85): use atoms
-    if      tag == ~"a" { ~HTMLAnchorElement }
-    else if tag == ~"aside" { ~HTMLAsideElement }
-    else if tag == ~"br" { ~HTMLBRElement }
-    else if tag == ~"body" { ~HTMLBodyElement }
-    else if tag == ~"bold" { ~HTMLBoldElement }
-    else if tag == ~"div" { ~HTMLDivElement }
-    else if tag == ~"font" { ~HTMLFontElement }
-    else if tag == ~"form" { ~HTMLFormElement }
-    else if tag == ~"hr" { ~HTMLHRElement }
-    else if tag == ~"head" { ~HTMLHeadElement }
-    else if tag == ~"h1" { ~HTMLHeadingElement(Heading1) }
-    else if tag == ~"h2" { ~HTMLHeadingElement(Heading2) }
-    else if tag == ~"h3" { ~HTMLHeadingElement(Heading3) }
-    else if tag == ~"h4" { ~HTMLHeadingElement(Heading4) }
-    else if tag == ~"h5" { ~HTMLHeadingElement(Heading5) }
-    else if tag == ~"h6" { ~HTMLHeadingElement(Heading6) }
-    else if tag == ~"html" { ~HTMLHtmlElement }
-    else if tag == ~"img" { ~HTMLImageElement(HTMLImageData()) }
-    else if tag == ~"input" { ~HTMLInputElement }
-    else if tag == ~"i" { ~HTMLItalicElement }
-    else if tag == ~"link" { ~HTMLLinkElement }
-    else if tag == ~"li" { ~HTMLListItemElement }
-    else if tag == ~"meta" { ~HTMLMetaElement }
-    else if tag == ~"ol" { ~HTMLOListElement }
-    else if tag == ~"option" { ~HTMLOptionElement }
-    else if tag == ~"p" { ~HTMLParagraphElement }
-    else if tag == ~"script" { ~HTMLScriptElement }
-    else if tag == ~"section" { ~HTMLSectionElement }
-    else if tag == ~"select" { ~HTMLSelectElement }
-    else if tag == ~"small" { ~HTMLSmallElement }
-    else if tag == ~"span" { ~HTMLSpanElement }
-    else if tag == ~"style" { ~HTMLStyleElement }
-    else if tag == ~"tbody" { ~HTMLTableBodyElement }
-    else if tag == ~"td" { ~HTMLTableCellElement }
-    else if tag == ~"table" { ~HTMLTableElement }
-    else if tag == ~"tr" { ~HTMLTableRowElement }
-    else if tag == ~"title" { ~HTMLTitleElement }
-    else if tag == ~"ul" { ~HTMLUListElement }
-    else { ~UnknownElement }
+    handle_element!(tag, "a",       HTMLAnchorElement,      HTMLAnchorElementTypeId);
+    handle_element!(tag, "aside",   HTMLAsideElement,       HTMLAsideElementTypeId);
+    handle_element!(tag, "br",      HTMLBRElement,          HTMLBRElementTypeId);
+    handle_element!(tag, "body",    HTMLBodyElement,        HTMLBodyElementTypeId);
+    handle_element!(tag, "bold",    HTMLBoldElement,        HTMLBoldElementTypeId);
+    handle_element!(tag, "div",     HTMLDivElement,         HTMLDivElementTypeId);
+    handle_element!(tag, "font",    HTMLFontElement,        HTMLFontElementTypeId);
+    handle_element!(tag, "form",    HTMLFormElement,        HTMLFormElementTypeId);
+    handle_element!(tag, "hr",      HTMLHRElement,          HTMLHRElementTypeId);
+    handle_element!(tag, "head",    HTMLHeadElement,        HTMLHeadElementTypeId);
+    handle_element!(tag, "html",    HTMLHtmlElement,        HTMLHtmlElementTypeId);
+    handle_element!(tag, "input",   HTMLInputElement,       HTMLInputElementTypeId);
+    handle_element!(tag, "i",       HTMLItalicElement,      HTMLItalicElementTypeId);
+    handle_element!(tag, "link",    HTMLLinkElement,        HTMLLinkElementTypeId);
+    handle_element!(tag, "li",      HTMLListItemElement,    HTMLListItemElementTypeId);
+    handle_element!(tag, "meta",    HTMLMetaElement,        HTMLMetaElementTypeId);
+    handle_element!(tag, "ol",      HTMLOListElement,       HTMLOListElementTypeId);
+    handle_element!(tag, "option",  HTMLOptionElement,      HTMLOptionElementTypeId);
+    handle_element!(tag, "p",       HTMLParagraphElement,   HTMLParagraphElementTypeId);
+    handle_element!(tag, "script",  HTMLScriptElement,      HTMLScriptElementTypeId);
+    handle_element!(tag, "section", HTMLSectionElement,     HTMLSectionElementTypeId);
+    handle_element!(tag, "select",  HTMLSelectElement,      HTMLSelectElementTypeId);
+    handle_element!(tag, "small",   HTMLSmallElement,       HTMLSmallElementTypeId);
+    handle_element!(tag, "span",    HTMLSpanElement,        HTMLSpanElementTypeId);
+    handle_element!(tag, "style",   HTMLStyleElement,       HTMLStyleElementTypeId);
+    handle_element!(tag, "tbody",   HTMLTableBodyElement,   HTMLTableBodyElementTypeId);
+    handle_element!(tag, "td",      HTMLTableCellElement,   HTMLTableCellElementTypeId);
+    handle_element!(tag, "table",   HTMLTableElement,       HTMLTableElementTypeId);
+    handle_element!(tag, "tr",      HTMLTableRowElement,    HTMLTableRowElementTypeId);
+    handle_element!(tag, "title",   HTMLTitleElement,       HTMLTitleElementTypeId);
+    handle_element!(tag, "ul",      HTMLUListElement,       HTMLUListElementTypeId);
+
+    handle_heading_element!(tag, "h1", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading1);
+    handle_heading_element!(tag, "h2", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading2);
+    handle_heading_element!(tag, "h3", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading3);
+    handle_heading_element!(tag, "h4", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading4);
+    handle_heading_element!(tag, "h5", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading5);
+    handle_heading_element!(tag, "h6", HTMLHeadingElement, HTMLHeadingElementTypeId, Heading6);
+
+    unsafe {
+        Node::as_abstract_node(~Element::new(UnknownElementTypeId, tag.to_str()))
+    }
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-pub fn parse_html(scope: NodeScope,
-                  url: Url,
+pub fn parse_html(url: Url,
                   resource_task: ResourceTask,
                   image_cache_task: ImageCacheTask) -> HtmlParserResult {
     // Spawn a CSS parser to receive links to CSS style sheets.
     let resource_task2 = resource_task.clone();
     let (css_port, css_chan): (Port<Option<Stylesheet>>, Chan<CSSMessage>) =
             do spawn_conversation |css_port: Port<CSSMessage>,
                                    css_chan: Chan<Option<Stylesheet>>| {
         css_link_listener(css_chan, css_port, resource_task2.clone());
@@ -180,153 +225,158 @@ pub fn parse_html(scope: NodeScope,
     let resource_task2 = resource_task.clone();
     let (js_port, js_chan): (Port<JSResult>, Chan<JSMessage>) =
             do spawn_conversation |js_port: Port<JSMessage>,
                                    js_chan: Chan<JSResult>| {
         js_script_listener(js_chan, js_port, resource_task2.clone());
     };
     let js_chan = SharedChan(js_chan);
 
-    let (scope, url) = (@copy scope, @url);
+    let url = @url;
 
     unsafe {
         // Build the root node.
-        let root = scope.new_node(Element(ElementData(~"html", ~HTMLDivElement)));
+        let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") };
+        let root = unsafe { Node::as_abstract_node(root) };
         debug!("created new node");
         let parser = hubbub::Parser("UTF-8", false);
         debug!("created parser");
-        parser.set_document_node(cast::transmute(cow::unwrap(root)));
+        parser.set_document_node(root.to_hubbub_node());
         parser.enable_scripting(true);
 
-        // Performs various actions necessary after appending has taken place. Currently, this consists
-        // of processing inline stylesheets, but in the future it might perform prefetching, etc.
+        // Performs various actions necessary after appending has taken place. Currently, this
+        // consists of processing inline stylesheets, but in the future it might perform
+        // prefetching, etc.
         let css_chan2 = css_chan.clone();
-        let append_hook: @fn(Node, Node) = |parent_node, child_node| {
-            do scope.read(&parent_node) |parent_node_contents| {
-                do scope.read(&child_node) |child_node_contents| {
-                    match (&parent_node_contents.kind, &child_node_contents.kind) {
-                        (&~Element(ref element), &~Text(ref data)) => {
-                            match element.kind {
-                                ~HTMLStyleElement => {
-                                    debug!("found inline CSS stylesheet");
-                                    let url = url::from_str("http://example.com/"); // FIXME
-                                    let provenance = InlineProvenance(result::unwrap(url),
-                                                                      copy *data);
-                                    css_chan2.send(CSSTaskNewFile(provenance));
-                                }
-                                _ => {} // Nothing to do.
-                            }
-                         }
-                         _ => {} // Nothing to do.
-                    }
+        let append_hook: @fn(AbstractNode, AbstractNode) = |parent_node, child_node| {
+            if parent_node.is_style_element() && child_node.is_text() {
+                debug!("found inline CSS stylesheet");
+                let url = url::from_str("http://example.com/"); // FIXME
+                let url_cell = Cell(url);
+                do child_node.with_imm_text |text_node| {
+                    let data = text_node.text.to_str();  // FIXME: Bad copy.
+                    let provenance = InlineProvenance(result::unwrap(url_cell.take()), data);
+                    css_chan2.send(CSSTaskNewFile(provenance));
                 }
             }
         };
 
         let (css_chan2, js_chan2) = (css_chan.clone(), js_chan.clone());
         parser.set_tree_handler(@hubbub::TreeHandler {
             create_comment: |data: ~str| {
                 debug!("create comment");
-                let new_node = scope.new_node(Comment(data));
-                unsafe { cast::transmute(cow::unwrap(new_node)) }
+                unsafe {
+                    Node::as_abstract_node(~Comment::new(data)).to_hubbub_node()
+                }
             },
             create_doctype: |doctype: ~hubbub::Doctype| {
                 debug!("create doctype");
                 // TODO: remove copying here by using struct pattern matching to 
                 // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
                 let public_id = match &doctype.public_id {
                   &None => None,
                   &Some(ref id) => Some(copy *id)
                 };
                 let system_id = match &doctype.system_id {
                   &None => None,
                   &Some(ref id) => Some(copy *id)
                 };
-                let data = DoctypeData(copy doctype.name,
-                                       public_id,
-                                       system_id,
-                                       copy doctype.force_quirks);
-                let new_node = scope.new_node(Doctype(data));
-                unsafe { cast::transmute(cow::unwrap(new_node)) }
+                let node = ~Doctype::new(copy doctype.name,
+                                         public_id,
+                                         system_id,
+                                         doctype.force_quirks);
+                unsafe {
+                    Node::as_abstract_node(node).to_hubbub_node()
+                }
             },
             create_element: |tag: ~hubbub::Tag| {
                 debug!("create element");
                 // TODO: remove copying here by using struct pattern matching to 
                 // move all ~strs at once (blocked on Rust #3845, #3846, #3847)
-                let elem_kind = build_element_kind(tag.name);
-                let elem = ElementData(copy tag.name, elem_kind);
+                let node = build_element_from_tag(tag.name);
 
                 debug!("-- attach attrs");
-                for tag.attributes.each |attr| {
-                    elem.attrs.push(~Attr(copy attr.name, copy attr.value));
+                do node.as_mut_element |element| {
+                    for tag.attributes.each |attr| {
+                        element.attrs.push(Attr::new(copy attr.name, copy attr.value));
+                    }
                 }
 
                 // Spawn additional parsing, network loads, etc. from tag and attrs
-                match elem.kind {
-                    //Handle CSS style sheets from <link> elements
-                    ~HTMLLinkElement => {
-                        match (elem.get_attr(~"rel"), elem.get_attr(~"href")) {
-                            (Some(rel), Some(href)) => {
-                                if rel == ~"stylesheet" {
-                                    debug!("found CSS stylesheet: %s", href);
-                                    css_chan2.send(CSSTaskNewFile(UrlProvenance(make_url(
-                                        href, Some(copy *url)))));
+                match node.type_id() {
+                    // Handle CSS style sheets from <link> elements
+                    ElementNodeTypeId(HTMLLinkElementTypeId) => {
+                        do node.with_imm_element |element| {
+                            match (element.get_attr(~"rel"), element.get_attr(~"href")) {
+                                (Some(rel), Some(href)) => {
+                                    if rel == ~"stylesheet" {
+                                        debug!("found CSS stylesheet: %s", href);
+                                        let url = make_url(href.to_str(), Some(copy *url));
+                                        css_chan2.send(CSSTaskNewFile(UrlProvenance(url)));
+                                    }
+                                }
+                                _ => {}
+                            }
+                        }
+                    },
+                    ElementNodeTypeId(HTMLImageElementTypeId) => {
+                        do node.with_mut_image_element |image_element| {
+                            let src_opt = image_element.parent.get_attr(~"src").map(|x| x.to_str());
+                            match src_opt {
+                                None => {}
+                                Some(src) => {
+                                    let img_url = make_url(src, Some(copy *url));
+                                    image_element.image = Some(copy img_url);
+                                    // inform the image cache to load this, but don't store a handle.
+                                    // TODO (Issue #84): don't prefetch if we are within a <noscript>
+                                    // tag.
+                                    image_cache_task.send(image_cache_task::Prefetch(img_url));
                                 }
                             }
-                            _ => {}
-                        }
-                    },
-                    ~HTMLImageElement(ref d) => {
-                        do elem.get_attr(~"src").iter |img_url_str| {
-                            let img_url = make_url(copy *img_url_str, Some(copy *url));
-                            d.image = Some(copy img_url);
-                            // inform the image cache to load this, but don't store a handle.
-                            // TODO (Issue #84): don't prefetch if we are within a <noscript> tag.
-                            image_cache_task.send(image_cache_task::Prefetch(img_url));
                         }
                     }
                     //TODO (Issue #86): handle inline styles ('style' attr)
                     _ => {}
                 }
-                let node = scope.new_node(Element(elem));
-                unsafe { cast::transmute(cow::unwrap(node)) }
+
+                unsafe {
+                    node.to_hubbub_node()
+                }
             },
             create_text: |data: ~str| {
                 debug!("create text");
-                let new_node = scope.new_node(Text(data));
-                unsafe { cast::transmute(cow::unwrap(new_node)) }
+                unsafe {
+                    Node::as_abstract_node(~Text::new(data)).to_hubbub_node()
+                }
             },
-            ref_node: |_node| {},
-            unref_node: |_node| {},
+            ref_node: |_| {},
+            unref_node: |_| {},
             append_child: |parent: hubbub::NodeDataPtr, child: hubbub::NodeDataPtr| {
                 unsafe {
                     debug!("append child %x %x", cast::transmute(parent), cast::transmute(child));
-                    let p: Node = cow::wrap(cast::transmute(parent));
-                    let c: Node = cow::wrap(cast::transmute(child));
-                    scope.add_child(p, c);
-                    append_hook(p, c);
+                    let parent: AbstractNode = NodeWrapping::from_hubbub_node(parent);
+                    let child: AbstractNode = NodeWrapping::from_hubbub_node(child);
+                    parent.append_child(child);
+                    append_hook(parent, child);
                 }
                 child
             },
             insert_before: |_parent, _child| {
                 debug!("insert before");
                 0u
             },
             remove_child: |_parent, _child| {
                 debug!("remove child");
                 0u
             },
             clone_node: |node, deep| {
                 debug!("clone node");
                 unsafe {
                     if deep { error!("-- deep clone unimplemented"); }
-                    let n: Node = cow::wrap(cast::transmute(node));
-                    let data = n.read(|read_data| copy *read_data.kind);
-                    let new_node = scope.new_node(data);
-                    cast::transmute(cow::unwrap(new_node))
+                    fail!(~"clone node unimplemented")
                 }
             },
             reparent_children: |_node, _new_parent| {
                 debug!("reparent children");
                 0u
             },
             get_parent: |_node, _element_only| {
                 debug!("get parent");
@@ -346,39 +396,34 @@ pub fn parse_html(scope: NodeScope,
                 debug!("set quirks mode");
             },
             encoding_change: |_encname| {
                 debug!("encoding change");
             },
             complete_script: |script| {
                 // A little function for holding this lint attr
                 #[allow(non_implicitly_copyable_typarams)]
-                fn complete_script(scope: &NodeScope,
-                                   script: hubbub::NodeDataPtr,
+                fn complete_script(script: hubbub::NodeDataPtr,
                                    url: &Url,
                                    js_chan: SharedChan<JSMessage>) {
                     unsafe {
-                        do scope.read(&cow::wrap(cast::transmute(script))) |node_contents| {
-                            match *node_contents.kind {
-                                Element(ref element) if element.tag_name == ~"script" => {
-                                    match element.get_attr(~"src") {
-                                        Some(src) => {
-                                            debug!("found script: %s", src);
-                                            let new_url = make_url(src, Some(copy *url));
-                                            js_chan.send(JSTaskNewFile(new_url));
-                                        }
-                                        None => {}
-                                    }
+                        let script: AbstractNode = NodeWrapping::from_hubbub_node(script);
+                        do script.with_imm_element |script| {
+                            match script.get_attr(~"src") {
+                                Some(src) => {
+                                    debug!("found script: %s", src);
+                                    let new_url = make_url(src.to_str(), Some(copy *url));
+                                    js_chan.send(JSTaskNewFile(new_url));
                                 }
-                                _ => {}
+                                None => {}
                             }
                         }
                     }
                 }
-                complete_script(scope, script, url, js_chan2.clone());
+                complete_script(script, url, js_chan2.clone());
                 debug!("complete script");
             }
         });
         debug!("set tree handler");
 
         let (input_port, input_chan) = comm::stream();
         resource_task.send(Load(copy *url, input_chan));
         debug!("loaded page");
--- a/servo/src/servo/layout/aux.rs
+++ b/servo/src/servo/layout/aux.rs
@@ -1,43 +1,37 @@
 /**
 Code for managing the DOM aux pointer
 */
 
-use dom::node::{Node, LayoutData};
+use dom::node::{AbstractNode, LayoutData};
 use core::dvec::DVec;
 
 pub trait LayoutAuxMethods {
-    fn initialize_layout_data() -> Option<@LayoutData>;
-    fn initialize_style_for_subtree(refs: &DVec<@LayoutData>);
+    fn initialize_layout_data(self) -> Option<@mut LayoutData>;
+    fn initialize_style_for_subtree(self, refs: &DVec<@mut LayoutData>);
 }
 
-impl LayoutAuxMethods for Node {
-    /** If none exists, creates empty layout data for the node (the reader-auxiliary
-     * box in the COW model) and populates it with an empty style object.
-     */
-    fn initialize_layout_data() -> Option<@LayoutData> {
-        match self.has_aux() {
-            false => {
-                let data = @LayoutData {
-                    mut style : None,
-                    mut flow  : None
-                };
-                self.set_aux(data); Some(data)
-            },
-            true => None
+impl LayoutAuxMethods for AbstractNode {
+    /// If none exists, creates empty layout data for the node (the reader-auxiliary
+    /// box in the COW model) and populates it with an empty style object.
+    fn initialize_layout_data(self) -> Option<@mut LayoutData> {
+        if self.has_layout_data() {
+            None
+        } else {
+            let data = @mut LayoutData::new();
+            self.set_layout_data(data);
+            Some(data)
         }
     }
 
-    /**
-     * Initializes layout data and styles for a Node tree, if any nodes do not have
-     * this data already. Append created layout data to the task's GC roots.
-     */
-    fn initialize_style_for_subtree(refs: &DVec<@LayoutData>) {
-        do self.traverse_preorder |n| {
+    /// Initializes layout data and styles for a Node tree, if any nodes do not have
+    /// this data already. Append created layout data to the task's GC roots.
+    fn initialize_style_for_subtree(self, refs: &DVec<@mut LayoutData>) {
+        let _ = for self.traverse_preorder |n| {
             match n.initialize_layout_data() {
                 Some(r) => refs.push(r),
                 None => {}
             }
-        }
+        };
     }
 
 }
--- a/servo/src/servo/layout/box.rs
+++ b/servo/src/servo/layout/box.rs
@@ -1,13 +1,12 @@
 /* Fundamental layout structures and algorithms. */
 
 use css::node_style::StyledNode;
-use dom::element::{ElementKind, HTMLDivElement, HTMLImageElement};
-use dom::node::{Element, Node, NodeData, NodeKind, NodeTree};
+use dom::node::AbstractNode;
 use layout::context::LayoutContext;
 use layout::debug::BoxedDebugMethods;
 use layout::display_list_builder::DisplayListBuilder;
 use layout::flow::FlowContext;
 use layout::text::TextBoxData;
 use layout;
 use newcss::color::{Color, rgba, rgb};
 use newcss::complete::CompleteStyle;
@@ -73,17 +72,17 @@ padding, backgrounds. It is analogous to
 /* A box's kind influences how its styles are interpreted during
    layout.  For example, replaced content such as images are resized
    differently than tables, text, or other content.
 
    It also holds data specific to different box types, such as text.
 */
 pub struct RenderBoxData {
     /* originating DOM node */
-    node : Node,
+    node : AbstractNode,
     /* reference to containing flow context, which this box
        participates in */
     ctx  : @FlowContext,
     /* position of this box relative to owning flow */
     mut position : Rect<Au>,
     font_size : Length,
     /* TODO (Issue #87): debug only */
     mut id: int
@@ -105,17 +104,17 @@ pub enum RenderBox {
 pub enum SplitBoxResult {
     CannotSplit(@RenderBox),
     // in general, when splitting the left or right side can
     // be zero length, due to leading/trailing trimmable whitespace
     SplitDidFit(Option<@RenderBox>, Option<@RenderBox>),
     SplitDidNotFit(Option<@RenderBox>, Option<@RenderBox>)
 }
 
-pub fn RenderBoxData(node: Node, ctx: @FlowContext, id: int) -> RenderBoxData {
+pub fn RenderBoxData(node: AbstractNode, ctx: @FlowContext, id: int) -> RenderBoxData {
     RenderBoxData {
         node : node,
         mut ctx  : ctx,
         mut position : Au::zero_rect(),
         font_size: Px(0.0),
         id : id
     }
 }
@@ -356,17 +355,17 @@ impl RenderBox  {
     fn style(&self) -> CompleteStyle/&self {
         let d: &self/RenderBoxData = self.d();
         d.node.style()
     }
 
     fn with_style_of_nearest_element<R>(@self, f: &fn(CompleteStyle) -> R) -> R {
         let mut node = self.d().node;
         while !node.is_element() {
-            node = NodeTree.get_parent(&node).get();
+            node = node.parent_node().get();
         }
         f(node.style())
     }
 
     // TODO: to implement stacking contexts correctly, we need to
     // create a set of display lists, one per each layer of a stacking
     // context. (CSS 2.1, Section 9.9.1). Each box is passed the list
     // set representing the box's stacking context. When asked to
@@ -599,20 +598,20 @@ impl BoxedDebugMethods for RenderBox {
 
         fmt!("box b%?: %?", self.d().id, repr)
     }
 }
 
 // Other methods
 impl RenderBox {
     /// Returns the nearest ancestor-or-self element node. Infallible.
-    fn nearest_ancestor_element(@self) -> Node {
+    fn nearest_ancestor_element(@self) -> AbstractNode {
         let mut node = self.d().node;
         while !node.is_element() {
-            match NodeTree.get_parent(&node) {
+            match node.parent_node() {
                 None => fail!(~"no nearest element?!"),
                 Some(parent) => node = parent,
             }
         }
         node
     }
 }
 
--- a/servo/src/servo/layout/box_builder.rs
+++ b/servo/src/servo/layout/box_builder.rs
@@ -1,15 +1,16 @@
 /** Creates CSS boxes from a DOM. */
 
+use dom::element::*;
+use dom::node::{AbstractNode, Comment, CommentNodeTypeId, Doctype, DoctypeNodeTypeId, Element};
+use dom::node::{ElementNodeTypeId, Node, Text, TextNodeTypeId};
 use dom;
-use dom::element::*;
-use dom::node::{Comment, Doctype, Element, Text, Node, LayoutData};
+use layout::block::BlockFlowData;
 use layout::box::*;
-use layout::block::BlockFlowData;
 use layout::context::LayoutContext;
 use layout::debug::{BoxedDebugMethods, DebugMethods};
 use layout::flow::*;
 use layout::inline::InlineFlowData;
 use layout::root::RootFlowData;
 use util::tree;
 
 use core::dvec::DVec;
@@ -41,73 +42,71 @@ struct BoxGenerator {
     range_stack: DVec<uint>,
 }
 
 enum InlineSpacerSide {
     LogicalBefore,
     LogicalAfter,
 }
 
-priv fn simulate_UA_display_rules(node: Node) -> CSSDisplay {
-
+priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay {
     // FIXME
     /*let resolved = do node.aux |nd| {
         match nd.style.display_type {
             Inherit | Initial => DisplayInline, // TODO: remove once resolve works
             Specified(v) => v
         }
     };*/
+
     let resolved = CSSDisplayInline;
     if (resolved == CSSDisplayNone) { return resolved; }
 
-    do node.read |n| {
-        let kind: &dom::node::NodeKind = n.kind;
-        match kind {
-            &Doctype(*) | &Comment(*) => CSSDisplayNone,
-            &Text(*) => CSSDisplayInline,
-            &Element(ref e) => {
-                let kind: &dom::element::ElementKind = e.kind;
-                match kind {
-                    &HTMLHeadElement(*) => CSSDisplayNone,
-                    &HTMLScriptElement(*) => CSSDisplayNone,
-                    &HTMLParagraphElement(*) => CSSDisplayBlock,
-                    &HTMLDivElement(*) => CSSDisplayBlock,
-                    &HTMLBodyElement(*) => CSSDisplayBlock,
-                    &HTMLHeadingElement(*) => CSSDisplayBlock,
-                    &HTMLHtmlElement(*) => CSSDisplayBlock,
-                    &HTMLUListElement(*) => CSSDisplayBlock,
-                    &HTMLOListElement(*) => CSSDisplayBlock,
-                    _ => resolved
-                }
+    match node.type_id() {
+        DoctypeNodeTypeId | CommentNodeTypeId => CSSDisplayNone,
+        TextNodeTypeId => CSSDisplayInline,
+        ElementNodeTypeId(element_type_id) => {
+            match element_type_id {
+                HTMLHeadElementTypeId |
+                HTMLScriptElementTypeId => CSSDisplayNone,
+                HTMLParagraphElementTypeId |
+                HTMLDivElementTypeId |
+                HTMLBodyElementTypeId |
+                HTMLHeadingElementTypeId |
+                HTMLHtmlElementTypeId |
+                HTMLUListElementTypeId |
+                HTMLOListElementTypeId => CSSDisplayBlock,
+                _ => resolved
             }
         }
     }
 }
 
 impl BoxGenerator {
     static pure fn new(flow: @FlowContext) -> BoxGenerator {
         unsafe { debug!("Creating box generator for flow: %s", flow.debug_str()); }
         BoxGenerator {
             flow: flow,
             range_stack: DVec()
         }
     }
 
     /* Whether "spacer" boxes are needed to stand in for this DOM node */
-    pure fn inline_spacers_needed_for_node(_node: Node) -> bool {
+    pure fn inline_spacers_needed_for_node(_: AbstractNode) -> bool {
         return false;
     }
 
     // TODO: implement this, generating spacer 
-    fn make_inline_spacer_for_node_side(_ctx: &LayoutContext, _node: Node,
-                                        _side: InlineSpacerSide) -> Option<@RenderBox> {
+    fn make_inline_spacer_for_node_side(_: &LayoutContext,
+                                        _: AbstractNode,
+                                        _: InlineSpacerSide)
+                                     -> Option<@RenderBox> {
         None
     }
 
-    pub fn push_node(ctx: &LayoutContext, builder: &LayoutTreeBuilder, node: Node) {
+    pub fn push_node(ctx: &LayoutContext, builder: &LayoutTreeBuilder, node: AbstractNode) {
         debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.d().id, node.debug_str());
 
         // first, determine the box type, based on node characteristics
         let simulated_display = simulate_UA_display_rules(node);
         // TODO: remove this once UA styles work
         let box_type = builder.decide_box_type(node, simulated_display);
 
         debug!("BoxGenerator[f%d]: point a", self.flow.d().id);
@@ -117,22 +116,23 @@ impl BoxGenerator {
             @InlineFlow(*) => {
                 let node_range_start = match self.flow {
                     @InlineFlow(*) => self.flow.inline().boxes.len(),
                     _ => 0
                 };
                 self.range_stack.push(node_range_start);
 
                 // if a leaf, make a box.
-                if tree::is_leaf(&NodeTree, &node) {
+                if node.is_leaf() {
                     let new_box = builder.make_box(ctx, box_type, node, self.flow);
                     self.flow.inline().boxes.push(new_box);
-                } // else, maybe make a spacer for "left" margin, border, padding
-                else if self.inline_spacers_needed_for_node(node) {
-                    do self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).iter |spacer: &@RenderBox| {
+                } else if self.inline_spacers_needed_for_node(node) {
+                    // else, maybe make a spacer for "left" margin, border, padding
+                    do self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).iter
+                            |spacer: &@RenderBox| {
                         self.flow.inline().boxes.push(*spacer);
                     }
                 }
                 // TODO: cases for inline-block, etc.
             },
             @BlockFlow(*) => {
                 debug!("BoxGenerator[f%d]: point b", self.flow.d().id);
                 let new_box = builder.make_box(ctx, box_type, node, self.flow);
@@ -151,17 +151,17 @@ impl BoxGenerator {
 
                 assert self.flow.root().box.is_none();
                 self.flow.root().box = Some(new_box);
             },
             _ => { warn!("push_node() not implemented for flow f%d", self.flow.d().id) }
         }
     }
 
-    pub fn pop_node(ctx: &LayoutContext, _builder: &LayoutTreeBuilder, node: Node) {
+    pub fn pop_node(ctx: &LayoutContext, _builder: &LayoutTreeBuilder, node: AbstractNode) {
         debug!("BoxGenerator[f%d]: popping node: %s", self.flow.d().id, node.debug_str());
 
         match self.flow {
             @InlineFlow(*) => {
                 if self.inline_spacers_needed_for_node(node) {
                     // if this non-leaf box generates extra horizontal
                     // spacing, add a SpacerBox for it.
                     do self.make_inline_spacer_for_node_side(ctx, node, LogicalAfter).iter |spacer: &@RenderBox| {
@@ -236,30 +236,31 @@ impl BuilderContext {
 
     priv fn clear_inline_collector() {
         self.inline_collector = None;
     }
 
     // returns a context for the current node, or None if the document subtree rooted
     // by the node should not generate a layout tree. For example, nodes with style 'display:none'
     // should just not generate any flows or boxes.
-    fn containing_context_for_node(node: Node,
-                                   builder: &LayoutTreeBuilder) -> Option<BuilderContext> {
+    fn containing_context_for_node(node: AbstractNode,
+                                   builder: &LayoutTreeBuilder)
+                                -> Option<BuilderContext> {
         // TODO: remove this once UA styles work
         // TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7)
         let simulated_display = match simulate_UA_display_rules(node) {
             CSSDisplayNone => return None, // tree ends here if 'display: none'
             v => v
         };
 
         let containing_context = match (simulated_display, self.default_collector.flow) { 
             (CSSDisplayBlock, @RootFlow(*)) => {
                 // If this is the root node, then use the root flow's
                 // context. Otherwise, make a child block context.
-                match NodeTree.get_parent(&node) {
+                match node.parent_node() {
                     Some(_) => { self.create_child_flow_of_type(Flow_Block, builder) }
                     None => { self.clone() },
                 }
             },
             (CSSDisplayBlock, @BlockFlow(*)) => {
                 self.clear_inline_collector();
                 self.create_child_flow_of_type(Flow_Block, builder)
             },
@@ -271,62 +272,65 @@ impl BuilderContext {
         };
 
         Some(containing_context)
     }
 }
 
 impl LayoutTreeBuilder {
     /* Debug-only ids */
-    fn next_box_id() -> int { self.next_bid += 1; self.next_bid }
-    fn next_flow_id() -> int { self.next_cid += 1; self.next_cid }
+    fn next_box_id(&self) -> int { self.next_bid += 1; self.next_bid }
+    fn next_flow_id(&self) -> int { self.next_cid += 1; self.next_cid }
 
     /** Creates necessary box(es) and flow context(s) for the current DOM node,
     and recurses on its children. */
-    fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: &BuilderContext) {
+    fn construct_recursively(&self,
+                             layout_ctx: &LayoutContext,
+                             cur_node: AbstractNode,
+                             parent_ctx: &BuilderContext) {
         debug!("Considering node: %s", cur_node.debug_str());
 
-        let this_ctx = match parent_ctx.containing_context_for_node(cur_node, &self) {
+        let this_ctx = match parent_ctx.containing_context_for_node(cur_node, self) {
             Some(ctx) => ctx,
             None => { return; } // no context because of display: none. Stop building subtree. 
         };
         debug!("point a: %s", cur_node.debug_str());
-        this_ctx.default_collector.push_node(layout_ctx, &self, cur_node);
+        this_ctx.default_collector.push_node(layout_ctx, self, cur_node);
         debug!("point b: %s", cur_node.debug_str());
 
         // recurse on child nodes.
-        for tree::each_child(&NodeTree, &cur_node) |child_node| {
-            self.construct_recursively(layout_ctx, *child_node, &this_ctx);
+        for cur_node.each_child |child_node| {
+            self.construct_recursively(layout_ctx, child_node, &this_ctx);
         }
 
-        this_ctx.default_collector.pop_node(layout_ctx, &self, cur_node);
+        this_ctx.default_collector.pop_node(layout_ctx, self, cur_node);
         self.simplify_children_of_flow(layout_ctx, &this_ctx);
 
         // store reference to the flow context which contains any
         // boxes that correspond to child_flow.node. These boxes may
         // eventually be elided or split, but the mapping between
         // nodes and FlowContexts should not change during layout.
         for tree::each_child(&FlowTree, &this_ctx.default_collector.flow) |child_flow: &@FlowContext| {
             do (copy child_flow.d().node).iter |node| {
-                assert node.has_aux();
-                do node.aux |data| { data.flow = Some(*child_flow) }
+                assert node.has_layout_data();
+                node.layout_data().flow = Some(*child_flow);
             }
         }
     }
 
     // Fixup any irregularities such as:
     //
     // * split inlines (CSS 2.1 Section 9.2.1.1)
     // * elide non-preformatted whitespace-only text boxes and their
     //   flows (CSS 2.1 Section 9.2.2.1).
     //
     // The latter can only be done immediately adjacent to, or at the
     // beginning or end of a block flow. Otherwise, the whitespace
     // might affect whitespace collapsing with adjacent text.
-    fn simplify_children_of_flow(_layout_ctx: &LayoutContext, parent_ctx: &BuilderContext) {
+    fn simplify_children_of_flow(&self, _: &LayoutContext, parent_ctx: &BuilderContext) {
         match *parent_ctx.default_collector.flow {
             InlineFlow(*) => {
                 let mut found_child_inline = false;
                 let mut found_child_block = false;
 
                 for tree::each_child(&FlowTree, &parent_ctx.default_collector.flow) |child_ctx: &@FlowContext| {
                     match **child_ctx {
                         InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true,
@@ -364,34 +368,35 @@ impl LayoutTreeBuilder {
                         }
                     }
                 }
             },
             _ => {}
         }
     }
 
-    fn fixup_split_inline(_foo: @FlowContext) {
+    fn fixup_split_inline(&self, _: @FlowContext) {
         // TODO: finish me. 
         fail!(~"TODO: handle case where an inline is split by a block")
     }
 
     /** entry point for box creation. Should only be 
     called on root DOM element. */
-    fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@FlowContext, ()> {
+    fn construct_trees(&self, layout_ctx: &LayoutContext, root: AbstractNode)
+                    -> Result<@FlowContext, ()> {
         let new_flow = self.make_flow(Flow_Root);
         let new_generator = @BoxGenerator::new(new_flow);
         let root_ctx = BuilderContext::new(new_generator);
 
         self.root_flow = Some(new_flow);
         self.construct_recursively(layout_ctx, root, &root_ctx);
         return Ok(new_flow)
     }
 
-    fn make_flow(ty : FlowContextType) -> @FlowContext {
+    fn make_flow(&self, ty: FlowContextType) -> @FlowContext {
         let data = FlowData(self.next_flow_id());
         let ret = match ty {
             Flow_Absolute    => @AbsoluteFlow(data),
             Flow_Block       => @BlockFlow(data, BlockFlowData()),
             Flow_Float       => @FloatFlow(data),
             Flow_InlineBlock => @InlineBlockFlow(data),
             Flow_Inline      => @InlineFlow(data, InlineFlowData()),
             Flow_Root        => @RootFlow(data, RootFlowData()),
@@ -400,78 +405,85 @@ impl LayoutTreeBuilder {
         debug!("LayoutTreeBuilder: created flow: %s", ret.debug_str());
         ret
     }
 
     /**
        disambiguate between different methods here instead of inlining, since each
        case has very different complexity 
     */
-    fn make_box(layout_ctx: &LayoutContext, ty: RenderBoxType, node: Node, ctx: @FlowContext) -> @RenderBox {
+    fn make_box(&self,
+                layout_ctx: &LayoutContext,
+                ty: RenderBoxType,
+                node: AbstractNode,
+                ctx: @FlowContext)
+             -> @RenderBox {
         let ret = match ty {
             RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx),
             RenderBox_Text    => self.make_text_box(layout_ctx, node, ctx),
             RenderBox_Image   => self.make_image_box(layout_ctx, node, ctx),
         };
         debug!("LayoutTreeBuilder: created box: %s", ret.debug_str());
         ret
     }
 
-    fn make_generic_box(_layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox {
+    fn make_generic_box(&self,
+                        _: &LayoutContext,
+                        node: AbstractNode,
+                        ctx: @FlowContext)
+                     -> @RenderBox {
         @GenericBox(RenderBoxData(copy node, ctx, self.next_box_id()))
     }
 
-    fn make_image_box(layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox {
-        do node.read |n| {
-            match n.kind {
-                ~Element(ref ed) => match ed.kind {
-                    ~HTMLImageElement(ref d) => {
-                        // TODO: this could be written as a pattern guard, but it triggers
-                        // an ICE (mozilla/rust issue #3601)
-                        if d.image.is_some() {
-                            let holder = ImageHolder::new({copy *d.image.get_ref()},
-                                                           layout_ctx.image_cache);
-
-                            @ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
-                        } else {
-                            info!("Tried to make image box, but couldn't find image. Made generic box instead.");
-                            self.make_generic_box(layout_ctx, node, ctx)
-                        }
-                    },
-                    _ => fail!(~"WAT error: why couldn't we make an image box?")
-                },
-                _ => fail!(~"WAT error: why couldn't we make an image box?")
-            }
+    fn make_image_box(&self,
+                      layout_ctx: &LayoutContext,
+                      node: AbstractNode,
+                      ctx: @FlowContext)
+                   -> @RenderBox {
+        if !node.is_image_element() {
+            fail!(~"WAT error: why couldn't we make an image box?");
         }
 
-    }
-
-    fn make_text_box(_layout_ctx: &LayoutContext, node: Node, ctx: @FlowContext) -> @RenderBox {
-        do node.read |n| {
-            match n.kind {
-                ~Text(ref string) => @UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), copy *string),
-                _ => fail!(~"WAT error: why couldn't we make a text box?")
+        do node.with_imm_image_element |image_element| {
+            if image_element.image.is_some() {
+                let holder = ImageHolder::new(copy *image_element.image.get_ref(),
+                                              layout_ctx.image_cache);
+                @ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
+            } else {
+                info!("Tried to make image box, but couldn't find image. Made generic box instead.");
+                self.make_generic_box(layout_ctx, node, ctx)
             }
         }
     }
 
-    fn decide_box_type(node: Node, display: CSSDisplay) -> RenderBoxType {
-        do node.read |n| {
-            match n.kind {
-                ~Doctype(*) | ~Comment(*) => {
-                    fail!(~"Hey, doctypes and comments shouldn't get here! \
-                            They are display:none!")
-                }
-                ~Text(*) => RenderBox_Text,
-                ~Element(ref element) => {
-                    match (&element.kind, display) {
-                        (&~HTMLImageElement(ref d), _) if d.image.is_some() => RenderBox_Image,
-//                      (_, Specified(_)) => GenericBox,
-                        (_, _) => RenderBox_Generic // TODO: replace this with the commented lines
-                        //(_, _) => {
-                        //  fail!(~"Can't create box for Node with non-specified 'display' type")
-                        //}
-                    }
+    fn make_text_box(&self,
+                     _: &LayoutContext,
+                     node: AbstractNode,
+                     ctx: @FlowContext)
+                  -> @RenderBox {
+        if !node.is_text() {
+            fail!(~"WAT error: why couldn't we make a text box?");
+        }
+
+        // FIXME: Don't copy text. I guess it should be atomically reference counted?
+        do node.with_imm_text |text_node| {
+            let string = text_node.text.to_str();
+            @UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), string)
+        }
+    }
+
+    fn decide_box_type(&self, node: AbstractNode, display: CSSDisplay) -> RenderBoxType {
+        if node.is_text() {
+            RenderBox_Text
+        } else if node.is_image_element() {
+            do node.with_imm_image_element |image_element| {
+                match image_element.image {
+                    Some(_) => RenderBox_Image,
+                    None => RenderBox_Generic,
                 }
             }
+        } else if node.is_element() {
+            RenderBox_Generic
+        } else {
+            fail!(~"Hey, doctypes and comments shouldn't get here! They are display:none!")
         }
     }
 }
--- a/servo/src/servo/layout/display_list_builder.rs
+++ b/servo/src/servo/layout/display_list_builder.rs
@@ -1,27 +1,30 @@
-use newcss::values::Specified;
-use newcss::values::{CSSBackgroundColorColor, CSSBackgroundColorTransparent};
-use dom::node::{Text, NodeScope};
-use dom::cow::Scope;
-use dvec::DVec;
-use either::{Left, Right};
-use geom::point::Point2D;
-use geom::rect::Rect;
-use geom::size::Size2D;
+///
+/// Constructs display lists from render boxes.
+///
+
 use layout::box::{RenderBox, TextBox};
 use layout::context::LayoutContext;
 use layout::flow::FlowContext;
 use layout::text::TextBoxData;
+use newcss::values::Specified;
+use newcss::values::{CSSBackgroundColorColor, CSSBackgroundColorTransparent};
 use util::tree;
-use vec::push;
-use gfx;
+
+use core::dvec::DVec;
+use core::either::{Left, Right};
+use core::mutable::Mut;
+use core::vec::push;
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
 use gfx::display_list::DisplayList;
 use gfx::geometry::Au;
-use core::mutable::Mut;
+use gfx;
 
 /** A builder object that manages display list builder should mainly
  hold information about the initial request and desired result---for
  example, whether the DisplayList to be used for painting or hit
  testing. This can affect which boxes are created.
 
  Right now, the builder isn't used for much, but it  establishes the
  pattern we'll need once we support DL-based hit testing &c.  */
--- a/servo/src/servo/layout/flow.rs
+++ b/servo/src/servo/layout/flow.rs
@@ -1,10 +1,10 @@
 use core;
-use dom::node::Node;
+use dom::node::AbstractNode;
 use layout::block::{BlockFlowData, BlockLayout};
 use layout::box::RenderBox;
 use layout::context::LayoutContext;
 use layout::debug::BoxedDebugMethods;
 use layout::display_list_builder::DisplayListBuilder;
 use layout::inline::{InlineFlowData, InlineLayout, NodeRange};
 use layout::root::{RootFlowData, RootLayout};
 use util::tree;
@@ -62,17 +62,17 @@ enum FlowContextType {
     Flow_Inline,
     Flow_Root,
     Flow_Table
 }
 
 /* A particular kind of layout context. It manages the positioning of
    render boxes within the context.  */
 struct FlowData {
-    mut node: Option<Node>,
+    mut node: Option<AbstractNode>,
     /* reference to parent, children flow contexts */
     tree: tree::Tree<@FlowContext>,
     /* TODO (Issue #87): debug only */
     mut id: int,
 
     /* layout computations */
     // TODO: min/pref and position are used during disjoint phases of
     // layout; maybe combine into a single enum to save space.
@@ -172,34 +172,36 @@ impl FlowContext  {
         match self {
             RootFlow(*)   => option::map_default(&self.root().box, seed, |box| { cb(seed, *box) }),
             BlockFlow(*)  => option::map_default(&self.block().box, seed, |box| { cb(seed, *box) }),
             InlineFlow(*) => do self.inline().boxes.foldl(seed) |acc, box| { cb(*acc, *box) },
             _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self))
         }
     }
 
-    pure fn foldl_boxes_for_node<B: Copy>(node: Node, seed: B, 
-                                          cb: pure fn&(a: B,@RenderBox) -> B) -> B {
+    pure fn foldl_boxes_for_node<B: Copy>(node: AbstractNode,
+                                          seed: B, 
+                                          cb: pure fn&(a: B,@RenderBox) -> B)
+                                       -> B {
         do self.foldl_all_boxes(seed) |acc, box| {
             if box.d().node == node { cb(acc, box) }
             else { acc }
         }
     }
 
     pure fn iter_all_boxes<T>(cb: pure fn&(@RenderBox) -> T) {
         match self {
             RootFlow(*)   => do self.root().box.iter |box| { cb(*box); },
             BlockFlow(*)  => do self.block().box.iter |box| { cb(*box); },
             InlineFlow(*) => for self.inline().boxes.each |box| { cb(*box); },
             _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self))
         }
     }
 
-    pure fn iter_boxes_for_node<T>(node: Node,
+    pure fn iter_boxes_for_node<T>(node: AbstractNode,
                                    cb: pure fn&(@RenderBox) -> T) {
         do self.iter_all_boxes |box| {
             if box.d().node == node { cb(box); }
         }
     }
 }
 
 /* The tree holding FlowContexts */
--- a/servo/src/servo/layout/inline.rs
+++ b/servo/src/servo/layout/inline.rs
@@ -1,10 +1,10 @@
 use core;
-use dom::node::Node;
+use dom::node::AbstractNode;
 use layout::box::*;
 use layout::context::LayoutContext;
 use layout::debug::{BoxedDebugMethods, DebugMethods};
 use layout::flow::{FlowContext, InlineFlow};
 use layout::text::{TextBoxData, UnscannedMethods};
 use util::tree;
 
 use core::dlist::DList;
@@ -36,36 +36,36 @@ outlines.
 N.B. roc has an alternative design where the list instead consists of
 things like "start outer box, text, start inner box, text, end inner
 box, text, end outer box, text". This seems a little complicated to
 serve as the starting point, but the current design doesn't make it
 hard to try out that alternative.
 */
 
 pub struct NodeRange {
-    node: Node,
+    node: AbstractNode,
     range: Range,
 }
 
 pub impl NodeRange {
-    static pure fn new(node: Node, range: &const Range) -> NodeRange {
+    static pure fn new(node: AbstractNode, range: &const Range) -> NodeRange {
         NodeRange { node: node, range: copy *range }
     }
 }
 
 struct ElementMapping {
     priv entries: DVec<NodeRange>,
 }
 
 impl ElementMapping {
     static pure fn new() -> ElementMapping {
         ElementMapping { entries: DVec() }
     }
 
-    fn add_mapping(node: Node, range: &const Range) {
+    fn add_mapping(node: AbstractNode, range: &const Range) {
         self.entries.push(NodeRange::new(node, range))
     }
 
     fn each(cb: pure fn&(nr: &NodeRange) -> bool) {
         do self.entries.each |nr| { cb(nr) }
     }
 
     fn eachi(cb: pure fn&(i: uint, nr: &NodeRange) -> bool) {
--- a/servo/src/servo/layout/layout_task.rs
+++ b/servo/src/servo/layout/layout_task.rs
@@ -1,16 +1,16 @@
 /// The layout task. Performs layout on the DOM, builds display lists and sends them to be
 /// rendered.
 
 use content::content_task;
 use css::matching::MatchMethods;
 use css::select::new_css_select_ctx;
 use dom::event::{Event, ReflowEvent};
-use dom::node::{Node, LayoutData};
+use dom::node::{AbstractNode, LayoutData};
 use layout::aux::LayoutAuxMethods;
 use layout::box::RenderBox;
 use layout::box_builder::LayoutTreeBuilder;
 use layout::context::LayoutContext;
 use layout::debug::{BoxedDebugMethods, DebugMethods};
 use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
 use layout::traverse::*;
 use resource::image_cache_task::{ImageCacheTask, ImageResponseMsg};
@@ -37,28 +37,28 @@ use newcss::stylesheet::Stylesheet;
 use newcss::types::OriginAuthor;
 use std::arc::ARC;
 use std::cell::Cell;
 use std::net::url::Url;
 
 pub type LayoutTask = SharedChan<Msg>;
 
 pub enum LayoutQuery {
-    ContentBox(Node)
+    ContentBox(AbstractNode)
 }
 
 pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
 
 enum LayoutQueryResponse_ {
     ContentSize(Size2D<int>)
 }
 
 pub enum Msg {
     AddStylesheet(Stylesheet),
-    BuildMsg(BuildData),
+    BuildMsg(~BuildData),
     QueryMsg(LayoutQuery, Chan<LayoutQueryResponse>),
     ExitMsg
 }
 
 // Dirty bits for layout.
 pub enum Damage {
     NoDamage,               // Document is clean; do nothing.
     ReflowDamage,           // Reflow; don't perform CSS selector matching.
@@ -72,17 +72,17 @@ impl Damage {
             (ReflowDamage, NoDamage) => *self = ReflowDamage,
             (ReflowDamage, new_damage) => *self = new_damage,
             (MatchSelectorsDamage, _) => *self = MatchSelectorsDamage
         }
     }
 }
 
 pub struct BuildData {
-    node: Node,
+    node: AbstractNode,
     url: Url,
     dom_event_chan: comm::SharedChan<Event>,
     window_size: Size2D<uint>,
     content_join_chan: comm::Chan<()>,
     damage: Damage,
 }
 
 pub fn LayoutTask(render_task: RenderTask,
@@ -95,18 +95,18 @@ pub fn LayoutTask(render_task: RenderTas
 
 struct Layout {
     render_task: RenderTask,
     image_cache_task: ImageCacheTask,
     local_image_cache: @LocalImageCache,
     from_content: Port<Msg>,
 
     font_ctx: @FontContext,
-    // This is used to root auxilliary RCU reader data
-    layout_refs: DVec<@LayoutData>,
+    // This is used to root reader data
+    layout_refs: DVec<@mut LayoutData>,
     css_select_ctx: Mut<SelectCtx>,
 }
 
 fn Layout(render_task: RenderTask, 
           image_cache_task: ImageCacheTask,
           from_content: Port<Msg>,
           opts: &Opts)
        -> Layout {
@@ -162,17 +162,17 @@ impl Layout {
 
     fn handle_add_stylesheet(sheet: Stylesheet) {
         let sheet = Cell(sheet);
         do self.css_select_ctx.borrow_mut |ctx| {
             ctx.append_sheet(sheet.take(), OriginAuthor);
         }
     }
 
-    fn handle_build(data: BuildData) {
+    fn handle_build(data: &BuildData) {
         let node = &data.node;
         // FIXME: Bad copy
         let doc_url = copy data.url;
         // FIXME: Bad clone
         let dom_event_chan = data.dom_event_chan.clone();
 
         debug!("layout: received layout request for: %s", doc_url.to_str());
         debug!("layout: damage is %?", data.damage);
@@ -256,35 +256,33 @@ impl Layout {
         data.content_join_chan.send(());
     }
 
 
     fn handle_query(query: LayoutQuery, 
                     reply_chan: Chan<LayoutQueryResponse>) {
         match query {
             ContentBox(node) => {
-                let response = do node.aux |a| {
-                    match a.flow {
-                        None => Err(()),
-                        Some(flow) => {
-                            let start_val : Option<Rect<Au>> = None;
-                            let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
-                                match acc {
-                                    Some(acc) => Some(acc.union(&box.content_box())),
-                                    None => Some(box.content_box())
-                                }
-                            };
-                            
-                            match rect {
-                                None => Err(()),
-                                Some(rect) => {
-                                    let size = Size2D(rect.size.width.to_px(),
-                                                      rect.size.height.to_px());
-                                    Ok(ContentSize(size))
-                                }
+                let response = match node.layout_data().flow {
+                    None => Err(()),
+                    Some(flow) => {
+                        let start_val: Option<Rect<Au>> = None;
+                        let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
+                            match acc {
+                                Some(acc) => Some(acc.union(&box.content_box())),
+                                None => Some(box.content_box())
+                            }
+                        };
+                        
+                        match rect {
+                            None => Err(()),
+                            Some(rect) => {
+                                let size = Size2D(rect.size.width.to_px(),
+                                                  rect.size.height.to_px());
+                                Ok(ContentSize(size))
                             }
                         }
                     }
                 };
 
                 reply_chan.send(response)
             }
         }
--- a/servo/src/servo/servo.rc
+++ b/servo/src/servo/servo.rc
@@ -41,17 +41,16 @@ pub mod css {
 pub mod dom {
     pub mod bindings {
         pub mod document;
         pub mod element;
         pub mod node;
         pub mod utils;
         pub mod window;
     }
-    pub mod cow;
     pub mod document;
     pub mod element;
     pub mod event;
     pub mod node;
     pub mod window;
 }
 
 pub mod engine;