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). 30 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 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 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 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 82 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 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 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 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 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 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 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 152 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 162 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 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 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 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 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 224 fn previous_sibling( &self ) -> Option< Node > { 225 js!( 226 return @{self.as_ref()}.previousSibling; 227 ).try_into().ok() 228 } reload()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 234 fn text_content( &self ) -> Option< String > { 235 js!( 236 return @{self.as_ref()}.textContent; 237 ).try_into().unwrap() 238 } isEmpty()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 246 fn set_text_content( &self, text: &str ) { 247 js! { @(no_return) 248 @{self.as_ref()}.textContent = @{text}; 249 } 250 } removeFirst()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 256 fn child_nodes( &self ) -> NodeList { 257 unsafe { 258 js!( 259 return @{self.as_ref()}.childNodes; 260 ).into_reference_unchecked().unwrap() 261 } 262 } constBegin()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 268 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 278 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 288 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 300 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 310 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 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 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 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> 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 448 fn div() -> Node { 449 js!( 450 return document.createElement("div"); 451 ).try_into().unwrap() 452 } 453 454 fn text(text: &str) -> Node { 455 js!( 456 return new Text(@{text}); 457 ).try_into().unwrap() 458 } 459 460 fn comment(text: &str) -> Node { 461 js!( 462 return document.createComment(@{text}); 463 ).try_into().unwrap() 464 } 465 466 fn processing_instruction(target: &str, data: &str) -> Node { 467 js!( 468 return document.createProcessingInstruction(@{target}, @{data}); 469 ).try_into().unwrap() 470 } 471 472 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 482 fn document_fragment() -> Node { 483 js!( 484 return document.createDocumentFragment(); 485 ).try_into().unwrap() 486 } 487 488 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 735 fn test_base_uri() { 736 let node = div(); 737 assert!(!node.base_uri().is_empty()); 738 } 739 740 #[test] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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] 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