1use std::collections::{BTreeMap, BTreeSet};
8use std::num::NonZeroU32;
9
10use crate::accessor::NodeRef;
11use crate::dispatch::ListenerStore;
12use crate::error::{DomError, Result};
13use crate::indexes::Indexes;
14use crate::node::{Node, NodeData, NodeType};
15use crate::node_id::NodeId;
16use crate::node_list::NodeList;
17use crate::observer::{InteractionKind, Mutation, ObserverStore};
18use crate::selection::{Position, Range};
19
20const FIRST_GENERATION: NonZeroU32 = NonZeroU32::MIN;
32
33#[derive(Debug)]
39pub(crate) struct Slot<Ext> {
40 pub(crate) generation: NonZeroU32,
41 pub(crate) node: Option<Node<Ext>>,
42}
43
44impl<Ext> Slot<Ext> {
45 fn fresh(node: Node<Ext>) -> Self {
46 Self {
47 generation: FIRST_GENERATION,
48 node: Some(node),
49 }
50 }
51}
52
53#[derive(Debug)]
54pub struct Dom<Ext: 'static = ()> {
55 pub(crate) nodes: Vec<Slot<Ext>>,
57 pub(crate) free: Vec<u32>,
59 pub(crate) root: NodeId,
62 pub(crate) activation_hook: crate::dispatch::ActivationSlot<Ext>,
71 pub(crate) validity_hook: crate::constraint::ValiditySlot<Ext>,
74 pub(crate) indexes: Indexes,
78 pub(crate) listeners: ListenerStore<Ext>,
81 pub(crate) hovered: Option<NodeId>,
87 pub(crate) focused: Option<NodeId>,
88 pub(crate) active: Option<NodeId>,
91 pub(crate) focus_visible: bool,
96 pub(crate) pointer_capture: Option<NodeId>,
103 pub(crate) drag_autoscroll: bool,
110 pub(crate) selection: Option<crate::Selection>,
114 pub(crate) selection_serial: crate::SelectionSerial,
116 pub(crate) observers: ObserverStore<Ext>,
118 pub(crate) is_observing: bool,
121}
122
123impl<Ext: Default> Default for Dom<Ext> {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl<Ext: Default> Dom<Ext> {
130 pub fn new() -> Self {
133 let root_node: Node<Ext> = Node::new(NodeData::Fragment);
134 let nodes = vec![Slot::fresh(root_node)];
135 let root = NodeId::from_parts(0, FIRST_GENERATION);
136 Self {
137 nodes,
138 free: Vec::new(),
139 root,
140 indexes: Indexes::default(),
141 listeners: ListenerStore::default(),
142 hovered: None,
143 focused: None,
144 active: None,
145 focus_visible: true,
146 pointer_capture: None,
147 drag_autoscroll: false,
148 selection: None,
149 selection_serial: crate::SelectionSerial::new(0),
150 observers: ObserverStore::default(),
151 is_observing: false,
152 activation_hook: crate::dispatch::ActivationSlot(None),
153 validity_hook: crate::constraint::ValiditySlot(None),
154 }
155 }
156}
157
158impl<Ext> Dom<Ext> {
159 pub fn hovered(&self) -> Option<NodeId> {
162 self.hovered
163 }
164
165 pub fn focused(&self) -> Option<NodeId> {
167 self.focused
168 }
169
170 pub fn active(&self) -> Option<NodeId> {
174 self.active
175 }
176
177 pub fn focus_visible(&self) -> bool {
187 self.focus_visible
188 }
189
190 pub fn pointer_capture(&self) -> Option<NodeId> {
194 self.pointer_capture
195 }
196
197 pub fn selection(&self) -> Option<&crate::Selection> {
202 self.selection.as_ref()
203 }
204
205 pub fn selection_range(&self) -> Option<crate::Range> {
214 let sel = self.selection.as_ref()?;
215 let a = sel.anchor;
216 let f = sel.focus;
217 let (start, end) = match self.compare_boundary_points(a, f)? {
218 std::cmp::Ordering::Greater => (f, a),
219 _ => (a, f),
220 };
221 Some(crate::Range::ordered_unchecked(start, end))
222 }
223}
224
225impl<Ext: 'static> Dom<Ext> {
226 pub fn set_hovered(&mut self, id: Option<NodeId>) {
230 if self.hovered == id {
231 return;
232 }
233 let prev = self.hovered;
234 self.hovered = id;
235 self.fire_mutation(Mutation::InteractionChanged {
236 prev,
237 next: id,
238 kind: InteractionKind::Hover,
239 });
240 }
241
242 pub fn set_active(&mut self, id: Option<NodeId>) {
247 if self.active == id {
248 return;
249 }
250 let prev = self.active;
251 self.active = id;
252 self.fire_mutation(Mutation::InteractionChanged {
253 prev,
254 next: id,
255 kind: InteractionKind::Active,
256 });
257 }
258
259 pub fn set_focused(&mut self, id: Option<NodeId>) {
262 if self.focused == id {
263 return;
264 }
265 let prev = self.focused;
266 self.focused = id;
267 self.fire_mutation(Mutation::InteractionChanged {
268 prev,
269 next: id,
270 kind: InteractionKind::Focus,
271 });
272 }
273
274 pub fn set_focus_visible(&mut self, visible: bool) {
280 if self.focus_visible == visible {
281 return;
282 }
283 self.focus_visible = visible;
284 self.fire_mutation(Mutation::InteractionChanged {
285 prev: self.focused,
286 next: self.focused,
287 kind: InteractionKind::FocusVisible,
288 });
289 }
290
291 pub fn set_pointer_capture(&mut self, id: NodeId) -> crate::Result<()> {
316 self.node_or_err(id)?;
317 self.pointer_capture = Some(id);
318 self.drag_autoscroll = false;
322 Ok(())
323 }
324
325 pub fn release_pointer_capture(&mut self) {
328 self.pointer_capture = None;
329 self.drag_autoscroll = false;
330 }
331
332 pub fn set_drag_autoscroll(&mut self, on: bool) {
340 if self.pointer_capture.is_some() {
341 self.drag_autoscroll = on;
342 }
343 }
344
345 pub fn drag_autoscroll(&self) -> bool {
348 self.drag_autoscroll
349 }
350
351 pub fn set_selection(&mut self, next: Option<crate::Selection>) {
357 if self.selection == next {
358 return;
359 }
360 let prev = self.selection.take();
361 self.selection = next;
362 self.selection_serial = self.selection_serial.next();
363 self.fire_mutation(Mutation::SelectionChanged { prev, next });
364 }
365
366 pub fn selection_serial(&self) -> crate::SelectionSerial {
378 self.selection_serial
379 }
380
381 pub fn query_selector(&self, selector: &str) -> Option<NodeRef<'_, Ext>> {
391 self.query_selector_in(self.root, selector)
392 .ok()
393 .flatten()
394 .map(|id| self.node(id))
395 }
396
397 pub fn query_selector_all(&self, selector: &str) -> NodeList<'_, Ext> {
404 let ids = self
405 .query_selector_all_in(self.root, selector)
406 .unwrap_or_default();
407 NodeList::from_ids(self, ids)
408 }
409
410 pub fn elements_by_tag(&self, tag: &str) -> NodeList<'_, Ext> {
418 let mut out: Vec<NodeId> = Vec::new();
419 self.walk_descendants(self.root, &mut |id, data| {
420 if let NodeData::Element { tag: t, .. } = data
421 && t == tag
422 {
423 out.push(id);
424 }
425 });
426 NodeList::from_ids(self, out)
427 }
428
429 pub fn document_element(&self) -> NodeRef<'_, Ext> {
437 let root = self.root;
438 if matches!(
439 self.get_node(root).map(|n| &n.data),
440 Some(NodeData::Fragment)
441 ) && let Some(first_el) = self.node(root).first_element_child()
442 {
443 return first_el;
444 }
445 self.node(root)
446 }
447
448 pub fn active_element(&self) -> Option<NodeRef<'_, Ext>> {
451 self.focused.map(|id| self.node(id))
452 }
453
454 pub fn has_focus(&self) -> bool {
458 self.focused.is_some()
459 }
460
461 pub fn create_range(&self) -> Range {
468 let pos = Position::new(self.root, 0);
469 Range::ordered_unchecked(pos, pos)
470 }
471}
472
473impl<Ext: Default> Dom<Ext> {
474 pub fn with_root_tag(tag: &str) -> Self {
476 let root_node: Node<Ext> = Node::new(NodeData::Element {
477 tag: tag.to_string(),
478 attrs: BTreeMap::new(),
479 classes: BTreeSet::new(),
480 ext: Ext::default(),
481 });
482 let nodes = vec![Slot::fresh(root_node)];
483 let root = NodeId::from_parts(0, FIRST_GENERATION);
484 let mut dom = Self {
485 nodes,
486 free: Vec::new(),
487 root,
488 indexes: Indexes::default(),
489 listeners: ListenerStore::default(),
490 hovered: None,
491 focused: None,
492 active: None,
493 focus_visible: true,
494 pointer_capture: None,
495 drag_autoscroll: false,
496 selection: None,
497 selection_serial: crate::SelectionSerial::new(0),
498 observers: ObserverStore::default(),
499 is_observing: false,
500 activation_hook: crate::dispatch::ActivationSlot(None),
501 validity_hook: crate::constraint::ValiditySlot(None),
502 };
503 dom.hook_register(root);
504 dom
505 }
506
507 pub fn create_element(&mut self, tag: &str) -> NodeId {
510 self.alloc(Node::new(NodeData::Element {
511 tag: tag.to_string(),
512 attrs: BTreeMap::new(),
513 classes: BTreeSet::new(),
514 ext: Ext::default(),
515 }))
516 }
517}
518
519impl<Ext> Dom<Ext> {
520 pub fn create_element_with_ext(&mut self, tag: &str, ext: Ext) -> NodeId {
523 self.alloc(Node::new(NodeData::Element {
524 tag: tag.to_string(),
525 attrs: BTreeMap::new(),
526 classes: BTreeSet::new(),
527 ext,
528 }))
529 }
530
531 pub fn create_text_node(&mut self, data: &str) -> NodeId {
533 self.alloc(Node::new(NodeData::Text {
534 data: data.to_string(),
535 }))
536 }
537
538 pub fn create_comment(&mut self, data: &str) -> NodeId {
540 self.alloc(Node::new(NodeData::Comment {
541 data: data.to_string(),
542 }))
543 }
544
545 pub fn create_document_fragment(&mut self) -> NodeId {
548 self.alloc(Node::new(NodeData::Fragment))
549 }
550
551 pub fn root(&self) -> NodeId {
553 self.root
554 }
555
556 pub fn contains(&self, id: NodeId) -> bool {
559 self.get_node(id).is_some()
560 }
561
562 pub fn len(&self) -> usize {
564 self.nodes.len() - self.free.len()
565 }
566
567 pub fn is_empty(&self) -> bool {
568 self.len() == 0
569 }
570
571 pub(crate) fn alloc(&mut self, node: Node<Ext>) -> NodeId {
576 let new_id = if let Some(idx) = self.free.pop() {
577 let idx = idx as usize;
578 let slot = &mut self.nodes[idx];
579 debug_assert!(slot.node.is_none(), "free list held a live slot");
580 slot.node = Some(node);
581 NodeId::from_parts(idx, slot.generation)
582 } else {
583 let idx = self.nodes.len();
584 self.nodes.push(Slot::fresh(node));
585 NodeId::from_parts(idx, FIRST_GENERATION)
586 };
587 self.hook_register(new_id);
588 new_id
589 }
590
591 pub(crate) fn free(&mut self, id: NodeId) {
595 if self.get_node(id).is_none() {
596 return;
597 }
598 let idx = id.index();
599 self.hook_unregister(id);
600 self.drop_listeners(id);
601 let slot = &mut self.nodes[idx];
602 slot.node = None;
603 slot.generation = slot.generation.checked_add(1).unwrap_or(FIRST_GENERATION);
607 self.free.push(idx as u32);
608 }
609
610 #[inline]
613 pub(crate) fn get_node(&self, id: NodeId) -> Option<&Node<Ext>> {
614 let slot = self.nodes.get(id.index())?;
615 if slot.generation != id.generation_raw() {
616 return None;
617 }
618 slot.node.as_ref()
619 }
620
621 #[inline]
623 pub(crate) fn get_node_mut(&mut self, id: NodeId) -> Option<&mut Node<Ext>> {
624 let slot = self.nodes.get_mut(id.index())?;
625 if slot.generation != id.generation_raw() {
626 return None;
627 }
628 slot.node.as_mut()
629 }
630
631 pub(crate) fn node_or_err(&self, id: NodeId) -> Result<&Node<Ext>> {
635 self.get_node(id).ok_or(DomError::InvalidNode(id))
636 }
637
638 pub(crate) fn node_mut_or_err(&mut self, id: NodeId) -> Result<&mut Node<Ext>> {
639 self.get_node_mut(id).ok_or(DomError::InvalidNode(id))
640 }
641
642 pub(crate) fn is_ancestor(&self, ancestor: NodeId, descendant: NodeId) -> bool {
645 let mut current = Some(descendant);
646 while let Some(id) = current {
647 if id == ancestor {
648 return true;
649 }
650 current = self.get_node(id).and_then(|n| n.parent);
651 }
652 false
653 }
654
655 pub fn node_type(&self, id: NodeId) -> Option<NodeType> {
657 self.get_node(id).map(|n| n.node_type())
658 }
659}
660
661#[cfg(test)]
662mod tests {
663 use super::*;
664
665 #[test]
666 fn new_dom_has_fragment_root() {
667 let dom: Dom = Dom::new();
668 let root = dom.root();
669 assert!(dom.contains(root));
670 assert_eq!(dom.node_type(root), Some(NodeType::Fragment));
671 assert_eq!(dom.len(), 1);
672 }
673
674 #[test]
675 fn with_root_tag_creates_element_root() {
676 let dom: Dom = Dom::with_root_tag("body");
677 let root = dom.root();
678 let n = dom.get_node(root).unwrap();
679 assert_eq!(n.node_type(), NodeType::Element);
680 assert_eq!(n.tag_name(), Some("body"));
681 }
682
683 #[test]
684 fn create_element_is_orphan() {
685 let mut dom: Dom = Dom::new();
686 let el = dom.create_element("div");
687 let n = dom.get_node(el).unwrap();
688 assert!(n.parent.is_none());
689 assert!(n.first_child.is_none());
690 assert_eq!(n.node_type(), NodeType::Element);
691 }
692
693 #[test]
694 fn create_allocates_distinct_ids() {
695 let mut dom: Dom = Dom::new();
696 let a = dom.create_element("a");
697 let b = dom.create_element("b");
698 let c = dom.create_text_node("hi");
699 assert_ne!(a, b);
700 assert_ne!(b, c);
701 assert_ne!(a, c);
702 assert_eq!(dom.len(), 4); }
704
705 #[test]
706 fn freed_slot_gets_reused() {
707 let mut dom: Dom = Dom::new();
708 let a = dom.create_element("a");
709 assert!(dom.contains(a));
710 dom.free(a);
711 assert!(!dom.contains(a));
712
713 let b = dom.create_element("b");
715 assert_eq!(a.index(), b.index());
716 }
717
718 #[test]
722 fn stale_id_after_slot_reuse_is_rejected() {
723 let mut dom: Dom = Dom::new();
724 let root = dom.root();
725 let a = dom.create_element("a");
726 dom.append_child(root, a).unwrap();
727 dom.remove_child_dropping(root, a).unwrap();
728 assert!(!dom.contains(a));
729
730 let b = dom.create_element("b");
731 assert_eq!(a.index(), b.index(), "slot is recycled");
732 assert_ne!(a, b, "but the id is not");
733 assert!(dom.contains(b));
734 assert!(!dom.contains(a), "the stale id is still dead");
735
736 assert!(matches!(
737 dom.node_or_err(a).unwrap_err(),
738 DomError::InvalidNode(_)
739 ));
740 assert!(matches!(
741 dom.set_attribute(a, "id", "x").unwrap_err(),
742 DomError::InvalidNode(_)
743 ));
744 assert!(matches!(
745 dom.append_child(a, b).unwrap_err(),
746 DomError::InvalidNode(_)
747 ));
748 assert_eq!(dom.get_attribute(b, "id"), None);
750 assert!(dom.validate().is_empty());
751 }
752
753 #[test]
755 fn each_recycle_of_a_slot_bumps_the_generation() {
756 let mut dom: Dom = Dom::new();
757 let mut prev = dom.create_element("x");
758 for _ in 0..5 {
759 dom.free(prev);
760 let next = dom.create_element("x");
761 assert_eq!(prev.index(), next.index());
762 assert_eq!(next.generation(), prev.generation() + 1);
763 assert!(!dom.contains(prev));
764 prev = next;
765 }
766 }
767
768 #[test]
769 fn invalid_id_returns_error() {
770 let dom: Dom = Dom::new();
771 let ghost = NodeId::from_parts(999, std::num::NonZeroU32::MIN);
772 assert!(matches!(
773 dom.node_or_err(ghost).unwrap_err(),
774 DomError::InvalidNode(_)
775 ));
776 }
777
778 #[test]
779 fn is_ancestor_detects_self() {
780 let mut dom: Dom = Dom::new();
781 let a = dom.create_element("a");
782 assert!(dom.is_ancestor(a, a));
785 }
786
787 #[test]
790 fn pointer_capture_none_by_default() {
791 let dom: Dom = Dom::new();
792 assert_eq!(dom.pointer_capture(), None);
793 }
794
795 #[test]
796 fn set_pointer_capture_records_node() {
797 let mut dom: Dom = Dom::new();
798 let el = dom.create_element("handle");
799 dom.set_pointer_capture(el).unwrap();
800 assert_eq!(dom.pointer_capture(), Some(el));
801 }
802
803 #[test]
804 fn set_pointer_capture_invalid_node_errors() {
805 let mut dom: Dom = Dom::new();
806 let el = dom.create_element("x");
807 dom.free(el);
808 assert!(dom.set_pointer_capture(el).is_err());
809 assert_eq!(dom.pointer_capture(), None);
810 }
811
812 #[test]
813 fn release_pointer_capture_clears_state() {
814 let mut dom: Dom = Dom::new();
815 let el = dom.create_element("handle");
816 dom.set_pointer_capture(el).unwrap();
817 dom.release_pointer_capture();
818 assert_eq!(dom.pointer_capture(), None);
819 }
820
821 #[test]
822 fn release_pointer_capture_is_idempotent() {
823 let mut dom: Dom = Dom::new();
824 dom.release_pointer_capture();
826 dom.release_pointer_capture();
827 assert_eq!(dom.pointer_capture(), None);
828 }
829
830 #[test]
831 fn set_pointer_capture_replaces_previous() {
832 let mut dom: Dom = Dom::new();
833 let a = dom.create_element("a");
834 let b = dom.create_element("b");
835 dom.set_pointer_capture(a).unwrap();
836 dom.set_pointer_capture(b).unwrap();
837 assert_eq!(dom.pointer_capture(), Some(b));
838 }
839
840 #[test]
841 fn drag_autoscroll_defaults_off_and_needs_a_capture() {
842 let mut dom: Dom = Dom::new();
843 let a = dom.create_element("a");
844 assert!(!dom.drag_autoscroll(), "off by default");
845 dom.set_drag_autoscroll(true);
847 assert!(!dom.drag_autoscroll(), "ignored with no capture");
848 dom.set_pointer_capture(a).unwrap();
850 dom.set_drag_autoscroll(true);
851 assert!(dom.drag_autoscroll());
852 }
853
854 #[test]
855 fn drag_autoscroll_clears_on_release_and_recapture() {
856 let mut dom: Dom = Dom::new();
857 let a = dom.create_element("a");
858 let b = dom.create_element("b");
859 dom.set_pointer_capture(a).unwrap();
860 dom.set_drag_autoscroll(true);
861 dom.release_pointer_capture();
862 assert!(!dom.drag_autoscroll(), "release clears the opt-in");
863 dom.set_pointer_capture(a).unwrap();
865 dom.set_drag_autoscroll(true);
866 dom.set_pointer_capture(b).unwrap(); assert!(!dom.drag_autoscroll(), "re-capture resets the opt-in");
868 }
869
870 #[test]
873 fn selection_none_by_default() {
874 let dom: Dom = Dom::new();
875 assert_eq!(dom.selection(), None);
876 assert_eq!(dom.selection_range(), None);
877 }
878
879 #[test]
880 fn set_selection_stores_value() {
881 use crate::{Position, Selection};
882 let mut dom: Dom = Dom::new();
883 let t = dom.create_text_node("hello");
884 let sel = Selection::new(Position::new(t, 1), Position::new(t, 4));
885 dom.set_selection(Some(sel));
886 assert_eq!(dom.selection().copied(), Some(sel));
887 }
888
889 #[test]
890 fn set_selection_none_clears() {
891 use crate::{Position, Selection};
892 let mut dom: Dom = Dom::new();
893 let t = dom.create_text_node("hello");
894 dom.set_selection(Some(Selection::caret(Position::new(t, 0))));
895 dom.set_selection(None);
896 assert_eq!(dom.selection(), None);
897 }
898
899 #[test]
900 fn selection_serial_advances_on_every_actual_change() {
901 use crate::{Position, Selection};
902 let mut dom: Dom = Dom::new();
903 let t = dom.create_text_node("hello");
904 let s0 = dom.selection_serial();
905 dom.set_selection(Some(Selection::caret(Position::new(t, 1))));
906 let s1 = dom.selection_serial();
907 assert_ne!(s0, s1);
908 dom.set_selection(Some(Selection::caret(Position::new(t, 1))));
909 assert_eq!(dom.selection_serial(), s1, "a no-op set does not advance");
910 dom.set_selection(Some(Selection::caret(Position::new(t, 2))));
911 dom.set_selection(Some(Selection::caret(Position::new(t, 1))));
912 assert_ne!(
913 dom.selection_serial(),
914 s1,
915 "moving away and back is two changes"
916 );
917 let later: crate::SelectionSerial = dom.selection_serial();
918 assert!(later > s1, "serials are ordered: {later:?} after {s1:?}");
919 }
920
921 #[test]
922 fn selection_range_same_node_orders_by_offset() {
923 use crate::{Position, Range, Selection};
924 let mut dom: Dom = Dom::new();
925 let t = dom.create_text_node("hello");
926 dom.set_selection(Some(Selection::new(
928 Position::new(t, 4),
929 Position::new(t, 1),
930 )));
931 let r = dom.selection_range().unwrap();
932 assert_eq!(
933 r,
934 Range::ordered_unchecked(Position::new(t, 1), Position::new(t, 4))
935 );
936 }
937
938 #[test]
939 fn selection_range_different_nodes_orders_by_document_position() {
940 use crate::{Position, Selection};
941 let mut dom: Dom = Dom::new();
942 let root = dom.root();
943 let t1 = dom.create_text_node("first");
944 let t2 = dom.create_text_node("second");
945 dom.append_child(root, t1).unwrap();
946 dom.append_child(root, t2).unwrap();
947
948 dom.set_selection(Some(Selection::new(
950 Position::new(t2, 2),
951 Position::new(t1, 3),
952 )));
953 let r = dom.selection_range().unwrap();
954 assert_eq!(r.start.node, t1);
955 assert_eq!(r.end.node, t2);
956 }
957
958 #[test]
962 fn selection_range_ancestor_position_orders_by_child_index() {
963 use crate::{Position, Selection};
964 let mut dom: Dom = Dom::new();
965 let root = dom.root();
966 let p = dom.create_element("p");
967 let t = dom.create_text_node("hello");
968 dom.append_child(root, p).unwrap();
969 dom.append_child(p, t).unwrap();
970
971 dom.set_selection(Some(Selection::new(
973 Position::new(t, 2),
974 Position::new(root, 1),
975 )));
976 let r = dom.selection_range().unwrap();
977 assert_eq!(r.start, Position::new(t, 2));
978 assert_eq!(r.end, Position::new(root, 1));
979
980 dom.set_selection(Some(Selection::new(
982 Position::new(t, 2),
983 Position::new(root, 0),
984 )));
985 let r = dom.selection_range().unwrap();
986 assert_eq!(r.start, Position::new(root, 0));
987 assert_eq!(r.end, Position::new(t, 2));
988 }
989
990 #[test]
993 fn query_selector_one_arg_runs_from_root() {
994 let mut dom: Dom = Dom::new();
995 let root = dom.root();
996 let a = dom.create_element("p");
997 dom.node_mut(a).add_class("hit").unwrap();
998 let b = dom.create_element("p");
999 dom.node_mut(b).add_class("hit").unwrap();
1000 dom.append_child(root, a).unwrap();
1001 dom.append_child(root, b).unwrap();
1002 let hit = dom.query_selector(".hit").unwrap();
1003 assert_eq!(hit.id(), a);
1004 }
1005
1006 #[test]
1007 fn query_selector_one_arg_returns_none_on_invalid_selector() {
1008 let dom: Dom = Dom::new();
1009 assert!(dom.query_selector("!!!").is_none());
1010 }
1011
1012 #[test]
1013 fn query_selector_all_one_arg_returns_doc_order_node_list() {
1014 let mut dom: Dom = Dom::new();
1015 let root = dom.root();
1016 let a = dom.create_element("p");
1017 let b = dom.create_element("p");
1018 let c = dom.create_element("p");
1019 dom.append_child(root, a).unwrap();
1020 dom.append_child(root, b).unwrap();
1021 dom.append_child(root, c).unwrap();
1022 let list = dom.query_selector_all("p");
1023 assert_eq!(list.len(), 3);
1024 let ids: Vec<NodeId> = list.iter().map(|n| n.id()).collect();
1025 assert_eq!(ids, vec![a, b, c]);
1026 }
1027
1028 #[test]
1029 fn elements_by_tag_returns_node_list_in_doc_order() {
1030 let mut dom: Dom = Dom::new();
1031 let root = dom.root();
1032 let div = dom.create_element("div");
1033 let span1 = dom.create_element("span");
1034 let span2 = dom.create_element("span");
1035 let p = dom.create_element("p");
1036 dom.append_child(div, span1).unwrap();
1037 dom.append_child(div, p).unwrap();
1038 dom.append_child(div, span2).unwrap();
1039 dom.append_child(root, div).unwrap();
1040 let list = dom.elements_by_tag("span");
1041 let ids: Vec<NodeId> = list.iter().map(|n| n.id()).collect();
1042 assert_eq!(ids, vec![span1, span2]);
1043 }
1044
1045 #[test]
1046 fn document_element_returns_root_when_element() {
1047 let dom: Dom<()> = Dom::with_root_tag("html");
1048 let html_id = dom.root();
1049 assert_eq!(dom.document_element().id(), html_id);
1050 }
1051
1052 #[test]
1053 fn document_element_returns_first_element_child_when_root_is_fragment() {
1054 let mut dom: Dom = Dom::new();
1055 let root = dom.root();
1056 let html = dom.create_element("html");
1058 let comment = dom.create_comment("note");
1059 dom.append_child(root, comment).unwrap();
1060 dom.append_child(root, html).unwrap();
1061 assert_eq!(dom.document_element().id(), html);
1062 }
1063
1064 #[test]
1065 fn document_element_returns_root_fragment_when_no_element_child() {
1066 let dom: Dom = Dom::new();
1068 let root = dom.root();
1069 assert_eq!(dom.document_element().id(), root);
1070 }
1071
1072 #[test]
1073 fn active_element_and_has_focus_track_focused() {
1074 let mut dom: Dom = Dom::new();
1075 let root = dom.root();
1076 let el = dom.create_element("input");
1077 dom.append_child(root, el).unwrap();
1078 assert!(!dom.has_focus());
1079 assert!(dom.active_element().is_none());
1080 dom.set_focused(Some(el));
1081 assert!(dom.has_focus());
1082 assert_eq!(dom.active_element().map(|n| n.id()), Some(el));
1083 dom.set_focused(None);
1084 assert!(!dom.has_focus());
1085 assert!(dom.active_element().is_none());
1086 }
1087
1088 #[test]
1089 fn create_range_returns_collapsed_at_root() {
1090 use crate::Position;
1091 let dom: Dom = Dom::new();
1092 let r = dom.create_range();
1093 assert!(r.is_collapsed());
1094 assert_eq!(r.start, Position::new(dom.root(), 0));
1095 }
1096}