/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.

use dom::attr::{Attr, AttrHelpers};
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived};
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast};
use dom::bindings::codegen::InheritTypes::{HTMLLegendElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived;
use dom::bindings::error::{Fallible, NotFound, HierarchyRequest, Syntax};
use dom::bindings::global::{GlobalRef, Window};
use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnrootable};
use dom::bindings::js::{OptionalSettable, TemporaryPushable, OptionalRootedRootable};
use dom::bindings::js::{ResultRootable, OptionalRootable};
use dom::bindings::trace::Traceable;
use dom::bindings::utils;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::characterdata::CharacterData;
use dom::comment::Comment;
use dom::document::{Document, DocumentHelpers, HTMLDocument, NonHTMLDocument};
use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType;
use dom::element::{AttributeHandlers, Element, ElementTypeId};
use dom::element::{HTMLAnchorElementTypeId, HTMLButtonElementTypeId, ElementHelpers};
use dom::element::{HTMLInputElementTypeId, HTMLSelectElementTypeId};
use dom::element::{HTMLTextAreaElementTypeId, HTMLOptGroupElementTypeId};
use dom::element::{HTMLOptionElementTypeId, HTMLFieldSetElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::nodelist::{NodeList};
use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use dom::window::Window;
use geom::rect::Rect;
use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC,
                       LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress};
use servo_util::geometry::Au;
use servo_util::str::{DOMString, null_str_as_empty};
use style::{parse_selector_list_from_str, matches};

use js::jsapi::{JSContext, JSObject, JSRuntime};
use js::jsfriendapi;
use libc;
use libc::uintptr_t;
use std::cell::{Cell, RefCell, Ref, RefMut};
use std::iter::{Map, Filter};
use std::mem;
use style;
use style::ComputedValues;
use sync::Arc;

use serialize::{Encoder, Encodable};

//
// The basic Node structure
//

/// An HTML node.
#[deriving(Encodable)]
#[must_root]
pub struct Node {
    /// The JavaScript reflector for this node.
    pub eventtarget: EventTarget,

    /// The type of node that this is.
    type_id: NodeTypeId,

    /// The parent of this node.
    parent_node: Cell<Option<JS<Node>>>,

    /// The first child of this node.
    first_child: Cell<Option<JS<Node>>>,

    /// The last child of this node.
    last_child: Cell<Option<JS<Node>>>,

    /// The next sibling of this node.
    next_sibling: Cell<Option<JS<Node>>>,

    /// The previous sibling of this node.
    prev_sibling: Cell<Option<JS<Node>>>,

    /// The document that this node belongs to.
    owner_doc: Cell<Option<JS<Document>>>,

    /// The live list of children return by .childNodes.
    child_list: Cell<Option<JS<NodeList>>>,

    /// A bitfield of flags for node items.
    flags: Traceable<RefCell<NodeFlags>>,

    /// Layout information. Only the layout task may touch this data.
    ///
    /// Must be sent back to the layout task to be destroyed when this
    /// node is finalized.
    pub layout_data: LayoutDataRef,
}

impl<S: Encoder<E>, E> Encodable<S, E> for LayoutDataRef {
    fn encode(&self, _s: &mut S) -> Result<(), E> {
        Ok(())
    }
}

impl NodeDerived for EventTarget {
    fn is_node(&self) -> bool {
        match self.type_id {
            NodeTargetTypeId(_) => true,
            _ => false
        }
    }
}

bitflags! {
    #[doc = "Flags for node items."]
    #[deriving(Encodable)]
    flags NodeFlags: u8 {
        #[doc = "Specifies whether this node is in a document."]
        static IsInDoc = 0x01,
        #[doc = "Specifies whether this node is in hover state."]
        static InHoverState = 0x02,
        #[doc = "Specifies whether this node is in disabled state."]
        static InDisabledState = 0x04,
        #[doc = "Specifies whether this node is in enabled state."]
        static InEnabledState = 0x08
    }
}

impl NodeFlags {
    pub fn new(type_id: NodeTypeId) -> NodeFlags {
        match type_id {
            DocumentNodeTypeId => IsInDoc,
            // The following elements are enabled by default.
            ElementNodeTypeId(HTMLButtonElementTypeId) |
            ElementNodeTypeId(HTMLInputElementTypeId) |
            ElementNodeTypeId(HTMLSelectElementTypeId) |
            ElementNodeTypeId(HTMLTextAreaElementTypeId) |
            ElementNodeTypeId(HTMLOptGroupElementTypeId) |
            ElementNodeTypeId(HTMLOptionElementTypeId) |
            //ElementNodeTypeId(HTMLMenuItemElementTypeId) |
            ElementNodeTypeId(HTMLFieldSetElementTypeId) => InEnabledState,
            _ => NodeFlags::empty(),
        }
    }
}

#[unsafe_destructor]
impl Drop for Node {
    fn drop(&mut self) {
        unsafe {
            self.reap_layout_data();
        }
    }
}

/// suppress observers flag
/// http://dom.spec.whatwg.org/#concept-node-insert
/// http://dom.spec.whatwg.org/#concept-node-remove
enum SuppressObserver {
    Suppressed,
    Unsuppressed
}

/// Layout data that is shared between the script and layout tasks.
pub struct SharedLayoutData {
    /// The results of CSS styling for this node.
    pub style: Option<Arc<ComputedValues>>,
}

/// Encapsulates the abstract layout data.
pub struct LayoutData {
    chan: Option<LayoutChan>,
    _shared_data: SharedLayoutData,
    _data: *const (),
}

pub struct LayoutDataRef {
    pub data_cell: RefCell<Option<LayoutData>>,
}

impl LayoutDataRef {
    pub fn new() -> LayoutDataRef {
        LayoutDataRef {
            data_cell: RefCell::new(None),
        }
    }

    /// Returns true if there is layout data present.
    #[inline]
    pub fn is_present(&self) -> bool {
        self.data_cell.borrow().is_some()
    }

    /// Take the chan out of the layout data if it is present.
    pub fn take_chan(&self) -> Option<LayoutChan> {
        let mut layout_data = self.data_cell.borrow_mut();
        match *layout_data {
            None => None,
            Some(..) => Some(layout_data.get_mut_ref().chan.take_unwrap()),
        }
    }

    /// Borrows the layout data immutably, *asserting that there are no mutators*. Bad things will
    /// happen if you try to mutate the layout data while this is held. This is the only thread-
    /// safe layout data accessor.
    #[inline]
    pub unsafe fn borrow_unchecked(&self) -> *const Option<LayoutData> {
        mem::transmute(&self.data_cell)
    }

    /// Borrows the layout data immutably. This function is *not* thread-safe.
    #[inline]
    pub fn borrow<'a>(&'a self) -> Ref<'a,Option<LayoutData>> {
        self.data_cell.borrow()
    }

    /// Borrows the layout data mutably. This function is *not* thread-safe.
    ///
    /// FIXME(pcwalton): We should really put this behind a `MutLayoutView` phantom type, to
    /// prevent CSS selector matching from mutably accessing nodes it's not supposed to and racing
    /// on it. This has already resulted in one bug!
    #[inline]
    pub fn borrow_mut<'a>(&'a self) -> RefMut<'a,Option<LayoutData>> {
        self.data_cell.borrow_mut()
    }
}

/// The different types of nodes.
#[deriving(PartialEq,Encodable)]
pub enum NodeTypeId {
    DoctypeNodeTypeId,
    DocumentFragmentNodeTypeId,
    CommentNodeTypeId,
    DocumentNodeTypeId,
    ElementNodeTypeId(ElementTypeId),
    TextNodeTypeId,
    ProcessingInstructionNodeTypeId,
}

trait PrivateNodeHelpers {
    fn node_inserted(&self);
    fn node_removed(&self, parent_in_doc: bool);
    fn add_child(&self, new_child: &JSRef<Node>, before: Option<JSRef<Node>>);
    fn remove_child(&self, child: &JSRef<Node>);
}

impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
    // http://dom.spec.whatwg.org/#node-is-inserted
    fn node_inserted(&self) {
        assert!(self.parent_node().is_some());
        let document = document_from_node(self).root();
        let is_in_doc = self.is_in_doc();

        for node in self.traverse_preorder() {
            vtable_for(&node).bind_to_tree(is_in_doc);
        }

        let parent = self.parent_node().root();
        parent.map(|parent| vtable_for(&*parent).child_inserted(self));

        document.deref().content_changed();
    }

    // http://dom.spec.whatwg.org/#node-is-removed
    fn node_removed(&self, parent_in_doc: bool) {
        assert!(self.parent_node().is_none());
        let document = document_from_node(self).root();

        for node in self.traverse_preorder() {
            vtable_for(&node).unbind_from_tree(parent_in_doc);
        }

        document.deref().content_changed();
    }

    //
    // Pointer stitching
    //

    /// Adds a new child to the end of this node's list of children.
    ///
    /// Fails unless `new_child` is disconnected from the tree.
    fn add_child(&self, new_child: &JSRef<Node>, before: Option<JSRef<Node>>) {
        let doc = self.owner_doc().root();
        doc.deref().wait_until_safe_to_modify_dom();

        assert!(new_child.parent_node().is_none());
        assert!(new_child.prev_sibling().is_none());
        assert!(new_child.next_sibling().is_none());
        match before {
            Some(ref before) => {
                assert!(before.parent_node().root().root_ref() == Some(*self));
                match before.prev_sibling().root() {
                    None => {
                        assert!(Some(*before) == self.first_child().root().root_ref());
                        self.first_child.assign(Some(*new_child));
                    },
                    Some(ref prev_sibling) => {
                        prev_sibling.next_sibling.assign(Some(*new_child));
                        new_child.prev_sibling.assign(Some(**prev_sibling));
                    },
                }
                before.prev_sibling.assign(Some(*new_child));
                new_child.next_sibling.assign(Some(*before));
            },
            None => {
                match self.last_child().root() {
                    None => self.first_child.assign(Some(*new_child)),
                    Some(ref last_child) => {
                        assert!(last_child.next_sibling().is_none());
                        last_child.next_sibling.assign(Some(*new_child));
                        new_child.prev_sibling.assign(Some(**last_child));
                    }
                }

                self.last_child.assign(Some(*new_child));
            },
        }

        new_child.parent_node.assign(Some(*self));
    }

    /// Removes the given child from this node's list of children.
    ///
    /// Fails unless `child` is a child of this node.
    fn remove_child(&self, child: &JSRef<Node>) {
        let doc = self.owner_doc().root();
        doc.deref().wait_until_safe_to_modify_dom();

        assert!(child.parent_node().root().root_ref() == Some(*self));

        match child.prev_sibling.get().root() {
            None => {
                self.first_child.assign(child.next_sibling.get());
            }
            Some(ref prev_sibling) => {
                prev_sibling.next_sibling.assign(child.next_sibling.get());
            }
        }

        match child.next_sibling.get().root() {
            None => {
                self.last_child.assign(child.prev_sibling.get());
            }
            Some(ref next_sibling) => {
                next_sibling.prev_sibling.assign(child.prev_sibling.get());
            }
        }

        child.prev_sibling.set(None);
        child.next_sibling.set(None);
        child.parent_node.set(None);
    }
}

pub trait NodeHelpers<'m, 'n> {
    fn ancestors(&self) -> AncestorIterator<'n>;
    fn children(&self) -> AbstractNodeChildrenIterator<'n>;
    fn child_elements(&self) -> ChildElementIterator<'m, 'n>;
    fn following_siblings(&self) -> AbstractNodeChildrenIterator<'n>;
    fn is_in_doc(&self) -> bool;
    fn is_inclusive_ancestor_of(&self, parent: &JSRef<Node>) -> bool;
    fn is_parent_of(&self, child: &JSRef<Node>) -> bool;

    fn type_id(&self) -> NodeTypeId;

    fn parent_node(&self) -> Option<Temporary<Node>>;
    fn first_child(&self) -> Option<Temporary<Node>>;
    fn last_child(&self) -> Option<Temporary<Node>>;
    fn prev_sibling(&self) -> Option<Temporary<Node>>;
    fn next_sibling(&self) -> Option<Temporary<Node>>;

    fn owner_doc(&self) -> Temporary<Document>;
    fn set_owner_doc(&self, document: &JSRef<Document>);
    fn is_in_html_doc(&self) -> bool;

    fn wait_until_safe_to_modify_dom(&self);

    fn is_element(&self) -> bool;
    fn is_document(&self) -> bool;
    fn is_doctype(&self) -> bool;
    fn is_text(&self) -> bool;
    fn is_anchor_element(&self) -> bool;

    fn get_hover_state(&self) -> bool;
    fn set_hover_state(&self, state: bool);

    fn get_disabled_state(&self) -> bool;
    fn set_disabled_state(&self, state: bool);

    fn get_enabled_state(&self) -> bool;
    fn set_enabled_state(&self, state: bool);

    fn dump(&self);
    fn dump_indent(&self, indent: uint);
    fn debug_str(&self) -> String;

    fn traverse_preorder(&self) -> TreeIterator<'n>;
    fn sequential_traverse_postorder(&self) -> TreeIterator<'n>;
    fn inclusively_following_siblings(&self) -> AbstractNodeChildrenIterator<'n>;

    fn to_trusted_node_address(&self) -> TrustedNodeAddress;

    fn get_bounding_content_box(&self) -> Rect<Au>;
    fn get_content_boxes(&self) -> Vec<Rect<Au>>;

    fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>;
    fn query_selector_all(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>>;

    fn remove_self(&self);
}

impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> {
    /// Dumps the subtree rooted at this node, for debugging.
    fn dump(&self) {
        self.dump_indent(0);
    }

    /// Dumps the node tree, for debugging, with indentation.
    fn dump_indent(&self, indent: uint) {
        let mut s = String::new();
        for _ in range(0, indent) {
            s.push_str("    ");
        }

        s.push_str(self.debug_str().as_slice());
        debug!("{:s}", s);

        // FIXME: this should have a pure version?
        for kid in self.children() {
            kid.dump_indent(indent + 1u)
        }
    }

    /// Returns a string that describes this node.
    fn debug_str(&self) -> String {
        format!("{:?}", self.type_id)
    }

    fn is_in_doc(&self) -> bool {
        self.deref().flags.deref().borrow().contains(IsInDoc)
    }

    /// Returns the type ID of this node. Fails if this node is borrowed mutably.
    fn type_id(&self) -> NodeTypeId {
        self.deref().type_id
    }

    fn parent_node(&self) -> Option<Temporary<Node>> {
        self.deref().parent_node.get().map(|node| Temporary::new(node))
    }

    fn first_child(&self) -> Option<Temporary<Node>> {
        self.deref().first_child.get().map(|node| Temporary::new(node))
    }

    fn last_child(&self) -> Option<Temporary<Node>> {
        self.deref().last_child.get().map(|node| Temporary::new(node))
    }

    /// Returns the previous sibling of this node. Fails if this node is borrowed mutably.
    fn prev_sibling(&self) -> Option<Temporary<Node>> {
        self.deref().prev_sibling.get().map(|node| Temporary::new(node))
    }

    /// Returns the next sibling of this node. Fails if this node is borrowed mutably.
    fn next_sibling(&self) -> Option<Temporary<Node>> {
        self.deref().next_sibling.get().map(|node| Temporary::new(node))
    }

    #[inline]
    fn is_element(&self) -> bool {
        match self.type_id {
            ElementNodeTypeId(..) => true,
            _ => false
        }
    }

    #[inline]
    fn is_document(&self) -> bool {
        self.type_id == DocumentNodeTypeId
    }

    #[inline]
    fn is_anchor_element(&self) -> bool {
        self.type_id == ElementNodeTypeId(HTMLAnchorElementTypeId)
    }

    #[inline]
    fn is_doctype(&self) -> bool {
        self.type_id == DoctypeNodeTypeId
    }

    #[inline]
    fn is_text(&self) -> bool {
        self.type_id == TextNodeTypeId
    }

    fn get_hover_state(&self) -> bool {
        self.flags.deref().borrow().contains(InHoverState)
    }

    fn set_hover_state(&self, state: bool) {
        if state {
            self.flags.deref().borrow_mut().insert(InHoverState);
        } else {
            self.flags.deref().borrow_mut().remove(InHoverState);
        }
    }

    fn get_disabled_state(&self) -> bool {
        self.flags.deref().borrow().contains(InDisabledState)
    }

    fn set_disabled_state(&self, state: bool) {
        if state {
            self.flags.deref().borrow_mut().insert(InDisabledState);
        } else {
            self.flags.deref().borrow_mut().remove(InDisabledState);
        }
    }

    fn get_enabled_state(&self) -> bool {
        self.flags.deref().borrow().contains(InEnabledState)
    }

    fn set_enabled_state(&self, state: bool) {
        if state {
            self.flags.deref().borrow_mut().insert(InEnabledState);
        } else {
            self.flags.deref().borrow_mut().remove(InEnabledState);
        }
    }

    /// Iterates over this node and all its descendants, in preorder.
    fn traverse_preorder(&self) -> TreeIterator<'n> {
        let mut nodes = vec!();
        gather_abstract_nodes(self, &mut nodes, false);
        TreeIterator::new(nodes)
    }

