web engine - experimental web browser

Merge branch 'dom-tree': DOM tree with Document, Element, Text, Comment nodes

+822
+822
crates/dom/src/lib.rs
··· 1 1 //! DOM tree, nodes, and events. 2 + //! 3 + //! Arena-based DOM tree with Document, Element, Text, and Comment node types. 4 + //! Each node is stored in a flat `Vec` and referenced by `NodeId`. 5 + 6 + use std::fmt; 7 + 8 + /// A handle to a node in the DOM tree. 9 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 10 + pub struct NodeId(usize); 11 + 12 + impl NodeId { 13 + /// Returns the underlying index. 14 + pub fn index(self) -> usize { 15 + self.0 16 + } 17 + } 18 + 19 + /// An HTML/XML attribute (name-value pair). 20 + #[derive(Debug, Clone, PartialEq, Eq)] 21 + pub struct Attribute { 22 + pub name: String, 23 + pub value: String, 24 + } 25 + 26 + /// The data specific to each node type. 27 + #[derive(Debug, Clone, PartialEq, Eq)] 28 + pub enum NodeData { 29 + /// The root document node. 30 + Document, 31 + /// An element node with tag name, attributes, and optional namespace. 32 + Element { 33 + tag_name: String, 34 + attributes: Vec<Attribute>, 35 + namespace: Option<String>, 36 + }, 37 + /// A text node containing character data. 38 + Text { data: String }, 39 + /// A comment node. 40 + Comment { data: String }, 41 + } 42 + 43 + /// A node in the DOM tree, with links to parent, children, and siblings. 44 + #[derive(Debug)] 45 + struct Node { 46 + data: NodeData, 47 + parent: Option<NodeId>, 48 + first_child: Option<NodeId>, 49 + last_child: Option<NodeId>, 50 + next_sibling: Option<NodeId>, 51 + prev_sibling: Option<NodeId>, 52 + } 53 + 54 + /// The DOM document: an arena of nodes with a root document node. 55 + pub struct Document { 56 + nodes: Vec<Node>, 57 + root: NodeId, 58 + } 59 + 60 + impl fmt::Debug for Document { 61 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 62 + f.debug_struct("Document") 63 + .field("node_count", &self.nodes.len()) 64 + .field("root", &self.root) 65 + .finish() 66 + } 67 + } 68 + 69 + impl Document { 70 + /// Create a new document with a root Document node. 71 + pub fn new() -> Self { 72 + let root_node = Node { 73 + data: NodeData::Document, 74 + parent: None, 75 + first_child: None, 76 + last_child: None, 77 + next_sibling: None, 78 + prev_sibling: None, 79 + }; 80 + Document { 81 + nodes: vec![root_node], 82 + root: NodeId(0), 83 + } 84 + } 85 + 86 + /// Returns the root document node ID. 87 + pub fn root(&self) -> NodeId { 88 + self.root 89 + } 90 + 91 + /// Create an element node. Returns its `NodeId` (not yet attached to the tree). 92 + pub fn create_element(&mut self, tag_name: &str) -> NodeId { 93 + self.create_element_ns(tag_name, None) 94 + } 95 + 96 + /// Create an element node with an optional namespace. 97 + pub fn create_element_ns(&mut self, tag_name: &str, namespace: Option<&str>) -> NodeId { 98 + self.push_node(NodeData::Element { 99 + tag_name: tag_name.to_string(), 100 + attributes: Vec::new(), 101 + namespace: namespace.map(|s| s.to_string()), 102 + }) 103 + } 104 + 105 + /// Create a text node. Returns its `NodeId` (not yet attached to the tree). 106 + pub fn create_text(&mut self, data: &str) -> NodeId { 107 + self.push_node(NodeData::Text { 108 + data: data.to_string(), 109 + }) 110 + } 111 + 112 + /// Create a comment node. Returns its `NodeId` (not yet attached to the tree). 113 + pub fn create_comment(&mut self, data: &str) -> NodeId { 114 + self.push_node(NodeData::Comment { 115 + data: data.to_string(), 116 + }) 117 + } 118 + 119 + /// Append `child` as the last child of `parent`. 120 + /// 121 + /// If `child` is already attached elsewhere, it is first removed from its 122 + /// current position. 123 + pub fn append_child(&mut self, parent: NodeId, child: NodeId) { 124 + self.detach(child); 125 + 126 + let old_last = self.nodes[parent.0].last_child; 127 + 128 + self.nodes[child.0].parent = Some(parent); 129 + self.nodes[child.0].prev_sibling = old_last; 130 + self.nodes[child.0].next_sibling = None; 131 + 132 + if let Some(old_last_id) = old_last { 133 + self.nodes[old_last_id.0].next_sibling = Some(child); 134 + } else { 135 + self.nodes[parent.0].first_child = Some(child); 136 + } 137 + 138 + self.nodes[parent.0].last_child = Some(child); 139 + } 140 + 141 + /// Insert `new_child` before `reference` under `parent`. 142 + /// 143 + /// If `new_child` is already attached elsewhere, it is first removed. 144 + /// Panics if `reference` is not a child of `parent`. 145 + pub fn insert_before(&mut self, parent: NodeId, new_child: NodeId, reference: NodeId) { 146 + assert_eq!( 147 + self.nodes[reference.0].parent, 148 + Some(parent), 149 + "reference node is not a child of parent" 150 + ); 151 + 152 + self.detach(new_child); 153 + 154 + let prev = self.nodes[reference.0].prev_sibling; 155 + 156 + self.nodes[new_child.0].parent = Some(parent); 157 + self.nodes[new_child.0].next_sibling = Some(reference); 158 + self.nodes[new_child.0].prev_sibling = prev; 159 + 160 + self.nodes[reference.0].prev_sibling = Some(new_child); 161 + 162 + if let Some(prev_id) = prev { 163 + self.nodes[prev_id.0].next_sibling = Some(new_child); 164 + } else { 165 + self.nodes[parent.0].first_child = Some(new_child); 166 + } 167 + } 168 + 169 + /// Remove `child` from `parent`. 170 + /// 171 + /// Panics if `child` is not a child of `parent`. 172 + pub fn remove_child(&mut self, parent: NodeId, child: NodeId) { 173 + assert_eq!( 174 + self.nodes[child.0].parent, 175 + Some(parent), 176 + "node is not a child of parent" 177 + ); 178 + self.detach(child); 179 + } 180 + 181 + /// Returns the parent of `node`, or `None` for the root. 182 + pub fn parent(&self, node: NodeId) -> Option<NodeId> { 183 + self.nodes[node.0].parent 184 + } 185 + 186 + /// Returns an iterator over the direct children of `node`. 187 + pub fn children(&self, node: NodeId) -> Children<'_> { 188 + Children { 189 + doc: self, 190 + next: self.nodes[node.0].first_child, 191 + } 192 + } 193 + 194 + /// Returns the first child of `node`, if any. 195 + pub fn first_child(&self, node: NodeId) -> Option<NodeId> { 196 + self.nodes[node.0].first_child 197 + } 198 + 199 + /// Returns the last child of `node`, if any. 200 + pub fn last_child(&self, node: NodeId) -> Option<NodeId> { 201 + self.nodes[node.0].last_child 202 + } 203 + 204 + /// Returns the next sibling of `node`, if any. 205 + pub fn next_sibling(&self, node: NodeId) -> Option<NodeId> { 206 + self.nodes[node.0].next_sibling 207 + } 208 + 209 + /// Returns the previous sibling of `node`, if any. 210 + pub fn prev_sibling(&self, node: NodeId) -> Option<NodeId> { 211 + self.nodes[node.0].prev_sibling 212 + } 213 + 214 + /// Returns a reference to the node's data. 215 + pub fn node_data(&self, node: NodeId) -> &NodeData { 216 + &self.nodes[node.0].data 217 + } 218 + 219 + /// Returns the tag name if `node` is an Element, or `None`. 220 + pub fn tag_name(&self, node: NodeId) -> Option<&str> { 221 + match &self.nodes[node.0].data { 222 + NodeData::Element { tag_name, .. } => Some(tag_name), 223 + _ => None, 224 + } 225 + } 226 + 227 + /// Get an attribute value by name. Returns `None` if the node is not 228 + /// an element or the attribute is not present. 229 + pub fn get_attribute(&self, node: NodeId, name: &str) -> Option<&str> { 230 + match &self.nodes[node.0].data { 231 + NodeData::Element { attributes, .. } => attributes 232 + .iter() 233 + .find(|a| a.name == name) 234 + .map(|a| a.value.as_str()), 235 + _ => None, 236 + } 237 + } 238 + 239 + /// Set an attribute on an element node. If the attribute already exists, 240 + /// its value is replaced. Does nothing if `node` is not an element. 241 + pub fn set_attribute(&mut self, node: NodeId, name: &str, value: &str) { 242 + if let NodeData::Element { attributes, .. } = &mut self.nodes[node.0].data { 243 + if let Some(attr) = attributes.iter_mut().find(|a| a.name == name) { 244 + attr.value = value.to_string(); 245 + } else { 246 + attributes.push(Attribute { 247 + name: name.to_string(), 248 + value: value.to_string(), 249 + }); 250 + } 251 + } 252 + } 253 + 254 + /// Remove an attribute from an element node. Returns `true` if the 255 + /// attribute was present. 256 + pub fn remove_attribute(&mut self, node: NodeId, name: &str) -> bool { 257 + if let NodeData::Element { attributes, .. } = &mut self.nodes[node.0].data { 258 + let len_before = attributes.len(); 259 + attributes.retain(|a| a.name != name); 260 + attributes.len() < len_before 261 + } else { 262 + false 263 + } 264 + } 265 + 266 + /// Returns the text content of a Text or Comment node, or `None` for 267 + /// other node types. 268 + pub fn text_content(&self, node: NodeId) -> Option<&str> { 269 + match &self.nodes[node.0].data { 270 + NodeData::Text { data } | NodeData::Comment { data } => Some(data), 271 + _ => None, 272 + } 273 + } 274 + 275 + /// Set the text content of a Text or Comment node. 276 + /// Does nothing for other node types. 277 + pub fn set_text_content(&mut self, node: NodeId, new_data: &str) { 278 + match &mut self.nodes[node.0].data { 279 + NodeData::Text { data } | NodeData::Comment { data } => { 280 + *data = new_data.to_string(); 281 + } 282 + _ => {} 283 + } 284 + } 285 + 286 + /// Returns the total number of nodes in the arena (including detached nodes). 287 + pub fn len(&self) -> usize { 288 + self.nodes.len() 289 + } 290 + 291 + /// Returns true if the document has no nodes besides the root. 292 + pub fn is_empty(&self) -> bool { 293 + self.nodes.len() == 1 294 + } 295 + 296 + // --- private helpers --- 297 + 298 + fn push_node(&mut self, data: NodeData) -> NodeId { 299 + let id = NodeId(self.nodes.len()); 300 + self.nodes.push(Node { 301 + data, 302 + parent: None, 303 + first_child: None, 304 + last_child: None, 305 + next_sibling: None, 306 + prev_sibling: None, 307 + }); 308 + id 309 + } 310 + 311 + /// Detach a node from its current parent (if any), updating sibling links. 312 + fn detach(&mut self, node: NodeId) { 313 + let parent = match self.nodes[node.0].parent { 314 + Some(p) => p, 315 + None => return, 316 + }; 317 + 318 + let prev = self.nodes[node.0].prev_sibling; 319 + let next = self.nodes[node.0].next_sibling; 320 + 321 + if let Some(prev_id) = prev { 322 + self.nodes[prev_id.0].next_sibling = next; 323 + } else { 324 + self.nodes[parent.0].first_child = next; 325 + } 326 + 327 + if let Some(next_id) = next { 328 + self.nodes[next_id.0].prev_sibling = prev; 329 + } else { 330 + self.nodes[parent.0].last_child = prev; 331 + } 332 + 333 + self.nodes[node.0].parent = None; 334 + self.nodes[node.0].prev_sibling = None; 335 + self.nodes[node.0].next_sibling = None; 336 + } 337 + } 338 + 339 + impl Default for Document { 340 + fn default() -> Self { 341 + Self::new() 342 + } 343 + } 344 + 345 + /// Iterator over the direct children of a node. 346 + pub struct Children<'a> { 347 + doc: &'a Document, 348 + next: Option<NodeId>, 349 + } 350 + 351 + impl<'a> Iterator for Children<'a> { 352 + type Item = NodeId; 353 + 354 + fn next(&mut self) -> Option<NodeId> { 355 + let current = self.next?; 356 + self.next = self.doc.nodes[current.0].next_sibling; 357 + Some(current) 358 + } 359 + } 360 + 361 + #[cfg(test)] 362 + mod tests { 363 + use super::*; 364 + 365 + #[test] 366 + fn new_document_has_root() { 367 + let doc = Document::new(); 368 + assert_eq!(doc.root(), NodeId(0)); 369 + assert_eq!(*doc.node_data(doc.root()), NodeData::Document); 370 + assert!(doc.children(doc.root()).next().is_none()); 371 + } 372 + 373 + #[test] 374 + fn create_element() { 375 + let mut doc = Document::new(); 376 + let div = doc.create_element("div"); 377 + assert_eq!(doc.tag_name(div), Some("div")); 378 + assert!(doc.parent(div).is_none()); 379 + } 380 + 381 + #[test] 382 + fn create_element_with_namespace() { 383 + let mut doc = Document::new(); 384 + let svg = doc.create_element_ns("svg", Some("http://www.w3.org/2000/svg")); 385 + match doc.node_data(svg) { 386 + NodeData::Element { namespace, .. } => { 387 + assert_eq!(namespace.as_deref(), Some("http://www.w3.org/2000/svg")); 388 + } 389 + _ => panic!("expected element"), 390 + } 391 + } 392 + 393 + #[test] 394 + fn create_text() { 395 + let mut doc = Document::new(); 396 + let t = doc.create_text("hello"); 397 + assert_eq!(doc.text_content(t), Some("hello")); 398 + assert_eq!(doc.tag_name(t), None); 399 + } 400 + 401 + #[test] 402 + fn create_comment() { 403 + let mut doc = Document::new(); 404 + let c = doc.create_comment("a comment"); 405 + assert_eq!(doc.text_content(c), Some("a comment")); 406 + match doc.node_data(c) { 407 + NodeData::Comment { data } => assert_eq!(data, "a comment"), 408 + _ => panic!("expected comment"), 409 + } 410 + } 411 + 412 + #[test] 413 + fn append_child_single() { 414 + let mut doc = Document::new(); 415 + let root = doc.root(); 416 + let child = doc.create_element("div"); 417 + doc.append_child(root, child); 418 + 419 + assert_eq!(doc.parent(child), Some(root)); 420 + assert_eq!(doc.first_child(root), Some(child)); 421 + assert_eq!(doc.last_child(root), Some(child)); 422 + assert!(doc.next_sibling(child).is_none()); 423 + assert!(doc.prev_sibling(child).is_none()); 424 + } 425 + 426 + #[test] 427 + fn append_child_multiple() { 428 + let mut doc = Document::new(); 429 + let root = doc.root(); 430 + let a = doc.create_element("a"); 431 + let b = doc.create_element("b"); 432 + let c = doc.create_element("c"); 433 + doc.append_child(root, a); 434 + doc.append_child(root, b); 435 + doc.append_child(root, c); 436 + 437 + assert_eq!(doc.first_child(root), Some(a)); 438 + assert_eq!(doc.last_child(root), Some(c)); 439 + 440 + assert_eq!(doc.next_sibling(a), Some(b)); 441 + assert_eq!(doc.next_sibling(b), Some(c)); 442 + assert!(doc.next_sibling(c).is_none()); 443 + 444 + assert!(doc.prev_sibling(a).is_none()); 445 + assert_eq!(doc.prev_sibling(b), Some(a)); 446 + assert_eq!(doc.prev_sibling(c), Some(b)); 447 + } 448 + 449 + #[test] 450 + fn children_iterator() { 451 + let mut doc = Document::new(); 452 + let root = doc.root(); 453 + let a = doc.create_element("a"); 454 + let b = doc.create_element("b"); 455 + let c = doc.create_element("c"); 456 + doc.append_child(root, a); 457 + doc.append_child(root, b); 458 + doc.append_child(root, c); 459 + 460 + let children: Vec<NodeId> = doc.children(root).collect(); 461 + assert_eq!(children, vec![a, b, c]); 462 + } 463 + 464 + #[test] 465 + fn children_iterator_empty() { 466 + let doc = Document::new(); 467 + let children: Vec<NodeId> = doc.children(doc.root()).collect(); 468 + assert!(children.is_empty()); 469 + } 470 + 471 + #[test] 472 + fn insert_before_first() { 473 + let mut doc = Document::new(); 474 + let root = doc.root(); 475 + let a = doc.create_element("a"); 476 + let b = doc.create_element("b"); 477 + doc.append_child(root, b); 478 + doc.insert_before(root, a, b); 479 + 480 + let children: Vec<NodeId> = doc.children(root).collect(); 481 + assert_eq!(children, vec![a, b]); 482 + assert_eq!(doc.first_child(root), Some(a)); 483 + assert_eq!(doc.prev_sibling(b), Some(a)); 484 + assert_eq!(doc.next_sibling(a), Some(b)); 485 + } 486 + 487 + #[test] 488 + fn insert_before_middle() { 489 + let mut doc = Document::new(); 490 + let root = doc.root(); 491 + let a = doc.create_element("a"); 492 + let b = doc.create_element("b"); 493 + let c = doc.create_element("c"); 494 + doc.append_child(root, a); 495 + doc.append_child(root, c); 496 + doc.insert_before(root, b, c); 497 + 498 + let children: Vec<NodeId> = doc.children(root).collect(); 499 + assert_eq!(children, vec![a, b, c]); 500 + } 501 + 502 + #[test] 503 + fn remove_child_only() { 504 + let mut doc = Document::new(); 505 + let root = doc.root(); 506 + let child = doc.create_element("div"); 507 + doc.append_child(root, child); 508 + doc.remove_child(root, child); 509 + 510 + assert!(doc.parent(child).is_none()); 511 + assert!(doc.first_child(root).is_none()); 512 + assert!(doc.last_child(root).is_none()); 513 + } 514 + 515 + #[test] 516 + fn remove_child_first() { 517 + let mut doc = Document::new(); 518 + let root = doc.root(); 519 + let a = doc.create_element("a"); 520 + let b = doc.create_element("b"); 521 + doc.append_child(root, a); 522 + doc.append_child(root, b); 523 + doc.remove_child(root, a); 524 + 525 + assert_eq!(doc.first_child(root), Some(b)); 526 + assert_eq!(doc.last_child(root), Some(b)); 527 + assert!(doc.prev_sibling(b).is_none()); 528 + } 529 + 530 + #[test] 531 + fn remove_child_last() { 532 + let mut doc = Document::new(); 533 + let root = doc.root(); 534 + let a = doc.create_element("a"); 535 + let b = doc.create_element("b"); 536 + doc.append_child(root, a); 537 + doc.append_child(root, b); 538 + doc.remove_child(root, b); 539 + 540 + assert_eq!(doc.first_child(root), Some(a)); 541 + assert_eq!(doc.last_child(root), Some(a)); 542 + assert!(doc.next_sibling(a).is_none()); 543 + } 544 + 545 + #[test] 546 + fn remove_child_middle() { 547 + let mut doc = Document::new(); 548 + let root = doc.root(); 549 + let a = doc.create_element("a"); 550 + let b = doc.create_element("b"); 551 + let c = doc.create_element("c"); 552 + doc.append_child(root, a); 553 + doc.append_child(root, b); 554 + doc.append_child(root, c); 555 + doc.remove_child(root, b); 556 + 557 + let children: Vec<NodeId> = doc.children(root).collect(); 558 + assert_eq!(children, vec![a, c]); 559 + assert_eq!(doc.next_sibling(a), Some(c)); 560 + assert_eq!(doc.prev_sibling(c), Some(a)); 561 + } 562 + 563 + #[test] 564 + fn append_child_moves_from_old_parent() { 565 + let mut doc = Document::new(); 566 + let root = doc.root(); 567 + let parent1 = doc.create_element("div"); 568 + let parent2 = doc.create_element("span"); 569 + let child = doc.create_element("p"); 570 + doc.append_child(root, parent1); 571 + doc.append_child(root, parent2); 572 + doc.append_child(parent1, child); 573 + 574 + assert_eq!(doc.parent(child), Some(parent1)); 575 + 576 + // Move child from parent1 to parent2. 577 + doc.append_child(parent2, child); 578 + 579 + assert_eq!(doc.parent(child), Some(parent2)); 580 + assert!(doc.first_child(parent1).is_none()); 581 + assert_eq!(doc.first_child(parent2), Some(child)); 582 + } 583 + 584 + #[test] 585 + fn set_and_get_attribute() { 586 + let mut doc = Document::new(); 587 + let div = doc.create_element("div"); 588 + 589 + assert!(doc.get_attribute(div, "class").is_none()); 590 + 591 + doc.set_attribute(div, "class", "container"); 592 + assert_eq!(doc.get_attribute(div, "class"), Some("container")); 593 + 594 + doc.set_attribute(div, "id", "main"); 595 + assert_eq!(doc.get_attribute(div, "id"), Some("main")); 596 + assert_eq!(doc.get_attribute(div, "class"), Some("container")); 597 + } 598 + 599 + #[test] 600 + fn set_attribute_replaces() { 601 + let mut doc = Document::new(); 602 + let div = doc.create_element("div"); 603 + doc.set_attribute(div, "class", "old"); 604 + doc.set_attribute(div, "class", "new"); 605 + assert_eq!(doc.get_attribute(div, "class"), Some("new")); 606 + } 607 + 608 + #[test] 609 + fn remove_attribute() { 610 + let mut doc = Document::new(); 611 + let div = doc.create_element("div"); 612 + doc.set_attribute(div, "class", "x"); 613 + assert!(doc.remove_attribute(div, "class")); 614 + assert!(doc.get_attribute(div, "class").is_none()); 615 + assert!(!doc.remove_attribute(div, "class")); 616 + } 617 + 618 + #[test] 619 + fn attribute_on_non_element_is_noop() { 620 + let mut doc = Document::new(); 621 + let text = doc.create_text("hello"); 622 + doc.set_attribute(text, "class", "x"); 623 + assert!(doc.get_attribute(text, "class").is_none()); 624 + assert!(!doc.remove_attribute(text, "class")); 625 + } 626 + 627 + #[test] 628 + fn text_content_set() { 629 + let mut doc = Document::new(); 630 + let t = doc.create_text("hello"); 631 + doc.set_text_content(t, "world"); 632 + assert_eq!(doc.text_content(t), Some("world")); 633 + } 634 + 635 + #[test] 636 + fn text_content_on_element_is_none() { 637 + let mut doc = Document::new(); 638 + let div = doc.create_element("div"); 639 + assert!(doc.text_content(div).is_none()); 640 + } 641 + 642 + #[test] 643 + fn build_simple_html_tree() { 644 + // Build: <html><head><title>Test</title></head><body><p>Hello</p></body></html> 645 + let mut doc = Document::new(); 646 + let root = doc.root(); 647 + 648 + let html = doc.create_element("html"); 649 + let head = doc.create_element("head"); 650 + let title = doc.create_element("title"); 651 + let title_text = doc.create_text("Test"); 652 + let body = doc.create_element("body"); 653 + let p = doc.create_element("p"); 654 + let p_text = doc.create_text("Hello"); 655 + 656 + doc.append_child(root, html); 657 + doc.append_child(html, head); 658 + doc.append_child(head, title); 659 + doc.append_child(title, title_text); 660 + doc.append_child(html, body); 661 + doc.append_child(body, p); 662 + doc.append_child(p, p_text); 663 + 664 + // Verify structure. 665 + assert_eq!(doc.tag_name(html), Some("html")); 666 + assert_eq!(doc.parent(html), Some(root)); 667 + 668 + let html_children: Vec<NodeId> = doc.children(html).collect(); 669 + assert_eq!(html_children, vec![head, body]); 670 + 671 + let head_children: Vec<NodeId> = doc.children(head).collect(); 672 + assert_eq!(head_children, vec![title]); 673 + 674 + let title_children: Vec<NodeId> = doc.children(title).collect(); 675 + assert_eq!(title_children, vec![title_text]); 676 + assert_eq!(doc.text_content(title_text), Some("Test")); 677 + 678 + let body_children: Vec<NodeId> = doc.children(body).collect(); 679 + assert_eq!(body_children, vec![p]); 680 + 681 + let p_children: Vec<NodeId> = doc.children(p).collect(); 682 + assert_eq!(p_children, vec![p_text]); 683 + assert_eq!(doc.text_content(p_text), Some("Hello")); 684 + } 685 + 686 + #[test] 687 + fn build_tree_with_attributes() { 688 + let mut doc = Document::new(); 689 + let root = doc.root(); 690 + 691 + let a = doc.create_element("a"); 692 + doc.set_attribute(a, "href", "https://example.com"); 693 + doc.set_attribute(a, "class", "link"); 694 + doc.append_child(root, a); 695 + 696 + let text = doc.create_text("Click here"); 697 + doc.append_child(a, text); 698 + 699 + assert_eq!(doc.get_attribute(a, "href"), Some("https://example.com")); 700 + assert_eq!(doc.get_attribute(a, "class"), Some("link")); 701 + assert_eq!(doc.text_content(text), Some("Click here")); 702 + } 703 + 704 + #[test] 705 + fn nested_children_traversal() { 706 + // <div><span><em>deep</em></span></div> 707 + let mut doc = Document::new(); 708 + let root = doc.root(); 709 + 710 + let div = doc.create_element("div"); 711 + let span = doc.create_element("span"); 712 + let em = doc.create_element("em"); 713 + let text = doc.create_text("deep"); 714 + 715 + doc.append_child(root, div); 716 + doc.append_child(div, span); 717 + doc.append_child(span, em); 718 + doc.append_child(em, text); 719 + 720 + // Walk down. 721 + let mut current = doc.first_child(root).unwrap(); 722 + assert_eq!(doc.tag_name(current), Some("div")); 723 + 724 + current = doc.first_child(current).unwrap(); 725 + assert_eq!(doc.tag_name(current), Some("span")); 726 + 727 + current = doc.first_child(current).unwrap(); 728 + assert_eq!(doc.tag_name(current), Some("em")); 729 + 730 + let leaf = doc.first_child(current).unwrap(); 731 + assert_eq!(doc.text_content(leaf), Some("deep")); 732 + 733 + // Walk back up. 734 + assert_eq!(doc.parent(leaf).map(|n| doc.tag_name(n)), Some(Some("em"))); 735 + } 736 + 737 + #[test] 738 + fn document_len_and_is_empty() { 739 + let doc = Document::new(); 740 + assert_eq!(doc.len(), 1); // root only 741 + assert!(doc.is_empty()); // no children besides root 742 + 743 + let mut doc2 = Document::new(); 744 + let _ = doc2.create_element("div"); 745 + assert_eq!(doc2.len(), 2); 746 + assert!(!doc2.is_empty()); 747 + } 748 + 749 + #[test] 750 + fn default_document() { 751 + let doc = Document::default(); 752 + assert_eq!(doc.root(), NodeId(0)); 753 + } 754 + 755 + #[test] 756 + fn node_id_equality() { 757 + let id1 = NodeId(5); 758 + let id2 = NodeId(5); 759 + let id3 = NodeId(6); 760 + assert_eq!(id1, id2); 761 + assert_ne!(id1, id3); 762 + } 763 + 764 + #[test] 765 + #[should_panic(expected = "reference node is not a child of parent")] 766 + fn insert_before_wrong_parent_panics() { 767 + let mut doc = Document::new(); 768 + let root = doc.root(); 769 + let a = doc.create_element("a"); 770 + let b = doc.create_element("b"); 771 + let c = doc.create_element("c"); 772 + doc.append_child(root, a); 773 + // b is not a child of root, so this should panic. 774 + doc.insert_before(root, c, b); 775 + } 776 + 777 + #[test] 778 + #[should_panic(expected = "node is not a child of parent")] 779 + fn remove_child_wrong_parent_panics() { 780 + let mut doc = Document::new(); 781 + let root = doc.root(); 782 + let a = doc.create_element("a"); 783 + // a is not attached to root. 784 + doc.remove_child(root, a); 785 + } 786 + 787 + #[test] 788 + fn insert_before_moves_from_old_parent() { 789 + let mut doc = Document::new(); 790 + let root = doc.root(); 791 + let parent1 = doc.create_element("div"); 792 + let parent2 = doc.create_element("span"); 793 + let child = doc.create_element("p"); 794 + let reference = doc.create_element("em"); 795 + 796 + doc.append_child(root, parent1); 797 + doc.append_child(root, parent2); 798 + doc.append_child(parent1, child); 799 + doc.append_child(parent2, reference); 800 + 801 + // Move child from parent1 to parent2 before reference. 802 + doc.insert_before(parent2, child, reference); 803 + 804 + assert_eq!(doc.parent(child), Some(parent2)); 805 + assert!(doc.first_child(parent1).is_none()); 806 + let children: Vec<NodeId> = doc.children(parent2).collect(); 807 + assert_eq!(children, vec![child, reference]); 808 + } 809 + 810 + #[test] 811 + fn comment_in_tree() { 812 + let mut doc = Document::new(); 813 + let root = doc.root(); 814 + let comment = doc.create_comment("TODO: add content"); 815 + let div = doc.create_element("div"); 816 + doc.append_child(root, comment); 817 + doc.append_child(root, div); 818 + 819 + let children: Vec<NodeId> = doc.children(root).collect(); 820 + assert_eq!(children, vec![comment, div]); 821 + assert_eq!(doc.text_content(comment), Some("TODO: add content")); 822 + } 823 + }