diff options
Diffstat (limited to 'src/components/script/dom/node.rs')
-rw-r--r-- | src/components/script/dom/node.rs | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs new file mode 100644 index 00000000000..875c31f8322 --- /dev/null +++ b/src/components/script/dom/node.rs @@ -0,0 +1,473 @@ +/* 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::bindings::codegen; +use dom::bindings::node; +use dom::bindings::utils::WrapperCache; +use dom::bindings; +use dom::characterdata::CharacterData; +use dom::document::Document; +use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId}; +use dom::element::{HTMLStyleElementTypeId}; +use script_task::global_script_context; + +use core::cast::transmute; +use core::libc::c_void; +use js::rust::Compartment; +use netsurfcss::util::VoidPtrLike; +use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils}; + +// +// The basic Node structure +// + +/// A phantom type representing the script task's view of this node. Script is able to mutate +/// nodes but may not access layout data. +pub struct ScriptView; + +/// A phantom type representing the layout task's view of the node. Layout is not allowed to mutate +/// nodes but may access layout data. +pub struct LayoutView; + +/// 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<View> { + priv obj: *mut Node<View>, +} + +impl<View> Eq for AbstractNode<View> { + fn eq(&self, other: &AbstractNode<View>) -> bool { + self.obj == other.obj + } + fn ne(&self, other: &AbstractNode<View>) -> bool { + self.obj != other.obj + } +} + +/// An HTML node. +/// +/// `View` describes extra data associated with this node that this task has access to. For +/// the script task, this is the unit type `()`. For the layout task, this is +/// `layout::aux::LayoutData`. +pub struct Node<View> { + /// The JavaScript wrapper for this node. + wrapper: WrapperCache, + + /// The type of node that this is. + type_id: NodeTypeId, + + abstract: Option<AbstractNode<View>>, + + /// The parent of this node. + parent_node: Option<AbstractNode<View>>, + + /// The first child of this node. + first_child: Option<AbstractNode<View>>, + + /// The last child of this node. + last_child: Option<AbstractNode<View>>, + + /// The next sibling of this node. + next_sibling: Option<AbstractNode<View>>, + + /// The previous sibling of this node. + prev_sibling: Option<AbstractNode<View>>, + + /// The document that this node belongs to. + owner_doc: Option<@mut Document>, + + /// Layout information. Only the layout task may touch this data. + priv layout_data: Option<@mut ()> +} + +/// The different types of nodes. +#[deriving(Eq)] +pub enum NodeTypeId { + DoctypeNodeTypeId, + CommentNodeTypeId, + ElementNodeTypeId(ElementTypeId), + TextNodeTypeId, +} + +// +// Basic node types +// + +/// The `DOCTYPE` tag. +pub struct Doctype<View> { + parent: Node<View>, + name: ~str, + public_id: Option<~str>, + system_id: Option<~str>, + force_quirks: bool +} + +impl Doctype<ScriptView> { + /// Creates a new `DOCTYPE` tag. + pub fn new(name: ~str, + public_id: Option<~str>, + system_id: Option<~str>, + force_quirks: bool) + -> Doctype<ScriptView> { + Doctype { + parent: Node::new(DoctypeNodeTypeId), + name: name, + public_id: public_id, + system_id: system_id, + force_quirks: force_quirks, + } + } +} + +/// An HTML comment. +pub struct Comment { + parent: CharacterData, +} + +impl Comment { + /// Creates a new HTML comment. + pub fn new(text: ~str) -> Comment { + Comment { + parent: CharacterData::new(CommentNodeTypeId, text) + } + } +} + +/// An HTML text node. +pub struct Text { + parent: CharacterData, +} + +impl Text { + /// Creates a new HTML text node. + pub fn new(text: ~str) -> Text { + Text { + parent: CharacterData::new(TextNodeTypeId, text) + } + } +} + +impl<View> Clone for AbstractNode<View> { + fn clone(&self) -> AbstractNode<View> { + *self + } +} + +impl<View> TreeNode<AbstractNode<View>> for Node<View> { + fn parent_node(&self) -> Option<AbstractNode<View>> { + self.parent_node + } + fn first_child(&self) -> Option<AbstractNode<View>> { + self.first_child + } + fn last_child(&self) -> Option<AbstractNode<View>> { + self.last_child + } + fn prev_sibling(&self) -> Option<AbstractNode<View>> { + self.prev_sibling + } + fn next_sibling(&self) -> Option<AbstractNode<View>> { + self.next_sibling + } + + fn set_parent_node(&mut self, new_parent_node: Option<AbstractNode<View>>) { + self.parent_node = new_parent_node + } + fn set_first_child(&mut self, new_first_child: Option<AbstractNode<View>>) { + self.first_child = new_first_child + } + fn set_last_child(&mut self, new_last_child: Option<AbstractNode<View>>) { + self.last_child = new_last_child + } + fn set_prev_sibling(&mut self, new_prev_sibling: Option<AbstractNode<View>>) { + self.prev_sibling = new_prev_sibling + } + fn set_next_sibling(&mut self, new_next_sibling: Option<AbstractNode<View>>) { + self.next_sibling = new_next_sibling + } +} + +impl<View> TreeNodeRef<Node<View>> for AbstractNode<View> { + // FIXME: The duplication between `with_base` and `with_mut_base` is ugly. + fn with_base<R>(&self, callback: &fn(&Node<View>) -> R) -> R { + self.transmute(callback) + } + + fn with_mut_base<R>(&self, callback: &fn(&mut Node<View>) -> R) -> R { + self.transmute_mut(callback) + } +} + +impl<View> AbstractNode<View> { + // Unsafe accessors + + /// Returns the layout data, unsafely cast to whatever type layout wishes. Only layout is + /// allowed to call this. This is wildly unsafe and is therefore marked as such. + pub unsafe fn unsafe_layout_data<T>(self) -> @mut T { + do self.with_base |base| { + transmute(base.layout_data.get()) + } + } + /// Returns true if this node has layout data and false otherwise. + pub unsafe fn unsafe_has_layout_data(self) -> bool { + do self.with_base |base| { + base.layout_data.is_some() + } + } + /// Sets the layout data, unsafely casting the type as layout wishes. Only layout is allowed + /// to call this. This is wildly unsafe and is therefore marked as such. + pub unsafe fn unsafe_set_layout_data<T>(self, data: @mut T) { + do self.with_mut_base |base| { + base.layout_data = Some(transmute(data)) + } + } + + // Convenience accessors + + /// Returns the type ID of this node. Fails if this node is borrowed mutably. + pub fn type_id(self) -> NodeTypeId { + self.with_base(|b| b.type_id) + } + + /// Returns the parent node of this node. Fails if this node is borrowed mutably. + pub fn parent_node(self) -> Option<AbstractNode<View>> { + self.with_base(|b| b.parent_node) + } + + /// Returns the first child of this node. Fails if this node is borrowed mutably. + pub fn first_child(self) -> Option<AbstractNode<View>> { + self.with_base(|b| b.first_child) + } + + /// Returns the last child of this node. Fails if this node is borrowed mutably. + pub fn last_child(self) -> Option<AbstractNode<View>> { + self.with_base(|b| b.last_child) + } + + /// Returns the previous sibling of this node. Fails if this node is borrowed mutably. + pub fn prev_sibling(self) -> Option<AbstractNode<View>> { + self.with_base(|b| b.prev_sibling) + } + + /// Returns the next sibling of this node. Fails if this node is borrowed mutably. + pub fn next_sibling(self) -> Option<AbstractNode<View>> { + self.with_base(|b| b.next_sibling) + } + + // + // Downcasting borrows + // + + pub fn transmute<T, R>(self, f: &fn(&T) -> R) -> R { + unsafe { + let node_box: *mut bindings::utils::rust_box<Node<View>> = transmute(self.obj); + let node = &mut (*node_box).payload; + let old = node.abstract; + node.abstract = Some(self); + let box: *bindings::utils::rust_box<T> = transmute(self.obj); + let rv = f(&(*box).payload); + node.abstract = old; + rv + } + } + + pub fn transmute_mut<T, R>(self, f: &fn(&mut T) -> R) -> R { + unsafe { + let node_box: *mut bindings::utils::rust_box<Node<View>> = transmute(self.obj); + let node = &mut (*node_box).payload; + let old = node.abstract; + node.abstract = Some(self); + let box: *bindings::utils::rust_box<T> = transmute(self.obj); + let rv = f(cast::transmute(&(*box).payload)); + node.abstract = old; + rv + } + } + + pub fn is_text(self) -> bool { + self.type_id() == TextNodeTypeId + } + + // FIXME: This should be doing dynamic borrow checking for safety. + pub fn with_imm_text<R>(self, f: &fn(&Text) -> R) -> R { + if !self.is_text() { + fail!(~"node is not text"); + } + self.transmute(f) + } + + pub fn is_element(self) -> bool { + match self.type_id() { + ElementNodeTypeId(*) => true, + _ => false + } + } + + // FIXME: This should be doing dynamic borrow checking for safety. + pub fn with_imm_element<R>(self, f: &fn(&Element) -> R) -> R { + if !self.is_element() { + fail!(~"node is not an element"); + } + self.transmute(f) + } + + // FIXME: This should be doing dynamic borrow checking for safety. + pub fn as_mut_element<R>(self, f: &fn(&mut Element) -> R) -> R { + if !self.is_element() { + fail!(~"node is not an element"); + } + self.transmute_mut(f) + } + + pub fn is_image_element(self) -> bool { + self.type_id() == ElementNodeTypeId(HTMLImageElementTypeId) + } + + pub fn with_imm_image_element<R>(self, f: &fn(&HTMLImageElement) -> R) -> R { + if !self.is_image_element() { + fail!(~"node is not an image element"); + } + self.transmute(f) + } + + pub 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"); + } + self.transmute_mut(f) + } + + pub fn is_style_element(self) -> bool { + self.type_id() == ElementNodeTypeId(HTMLStyleElementTypeId) + } + + pub unsafe fn raw_object(self) -> *mut Node<View> { + self.obj + } + + pub fn from_raw(raw: *mut Node<View>) -> AbstractNode<View> { + AbstractNode { + obj: raw + } + } + + /// Dumps the subtree rooted at this node, for debugging. + pub fn dump(&self) { + self.dump_indent(0); + } + + /// Dumps the node tree, for debugging, with indentation. + pub 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? + for self.each_child() |kid| { + kid.dump_indent(indent + 1u) + } + } + + /// Returns a string that describes this node. + pub fn debug_str(&self) -> ~str { + fmt!("%?", self.type_id()) + } +} + +impl Node<ScriptView> { + pub unsafe fn as_abstract_node<N>(node: ~N) -> AbstractNode<ScriptView> { + // This surrenders memory management of the node! + let mut node = AbstractNode { + obj: transmute(node), + }; + let cx = global_script_context().js_compartment.cx.ptr; + node::create(cx, &mut node); + node + } + + pub fn add_to_doc(&mut self, doc: @mut Document) { + self.owner_doc = Some(doc); + let mut node = self.first_child; + while node.is_some() { + for node.get().traverse_preorder |node| { + do node.with_mut_base |node_base| { + node_base.owner_doc = Some(doc); + } + }; + node = node.get().next_sibling(); + } + } + + pub fn new(type_id: NodeTypeId) -> Node<ScriptView> { + Node { + wrapper: WrapperCache::new(), + type_id: type_id, + + abstract: None, + + parent_node: None, + first_child: None, + last_child: None, + next_sibling: None, + prev_sibling: None, + + owner_doc: None, + + layout_data: None, + } + } +} + +/// The CSS library requires that DOM nodes be convertible to `*c_void` via the `VoidPtrLike` +/// trait. +impl VoidPtrLike for AbstractNode<LayoutView> { + fn from_void_ptr(node: *c_void) -> AbstractNode<LayoutView> { + assert!(node.is_not_null()); + unsafe { + cast::transmute(node) + } + } + + fn to_void_ptr(&self) -> *c_void { + unsafe { + cast::transmute(*self) + } + } +} + +pub fn define_bindings(compartment: @mut Compartment) { + bindings::window::init(compartment); + bindings::document::init(compartment); + bindings::node::init(compartment); + bindings::element::init(compartment); + bindings::text::init(compartment); + bindings::utils::initialize_global(compartment.global_obj.ptr); + let mut unused = false; + assert!(codegen::ClientRectBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::ClientRectListBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::HTMLCollectionBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::DOMParserBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::EventBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::EventTargetBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); +} |