    /// Iterates over this node and all its descendants, in postorder.
    fn sequential_traverse_postorder(&self) -> TreeIterator<'n> {
        let mut nodes = vec!();
        gather_abstract_nodes(self, &mut nodes, true);
        TreeIterator::new(nodes)
    }

    fn inclusively_following_siblings(&self) -> AbstractNodeChildrenIterator<'n> {
        AbstractNodeChildrenIterator {
            current_node: Some(self.clone()),
        }
    }

    fn is_inclusive_ancestor_of(&self, parent: &JSRef<Node>) -> bool {
        self == parent || parent.ancestors().any(|ancestor| &ancestor == self)
    }

    fn following_siblings(&self) -> AbstractNodeChildrenIterator<'n> {
        AbstractNodeChildrenIterator {
            current_node: self.next_sibling().root().map(|next| next.deref().clone()),
        }
    }

    fn is_parent_of(&self, child: &JSRef<Node>) -> bool {
        match child.parent_node() {
            Some(ref parent) if *parent == Temporary::from_rooted(self) => true,
            _ => false
        }
    }

    fn to_trusted_node_address(&self) -> TrustedNodeAddress {
        TrustedNodeAddress(self.deref() as *const Node as *const libc::c_void)
    }

    fn get_bounding_content_box(&self) -> Rect<Au> {
        let window = window_from_node(self).root();
        let page = window.deref().page();
        let addr = self.to_trusted_node_address();

        let ContentBoxResponse(rect) = page.layout_rpc.content_box(addr);
        rect
    }

    fn get_content_boxes(&self) -> Vec<Rect<Au>> {
        let window = window_from_node(self).root();
        let page = window.deref().page();
        let addr = self.to_trusted_node_address();
        let ContentBoxesResponse(rects) = page.layout_rpc.content_boxes(addr);
        rects
    }

    // http://dom.spec.whatwg.org/#dom-parentnode-queryselector
    fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
        // Step 1.
        match parse_selector_list_from_str(selectors.as_slice()) {
            // Step 2.
            Err(()) => return Err(Syntax),
            // Step 3.
            Ok(ref selectors) => {
                let root = self.ancestors().last().unwrap_or(self.clone());
                for node in root.traverse_preorder() {
                    if node.is_element() && matches(selectors, &node, &mut None) {
                        let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
                        return Ok(Some(Temporary::from_rooted(elem)));
                    }
                }
            }
        }
        Ok(None)
    }

    // http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
    fn query_selector_all(&self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
        // Step 1.
        let nodes;
        let root = self.ancestors().last().unwrap_or(self.clone());
        match parse_selector_list_from_str(selectors.as_slice()) {
            // Step 2.
            Err(()) => return Err(Syntax),
            // Step 3.
            Ok(ref selectors) => {
                nodes = root.traverse_preorder().filter(
                    // TODO(cgaebel): Is it worth it to build a bloom filter here
                    // (instead of passing `None`)? Probably.
                    |node| node.is_element() && matches(selectors, node, &mut None)).collect()
            }
        }
        let window = window_from_node(self).root();
        Ok(NodeList::new_simple_list(&window.root_ref(), nodes))
    }

    fn ancestors(&self) -> AncestorIterator<'n> {
        AncestorIterator {
            current: self.parent_node.get().map(|node| (*node.root()).clone()),
        }
    }

    fn owner_doc(&self) -> Temporary<Document> {
        Temporary::new(self.owner_doc.get().get_ref().clone())
    }

    fn set_owner_doc(&self, document: &JSRef<Document>) {
        self.owner_doc.assign(Some(document.clone()));
    }

    fn is_in_html_doc(&self) -> bool {
        self.owner_doc().root().is_html_document
    }

    fn children(&self) -> AbstractNodeChildrenIterator<'n> {
        AbstractNodeChildrenIterator {
            current_node: self.first_child.get().map(|node| (*node.root()).clone()),
        }
    }

    fn child_elements(&self) -> ChildElementIterator<'m, 'n> {
        self.children()
            .filter(|node| {
                node.is_element()
            })
            .map(|node| {
                let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap();
                elem.clone()
            })
    }

    fn wait_until_safe_to_modify_dom(&self) {
        let document = self.owner_doc().root();
        document.deref().wait_until_safe_to_modify_dom();
    }

    fn remove_self(&self) {
        match self.parent_node().root() {
            Some(ref parent) => parent.remove_child(self),
            None => ()
        }
    }
}

/// If the given untrusted node address represents a valid DOM node in the given runtime,
/// returns it.
pub fn from_untrusted_node_address(runtime: *mut JSRuntime, candidate: UntrustedNodeAddress)
    -> Temporary<Node> {
    unsafe {
        let candidate: uintptr_t = mem::transmute(candidate);
        let object: *mut JSObject = jsfriendapi::bindgen::JS_GetAddressableObject(runtime,
                                                                                  candidate);
        if object.is_null() {
            fail!("Attempted to create a `JS<Node>` from an invalid pointer!")
        }
        let boxed_node: *const Node = utils::unwrap(object);
        Temporary::new(JS::from_raw(boxed_node))
    }
}

pub trait LayoutNodeHelpers {
    unsafe fn type_id_for_layout(&self) -> NodeTypeId;

    unsafe fn parent_node_ref(&self) -> Option<JS<Node>>;
    unsafe fn first_child_ref(&self) -> Option<JS<Node>>;
    unsafe fn last_child_ref(&self) -> Option<JS<Node>>;
    unsafe fn prev_sibling_ref(&self) -> Option<JS<Node>>;
    unsafe fn next_sibling_ref(&self) -> Option<JS<Node>>;

    unsafe fn owner_doc_for_layout(&self) -> JS<Document>;

    unsafe fn is_element_for_layout(&self) -> bool;
}

impl LayoutNodeHelpers for JS<Node> {
    #[inline]
    unsafe fn type_id_for_layout(&self) -> NodeTypeId {
        (*self.unsafe_get()).type_id
    }

    #[inline]
    unsafe fn is_element_for_layout(&self) -> bool {
        (*self.unsafe_get()).is_element()
    }

    #[inline]
    unsafe fn parent_node_ref(&self) -> Option<JS<Node>> {
        (*self.unsafe_get()).parent_node.get()
    }

    #[inline]
    unsafe fn first_child_ref(&self) -> Option<JS<Node>> {
        (*self.unsafe_get()).first_child.get()
    }

    #[inline]
    unsafe fn last_child_ref(&self) -> Option<JS<Node>> {
        (*self.unsafe_get()).last_child.get()
    }

    #[inline]
    unsafe fn prev_sibling_ref(&self) -> Option<JS<Node>> {
        (*self.unsafe_get()).prev_sibling.get()
    }

    #[inline]
    unsafe fn next_sibling_ref(&self) -> Option<JS<Node>> {
        (*self.unsafe_get()).next_sibling.get()
    }

    #[inline]
    unsafe fn owner_doc_for_layout(&self) -> JS<Document> {
        (*self.unsafe_get()).owner_doc.get().unwrap()
    }
}

pub trait RawLayoutNodeHelpers {
    unsafe fn get_hover_state_for_layout(&self) -> bool;
    unsafe fn get_disabled_state_for_layout(&self) -> bool;
    unsafe fn get_enabled_state_for_layout(&self) -> bool;
    fn type_id_for_layout(&self) -> NodeTypeId;
}

impl RawLayoutNodeHelpers for Node {
    unsafe fn get_hover_state_for_layout(&self) -> bool {
        (*self.unsafe_get_flags()).contains(InHoverState)
    }
    unsafe fn get_disabled_state_for_layout(&self) -> bool {
        (*self.unsafe_get_flags()).contains(InDisabledState)
    }
    unsafe fn get_enabled_state_for_layout(&self) -> bool {
        (*self.unsafe_get_flags()).contains(InEnabledState)
    }

    fn type_id_for_layout(&self) -> NodeTypeId {
        self.type_id
    }
}


