Skip to main content

html5_parser/
document.rs

1// Tree/node model produced by tree_builder — element/text/comment/
2// processing-instruction/doctype nodes with expanded names and per-node
3// source positions.
4//
5// Shaped as a classic arena tree (`NonZeroU32` node ids, doubly-linked
6// sibling lists), structurally close enough to the tree API
7// `html-conform`'s existing `src/infoset.rs::normalize()` already adapts
8// (currently written against its current HTML5-parsing dependency's tree
9// shape) that switching `normalize()` over to this crate should need only
10// modest changes. See plan/03-tree-construction.md, "Zieldatenmodell".
11
12use std::num::NonZeroU32;
13
14use crate::tokenizer::{Attribute as TokenAttribute, Position};
15
16/// Identifies a node within a [`Document`]'s arena. `NonZeroU32` so that
17/// `Option<NodeId>` is the same size as `NodeId` — index 0 is never
18/// issued.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub struct NodeId(NonZeroU32);
21
22impl NodeId {
23    fn from_index(index: usize) -> Self {
24        Self(
25            NonZeroU32::new(u32::try_from(index).expect("node arena index overflowed u32"))
26                .expect("node arena index must be nonzero"),
27        )
28    }
29
30    fn index(self) -> usize {
31        self.0.get() as usize
32    }
33}
34
35/// An HTML attribute, resolved to its (possibly foreign-content-adjusted)
36/// namespace during tree construction — see plan/03-tree-construction.md's
37/// Foreign-Content-Dispatch step.
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct Attribute {
40    pub name: String,
41    pub value: String,
42    pub namespace: Option<String>,
43}
44
45impl From<TokenAttribute> for Attribute {
46    /// Attributes arrive off the tokenizer with no namespace at all
47    /// (`namespace: None`) — HTML tag/attribute parsing itself is
48    /// namespace-unaware, per §13.2.5. Namespace resolution (XHTML
49    /// default, or the foreign-content adjustment tables for SVG/MathML
50    /// attributes like `xlink:href`) is tree-construction's job, applied
51    /// on top of this conversion, not part of it.
52    fn from(attribute: TokenAttribute) -> Self {
53        Attribute {
54            name: attribute.name,
55            value: attribute.value,
56            namespace: None,
57        }
58    }
59}
60
61/// The kind of a document node and its associated data. Mostly covers
62/// what the HTML5 tokenizer can actually produce a token for (§13.2.5's
63/// token kinds) — no `CData`/`EntityRef` variants, since the HTML5
64/// tokenizer never emits those (character references and CDATA content
65/// both resolve straight to character tokens, see
66/// `tokenizer::TokenKind`'s doc comment). [`DocumentFragment`](Self::DocumentFragment)
67/// is the one exception: not tokenizer-token-shaped at all, synthesized
68/// directly by tree construction (§13.2.6.1's "create an element for a
69/// token" step, for `template` elements specifically).
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum NodeKind {
72    /// The document node — there is exactly one per [`Document`].
73    Document,
74    /// An element node, e.g. `<div class="x">`.
75    Element {
76        name: String,
77        namespace: Option<String>,
78        attributes: Vec<Attribute>,
79    },
80    /// A text node.
81    Text { content: String },
82    /// A comment node.
83    Comment { content: String },
84    /// A processing instruction node, e.g. `<?target data?>`. Every
85    /// insertion mode's token dispatch has an explicit "processing
86    /// instruction token" branch (verified against the raw spec text,
87    /// not assumed) that inserts one of these — `html-conform`'s
88    /// `normalize()` drops it afterwards, but tree-construction still
89    /// puts it in the tree, so the node kind exists here too.
90    ProcessingInstruction { target: String, data: String },
91    /// A DOCUMENT TYPE node, e.g. `<!DOCTYPE html>`. Also dropped by
92    /// `html-conform::normalize()`, but inserted into the tree by the
93    /// "initial" insertion mode per spec, same reasoning as above.
94    Doctype {
95        name: Option<String>,
96        public_identifier: Option<String>,
97        system_identifier: Option<String>,
98    },
99    /// A `template` element's "template contents" — an inert fragment
100    /// root that real content inserted "inside" a `template` element
101    /// actually lands in, per §13.2.6.1's "appropriate place for
102    /// inserting a node". Modeled here as the template element's sole
103    /// real tree child (created alongside it, see
104    /// `tree_builder.rs::create_element_for_token`), since `Document`
105    /// has no separate out-of-tree fragment concept — matching how
106    /// html5lib-tests' own `#document` dump format represents it (a
107    /// synthetic `content` line, with the real children nested one
108    /// level below that).
109    DocumentFragment,
110}
111
112/// A single node in a [`Document`]'s arena: its kind/payload, its source
113/// position (`None` for the document node itself and for any node a
114/// tree-construction algorithm synthesizes rather than parses — e.g. an
115/// implied `<html>`/`<head>`/`<body>` — `Some` for everything else), and
116/// its tree-navigation links.
117#[derive(Debug, Clone)]
118pub struct Node {
119    pub kind: NodeKind,
120    /// The node's position in the original input, or `None` for
121    /// synthesized nodes (see the struct-level doc comment above).
122    pub position: Option<Position>,
123    parent: Option<NodeId>,
124    first_child: Option<NodeId>,
125    last_child: Option<NodeId>,
126    next_sibling: Option<NodeId>,
127    prev_sibling: Option<NodeId>,
128}
129
130impl Node {
131    fn new(kind: NodeKind, position: Option<Position>) -> Self {
132        Node {
133            kind,
134            position,
135            parent: None,
136            first_child: None,
137            last_child: None,
138            next_sibling: None,
139            prev_sibling: None,
140        }
141    }
142}
143
144/// An HTML document tree, produced by [`crate::parse`].
145///
146/// Read access (`root`/`node`/`parent`/`children`) is the public surface:
147/// enough to walk the tree and read each node's kind and source position.
148/// Mutation (`new_node`/`append_child`/...) stays crate-internal — it's
149/// `tree_builder`'s job to build the tree in the first place, following
150/// the spec-level insertion algorithms ("insert a comment", "insert an
151/// HTML element", table foster parenting, ...), see
152/// plan/03-tree-construction.md's "gemeinsame
153/// Tree-Construction-Infrastruktur" step.
154#[derive(Debug)]
155pub struct Document {
156    nodes: Vec<Node>,
157    root: NodeId,
158}
159
160impl Document {
161    /// Creates a new document containing only its own [`Document`] node
162    /// (index 1 — index 0 is an unused placeholder, so `NodeId`'s
163    /// `NonZeroU32` never has to represent zero).
164    pub(crate) fn new() -> Self {
165        let placeholder = Node::new(NodeKind::Document, None);
166        let root_node = Node::new(NodeKind::Document, None);
167        Document {
168            nodes: vec![placeholder, root_node],
169            root: NodeId::from_index(1),
170        }
171    }
172
173    /// Returns the id of the document's own root node (the [`NodeKind::Document`] node).
174    pub fn root(&self) -> NodeId {
175        self.root
176    }
177
178    /// Returns the node identified by `id`: its kind, source position, and
179    /// tree-navigation links (via [`Document::parent`]/[`Document::children`]).
180    pub fn node(&self, id: NodeId) -> &Node {
181        &self.nodes[id.index()]
182    }
183
184    /// Mutable access to a node — used by `tree_builder` to append to an
185    /// existing text node's content ("insert a character", §13.2.6.1)
186    /// rather than always creating a new one.
187    pub(crate) fn node_mut(&mut self, id: NodeId) -> &mut Node {
188        &mut self.nodes[id.index()]
189    }
190
191    /// Returns `id`'s parent node, or `None` if `id` is the document's
192    /// own root node.
193    pub fn parent(&self, id: NodeId) -> Option<NodeId> {
194        self.node(id).parent
195    }
196
197    pub(crate) fn last_child(&self, id: NodeId) -> Option<NodeId> {
198        self.node(id).last_child
199    }
200
201    pub(crate) fn prev_sibling(&self, id: NodeId) -> Option<NodeId> {
202        self.node(id).prev_sibling
203    }
204
205    /// Creates a new, detached node and returns its id. Callers attach it
206    /// into the tree via [`append_child`](Self::append_child).
207    pub(crate) fn new_node(&mut self, kind: NodeKind, position: Option<Position>) -> NodeId {
208        self.nodes.push(Node::new(kind, position));
209        NodeId::from_index(self.nodes.len() - 1)
210    }
211
212    /// "Clone a node", with `subtree` implicitly always `true` (the only
213    /// case this crate needs — `<selectedcontent>`'s option-content
214    /// mirroring, see `tree_builder.rs::maybe_clone_option_into_selectedcontent`).
215    /// Returns a new, detached node with the same kind as `source` (and,
216    /// recursively, the same for its descendants) — a full, independent
217    /// copy, not sharing any state with the original. No source
218    /// position (a clone has no position of its own, same convention as
219    /// any other synthesized node). Simplified from the DOM Standard's
220    /// "clone" algorithm: no custom-element callbacks, no
221    /// registered-observer copying, no `Document`/shadow-root special
222    /// cases — none apply without a live, scripted DOM.
223    pub(crate) fn clone_subtree(&mut self, source: NodeId) -> NodeId {
224        let kind = self.node(source).kind.clone();
225        let clone = self.new_node(kind, None);
226        let children: Vec<_> = self.children(source).collect();
227        for child in children {
228            let child_clone = self.clone_subtree(child);
229            self.append_child(clone, child_clone);
230        }
231        clone
232    }
233
234    /// Detaches `node` from its current parent and siblings, if any — a
235    /// no-op if it has none. The node itself stays in the arena (nothing
236    /// is ever freed); it can be reinserted elsewhere afterward via
237    /// [`insert_before`](Self::insert_before)/[`append_child`](Self::append_child).
238    ///
239    /// This is the DOM Standard's "remove" primitive
240    /// (<https://dom.spec.whatwg.org/#concept-node-remove>), simplified
241    /// to the tree-shape bookkeeping this crate tracks (no live
242    /// ranges/mutation records/shadow DOM). Needed once tree-construction
243    /// actually relocates already-inserted nodes — first by the adoption
244    /// agency algorithm (§13.2.6.4.7) — rather than only ever inserting
245    /// freshly created ones.
246    pub(crate) fn remove(&mut self, node: NodeId) {
247        let Some(parent) = self.node(node).parent else {
248            return;
249        };
250        let previous_sibling = self.node(node).prev_sibling;
251        let next_sibling = self.node(node).next_sibling;
252        match previous_sibling {
253            Some(previous_sibling) => {
254                self.nodes[previous_sibling.index()].next_sibling = next_sibling;
255            }
256            None => self.nodes[parent.index()].first_child = next_sibling,
257        }
258        match next_sibling {
259            Some(next_sibling) => {
260                self.nodes[next_sibling.index()].prev_sibling = previous_sibling;
261            }
262            None => self.nodes[parent.index()].last_child = previous_sibling,
263        }
264        let node = &mut self.nodes[node.index()];
265        node.parent = None;
266        node.prev_sibling = None;
267        node.next_sibling = None;
268    }
269
270    /// True if `ancestor` is `node` itself or one of its ancestors —
271    /// the DOM Standard's "(inclusive) ancestor" relation
272    /// (<https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor>),
273    /// minus the "host-including" shadow-DOM extension (this crate has
274    /// no shadow DOM). Used by the adoption agency algorithm's insertion
275    /// guard (§13.2.6.4.7) to avoid creating a cycle.
276    pub(crate) fn is_inclusive_ancestor(&self, ancestor: NodeId, node: NodeId) -> bool {
277        let mut current = Some(node);
278        while let Some(current_node) = current {
279            if current_node == ancestor {
280                return true;
281            }
282            current = self.node(current_node).parent;
283        }
284        false
285    }
286
287    /// Inserts `new_node` as a child of `parent`, immediately before
288    /// `reference` — or, if `reference` is `None`, as the last child.
289    /// If `new_node` is already attached elsewhere, it is
290    /// [`remove`](Self::remove)d first — matching the DOM Standard's
291    /// "insert" algorithm, whose per-node "adopt" step does the same
292    /// (<https://dom.spec.whatwg.org/#concept-node-insert>: "Adopt node
293    /// into parent's node document", and adopt: "If node's parent is
294    /// non-null, then remove node."). `reference`, if given, must
295    /// already be a child of `parent`.
296    ///
297    /// This is the one primitive tree-construction's insertion algorithms
298    /// build on, both for the common "append as the last child of the
299    /// current node" path (`reference: None`) and the less common
300    /// mid-list cases (e.g. table foster parenting inserting before the
301    /// table itself, or the adoption agency algorithm relocating
302    /// already-inserted nodes).
303    pub(crate) fn insert_before(
304        &mut self,
305        parent: NodeId,
306        reference: Option<NodeId>,
307        new_node: NodeId,
308    ) {
309        self.remove(new_node);
310        match reference {
311            None => {
312                let previous_last_child = self.node(parent).last_child;
313                self.nodes[new_node.index()].parent = Some(parent);
314                self.nodes[new_node.index()].prev_sibling = previous_last_child;
315                if let Some(previous_last_child) = previous_last_child {
316                    self.nodes[previous_last_child.index()].next_sibling = Some(new_node);
317                } else {
318                    self.nodes[parent.index()].first_child = Some(new_node);
319                }
320                self.nodes[parent.index()].last_child = Some(new_node);
321            }
322            Some(reference) => {
323                debug_assert_eq!(
324                    self.node(reference).parent,
325                    Some(parent),
326                    "insert_before's reference node must already be a child of parent"
327                );
328                let previous_sibling = self.node(reference).prev_sibling;
329                self.nodes[new_node.index()].parent = Some(parent);
330                self.nodes[new_node.index()].next_sibling = Some(reference);
331                self.nodes[new_node.index()].prev_sibling = previous_sibling;
332                self.nodes[reference.index()].prev_sibling = Some(new_node);
333                if let Some(previous_sibling) = previous_sibling {
334                    self.nodes[previous_sibling.index()].next_sibling = Some(new_node);
335                } else {
336                    self.nodes[parent.index()].first_child = Some(new_node);
337                }
338            }
339        }
340    }
341
342    /// Appends `child` as the last child of `parent`. Shorthand for
343    /// [`insert_before`](Self::insert_before) with `reference: None`.
344    pub(crate) fn append_child(&mut self, parent: NodeId, child: NodeId) {
345        self.insert_before(parent, None, child);
346    }
347
348    /// Returns an iterator over the direct children of `id`, in document
349    /// order.
350    pub fn children(&self, id: NodeId) -> Children<'_> {
351        Children {
352            document: self,
353            next: self.node(id).first_child,
354        }
355    }
356}
357
358impl Default for Document {
359    fn default() -> Self {
360        Self::new()
361    }
362}
363
364/// Iterator over a node's direct children, in document order. Created by
365/// [`Document::children`].
366pub struct Children<'a> {
367    document: &'a Document,
368    next: Option<NodeId>,
369}
370
371impl Iterator for Children<'_> {
372    type Item = NodeId;
373
374    fn next(&mut self) -> Option<NodeId> {
375        let current = self.next?;
376        self.next = self.document.node(current).next_sibling;
377        Some(current)
378    }
379}
380
381#[cfg(test)]
382mod tests {
383    use super::{Document, NodeKind, Position};
384
385    fn pos(line: u32, column: u32, byte_offset: usize) -> Position {
386        Position {
387            line,
388            column,
389            byte_offset,
390        }
391    }
392
393    #[test]
394    fn new_document_has_only_its_own_document_node() {
395        let document = Document::new();
396        assert_eq!(document.node(document.root()).kind, NodeKind::Document);
397        assert_eq!(document.children(document.root()).count(), 0);
398        assert_eq!(document.node(document.root()).position, None);
399    }
400
401    #[test]
402    fn append_child_attaches_a_detached_node_as_the_last_child() {
403        let mut document = Document::new();
404        let root = document.root();
405        let p = document.new_node(
406            NodeKind::Element {
407                name: "p".to_owned(),
408                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
409                attributes: vec![],
410            },
411            Some(pos(1, 1, 0)),
412        );
413        document.append_child(root, p);
414
415        let children: Vec<_> = document.children(root).collect();
416        assert_eq!(children, vec![p]);
417        assert_eq!(document.parent(p), Some(root));
418    }
419
420    #[test]
421    fn multiple_children_are_yielded_in_document_order() {
422        let mut document = Document::new();
423        let root = document.root();
424        let first = document.new_node(
425            NodeKind::Text {
426                content: "a".to_owned(),
427            },
428            None,
429        );
430        let second = document.new_node(
431            NodeKind::Text {
432                content: "b".to_owned(),
433            },
434            None,
435        );
436        let third = document.new_node(
437            NodeKind::Text {
438                content: "c".to_owned(),
439            },
440            None,
441        );
442        document.append_child(root, first);
443        document.append_child(root, second);
444        document.append_child(root, third);
445
446        let children: Vec<_> = document.children(root).collect();
447        assert_eq!(children, vec![first, second, third]);
448    }
449
450    #[test]
451    fn insert_before_a_reference_places_the_new_node_in_the_middle() {
452        let mut document = Document::new();
453        let root = document.root();
454        let first = document.new_node(
455            NodeKind::Text {
456                content: "a".to_owned(),
457            },
458            None,
459        );
460        let third = document.new_node(
461            NodeKind::Text {
462                content: "c".to_owned(),
463            },
464            None,
465        );
466        document.append_child(root, first);
467        document.append_child(root, third);
468        let second = document.new_node(
469            NodeKind::Text {
470                content: "b".to_owned(),
471            },
472            None,
473        );
474        document.insert_before(root, Some(third), second);
475
476        let children: Vec<_> = document.children(root).collect();
477        assert_eq!(children, vec![first, second, third]);
478    }
479
480    #[test]
481    fn insert_before_at_the_start_updates_first_child() {
482        let mut document = Document::new();
483        let root = document.root();
484        let second = document.new_node(
485            NodeKind::Text {
486                content: "b".to_owned(),
487            },
488            None,
489        );
490        document.append_child(root, second);
491        let first = document.new_node(
492            NodeKind::Text {
493                content: "a".to_owned(),
494            },
495            None,
496        );
497        document.insert_before(root, Some(second), first);
498
499        let children: Vec<_> = document.children(root).collect();
500        assert_eq!(children, vec![first, second]);
501    }
502
503    #[test]
504    fn nested_children_are_independent_of_their_parents_siblings() {
505        let mut document = Document::new();
506        let root = document.root();
507        let div = document.new_node(
508            NodeKind::Element {
509                name: "div".to_owned(),
510                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
511                attributes: vec![],
512            },
513            Some(pos(1, 1, 0)),
514        );
515        document.append_child(root, div);
516        let text = document.new_node(
517            NodeKind::Text {
518                content: "hi".to_owned(),
519            },
520            Some(pos(1, 6, 5)),
521        );
522        document.append_child(div, text);
523
524        assert_eq!(document.children(root).collect::<Vec<_>>(), vec![div]);
525        assert_eq!(document.children(div).collect::<Vec<_>>(), vec![text]);
526        assert_eq!(document.parent(text), Some(div));
527    }
528
529    #[test]
530    fn synthesized_nodes_carry_no_position_while_parsed_nodes_do() {
531        let mut document = Document::new();
532        let root = document.root();
533        let implied_html = document.new_node(
534            NodeKind::Element {
535                name: "html".to_owned(),
536                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
537                attributes: vec![],
538            },
539            None,
540        );
541        document.append_child(root, implied_html);
542        let parsed_p = document.new_node(
543            NodeKind::Element {
544                name: "p".to_owned(),
545                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
546                attributes: vec![],
547            },
548            Some(pos(1, 1, 0)),
549        );
550        document.append_child(implied_html, parsed_p);
551
552        assert_eq!(document.node(implied_html).position, None);
553        assert_eq!(document.node(parsed_p).position, Some(pos(1, 1, 0)));
554    }
555
556    #[test]
557    fn remove_detaches_a_node_and_relinks_its_siblings() {
558        let mut document = Document::new();
559        let root = document.root();
560        let first = document.new_node(
561            NodeKind::Text {
562                content: "a".to_owned(),
563            },
564            None,
565        );
566        let second = document.new_node(
567            NodeKind::Text {
568                content: "b".to_owned(),
569            },
570            None,
571        );
572        let third = document.new_node(
573            NodeKind::Text {
574                content: "c".to_owned(),
575            },
576            None,
577        );
578        document.append_child(root, first);
579        document.append_child(root, second);
580        document.append_child(root, third);
581
582        document.remove(second);
583
584        assert_eq!(
585            document.children(root).collect::<Vec<_>>(),
586            vec![first, third]
587        );
588        assert_eq!(document.parent(second), None);
589    }
590
591    #[test]
592    fn remove_on_a_node_with_no_parent_is_a_no_op() {
593        let mut document = Document::new();
594        let detached = document.new_node(
595            NodeKind::Text {
596                content: "a".to_owned(),
597            },
598            None,
599        );
600        document.remove(detached);
601        assert_eq!(document.parent(detached), None);
602    }
603
604    #[test]
605    fn insert_before_an_already_attached_node_moves_it() {
606        // Matches the DOM Standard's "insert" algorithm, whose "adopt"
607        // step removes a node from its old parent before placing it in
608        // the new location — exercised for the first time by the
609        // adoption agency algorithm (§13.2.6.4.7), which relocates
610        // already-inserted nodes rather than only ever inserting fresh
611        // ones.
612        let mut document = Document::new();
613        let root = document.root();
614        let old_parent = document.new_node(
615            NodeKind::Element {
616                name: "div".to_owned(),
617                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
618                attributes: vec![],
619            },
620            None,
621        );
622        let new_parent = document.new_node(
623            NodeKind::Element {
624                name: "span".to_owned(),
625                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
626                attributes: vec![],
627            },
628            None,
629        );
630        document.append_child(root, old_parent);
631        document.append_child(root, new_parent);
632        let child = document.new_node(
633            NodeKind::Text {
634                content: "hi".to_owned(),
635            },
636            None,
637        );
638        document.append_child(old_parent, child);
639
640        document.append_child(new_parent, child);
641
642        assert_eq!(document.children(old_parent).count(), 0);
643        assert_eq!(
644            document.children(new_parent).collect::<Vec<_>>(),
645            vec![child]
646        );
647        assert_eq!(document.parent(child), Some(new_parent));
648    }
649
650    #[test]
651    fn is_inclusive_ancestor_covers_self_and_real_ancestors_but_not_others() {
652        let mut document = Document::new();
653        let root = document.root();
654        let div = document.new_node(
655            NodeKind::Element {
656                name: "div".to_owned(),
657                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
658                attributes: vec![],
659            },
660            None,
661        );
662        document.append_child(root, div);
663        let span = document.new_node(
664            NodeKind::Element {
665                name: "span".to_owned(),
666                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
667                attributes: vec![],
668            },
669            None,
670        );
671        document.append_child(div, span);
672        let unrelated = document.new_node(
673            NodeKind::Text {
674                content: "x".to_owned(),
675            },
676            None,
677        );
678        document.append_child(root, unrelated);
679
680        assert!(document.is_inclusive_ancestor(span, span));
681        assert!(document.is_inclusive_ancestor(div, span));
682        assert!(document.is_inclusive_ancestor(root, span));
683        assert!(!document.is_inclusive_ancestor(unrelated, span));
684        assert!(!document.is_inclusive_ancestor(span, div));
685    }
686
687    #[test]
688    fn clone_subtree_deep_copies_kind_and_structure_into_new_nodes() {
689        let mut document = Document::new();
690        let root = document.root();
691        let original = document.new_node(
692            NodeKind::Element {
693                name: "b".to_owned(),
694                namespace: Some("http://www.w3.org/1999/xhtml".to_owned()),
695                attributes: vec![],
696            },
697            Some(pos(1, 1, 0)),
698        );
699        document.append_child(root, original);
700        let text = document.new_node(
701            NodeKind::Text {
702                content: "hi".to_owned(),
703            },
704            Some(pos(1, 4, 3)),
705        );
706        document.append_child(original, text);
707
708        let clone = document.clone_subtree(original);
709
710        assert_ne!(clone, original);
711        assert_eq!(document.node(clone).kind, document.node(original).kind);
712        // The clone is detached — the caller decides where it goes.
713        assert_eq!(document.parent(clone), None);
714        let clone_children: Vec<_> = document.children(clone).collect();
715        assert_eq!(clone_children.len(), 1);
716        assert_ne!(clone_children[0], text);
717        assert_eq!(
718            document.node(clone_children[0]).kind,
719            NodeKind::Text {
720                content: "hi".to_owned()
721            }
722        );
723        // A clone carries no position of its own.
724        assert_eq!(document.node(clone).position, None);
725        // The original is untouched.
726        assert_eq!(document.children(original).collect::<Vec<_>>(), vec![text]);
727    }
728}