1 use std::mem;
2 
3 use webcore::value::Reference;
4 use webcore::try_from::{TryFrom, TryInto};
5 use webapi::document::Document;
6 use webapi::dom_exception::{HierarchyRequestError, NotFoundError, SyntaxError};
7 use webapi::element::Element;
8 use webapi::event_target::{IEventTarget, EventTarget};
9 use webapi::node_list::NodeList;
10 use private::TODO;
11 
12 /// An enum which determines whenever the DOM [Node](trait.INode.html)'s children will also be cloned or not.
13 ///
14 /// Mainly used in [INode::clone_node](trait.INode.html#method.clone_node).
15 /// Also used in [Document::import_node](struct.Document.html#method.import_node).
16 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
17 pub enum CloneKind {
18     /// Will not clone the children.
19     Shallow,
20     /// Will clone the children.
21     Deep
22 }
23 
24 /// `INode` is an interface from which a number of DOM API object types inherit.
25 ///
26 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node)
27 // https://dom.spec.whatwg.org/#node
28 pub trait INode: IEventTarget {
29     /// Casts a reference to this object into a reference to a [Node](struct.Node.html).
as_node( &self ) -> &Node30     fn as_node( &self ) -> &Node {
31         let reference: &Reference = self.as_ref();
32         unsafe {
33             mem::transmute( reference )
34         }
35     }
36 
37     /// Adds a node to the end of the list of children of a specified parent node.
38     ///
39     /// If the given child is a reference to an existing node in the document then
40     /// it is moved from its current position to the new position (there is no requirement
41     /// to remove the node from its parent node before appending it to some other node).
42     ///
43     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild)
44     // https://dom.spec.whatwg.org/#ref-for-dom-node-appendchild
append_child< T: INode >( &self, child: &T )45     fn append_child< T: INode >( &self, child: &T ) {
46         js! { @(no_return)
47             @{self.as_ref()}.appendChild( @{child.as_ref()} );
48         }
49     }
50 
51     /// Removes a child node from the DOM.
52     ///
53     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild)
54     // https://dom.spec.whatwg.org/#ref-for-dom-node-removechild
remove_child< T: INode >( &self, child: &T ) -> Result< Node, NotFoundError >55     fn remove_child< T: INode >( &self, child: &T ) -> Result< Node, NotFoundError > {
56         js_try! (
57             return @{self.as_ref()}.removeChild( @{child.as_ref()} );
58         ).unwrap()
59     }
60 
61     /// Returns a duplicate of the node on which this method was called.
62     ///
63     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode)
64     // https://dom.spec.whatwg.org/#ref-for-dom-node-clonenode
clone_node( &self, kind: CloneKind ) -> Result< Self, TODO >65     fn clone_node( &self, kind: CloneKind ) -> Result< Self, TODO > {
66         let is_deep = match kind {
67             CloneKind::Deep => true,
68             CloneKind::Shallow => false
69         };
70 
71         let cloned = js! {
72             return @{self.as_ref()}.cloneNode( @{is_deep} );
73         };
74 
75         Ok( cloned.into_reference().unwrap().downcast::< Self >().unwrap() )
76     }
77 
78     /// Checks whenever a given node is a descendant of this one or not.
79     ///
80     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/contains)
81     // https://dom.spec.whatwg.org/#ref-for-dom-node-contains
contains< T: INode >( &self, node: &T ) -> bool82     fn contains< T: INode >( &self, node: &T ) -> bool {
83         js!(
84             return @{self.as_ref()}.contains( @{node.as_ref()} );
85         ).try_into().unwrap()
86     }
87 
88     /// Inserts the specified node before the reference node as a child of the current node.
89     ///
90     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore)
91     // https://dom.spec.whatwg.org/#ref-for-dom-node-insertbefore
insert_before< T: INode, U: INode >( &self, new_node: &T, reference_node: &U ) -> Result< Node, InsertNodeError >92     fn insert_before< T: INode, U: INode >( &self, new_node: &T, reference_node: &U ) -> Result< Node, InsertNodeError > {
93         js_try! (
94             return @{self.as_ref()}.insertBefore( @{new_node.as_ref()}, @{reference_node.as_ref()} );
95         ).unwrap()
96     }
97 
98     /// Replaces one hild node of the specified node with another.
99     ///
100     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild)
101     // https://dom.spec.whatwg.org/#ref-for-dom-node-replacechild
replace_child< T: INode, U: INode >( &self, new_child: &T, old_child: &U ) -> Result< Node, InsertNodeError >102     fn replace_child< T: INode, U: INode >( &self, new_child: &T, old_child: &U ) -> Result< Node, InsertNodeError > {
103         js_try! (
104             return @{self.as_ref()}.replaceChild( @{new_child.as_ref()}, @{old_child.as_ref()} );
105         ).unwrap()
106     }
107 
108     /// Returns the parent of this node in the DOM tree.
109     ///
110     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode)
111     // https://dom.spec.whatwg.org/#ref-for-dom-node-parentnode
parent_node( &self ) -> Option< Node >112     fn parent_node( &self ) -> Option< Node > {
113         js!(
114             return @{self.as_ref()}.parentNode;
115         ).try_into().ok()
116     }
117 
118     /// Returns the node's first child in the tree, or `None` if the node is childless.
119     ///
120     /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/firstChild)
121     // https://dom.spec.whatwg.org/#ref-for-dom-node-firstchild
first_child( &self ) -> Option< Node >122     fn first_child( &self ) -> Option< Node > {
123         js!(
124             return @{self.as_ref()}.firstChild;
125         ).try_into().ok()
126     }
127 
128     /// Returns the node's last child in the tree, or `None` if the node is childless.
129     ///
130     /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/lastChild)
131     // https://dom.spec.whatwg.org/#ref-for-dom-node-lastchild
last_child( &self ) -> Option< Node >132     fn last_child( &self ) -> Option< Node > {
133         js!(
134             return @{self.as_ref()}.lastChild;
135         ).try_into().ok()
136     }
137 
138     /// Returns the node's next sibling in the tree, or `None` if there isn't such a node.
139     ///
140     /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/nextSibling)
141     // https://dom.spec.whatwg.org/#ref-for-dom-node-nextsibling
next_sibling( &self ) -> Option< Node >142     fn next_sibling( &self ) -> Option< Node > {
143         js!(
144             return @{self.as_ref()}.nextSibling;
145         ).try_into().ok()
146     }
147 
148     /// Returns the name of the node.
149     ///
150     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName)
151     // https://dom.spec.whatwg.org/#ref-for-dom-node-nodename
node_name( &self ) -> String152     fn node_name( &self ) -> String {
153         js!(
154             return @{self.as_ref()}.nodeName;
155         ).try_into().unwrap()
156     }
157 
158     /// Returns the type of the node.
159     ///
160     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType)
161     // https://dom.spec.whatwg.org/#ref-for-dom-node-nodetype
node_type( &self ) -> NodeType162     fn node_type( &self ) -> NodeType {
163         match js!(
164             return @{self.as_ref()}.nodeType;
165         ).try_into().unwrap() {
166             1 => NodeType::Element,
167             2 => NodeType::Attribute,
168             3 => NodeType::Text,
169             4 => NodeType::CDataSection,
170             7 => NodeType::ProcessingInstruction,
171             8 => NodeType::Comment,
172             9 => NodeType::Document,
173             10 => NodeType::DocumentType,
174             11 => NodeType::DocumentFragment,
175             _ => unreachable!("Unexpected nodeType")
176         }
177     }
178 
179     /// Returns the value of the node.
180     ///
181     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue)
182     // https://dom.spec.whatwg.org/#ref-for-dom-node-nodevalue
node_value( &self ) -> Option<String>183     fn node_value( &self ) -> Option<String> {
184         js!(
185             return @{self.as_ref()}.nodeValue;
186         ).try_into().ok()
187     }
188 
189     /// Sets the value of the node.
190     ///
191     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue)
192     // https://dom.spec.whatwg.org/#ref-for-dom-node-nodevalue
set_node_value( &self, value: Option< &str > )193     fn set_node_value( &self, value: Option< &str > ) {
194         js! { @(no_return)
195             @{self.as_ref()}.nodeValue = @{value};
196         }
197     }
198 
199     /// Returns the `Document` that this node belongs to.
200     ///
201     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/ownerDocument)
202     // https://dom.spec.whatwg.org/#ref-for-dom-node-ownerdocument
owner_document( &self ) -> Option< Document >203     fn owner_document( &self ) -> Option< Document > {
204         js!(
205             return @{self.as_ref()}.ownerDocument;
206         ).try_into().ok()
207     }
208 
209     /// Returns an `Element` that is the parent of this node. Returns `null` if the node
210     /// has no parent or the parent is not an `Element`.
211     ///
212     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement)
213     // https://dom.spec.whatwg.org/#ref-for-dom-node-parentelement
parent_element( &self ) -> Option< Element >214     fn parent_element( &self ) -> Option< Element > {
215         js!(
216             return @{self.as_ref()}.parentElement;
217         ).try_into().ok()
218     }
219 
220     /// Returns the node's previous sibling in the tree, or `None` if there isn't such a node.
221     ///
222     /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/previousSibling)
223     // https://dom.spec.whatwg.org/#ref-for-dom-node-previoussibling
previous_sibling( &self ) -> Option< Node >224     fn previous_sibling( &self ) -> Option< Node > {
225         js!(
226             return @{self.as_ref()}.previousSibling;
227         ).try_into().ok()
228     }
229 
230     /// A property which represents the text content of a node and its descendants.
231     ///
232     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
233     // https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent
text_content( &self ) -> Option< String >234     fn text_content( &self ) -> Option< String > {
235         js!(
236             return @{self.as_ref()}.textContent;
237         ).try_into().unwrap()
238     }
239 
240     /// Sets the text content of this node; calling thil removes all
241     /// of node's children and replaces them with a single text node
242     /// with the given value.
243     ///
244     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
245     // https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent
set_text_content( &self, text: &str )246     fn set_text_content( &self, text: &str ) {
247         js! { @(no_return)
248             @{self.as_ref()}.textContent = @{text};
249         }
250     }
251 
252     /// Returns a live collection of child nodes of this node.
253     ///
254     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes)
255     // https://dom.spec.whatwg.org/#ref-for-dom-node-childnodes
child_nodes( &self ) -> NodeList256     fn child_nodes( &self ) -> NodeList {
257         unsafe {
258             js!(
259                 return @{self.as_ref()}.childNodes;
260             ).into_reference_unchecked().unwrap()
261         }
262     }
263 
264     /// Gets the base URL.
265     ///
266     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/baseURI)
267     // https://dom.spec.whatwg.org/#ref-for-dom-node-baseuri
base_uri( &self ) -> String268     fn base_uri( &self ) -> String {
269         js!(
270             return @{self.as_ref()}.baseURI;
271         ).try_into().unwrap()
272     }
273 
274     /// Returns whether this node has children nodes.
275     ///
276     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/hasChildNodes)
277     // https://dom.spec.whatwg.org/#ref-for-dom-node-haschildnodes
has_child_nodes( &self ) -> bool278     fn has_child_nodes( &self ) -> bool {
279         js!(
280             return @{self.as_ref()}.hasChildNodes();
281         ).try_into().unwrap()
282     }
283 
284     /// Determines whether the given namespace is the default namespace of this node.
285     ///
286     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/isDefaultNamespace)
287     // https://dom.spec.whatwg.org/#ref-for-dom-node-isdefaultnamespace
is_default_namespace( &self, namespace: &str ) -> bool288     fn is_default_namespace( &self, namespace: &str ) -> bool {
289         js!(
290             return @{self.as_ref()}.isDefaultNamespace( @{namespace} );
291         ).try_into().unwrap()
292     }
293 
294     /// Tests whether this node is equal to another node. Two nodes are equal if
295     /// they have the same type, defining characteristics, matching attributes,
296     /// and so on.
297     ///
298     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/isEqualNode)
299     // https://dom.spec.whatwg.org/#ref-for-dom-node-isequalnode
is_equal_node< T: INode >( &self, node: &T ) -> bool300     fn is_equal_node< T: INode >( &self, node: &T ) -> bool {
301         js!(
302             return @{self.as_ref()}.isEqualNode( @{node.as_ref()} );
303         ).try_into().unwrap()
304     }
305 
306     /// Test whether two `Node` references are the same.
307     ///
308     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/isSameNode)
309     // https://dom.spec.whatwg.org/#ref-for-dom-node-issamenode
is_same_node< T: INode >( &self, node: &T ) -> bool310     fn is_same_node< T: INode >( &self, node: &T ) -> bool {
311         js!(
312             return @{self.as_ref()}.isSameNode( @{node.as_ref()} );
313         ).try_into().unwrap()
314     }
315 
316     /// Returns the prefix for the given namespace URI, if present.
317     ///
318     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/lookupPrefix)
319     // https://dom.spec.whatwg.org/#ref-for-dom-node-lookupprefix
lookup_prefix( &self, namespace: &str ) -> Option<String>320     fn lookup_prefix( &self, namespace: &str ) -> Option<String> {
321         js!(
322             return @{self.as_ref()}.lookupPrefix( @{namespace} );
323         ).try_into().ok()
324     }
325 
326     /// Returns the namespace URI for the given prefix.
327     ///
328     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/lookupNamespaceURI)
329     // https://dom.spec.whatwg.org/#ref-for-dom-node-lookupnamespaceuri
lookup_namespace_uri( &self, prefix: &str ) -> Option<String>330     fn lookup_namespace_uri( &self, prefix: &str ) -> Option<String> {
331         js!(
332             return @{self.as_ref()}.lookupNamespaceURI( @{prefix} );
333         ).try_into().ok()
334     }
335 
336     /// Merges any adjacent text nodes and removes empty text nodes under this node.
337     ///
338     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/normalize)
339     // https://dom.spec.whatwg.org/#ref-for-dom-node-normalize
normalize( &self )340     fn normalize( &self ) {
341         js! { @(no_return)
342             @{self.as_ref()}.normalize();
343         }
344     }
345 }
346 
347 /// Errors thrown by `Node` insertion methods.
348 error_enum_boilerplate! {
349     InsertNodeError,
350     NotFoundError, HierarchyRequestError
351 }
352 
353 /// A reference to a JavaScript object which implements the [INode](trait.INode.html)
354 /// interface.
355 ///
356 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node)
357 // https://dom.spec.whatwg.org/#interface-node
358 #[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
359 #[reference(instance_of = "Node")]
360 #[reference(subclass_of(EventTarget))]
361 pub struct Node( Reference );
362 
363 impl IEventTarget for Node {}
364 impl INode for Node {}
365 
366 impl Node {
367     /// Attempt to create the `Node` from raw html. The html string must contain **exactly one**
368     /// root node.
369     ///
370     /// Returns a `SyntaxError` if:
371     ///
372     /// - There is not **exactly one** root node.
373     /// - The html syntax is wrong. However, on most browsers the html parsing algorighm is
374     ///   _unbelievably_ forgiving and will just turn your html into text or maybe even an empty
375     ///   string.
376     ///
377     /// It is recommended to have control over the html being given to this function as not
378     /// having control is a security concern.
379     ///
380     /// For more details, see information about setting `innerHTML`:
381     ///
382     /// <https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML>
from_html(html: &str) -> Result<Node, SyntaxError>383     pub fn from_html(html: &str) -> Result<Node, SyntaxError> {
384         js_try!(
385             var span = document.createElement("span");
386             span.innerHTML = @{html};
387             if( span.childNodes.length != 1 ) {
388                 throw new DOMException(
389                     "Node::from_html requires a single root node but has: "
390                     + span.childNodes.length,
391                     "SyntaxError");
392             }
393             return span.childNodes[0];
394         ).unwrap()
395     }
396 }
397 
398 /// Determines the type of a `Node`.
399 ///
400 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType)
401 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
402 pub enum NodeType {
403     /// An `Element` such as `<p>` or `<div>`.
404     Element,
405 
406     /// The actual `Text` of `Element` or `Attr`.
407     Text,
408 
409     /// A `ProcessingInstruction` of an XML document.
410     ProcessingInstruction,
411 
412     /// A `Comment` node.
413     Comment,
414 
415     /// A 'Document' node.
416     Document,
417 
418     /// A 'DocumentType' node such as `<!DOCTYPE html>`
419     DocumentType,
420 
421     /// A 'DocumentFragment' node.
422     DocumentFragment,
423 
424     // The following types are deprecated and should not be used.
425 
426     /// Deprecated.
427     Attribute,
428 
429     /// Deprecated.
430     CDataSection,
431 
432     /// Deprecated.
433     XmlEntityReference,
434 
435     /// Deprecated.
436     XmlEntity,
437 
438     /// Deprecated.
439     XmlNotation,
440 }
441 
442 #[cfg(all(test, feature = "web_test"))]
443 mod tests {
444     use super::*;
445     use webapi::document::document;
446     use webcore::value::Value;
447 
div() -> Node448     fn div() -> Node {
449         js!(
450             return document.createElement("div");
451         ).try_into().unwrap()
452     }
453 
text(text: &str) -> Node454     fn text(text: &str) -> Node {
455         js!(
456             return new Text(@{text});
457         ).try_into().unwrap()
458     }
459 
comment(text: &str) -> Node460     fn comment(text: &str) -> Node {
461         js!(
462             return document.createComment(@{text});
463         ).try_into().unwrap()
464     }
465 
processing_instruction(target: &str, data: &str) -> Node466     fn processing_instruction(target: &str, data: &str) -> Node {
467         js!(
468             return document.createProcessingInstruction(@{target}, @{data});
469         ).try_into().unwrap()
470     }
471 
doc_type() -> Node472     fn doc_type() -> Node {
473         js!(
474             return document.implementation.createDocumentType(
475                 "svg:svg",
476                 "-//W3C//DTD SVG 1.1//EN",
477                 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
478             );
479         ).try_into().unwrap()
480     }
481 
document_fragment() -> Node482     fn document_fragment() -> Node {
483         js!(
484             return document.createDocumentFragment();
485         ).try_into().unwrap()
486     }
487 
xml(namespace_prefix: &str, namespace_url: &str) -> Node488     fn xml(namespace_prefix: &str, namespace_url: &str) -> Node {
489         let xml_text = format!(
490             "<?xml version = \"1.0\"?><foo xmlns:{} = \"{}\" />",
491             namespace_prefix,
492             namespace_url
493         );
494         js!(
495             return new DOMParser().parseFromString(@{xml_text}, "text/xml");
496         ).try_into().unwrap()
497     }
498 
499     #[test]
test_append_child()500     fn test_append_child() {
501         let parent = div();
502         let child = div();
503         parent.append_child(&child);
504         assert_eq!(parent.first_child().unwrap().as_ref(), child.as_ref());
505     }
506 
507     #[test]
test_remove_child()508     fn test_remove_child() {
509         let parent = div();
510         let child1 = div();
511         let child2 = div();
512         parent.append_child(&child1);
513         parent.append_child(&child2);
514 
515         let removed = parent.remove_child(&child1).unwrap();
516         assert_eq!(parent.first_child().unwrap().as_ref(), child2.as_ref());
517         assert_eq!(removed.as_ref(), child1.as_ref());
518         match parent.remove_child(&child1) {
519             Err(_) => (),
520             _ => panic!("Expected error")
521         }
522 
523         parent.remove_child(&child2).unwrap();
524         assert!(parent.first_child().is_none())
525     }
526 
527     #[test]
test_clone_node()528     fn test_clone_node() {
529         let node = div();
530         let child = text("test");
531         node.append_child(&child);
532 
533         let clone = node.clone_node(CloneKind::Shallow).unwrap();
534         assert_ne!(node.as_ref(), clone.as_ref());
535         assert_eq!(clone.node_name(), "DIV");
536         assert!(clone.first_child().is_none());
537 
538         let clone = node.clone_node(CloneKind::Deep).unwrap();
539         assert_ne!(node.as_ref(), clone.as_ref());
540         assert_eq!(clone.node_name(), "DIV");
541         let clone_child = clone.first_child().unwrap();
542         assert_ne!(clone_child.as_ref(), child.as_ref());
543         assert_eq!(&clone_child.node_value().unwrap(), "test");
544     }
545 
546     #[test]
test_contains()547     fn test_contains() {
548         let node = div();
549 
550         let child1 = div();
551         node.append_child(&child1);
552 
553         let child2 = div();
554         node.append_child(&child2);
555 
556         let grandchild = div();
557         child1.append_child(&grandchild);
558 
559         assert!(node.contains(&node));
560         assert!(node.contains(&child1));
561         assert!(node.contains(&child2));
562         assert!(node.contains(&grandchild));
563         assert!(child1.contains(&grandchild));
564         assert!(!child1.contains(&child2));
565         assert!(!grandchild.contains(&node));
566     }
567 
568     #[test]
test_insert_before()569     fn test_insert_before() {
570         let node = div();
571         let child1 = div();
572         let child2 = div();
573         let child3 = div();
574         node.append_child(&child1);
575         node.insert_before(&child2, &child1).unwrap();
576         assert_eq!(node.first_child().unwrap().as_ref(), child2.as_ref());
577 
578         match node.insert_before(&child3, &child3) {
579             Err(InsertNodeError::NotFoundError(_)) => (),
580             _ => panic!("Expected NotFoundError")
581         }
582 
583         match node.insert_before(&doc_type(), &child1) {
584             Err(InsertNodeError::HierarchyRequestError(_)) => (),
585             _ => panic!("Expected HierarchyRequestError")
586         }
587     }
588 
589     #[test]
test_replace_child()590     fn test_replace_child() {
591         let node = div();
592         let child1 = div();
593         let child2 = div();
594         node.append_child(&child1);
595         node.replace_child(&child2, &child1).unwrap();
596         assert_eq!(node.first_child().unwrap().as_ref(), child2.as_ref());
597         assert!(child1.parent_node().is_none());
598 
599         match node.replace_child(&child2, &child1) {
600             Err(InsertNodeError::NotFoundError(_)) => (),
601             _ => panic!("Expected NotFoundError")
602         }
603 
604         match node.replace_child(&doc_type(), &child2) {
605             Err(InsertNodeError::HierarchyRequestError(_)) => (),
606             _ => panic!("Expected HierarchyRequestError")
607         }
608     }
609 
610     #[test]
test_parent_node()611     fn test_parent_node() {
612         let node = div();
613         let child = div();
614         node.append_child(&child);
615         assert!(node.parent_node().is_none());
616         assert_eq!(child.parent_node().unwrap().as_ref(), node.as_ref());
617     }
618 
619     #[test]
test_first_child()620     fn test_first_child() {
621         let node = div();
622         assert!(node.first_child().is_none());
623 
624         let child = div();
625         node.append_child(&child);
626         assert_eq!(node.first_child().unwrap().as_ref(), child.as_ref());
627     }
628 
629     #[test]
test_last_child()630     fn test_last_child() {
631         let node = div();
632         assert!(node.last_child().is_none());
633 
634         let child1 = div();
635         node.append_child(&child1);
636         assert_eq!(node.last_child().unwrap().as_ref(), child1.as_ref());
637 
638         let child2 = div();
639         node.append_child(&child2);
640         assert_eq!(node.last_child().unwrap().as_ref(), child2.as_ref());
641     }
642 
643     #[test]
test_next_sibling()644     fn test_next_sibling() {
645         let node = div();
646         let child1 = div();
647         node.append_child(&child1);
648         assert!(child1.next_sibling().is_none());
649 
650         let child2 = div();
651         node.append_child(&child2);
652         assert_eq!(child1.next_sibling().unwrap().as_ref(), child2.as_ref());
653     }
654 
655     #[test]
test_previous_sibling()656     fn test_previous_sibling() {
657         let node = div();
658         let child1 = div();
659         let child2 = div();
660 
661         node.append_child(&child1);
662         assert!(child1.previous_sibling().is_none());
663         node.append_child(&child2);
664         assert_eq!(child2.previous_sibling().unwrap().as_ref(), child1.as_ref());
665     }
666 
667     #[test]
test_node_name()668     fn test_node_name() {
669         assert_eq!(div().node_name(), "DIV");
670         assert_eq!(text("x").node_name(), "#text");
671         assert_eq!(document_fragment().node_name(), "#document-fragment");
672         assert_eq!(doc_type().node_name(), "svg:svg");
673         assert_eq!(processing_instruction("foo", "bar").node_name(), "foo");
674     }
675 
676     #[test]
test_node_type()677     fn test_node_type() {
678         assert_eq!(div().node_type(), NodeType::Element);
679         assert_eq!(text("x").node_type(), NodeType::Text);
680         assert_eq!(processing_instruction("foo", "bar").node_type(), NodeType::ProcessingInstruction);
681         assert_eq!(comment("foo").node_type(), NodeType::Comment);
682         assert_eq!(document().node_type(), NodeType::Document);
683         assert_eq!(doc_type().node_type(), NodeType::DocumentType);
684         assert_eq!(document_fragment().node_type(), NodeType::DocumentFragment);
685     }
686 
687     #[test]
test_node_value()688     fn test_node_value() {
689         let node = text("x");
690         assert_eq!(node.node_value().unwrap(), "x");
691         node.set_node_value(Some("y"));
692         assert_eq!(node.node_value().unwrap(), "y");
693 
694         assert_eq!(processing_instruction("foo", "bar").node_value().unwrap(), "bar");
695         assert_eq!(comment("foo").node_value().unwrap(), "foo");
696 
697         let node: Node = div();
698         assert!(node.node_value().is_none());
699         node.set_node_value(Some("foo"));
700         assert!(node.node_value().is_none());
701 
702         assert!(document().node_value().is_none());
703         assert!(doc_type().node_value().is_none());
704         assert!(document_fragment().node_value().is_none());
705     }
706 
707     #[test]
test_owner_document()708     fn test_owner_document() {
709         let node = div();
710         assert_eq!(node.owner_document().unwrap().as_ref(), document().as_ref());
711     }
712 
713     #[test]
test_parent_element()714     fn test_parent_element() {
715         let node = div();
716         let child = div();
717         node.append_child(&child);
718         assert_eq!(child.parent_element().unwrap().as_ref(), node.as_ref());
719     }
720 
721     #[test]
test_text_content()722     fn test_text_content() {
723         let node: Node = div();
724         assert_eq!(node.text_content().unwrap(), "");
725         node.append_child(&text("foo "));
726         assert_eq!(node.text_content().unwrap(), "foo ");
727         node.append_child(&text("foo"));
728         assert_eq!(node.text_content().unwrap(), "foo foo");
729         node.set_text_content("bar");
730         assert_eq!(node.text_content().unwrap(), "bar");
731         assert_eq!(node.child_nodes().len(), 1);
732     }
733 
734     #[test]
test_base_uri()735     fn test_base_uri() {
736         let node = div();
737         assert!(!node.base_uri().is_empty());
738     }
739 
740     #[test]
test_has_child_nodes()741     fn test_has_child_nodes() {
742         let node = div();
743         assert!(!node.has_child_nodes());
744         node.append_child(&div());
745         assert!(node.has_child_nodes());
746     }
747 
748     #[test]
test_child_nodes()749     fn test_child_nodes() {
750         let node = div();
751         let node_list = node.child_nodes();
752         assert_eq!(node_list.len(), 0);
753         assert!(node_list.iter().next().is_none());
754 
755         let child1 = text("foo");
756         node.append_child(&child1);
757         let child2 = text("bar");
758         node.append_child(&child2);
759 
760         let node_list = node.child_nodes();
761         assert_eq!(node_list.len(), 2);
762         let mut iter = node_list.iter();
763         assert_eq!(iter.next().unwrap().as_ref(), child1.as_ref());
764         assert_eq!(iter.next().unwrap().as_ref(), child2.as_ref());
765     }
766 
767     #[test]
test_is_default_namespace()768     fn test_is_default_namespace() {
769         assert!(!div().is_default_namespace("foo"));
770         assert!(div().is_default_namespace("http://www.w3.org/1999/xhtml"));
771     }
772 
773     #[test]
test_is_equal_node()774     fn test_is_equal_node() {
775         let node1 = div();
776         let node2 = div();
777         assert!(node1.is_equal_node(&node2));
778 
779         let child1 = div();
780         node1.append_child(&child1);
781         assert!(!node1.is_equal_node(&node2));
782 
783         let child2 = div();
784         node2.append_child(&child2);
785         assert!(node1.is_equal_node(&node2));
786     }
787 
788     #[test]
test_is_same_node()789     fn test_is_same_node() {
790         let node1 = div();
791         assert!(node1.is_same_node(&node1));
792         assert!(!node1.is_same_node(&div()));
793     }
794 
795     #[test]
test_lookup_prefix()796     fn test_lookup_prefix() {
797         let xml = xml("x", "http://foo.com");
798         assert!(xml.lookup_prefix("bar").is_none());
799         assert_eq!(xml.lookup_prefix("http://foo.com").unwrap(), "x");
800     }
801 
802     #[test]
test_lookup_namespace_uri()803     fn test_lookup_namespace_uri() {
804         let xml = xml("x", "http://foo.com");
805         assert!(xml.lookup_namespace_uri("y").is_none());
806         assert_eq!(xml.lookup_namespace_uri("x").unwrap(), "http://foo.com");
807     }
808 
809     #[test]
test_normalize()810     fn test_normalize() {
811         let node = div();
812         node.append_child(&text("test "));
813         node.append_child(&text("123"));
814         node.normalize();
815         assert_eq!(node.child_nodes().len(), 1);
816         let child_text = node.first_child().unwrap().text_content().unwrap();
817         assert_eq!(child_text, "test 123");
818     }
819 
820     #[test]
option_node_is_constructible_from_value()821     fn option_node_is_constructible_from_value() {
822         let node: Value = js!( return document.createElement( "div" ) );
823         let opt_node: Option< Node > = node.clone().try_into().unwrap();
824         assert_eq!( opt_node.unwrap().as_ref(), node.as_ref() );
825     }
826 
827     #[test]
empty_option_node_is_constructible_from_null_value()828     fn empty_option_node_is_constructible_from_null_value() {
829         let empty_opt_node: Option< Node > = Value::Null.try_into().unwrap();
830         assert!( empty_opt_node.is_none() );
831     }
832 
833     #[test]
empty_option_node_is_constructible_from_undefined_value()834     fn empty_option_node_is_constructible_from_undefined_value() {
835         let empty_opt_node: Option< Node > = Value::Undefined.try_into().unwrap();
836         assert!( empty_opt_node.is_none() );
837     }
838 
839     #[test]
option_node_from_numeric_value_results_in_an_error()840     fn option_node_from_numeric_value_results_in_an_error() {
841         let value: Value = 123_i32.into();
842         let empty_opt_node: Result< Option< Node >, _ > = value.try_into();
843         assert!( empty_opt_node.is_err() );
844     }
845 
846     #[test]
from_html()847     fn from_html() {
848         let node = Node::from_html("<div>Some text, horray!</div>").unwrap();
849         let text = node.first_child().unwrap();
850 
851         assert_eq!(node.node_name(), "DIV");
852         assert_eq!(node.last_child().unwrap(), text);
853 
854         assert_eq!(text.node_name(), "#text");
855         assert_eq!(text.node_value().unwrap(), "Some text, horray!");
856         assert!(text.first_child().is_none());
857 
858         let err = Node::from_html("<div>foo</div><div>bar</div>").unwrap_err();
859         assert!(format!("{}", err).contains("requires a single root node"));
860         assert!(Node::from_html("<di").is_err());
861     }
862 }
863