//
// Iteration and traversal
//

pub type ChildElementIterator<'a, 'b> = Map<'a, JSRef<'b, Node>,
                                            JSRef<'b, Element>,
                                            Filter<'a, JSRef<'b, Node>, AbstractNodeChildrenIterator<'b>>>;

pub struct AbstractNodeChildrenIterator<'a> {
    current_node: Option<JSRef<'a, Node>>,
}

impl<'a> Iterator<JSRef<'a, Node>> for AbstractNodeChildrenIterator<'a> {
    fn next(&mut self) -> Option<JSRef<'a, Node>> {
        let node = self.current_node.clone();
        self.current_node = node.clone().and_then(|node| {
            node.next_sibling().map(|node| (*node.root()).clone())
        });
        node
    }
}

pub struct AncestorIterator<'a> {
    current: Option<JSRef<'a, Node>>,
}

impl<'a> Iterator<JSRef<'a, Node>> for AncestorIterator<'a> {
    fn next(&mut self) -> Option<JSRef<'a, Node>> {
        if self.current.is_none() {
            return None;
        }

        // FIXME: Do we need two clones here?
        let x = self.current.get_ref().clone();
        self.current = x.parent_node().map(|node| (*node.root()).clone());
        Some(x)
    }
}

// FIXME: Do this without precomputing a vector of refs.
// Easy for preorder; harder for postorder.
pub struct TreeIterator<'a> {
    nodes: Vec<JSRef<'a, Node>>,
    index: uint,
}

impl<'a> TreeIterator<'a> {
    fn new(nodes: Vec<JSRef<'a, Node>>) -> TreeIterator<'a> {
        TreeIterator {
            nodes: nodes,
            index: 0,
        }
    }
}

impl<'a> Iterator<JSRef<'a, Node>> for TreeIterator<'a> {
    fn next(&mut self) -> Option<JSRef<'a, Node>> {
        if self.index >= self.nodes.len() {
            None
        } else {
            let v = self.nodes[self.index];
            let v = v.clone();
            self.index += 1;
            Some(v)
        }
    }
}

#[must_root]
pub struct NodeIterator {
    pub start_node: JS<Node>,
    pub current_node: Option<JS<Node>>,
    pub depth: uint,
    include_start: bool,
    include_descendants_of_void: bool
}

impl NodeIterator {
    pub fn new<'a>(start_node: &JSRef<'a, Node>,
                   include_start: bool,
                   include_descendants_of_void: bool) -> NodeIterator {
        NodeIterator {
            start_node: JS::from_rooted(start_node),
            current_node: None,
            depth: 0,
            include_start: include_start,
            include_descendants_of_void: include_descendants_of_void
        }
    }

    fn next_child<'b>(&self, node: &JSRef<'b, Node>) -> Option<JSRef<'b, Node>> {
        if !self.include_descendants_of_void && node.is_element() {
            let elem: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
            if elem.deref().is_void() {
                None
            } else {
                node.first_child().map(|child| (*child.root()).clone())
            }
        } else {
            node.first_child().map(|child| (*child.root()).clone())
        }
    }
}

impl<'a> Iterator<JSRef<'a, Node>> for NodeIterator {
    fn next(&mut self) -> Option<JSRef<'a, Node>> {
        self.current_node = match self.current_node.as_ref().map(|node| node.root()) {
            None => {
                if self.include_start {
                    Some(self.start_node)
                } else {
                    self.next_child(&*self.start_node.root())
                        .map(|child| JS::from_rooted(&child))
                }
            },
            Some(node) => {
                match self.next_child(&*node) {
                    Some(child) => {
                        self.depth += 1;
                        Some(JS::from_rooted(&child))
                    },
                    None if JS::from_rooted(&*node) == self.start_node => None,
                    None => {
                        match node.deref().next_sibling().root() {
                            Some(sibling) => Some(JS::from_rooted(&*sibling)),
                            None => {
                                let mut candidate = node.deref().clone();
                                while candidate.next_sibling().is_none() {
                                    candidate = (*candidate.parent_node()
                                                          .expect("Got to root without reaching start node")
                                                          .root()).clone();
                                    self.depth -= 1;
                                    if JS::from_rooted(&candidate) == self.start_node {
                                        break;
                                    }
                                }
                                if JS::from_rooted(&candidate) != self.start_node {
                                    candidate.next_sibling().map(|node| JS::from_rooted(node.root().deref()))
                                } else {
                                    None
                                }
                            }
                        }
                    }
                }
            }
        };
        self.current_node.map(|node| (*node.root()).clone())
    }
}

fn gather_abstract_nodes<'a>(cur: &JSRef<'a, Node>, refs: &mut Vec<JSRef<'a, Node>>, postorder: bool) {
    if !postorder {
        refs.push(cur.clone());
    }
    for kid in cur.children() {
        gather_abstract_nodes(&kid, refs, postorder)
    }
    if postorder {
        refs.push(cur.clone());
    }
}

/// Specifies whether children must be recursively cloned or not.
#[deriving(PartialEq)]
pub enum CloneChildrenFlag {
    CloneChildren,
    DoNotCloneChildren
}

fn as_uintptr<T>(t: &T) -> uintptr_t { t as *const T as uintptr_t }

impl Node {
    pub fn reflect_node<N: Reflectable+NodeBase>
            (node:      Box<N>,
             document:  &JSRef<Document>,
             wrap_fn:   extern "Rust" fn(*mut JSContext, &GlobalRef, Box<N>) -> Temporary<N>)
             -> Temporary<N> {
        let window = document.window.root();
        reflect_dom_object(node, &Window(*window), wrap_fn)
    }

    pub fn new_inherited(type_id: NodeTypeId, doc: &JSRef<Document>) -> Node {
        Node::new_(type_id, Some(doc.clone()))
    }

    pub fn new_without_doc(type_id: NodeTypeId) -> Node {
        Node::new_(type_id, None)
    }

    fn new_(type_id: NodeTypeId, doc: Option<JSRef<Document>>) -> Node {
        Node {
            eventtarget: EventTarget::new_inherited(NodeTargetTypeId(type_id)),
            type_id: type_id,

            parent_node: Cell::new(None),
            first_child: Cell::new(None),
            last_child: Cell::new(None),
            next_sibling: Cell::new(None),
            prev_sibling: Cell::new(None),
            owner_doc: Cell::new(doc.unrooted()),
            child_list: Cell::new(None),

            flags: Traceable::new(RefCell::new(NodeFlags::new(type_id))),

            layout_data: LayoutDataRef::new(),
        }
    }

    // http://dom.spec.whatwg.org/#concept-node-adopt
    pub fn adopt(node: &JSRef<Node>, document: &JSRef<Document>) {
        // Step 1.
        match node.parent_node().root() {
            Some(parent) => {
                Node::remove(node, &*parent, Unsuppressed);
            }
            None => (),
        }

        // Step 2.
        let node_doc = document_from_node(node).root();
        if &*node_doc != document {
            for descendant in node.traverse_preorder() {
                descendant.set_owner_doc(document);
            }
        }

        // Step 3.
        // If node is an element, it is _affected by a base URL change_.
    }

    // http://dom.spec.whatwg.org/#concept-node-pre-insert
    fn pre_insert(node: &JSRef<Node>, parent: &JSRef<Node>, child: Option<JSRef<Node>>)
                  -> Fallible<Temporary<Node>> {
        // Step 1.
        match parent.type_id() {
            DocumentNodeTypeId |
            DocumentFragmentNodeTypeId |
            ElementNodeTypeId(..) => (),
            _ => return Err(HierarchyRequest)
        }

        // Step 2.
        if node.is_inclusive_ancestor_of(parent) {
            return Err(HierarchyRequest);
        }

        // Step 3.
        match child {
            Some(ref child) if !parent.is_parent_of(child) => return Err(NotFound),
            _ => ()
        }

        // Step 4-5.
        match node.type_id() {
            TextNodeTypeId => {
                match node.parent_node().root() {
                    Some(ref parent) if parent.is_document() => return Err(HierarchyRequest),
                    _ => ()
                }
            }
            DoctypeNodeTypeId => {
                match node.parent_node().root() {
                    Some(ref parent) if !parent.is_document() => return Err(HierarchyRequest),
                    _ => ()
                }
            }
            DocumentFragmentNodeTypeId |
            ElementNodeTypeId(_) |
            ProcessingInstructionNodeTypeId |
            CommentNodeTypeId => (),
            DocumentNodeTypeId => return Err(HierarchyRequest)
        }

        // Step 6.
        match parent.type_id() {
            DocumentNodeTypeId => {
                match node.type_id() {
                    // Step 6.1
                    DocumentFragmentNodeTypeId => {
                        // Step 6.1.1(b)
                        if node.children().any(|c| c.is_text()) {
                            return Err(HierarchyRequest);
                        }
                        match node.child_elements().count() {
                            0 => (),
                            // Step 6.1.2
                            1 => {
                                // FIXME: change to empty() when https://github.com/mozilla/rust/issues/11218
                                // will be fixed
                                if parent.child_elements().count() > 0 {
                                    return Err(HierarchyRequest);
                                }
                                match child {
                                    Some(ref child) => {
                                        if child.inclusively_following_siblings()
                                            .any(|child| child.is_doctype()) {
                                                return Err(HierarchyRequest)
                                            }
                                    }
                                    _ => (),
                                }
                            },
                            // Step 6.1.1(a)
                            _ => return Err(HierarchyRequest),
                        }
                    },
                    // Step 6.2
                    ElementNodeTypeId(_) => {
                        // FIXME: change to empty() when https://github.com/mozilla/rust/issues/11218
                        // will be fixed
                        if parent.child_elements().count() > 0 {
                            return Err(HierarchyRequest);
                        }
                        match child {
                            Some(ref child) => {
                                if child.inclusively_following_siblings()
                                    .any(|child| child.is_doctype()) {
                                        return Err(HierarchyRequest)
                                    }
                            }
                            _ => (),
                        }
                    },
                    // Step 6.3
                    DoctypeNodeTypeId => {
                        if parent.children().any(|c| c.is_doctype()) {
                            return Err(HierarchyRequest);
                        }
                        match child {
                            Some(ref child) => {
                                if parent.children()
                                    .take_while(|c| c != child)
                                    .any(|c| c.is_element()) {
                                    return Err(HierarchyRequest);
                                }
                            },
                            None => {
                                // FIXME: change to empty() when https://github.com/mozilla/rust/issues/11218
                                // will be fixed
                                if parent.child_elements().count() > 0 {
                                    return Err(HierarchyRequest);
                                }
                            },
                        }
                    },
                    TextNodeTypeId |
                    ProcessingInstructionNodeTypeId |
                    CommentNodeTypeId => (),
                    DocumentNodeTypeId => unreachable!(),
                }
            },
            _ => (),
        }

        // Step 7-8.
        let referenceChild = match child {
            Some(ref child) if child == node => node.next_sibling().map(|node| (*node.root()).clone()),
            _ => child
        };

        // Step 9.
        let document = document_from_node(parent).root();
        Node::adopt(node, &*document);

        // Step 10.
        Node::insert(node, parent, referenceChild, Unsuppressed);

        // Step 11.
        return Ok(Temporary::from_rooted(node))
    }

    // http://dom.spec.whatwg.org/#concept-node-insert
    fn insert(node: &JSRef<Node>,
              parent: &JSRef<Node>,
              child: Option<JSRef<Node>>,
              suppress_observers: SuppressObserver) {
        // XXX assert owner_doc
        // Step 1-3: ranges.
        // Step 4.
        let mut nodes = match node.type_id() {
            DocumentFragmentNodeTypeId => node.children().collect(),
            _ => vec!(node.clone()),
        };

        // Step 5: DocumentFragment, mutation records.
        // Step 6: DocumentFragment.
        match node.type_id() {
            DocumentFragmentNodeTypeId => {
                for c in node.children() {
                    Node::remove(&c, node, Suppressed);
                }
            },
            _ => (),
        }

        // Step 7: mutation records.
        // Step 8.
        for node in nodes.mut_iter() {
            parent.add_child(node, child);
            let is_in_doc = parent.is_in_doc();
            for kid in node.traverse_preorder() {
                if is_in_doc {
                    kid.flags.deref().borrow_mut().insert(IsInDoc);
                } else {
                    kid.flags.deref().borrow_mut().remove(IsInDoc);
                }
            }
        }

        // Step 9.
        match suppress_observers {
            Unsuppressed => {
                for node in nodes.iter() {
                    node.node_inserted();
                }
            }
            Suppressed => ()
        }
    }

    // http://dom.spec.whatwg.org/#concept-node-replace-all
    fn replace_all(node: Option<JSRef<Node>>, parent: &JSRef<Node>) {

        // Step 1.
        match node {
            Some(ref node) => {
                let document = document_from_node(parent).root();
                Node::adopt(node, &*document);
            }
            None => (),
        }

        // Step 2.
        let removedNodes: Vec<JSRef<Node>> = parent.children().collect();

        // Step 3.
        let addedNodes = match node {
            None => vec!(),
            Some(ref node) => match node.type_id() {
                DocumentFragmentNodeTypeId => node.children().collect(),
                _ => vec!(node.clone()),
            },
        };

        // Step 4.
        for child in parent.children() {
            Node::remove(&child, parent, Suppressed);
        }

        // Step 5.
        match node {
            Some(ref node) => Node::insert(node, parent, None, Suppressed),
            None => (),
        }

        // Step 6: mutation records.

        // Step 7.
        let parent_in_doc = parent.is_in_doc();
        for removedNode in removedNodes.iter() {
            removedNode.node_removed(parent_in_doc);
        }
        for addedNode in addedNodes.iter() {
            addedNode.node_inserted();
        }
    }

    // http://dom.spec.whatwg.org/#concept-node-pre-remove
    fn pre_remove(child: &JSRef<Node>, parent: &JSRef<Node>) -> Fallible<Temporary<Node>> {
        // Step 1.
        match child.parent_node() {
            Some(ref node) if *node != Temporary::from_rooted(parent) => return Err(NotFound),
            _ => ()
        }

        // Step 2.
        Node::remove(child, parent, Unsuppressed);

        // Step 3.
        Ok(Temporary::from_rooted(child))
    }

    // http://dom.spec.whatwg.org/#concept-node-remove
    fn remove(node: &JSRef<Node>, parent: &JSRef<Node>, suppress_observers: SuppressObserver) {
        assert!(node.parent_node().map_or(false, |node_parent| node_parent == Temporary::from_rooted(parent)));

        // Step 1-5: ranges.
        // Step 6-7: mutation observers.
        // Step 8.
        parent.remove_child(node);

        node.deref().flags.deref().borrow_mut().remove(IsInDoc);

        // Step 9.
        match suppress_observers {
            Suppressed => (),
            Unsuppressed => node.node_removed(parent.is_in_doc()),
        }
    }

    // http://dom.spec.whatwg.org/#concept-node-clone
    pub fn clone(node: &JSRef<Node>, maybe_doc: Option<&JSRef<Document>>,
                 clone_children: CloneChildrenFlag) -> Temporary<Node> {

        // Step 1.
        let document = match maybe_doc {
            Some(doc) => JS::from_rooted(doc).root(),
            None => node.owner_doc().root()
        };

        // Step 2.
        // XXXabinader: clone() for each node as trait?
        let copy: Root<Node> = match node.type_id() {
            DoctypeNodeTypeId => {
                let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(node).unwrap();
                let doctype = doctype.deref();
                let doctype = DocumentType::new(doctype.name.clone(),
                                                Some(doctype.public_id.clone()),
                                                Some(doctype.system_id.clone()), &*document);
                NodeCast::from_temporary(doctype)
            },
            DocumentFragmentNodeTypeId => {
                let doc_fragment = DocumentFragment::new(&*document);
                NodeCast::from_temporary(doc_fragment)
            },
            CommentNodeTypeId => {
                let comment: &JSRef<Comment> = CommentCast::to_ref(node).unwrap();
                let comment = comment.deref();
                let comment = Comment::new(comment.characterdata.data.deref().borrow().clone(), &*document);
                NodeCast::from_temporary(comment)
            },
            DocumentNodeTypeId => {
                let document: &JSRef<Document> = DocumentCast::to_ref(node).unwrap();
                let is_html_doc = match document.is_html_document {
                    true => HTMLDocument,
                    false => NonHTMLDocument
                };
                let window = document.window.root();
                let document = Document::new(&*window, Some(document.url().clone()),
                                             is_html_doc, None);
                NodeCast::from_temporary(document)
            },
            ElementNodeTypeId(..) => {
                let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
                let element = element.deref();
                let element = build_element_from_tag(element.local_name.as_slice().to_string(),
                    element.namespace.clone(), &*document);
                NodeCast::from_temporary(element)
            },
            TextNodeTypeId => {
                let text: &JSRef<Text> = TextCast::to_ref(node).unwrap();
                let text = text.deref();
                let text = Text::new(text.characterdata.data.deref().borrow().clone(), &*document);
                NodeCast::from_temporary(text)
            },
            ProcessingInstructionNodeTypeId => {
                let pi: &JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
                let pi = pi.deref();
                let pi = ProcessingInstruction::new(pi.target.clone(),
                                                    pi.characterdata.data.deref().borrow().clone(), &*document);
                NodeCast::from_temporary(pi)
            },
        }.root();

        // Step 3.
        let document = if copy.is_document() {
            let doc: &JSRef<Document> = DocumentCast::to_ref(&*copy).unwrap();
            JS::from_rooted(doc).root()
        } else {
            JS::from_rooted(&*document).root()
        };
        assert!(&*copy.owner_doc().root() == &*document);

        // Step 4 (some data already copied in step 2).
        match node.type_id() {
            DocumentNodeTypeId => {
                let node_doc: &JSRef<Document> = DocumentCast::to_ref(node).unwrap();
                let copy_doc: &JSRef<Document> = DocumentCast::to_ref(&*copy).unwrap();
                copy_doc.set_encoding_name(node_doc.encoding_name.deref().borrow().clone());
                copy_doc.set_quirks_mode(node_doc.quirks_mode());
            },
            ElementNodeTypeId(..) => {
                let node_elem: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
                let copy_elem: &JSRef<Element> = ElementCast::to_ref(&*copy).unwrap();

                // FIXME: https://github.com/mozilla/servo/issues/1737
                let window = document.deref().window.root();
                for attr in node_elem.deref().attrs.borrow().iter().map(|attr| attr.root()) {
                    copy_elem.deref().attrs.borrow_mut().push_unrooted(
                        &Attr::new(&*window,
                                   attr.local_name().clone(), attr.deref().value().clone(),
                                   attr.deref().name.clone(), attr.deref().namespace.clone(),
                                   attr.deref().prefix.clone(), copy_elem));
                }
            },
            _ => ()
        }

        // Step 5: cloning steps.

        // Step 6.
        if clone_children == CloneChildren {
            for ref child in node.children() {
                let child_copy = Node::clone(&*child, Some(&*document), clone_children).root();
                let _inserted_node = Node::pre_insert(&*child_copy, &*copy, None);
            }
        }

        // Step 7.
        Temporary::from_rooted(&*copy)
    }

    /// Sends layout data, if any, back to the layout task to be destroyed.
    unsafe fn reap_layout_data(&mut self) {
        if self.layout_data.is_present() {
            let layout_data = mem::replace(&mut self.layout_data, LayoutDataRef::new());
            let layout_chan = layout_data.take_chan();
            match layout_chan {
                None => {}
                Some(chan) => {
                    let LayoutChan(chan) = chan;
                    chan.send(ReapLayoutDataMsg(layout_data))
                },
            }
        }
    }

    pub unsafe fn unsafe_get_flags(&self) -> *const NodeFlags {
        mem::transmute(&self.flags)
    }

    pub fn collect_text_contents<'a, T: Iterator<JSRef<'a, Node>>>(mut iterator: T) -> String {
        let mut content = String::new();
        for node in iterator {
            let text: Option<&JSRef<Text>> = TextCast::to_ref(&node);
            match text {
                Some(text) => content.push_str(text.characterdata.data.borrow().as_slice()),
                None => (),
            }
        }
        content
    }
}

impl<'a> NodeMethods for JSRef<'a, Node> {
    // http://dom.spec.whatwg.org/#dom-node-nodetype
    fn NodeType(&self) -> u16 {
        match self.type_id {
            ElementNodeTypeId(_)            => NodeConstants::ELEMENT_NODE,
            TextNodeTypeId                  => NodeConstants::TEXT_NODE,
            ProcessingInstructionNodeTypeId => NodeConstants::PROCESSING_INSTRUCTION_NODE,
            CommentNodeTypeId               => NodeConstants::COMMENT_NODE,
            DocumentNodeTypeId              => NodeConstants::DOCUMENT_NODE,
            DoctypeNodeTypeId               => NodeConstants::DOCUMENT_TYPE_NODE,
            DocumentFragmentNodeTypeId      => NodeConstants::DOCUMENT_FRAGMENT_NODE,
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-nodename
    fn NodeName(&self) -> DOMString {
        match self.type_id {
            ElementNodeTypeId(..) => {
                let elem: &JSRef<Element> = ElementCast::to_ref(self).unwrap();
                elem.TagName()
            }
            TextNodeTypeId => "#text".to_string(),
            ProcessingInstructionNodeTypeId => {
                let processing_instruction: &JSRef<ProcessingInstruction> =
                    ProcessingInstructionCast::to_ref(self).unwrap();
                processing_instruction.Target()
            }
            CommentNodeTypeId => "#comment".to_string(),
            DoctypeNodeTypeId => {
                let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(self).unwrap();
                doctype.deref().name.clone()
            },
            DocumentFragmentNodeTypeId => "#document-fragment".to_string(),
            DocumentNodeTypeId => "#document".to_string()
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-baseuri
    fn GetBaseURI(&self) -> Option<DOMString> {
        // FIXME (#1824) implement.
        None
    }

    // http://dom.spec.whatwg.org/#dom-node-ownerdocument
    fn GetOwnerDocument(&self) -> Option<Temporary<Document>> {
        match self.type_id {
            ElementNodeTypeId(..) |
            CommentNodeTypeId |
            TextNodeTypeId |
            ProcessingInstructionNodeTypeId |
            DoctypeNodeTypeId |
            DocumentFragmentNodeTypeId => Some(self.owner_doc()),
            DocumentNodeTypeId => None
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-parentnode
    fn GetParentNode(&self) -> Option<Temporary<Node>> {
        self.parent_node.get().map(|node| Temporary::new(node))
    }

    // http://dom.spec.whatwg.org/#dom-node-parentelement
    fn GetParentElement(&self) -> Option<Temporary<Element>> {
        self.parent_node.get()
                        .and_then(|parent| {
                            let parent = parent.root();
                            ElementCast::to_ref(&*parent).map(|elem| {
                                Temporary::from_rooted(elem)
                            })
                        })
    }

    // http://dom.spec.whatwg.org/#dom-node-haschildnodes
    fn HasChildNodes(&self) -> bool {
        self.first_child.get().is_some()
    }

    // http://dom.spec.whatwg.org/#dom-node-childnodes
    fn ChildNodes(&self) -> Temporary<NodeList> {
        match self.child_list.get() {
            None => (),
            Some(ref list) => return Temporary::new(list.clone()),
        }

        let doc = self.owner_doc().root();
        let window = doc.deref().window.root();
        let child_list = NodeList::new_child_list(&*window, self);
        self.child_list.assign(Some(child_list));
        Temporary::new(self.child_list.get().get_ref().clone())
    }

    // http://dom.spec.whatwg.org/#dom-node-firstchild
    fn GetFirstChild(&self) -> Option<Temporary<Node>> {
        self.first_child.get().map(|node| Temporary::new(node))
    }

    // http://dom.spec.whatwg.org/#dom-node-lastchild
    fn GetLastChild(&self) -> Option<Temporary<Node>> {
        self.last_child.get().map(|node| Temporary::new(node))
    }

    // http://dom.spec.whatwg.org/#dom-node-previoussibling
    fn GetPreviousSibling(&self) -> Option<Temporary<Node>> {
        self.prev_sibling.get().map(|node| Temporary::new(node))
    }

    // http://dom.spec.whatwg.org/#dom-node-nextsibling
    fn GetNextSibling(&self) -> Option<Temporary<Node>> {
        self.next_sibling.get().map(|node| Temporary::new(node))
    }

    // http://dom.spec.whatwg.org/#dom-node-nodevalue
    fn GetNodeValue(&self) -> Option<DOMString> {
        match self.type_id {
            CommentNodeTypeId |
            TextNodeTypeId |
            ProcessingInstructionNodeTypeId => {
                let chardata: &JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
                Some(chardata.Data())
            }
            _ => {
                None
            }
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-nodevalue
    fn SetNodeValue(&self, val: Option<DOMString>) {
        match self.type_id {
            CommentNodeTypeId |
            TextNodeTypeId |
            ProcessingInstructionNodeTypeId => {
                self.SetTextContent(val)
            }
            _ => {}
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-textcontent
    fn GetTextContent(&self) -> Option<DOMString> {
        match self.type_id {
            DocumentFragmentNodeTypeId |
            ElementNodeTypeId(..) => {
                let content = Node::collect_text_contents(self.traverse_preorder());
                Some(content)
            }
            CommentNodeTypeId |
            TextNodeTypeId |
            ProcessingInstructionNodeTypeId => {
                let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
                Some(characterdata.Data())
            }
            DoctypeNodeTypeId |
            DocumentNodeTypeId => {
                None
            }
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-textcontent
    fn SetTextContent(&self, value: Option<DOMString>) {
        let value = null_str_as_empty(&value);
        match self.type_id {
            DocumentFragmentNodeTypeId |
            ElementNodeTypeId(..) => {
                // Step 1-2.
                let node = if value.len() == 0 {
                    None
                } else {
                    let document = self.owner_doc().root();
                    Some(NodeCast::from_temporary(document.deref().CreateTextNode(value)))
                }.root();

                // Step 3.
                Node::replace_all(node.root_ref(), self);
            }
            CommentNodeTypeId |
            TextNodeTypeId |
            ProcessingInstructionNodeTypeId => {
                self.wait_until_safe_to_modify_dom();

                let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
                *characterdata.data.deref().borrow_mut() = value;

                // Notify the document that the content of this node is different
                let document = self.owner_doc().root();
                document.deref().content_changed();
            }
            DoctypeNodeTypeId |
            DocumentNodeTypeId => {}
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-insertbefore
    fn InsertBefore(&self, node: &JSRef<Node>, child: Option<JSRef<Node>>) -> Fallible<Temporary<Node>> {
        Node::pre_insert(node, self, child)
    }

    // http://dom.spec.whatwg.org/#dom-node-appendchild
    fn AppendChild(&self, node: &JSRef<Node>) -> Fallible<Temporary<Node>> {
        Node::pre_insert(node, self, None)
    }

    // http://dom.spec.whatwg.org/#concept-node-replace
    fn ReplaceChild(&self, node: &JSRef<Node>, child: &JSRef<Node>) -> Fallible<Temporary<Node>> {

        // Step 1.
        match self.type_id {
            DocumentNodeTypeId |
            DocumentFragmentNodeTypeId |
            ElementNodeTypeId(..) => (),
            _ => return Err(HierarchyRequest)
        }

        // Step 2.
        if node.is_inclusive_ancestor_of(self) {
            return Err(HierarchyRequest);
        }

        // Step 3.
        if !self.is_parent_of(child) {
            return Err(NotFound);
        }

        // Step 4-5.
        match node.type_id() {
            TextNodeTypeId if self.is_document() => return Err(HierarchyRequest),
            DoctypeNodeTypeId if !self.is_document() => return Err(HierarchyRequest),
            DocumentFragmentNodeTypeId |
            DoctypeNodeTypeId |
            ElementNodeTypeId(..) |
            TextNodeTypeId |
            ProcessingInstructionNodeTypeId |
            CommentNodeTypeId => (),
            DocumentNodeTypeId => return Err(HierarchyRequest)
        }

        // Step 6.
        match self.type_id {
            DocumentNodeTypeId => {
                match node.type_id() {
                    // Step 6.1
                    DocumentFragmentNodeTypeId => {
                        // Step 6.1.1(b)
                        if node.children().any(|c| c.is_text()) {
                            return Err(HierarchyRequest);
                        }
                        match node.child_elements().count() {
                            0 => (),
                            // Step 6.1.2
                            1 => {
                                if self.child_elements().any(|c| NodeCast::from_ref(&c) != child) {
                                    return Err(HierarchyRequest);
                                }
                                if child.following_siblings()
                                        .any(|child| child.is_doctype()) {
                                    return Err(HierarchyRequest);
                                }
                            },
                            // Step 6.1.1(a)
                            _ => return Err(HierarchyRequest)
                        }
                    },
                    // Step 6.2
                    ElementNodeTypeId(..) => {
                        if self.child_elements().any(|c| NodeCast::from_ref(&c) != child) {
                            return Err(HierarchyRequest);
                        }
                        if child.following_siblings()
                                .any(|child| child.is_doctype()) {
                            return Err(HierarchyRequest);
                        }
                    },
                    // Step 6.3
                    DoctypeNodeTypeId => {
                        if self.children().any(|c| c.is_doctype() && &c != child) {
                            return Err(HierarchyRequest);
                        }
                        if self.children()
                            .take_while(|c| c != child)
                            .any(|c| c.is_element()) {
                            return Err(HierarchyRequest);
                        }
                    },
                    TextNodeTypeId |
                    ProcessingInstructionNodeTypeId |
                    CommentNodeTypeId => (),
                    DocumentNodeTypeId => unreachable!()
                }
            },
            _ => ()
        }

        // Ok if not caught by previous error checks.
        if *node == *child {
            return Ok(Temporary::from_rooted(child));
        }

        // Step 7-8.
        let next_sibling = child.next_sibling().map(|node| (*node.root()).clone());
        let reference_child = match next_sibling {
            Some(ref sibling) if sibling == node => node.next_sibling().map(|node| (*node.root()).clone()),
            _ => next_sibling
        };

        // Step 9.
        let document = document_from_node(self).root();
        Node::adopt(node, &*document);

        {
            // Step 10.
            Node::remove(child, self, Suppressed);

            // Step 11.
            Node::insert(node, self, reference_child, Suppressed);
        }

        // Step 12-14.
        // Step 13: mutation records.
        child.node_removed(self.is_in_doc());
        if node.type_id() == DocumentFragmentNodeTypeId {
            for child_node in node.children() {
                child_node.node_inserted();
            }
        } else {
            node.node_inserted();
        }

        // Step 15.
        Ok(Temporary::from_rooted(child))
    }

    // http://dom.spec.whatwg.org/#dom-node-removechild
    fn RemoveChild(&self, node: &JSRef<Node>)
                       -> Fallible<Temporary<Node>> {
        Node::pre_remove(node, self)
    }

    // http://dom.spec.whatwg.org/#dom-node-normalize
    fn Normalize(&self) {
        let mut prev_text = None;
        for child in self.children() {
            if child.is_text() {
                let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(&child).unwrap();
                if characterdata.Length() == 0 {
                    self.remove_child(&child);
                } else {
                    match prev_text {
                        Some(ref mut text_node) => {
                            let prev_characterdata: &mut JSRef<CharacterData> = CharacterDataCast::to_mut_ref(text_node).unwrap();
                            let _ = prev_characterdata.AppendData(characterdata.Data());
                            self.remove_child(&child);
                        },
                        None => prev_text = Some(child)
                    }
                }
            } else {
                child.Normalize();
                prev_text = None;
            }

        }
    }

    // http://dom.spec.whatwg.org/#dom-node-clonenode
    fn CloneNode(&self, deep: bool) -> Temporary<Node> {
        match deep {
            true => Node::clone(self, None, CloneChildren),
            false => Node::clone(self, None, DoNotCloneChildren)
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-isequalnode
    fn IsEqualNode(&self, maybe_node: Option<JSRef<Node>>) -> bool {
        fn is_equal_doctype(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
            let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(node).unwrap();
            let other_doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(other).unwrap();
            (doctype.deref().name == other_doctype.deref().name) &&
            (doctype.deref().public_id == other_doctype.deref().public_id) &&
            (doctype.deref().system_id == other_doctype.deref().system_id)
        }
        fn is_equal_element(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
            let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
            let other_element: &JSRef<Element> = ElementCast::to_ref(other).unwrap();
            // FIXME: namespace prefix
            let element = element.deref();
            let other_element = other_element.deref();
            (element.namespace == other_element.namespace) &&
            (element.local_name == other_element.local_name) &&
            (element.attrs.borrow().len() == other_element.attrs.borrow().len())
        }
        fn is_equal_processinginstruction(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
            let pi: &JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
            let other_pi: &JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(other).unwrap();
            (pi.deref().target == other_pi.deref().target) &&
            (*pi.deref().characterdata.data.deref().borrow() == *other_pi.deref().characterdata.data.deref().borrow())
        }
        fn is_equal_characterdata(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
            let characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(node).unwrap();
            let other_characterdata: &JSRef<CharacterData> = CharacterDataCast::to_ref(other).unwrap();
            *characterdata.deref().data.deref().borrow() == *other_characterdata.deref().data.deref().borrow()
        }
        fn is_equal_element_attrs(node: &JSRef<Node>, other: &JSRef<Node>) -> bool {
            let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
            let other_element: &JSRef<Element> = ElementCast::to_ref(other).unwrap();
            let element = element.deref();
            let other_element = other_element.deref();
            assert!(element.attrs.borrow().len() == other_element.attrs.borrow().len());
            element.attrs.borrow().iter().map(|attr| attr.root()).all(|attr| {
                other_element.attrs.borrow().iter().map(|attr| attr.root()).any(|other_attr| {
                    (attr.namespace == other_attr.namespace) &&
                    (attr.local_name() == other_attr.local_name()) &&
                    (attr.deref().value().as_slice() == other_attr.deref().value().as_slice())
                })
            })
        }
        fn is_equal_node(this: &JSRef<Node>, node: &JSRef<Node>) -> bool {
            // Step 2.
            if this.type_id() != node.type_id() {
                return false;
            }

            match node.type_id() {
                // Step 3.
                DoctypeNodeTypeId if !is_equal_doctype(this, node) => return false,
                ElementNodeTypeId(..) if !is_equal_element(this, node) => return false,
                ProcessingInstructionNodeTypeId if !is_equal_processinginstruction(this, node) => return false,
                TextNodeTypeId |
                CommentNodeTypeId if !is_equal_characterdata(this, node) => return false,
                // Step 4.
                ElementNodeTypeId(..) if !is_equal_element_attrs(this, node) => return false,
                _ => ()
            }

            // Step 5.
            if this.children().count() != node.children().count() {
                return false;
            }

            // Step 6.
            this.children().zip(node.children()).all(|(ref child, ref other_child)| {
                is_equal_node(child, other_child)
            })
        }
        match maybe_node {
            // Step 1.
            None => false,
            // Step 2-6.
            Some(ref node) => is_equal_node(self, node)
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-comparedocumentposition
    fn CompareDocumentPosition(&self, other: &JSRef<Node>) -> u16 {
        if self == other {
            // step 2.
            0
        } else {
            let mut lastself = self.clone();
            let mut lastother = other.clone();
            for ancestor in self.ancestors() {
                if &ancestor == other {
                    // step 4.
                    return NodeConstants::DOCUMENT_POSITION_CONTAINS +
                           NodeConstants::DOCUMENT_POSITION_PRECEDING;
                }
                lastself = ancestor.clone();
            }
            for ancestor in other.ancestors() {
                if &ancestor == self {
                    // step 5.
                    return NodeConstants::DOCUMENT_POSITION_CONTAINED_BY +
                           NodeConstants::DOCUMENT_POSITION_FOLLOWING;
                }
                lastother = ancestor.clone();
            }

            if lastself != lastother {
                let abstract_uint: uintptr_t = as_uintptr(&*self);
                let other_uint: uintptr_t = as_uintptr(&*other);

                let random = if abstract_uint < other_uint {
                    NodeConstants::DOCUMENT_POSITION_FOLLOWING
                } else {
                    NodeConstants::DOCUMENT_POSITION_PRECEDING
                };
                // step 3.
                return random +
                    NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
                    NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
            }

            for child in lastself.traverse_preorder() {
                if &child == other {
                    // step 6.
                    return NodeConstants::DOCUMENT_POSITION_PRECEDING;
                }
                if &child == self {
                    // step 7.
                    return NodeConstants::DOCUMENT_POSITION_FOLLOWING;
                }
            }
            unreachable!()
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-contains
    fn Contains(&self, maybe_other: Option<JSRef<Node>>) -> bool {
        match maybe_other {
            None => false,
            Some(ref other) => self.is_inclusive_ancestor_of(other)
        }
    }

    // http://dom.spec.whatwg.org/#dom-node-lookupprefix
    fn LookupPrefix(&self, _prefix: Option<DOMString>) -> Option<DOMString> {
        // FIXME (#1826) implement.
        None
    }

    // http://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
    fn LookupNamespaceURI(&self, _namespace: Option<DOMString>) -> Option<DOMString> {
        // FIXME (#1826) implement.
        None
    }

    // http://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
    fn IsDefaultNamespace(&self, _namespace: Option<DOMString>) -> bool {
        // FIXME (#1826) implement.
        false
    }
}


impl Reflectable for Node {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.eventtarget.reflector()
    }
}

pub fn document_from_node<T: NodeBase>(derived: &JSRef<T>) -> Temporary<Document> {
    let node: &JSRef<Node> = NodeCast::from_ref(derived);
    node.owner_doc()
}

pub fn window_from_node<T: NodeBase>(derived: &JSRef<T>) -> Temporary<Window> {
    let document = document_from_node(derived).root();
    Temporary::new(document.deref().window.clone())
}

impl<'a> VirtualMethods for JSRef<'a, Node> {
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
        let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
        Some(eventtarget as &VirtualMethods)
    }
}

impl<'a> style::TNode<JSRef<'a, Element>> for JSRef<'a, Node> {
    fn parent_node(&self) -> Option<JSRef<'a, Node>> {
        (self as &NodeHelpers).parent_node().map(|node| *node.root())
    }

    fn tnode_first_child(&self) -> Option<JSRef<'a, Node>> {
        (self as &NodeHelpers).first_child().map(|node| *node.root())
    }

    fn prev_sibling(&self) -> Option<JSRef<'a, Node>> {
        (self as &NodeHelpers).prev_sibling().map(|node| *node.root())
    }

    fn next_sibling(&self) -> Option<JSRef<'a, Node>> {
        (self as &NodeHelpers).next_sibling().map(|node| *node.root())
    }

    fn is_document(&self) -> bool {
        (self as &NodeHelpers).is_document()
    }

    fn is_element(&self) -> bool {
        (self as &NodeHelpers).is_element()
    }

    fn as_element(&self) -> JSRef<'a, Element> {
        let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self);
        assert!(elem.is_some());
        *elem.unwrap()
    }

    fn match_attr(&self, attr: &style::AttrSelector, test: |&str| -> bool) -> bool {
        let name = {
            if self.is_html_element_in_html_document() {
                attr.lower_name.as_slice()
            } else {
                attr.name.as_slice()
            }
        };
        match attr.namespace {
            style::SpecificNamespace(ref ns) => {
                self.as_element().get_attribute(ns.clone(), name).root()
                    .map_or(false, |attr| test(attr.deref().Value().as_slice()))
            },
            // FIXME: https://github.com/mozilla/servo/issues/1558
            style::AnyNamespace => false,
        }
    }

    fn is_html_element_in_html_document(&self) -> bool {
        let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self);
        assert!(elem.is_some());
        let elem: &ElementHelpers = elem.unwrap() as &ElementHelpers;
        elem.html_element_in_html_document()
    }
}

pub trait DisabledStateHelpers {
    fn check_ancestors_disabled_state_for_form_control(&self);
    fn check_parent_disabled_state_for_option(&self);
    fn check_disabled_attribute(&self);
}

impl<'a> DisabledStateHelpers for JSRef<'a, Node> {
    fn check_ancestors_disabled_state_for_form_control(&self) {
        if self.get_disabled_state() { return; }
        for ancestor in self.ancestors().filter(|ancestor| ancestor.is_htmlfieldsetelement()) {
            if !ancestor.get_disabled_state() { continue; }
            if ancestor.is_parent_of(self) {
                self.set_disabled_state(true);
                self.set_enabled_state(false);
                return;
            }
            match ancestor.children().find(|child| child.is_htmllegendelement()) {
                Some(ref legend) => {
                    // XXXabinader: should we save previous ancestor to avoid this iteration?
                    if self.ancestors().any(|ancestor| ancestor == *legend) { continue; }
                },
                None => ()
            }
            self.set_disabled_state(true);
            self.set_enabled_state(false);
            return;
        }
    }

    fn check_parent_disabled_state_for_option(&self) {
        if self.get_disabled_state() { return; }
        match self.parent_node().root() {
            Some(ref parent) if parent.is_htmloptgroupelement() && parent.get_disabled_state() => {
                self.set_disabled_state(true);
                self.set_enabled_state(false);
            },
            _ => ()
        }
    }

    fn check_disabled_attribute(&self) {
        let elem: &JSRef<'a, Element> = ElementCast::to_ref(self).unwrap();
        let has_disabled_attrib = elem.has_attribute("disabled");
        self.set_disabled_state(has_disabled_attrib);
        self.set_enabled_state(!has_disabled_attrib);
    }
}