Skip to main content

rusty_xml_tree/
lib.rs

1//! Arena DOM matching libxml2 `tree.h` ownership: the document owns every node.
2//! Handles are indices, not parent+child `&mut` pairs.
3
4#![forbid(unsafe_code)]
5
6use std::collections::HashMap;
7
8/// libxml2 `xmlElementType` discriminants.
9#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10#[repr(u32)]
11pub enum NodeKind {
12    Element = 1,
13    Attribute = 2,
14    Text = 3,
15    CData = 4,
16    EntityRef = 5,
17    Entity = 6,
18    Pi = 7,
19    Comment = 8,
20    Document = 9,
21    DocumentType = 10,
22    DocumentFrag = 11,
23    Notation = 12,
24    HtmlDocument = 13,
25    Dtd = 14,
26    ElementDecl = 15,
27    AttributeDecl = 16,
28    EntityDecl = 17,
29    Namespace = 18,
30    XIncludeStart = 19,
31    XIncludeEnd = 20,
32}
33
34/// Parsed DTD attached to a document (`xmlDtd`).
35#[derive(Clone, Debug, Default)]
36pub struct XmlDtd {
37    pub name: Option<String>,
38    pub public_id: Option<String>,
39    pub system_id: Option<String>,
40    pub int_subset: Option<String>,
41    /// General entity name → replacement text.
42    pub entities: HashMap<String, String>,
43    /// Parameter entity name → replacement.
44    pub parameter_entities: HashMap<String, String>,
45    /// Names of entities declared with an NDATA annotation. An attribute of
46    /// type ENTITY must name one of these, which needs them kept apart from
47    /// parsed entities rather than lumped in with them.
48    pub unparsed_entities: std::collections::HashSet<String>,
49    /// Element name → content model.
50    pub elements: HashMap<String, ElementDecl>,
51    /// (element, attribute) → declaration.
52    pub attributes: HashMap<(String, String), AttrDecl>,
53}
54
55#[derive(Clone, Debug)]
56pub enum ElementDecl {
57    Empty,
58    Any,
59    Mixed(Vec<String>),
60    Children(String),
61}
62
63#[derive(Clone, Debug)]
64pub struct AttrDecl {
65    pub att_type: String,
66    pub default: AttrDefault,
67    pub default_value: Option<String>,
68    pub enumerated: Vec<String>,
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum AttrDefault {
73    Required,
74    Implied,
75    Fixed,
76    Value,
77}
78
79/// Stable handle into an [`XmlDoc`] arena. Valid for the lifetime of the doc.
80#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
81pub struct NodeId(pub u32);
82
83impl NodeId {
84    /// Document node is always slot 0.
85    pub const DOCUMENT: NodeId = NodeId(0);
86
87    pub fn index(self) -> usize {
88        self.0 as usize
89    }
90}
91
92
93
94#[derive(Clone, Debug)]
95pub struct Node {
96    pub kind: NodeKind,
97    pub name: String,
98    pub prefix: Option<String>,
99    pub ns_uri: Option<String>,
100    pub content: String,
101    pub parent: Option<NodeId>,
102    pub first_child: Option<NodeId>,
103    pub last_child: Option<NodeId>,
104    pub prev_sibling: Option<NodeId>,
105    pub next_sibling: Option<NodeId>,
106    pub first_attr: Option<NodeId>,
107    pub last_attr: Option<NodeId>,
108    /// Namespace declarations on this element (`xmlns` / `xmlns:prefix`), in source order.
109    pub ns_defs: Vec<(Option<String>, String)>,
110}
111
112impl Node {
113    fn new(kind: NodeKind, name: String) -> Self {
114        Self {
115            kind,
116            name,
117            prefix: None,
118            ns_uri: None,
119            content: String::new(),
120            parent: None,
121            first_child: None,
122            last_child: None,
123            prev_sibling: None,
124            next_sibling: None,
125            first_attr: None,
126            last_attr: None,
127            ns_defs: Vec::new(),
128        }
129    }
130}
131
132/// libxml2 `xmlDoc`.
133#[derive(Clone, Debug)]
134pub struct XmlDoc {
135    nodes: Vec<Node>,
136    /// XML version string; default `"1.0"`.
137    pub version: String,
138    /// Encoding name from the XML declaration, if any.
139    pub encoding: Option<String>,
140    /// `Some(true/false)` from `standalone`, `None` if omitted.
141    pub standalone: Option<bool>,
142    /// First element child of the document (cached; also discoverable by walk).
143    root: Option<NodeId>,
144    /// Internal / attached DTD, if any.
145    pub dtd: Option<XmlDtd>,
146}
147
148impl Default for XmlDoc {
149    fn default() -> Self {
150        Self::xml_new_doc(Some("1.0"))
151    }
152}
153
154impl XmlDoc {
155    /// `xmlNewDoc`.
156    #[doc(alias = "xmlNewDoc")]
157    pub fn xml_new_doc(version: Option<&str>) -> Self {
158        Self::with_node_capacity(version, 1)
159    }
160
161    /// Build a document whose node arena is sized up front. Starting a parse
162    /// used to allocate the arena, push into it, then reallocate on reserve --
163    /// three trips for what one sized allocation does. The document node's
164    /// name is implied by its kind, so it stores no String either.
165    pub fn with_node_capacity(version: Option<&str>, cap: usize) -> Self {
166        const MAX_ARENA_BYTES: usize = 32 << 20;
167        let ceiling = MAX_ARENA_BYTES / std::mem::size_of::<Node>();
168        // A floor of 4 keeps a small document from reallocating its way up
169        // from a single slot, which is what Vec::new()+push used to give it.
170        let mut nodes = Vec::with_capacity(cap.clamp(4, ceiling));
171        nodes.push(Node::new(NodeKind::Document, String::new()));
172        Self {
173            nodes,
174            version: version.unwrap_or("1.0").to_string(),
175            encoding: None,
176            standalone: None,
177            root: None,
178            dtd: None,
179        }
180    }
181
182    /// Pre-size the node arena. XML runs about one node per 10-15 input bytes,
183    /// so a parser that knows the document length can skip most of the arena's
184    /// doubling-and-copy. Capped so a huge document cannot reserve wildly.
185    pub fn reserve_nodes(&mut self, n: usize) {
186        // Measured node density is ~1 per 10-12 input bytes. The previous cap
187        // of 65_536 was below a 700 KB document's node count, so the arena
188        // still doubled-and-copied its way up -- about 22 MB of memcpy on a
189        // 627 KB file. Shrinking Node itself would need an API break for a
190        // sub-1% effect; reserving correctly removes the same traffic for free.
191        // Cap by memory, not node count: 1<<20 nodes is ~190 MB of arena, which
192        // a large document would commit up front before parsing a byte.
193        const MAX_ARENA_BYTES: usize = 32 << 20;
194        let cap = MAX_ARENA_BYTES / std::mem::size_of::<Node>();
195        self.nodes.reserve(n.min(cap));
196    }
197
198    pub fn node(&self, id: NodeId) -> &Node {
199        &self.nodes[id.index()]
200    }
201
202    pub fn node_mut(&mut self, id: NodeId) -> &mut Node {
203        &mut self.nodes[id.index()]
204    }
205
206    pub fn kind(&self, id: NodeId) -> NodeKind {
207        self.node(id).kind
208    }
209
210    pub fn name(&self, id: NodeId) -> &str {
211        let n = self.node(id);
212        if n.name.is_empty() {
213            // Nodes whose name is fixed by their kind store no String at all;
214            // allocating "#text" once per text node was a measurable share of
215            // every parse. The canonical name is derived here instead.
216            return match n.kind {
217                NodeKind::Text => "#text",
218                NodeKind::CData => "#cdata-section",
219                NodeKind::Comment => "#comment",
220                NodeKind::Document => "#document",
221                _ => "",
222            };
223        }
224        &n.name
225    }
226
227    pub fn prefix(&self, id: NodeId) -> Option<&str> {
228        self.node(id).prefix.as_deref()
229    }
230
231    pub fn ns_uri(&self, id: NodeId) -> Option<&str> {
232        self.node(id).ns_uri.as_deref()
233    }
234
235    pub fn content(&self, id: NodeId) -> &str {
236        &self.node(id).content
237    }
238
239    pub fn parent(&self, id: NodeId) -> Option<NodeId> {
240        self.node(id).parent
241    }
242
243    pub fn first_child(&self, id: NodeId) -> Option<NodeId> {
244        self.node(id).first_child
245    }
246
247    pub fn last_child(&self, id: NodeId) -> Option<NodeId> {
248        self.node(id).last_child
249    }
250
251    pub fn next_sibling(&self, id: NodeId) -> Option<NodeId> {
252        self.node(id).next_sibling
253    }
254
255    pub fn prev_sibling(&self, id: NodeId) -> Option<NodeId> {
256        self.node(id).prev_sibling
257    }
258
259    pub fn first_attr(&self, id: NodeId) -> Option<NodeId> {
260        self.node(id).first_attr
261    }
262
263    pub fn ns_defs(&self, id: NodeId) -> &[(Option<String>, String)] {
264        &self.node(id).ns_defs
265    }
266
267    /// Allocate a node whose name is implied by its kind, storing no String.
268    /// [`XmlDoc::name`] reports the canonical name for these.
269    pub fn alloc_unnamed(&mut self, kind: NodeKind) -> NodeId {
270        let id = NodeId(self.nodes.len() as u32);
271        self.nodes.push(Node::new(kind, String::new()));
272        id
273    }
274
275    pub fn alloc(&mut self, kind: NodeKind, name: impl Into<String>) -> NodeId {
276        let id = NodeId(self.nodes.len() as u32);
277        self.nodes.push(Node::new(kind, name.into()));
278        id
279    }
280
281    /// `xmlDocGetRootElement`.
282    #[doc(alias = "xmlDocGetRootElement")]
283    pub fn xml_doc_get_root_element(&self) -> Option<NodeId> {
284        if let Some(r) = self.root {
285            return Some(r);
286        }
287        let mut c = self.first_child(NodeId::DOCUMENT);
288        while let Some(id) = c {
289            if self.kind(id) == NodeKind::Element {
290                return Some(id);
291            }
292            c = self.next_sibling(id);
293        }
294        None
295    }
296
297    /// `xmlDocSetRootElement`. Returns the previous root, if any.
298    #[doc(alias = "xmlDocSetRootElement")]
299    pub fn xml_doc_set_root_element(&mut self, elem: NodeId) -> Option<NodeId> {
300        let prev = self.xml_doc_get_root_element();
301        if let Some(p) = prev {
302            self.xml_unlink_node(p);
303        }
304        self.xml_add_child(NodeId::DOCUMENT, elem);
305        self.root = Some(elem);
306        prev
307    }
308
309    /// `xmlNewNode`.
310    #[doc(alias = "xmlNewNode")]
311    pub fn xml_new_node(&mut self, ns_uri: Option<&str>, name: &str) -> NodeId {
312        let id = self.alloc(NodeKind::Element, name);
313        self.node_mut(id).ns_uri = ns_uri.map(str::to_string);
314        id
315    }
316
317    /// `xmlNewDocNode`.
318    #[doc(alias = "xmlNewDocNode")]
319    pub fn xml_new_doc_node(
320        &mut self,
321        ns_uri: Option<&str>,
322        name: &str,
323        content: Option<&str>,
324    ) -> NodeId {
325        let id = self.xml_new_node(ns_uri, name);
326        if let Some(c) = content {
327            if !c.is_empty() {
328                let t = self.alloc(NodeKind::Text, "#text");
329                self.node_mut(t).content = c.to_string();
330                self.xml_add_child(id, t);
331            }
332        }
333        id
334    }
335
336    /// `xmlNewChild`.
337    #[doc(alias = "xmlNewChild")]
338    pub fn xml_new_child(
339        &mut self,
340        parent: NodeId,
341        ns_uri: Option<&str>,
342        name: &str,
343        content: Option<&str>,
344    ) -> NodeId {
345        let id = self.xml_new_doc_node(ns_uri, name, content);
346        self.xml_add_child(parent, id);
347        id
348    }
349
350    /// `xmlAddChild`.
351    #[doc(alias = "xmlAddChild")]
352    pub fn xml_add_child(&mut self, parent: NodeId, child: NodeId) {
353        if child == parent {
354            return;
355        }
356        self.xml_unlink_node(child);
357        self.node_mut(child).parent = Some(parent);
358        let last = self.node(parent).last_child;
359        if let Some(l) = last {
360            self.node_mut(l).next_sibling = Some(child);
361            self.node_mut(child).prev_sibling = Some(l);
362        } else {
363            self.node_mut(parent).first_child = Some(child);
364        }
365        self.node_mut(parent).last_child = Some(child);
366        if parent == NodeId::DOCUMENT && self.kind(child) == NodeKind::Element {
367            self.root = Some(child);
368        }
369    }
370
371    /// `xmlAddNextSibling`.
372    #[doc(alias = "xmlAddNextSibling")]
373    pub fn xml_add_next_sibling(&mut self, cur: NodeId, elem: NodeId) {
374        self.xml_unlink_node(elem);
375        let parent = self.node(cur).parent;
376        let next = self.node(cur).next_sibling;
377        self.node_mut(elem).parent = parent;
378        self.node_mut(elem).prev_sibling = Some(cur);
379        self.node_mut(elem).next_sibling = next;
380        self.node_mut(cur).next_sibling = Some(elem);
381        if let Some(n) = next {
382            self.node_mut(n).prev_sibling = Some(elem);
383        } else if let Some(p) = parent {
384            self.node_mut(p).last_child = Some(elem);
385        }
386    }
387
388    /// `xmlAddPrevSibling`.
389    #[doc(alias = "xmlAddPrevSibling")]
390    pub fn xml_add_prev_sibling(&mut self, cur: NodeId, elem: NodeId) {
391        self.xml_unlink_node(elem);
392        let parent = self.node(cur).parent;
393        let prev = self.node(cur).prev_sibling;
394        self.node_mut(elem).parent = parent;
395        self.node_mut(elem).next_sibling = Some(cur);
396        self.node_mut(elem).prev_sibling = prev;
397        self.node_mut(cur).prev_sibling = Some(elem);
398        if let Some(p) = prev {
399            self.node_mut(p).next_sibling = Some(elem);
400        } else if let Some(par) = parent {
401            self.node_mut(par).first_child = Some(elem);
402        }
403    }
404
405    /// `xmlUnlinkNode`.
406    #[doc(alias = "xmlUnlinkNode")]
407    pub fn xml_unlink_node(&mut self, id: NodeId) {
408        if id == NodeId::DOCUMENT {
409            return;
410        }
411        let parent = self.node(id).parent;
412        let prev = self.node(id).prev_sibling;
413        let next = self.node(id).next_sibling;
414        if let Some(p) = prev {
415            self.node_mut(p).next_sibling = next;
416        }
417        if let Some(n) = next {
418            self.node_mut(n).prev_sibling = prev;
419        }
420        if let Some(par) = parent {
421            if self.node(par).first_child == Some(id) {
422                self.node_mut(par).first_child = next;
423            }
424            if self.node(par).last_child == Some(id) {
425                self.node_mut(par).last_child = prev;
426            }
427        }
428        if self.root == Some(id) {
429            self.root = None;
430        }
431        self.node_mut(id).parent = None;
432        self.node_mut(id).prev_sibling = None;
433        self.node_mut(id).next_sibling = None;
434    }
435
436    /// `xmlReplaceNode`.
437    #[doc(alias = "xmlReplaceNode")]
438    pub fn xml_replace_node(&mut self, old: NodeId, new: NodeId) -> NodeId {
439        self.xml_add_next_sibling(old, new);
440        self.xml_unlink_node(old);
441        new
442    }
443
444    /// As [`XmlDoc::add_attr`], but takes ownership. The borrowing form has to
445    /// allocate a fresh String for the name, the prefix and the value, all of
446    /// which the parser already owns.
447    pub fn add_attr_owned(
448        &mut self,
449        elem: NodeId,
450        name: String,
451        prefix: Option<String>,
452        value: String,
453    ) -> NodeId {
454        let id = self.alloc(NodeKind::Attribute, name);
455        self.node_mut(id).prefix = prefix;
456        self.node_mut(id).content = value;
457        self.node_mut(id).parent = Some(elem);
458        let last = self.node(elem).last_attr;
459        if let Some(l) = last {
460            self.node_mut(l).next_sibling = Some(id);
461            self.node_mut(id).prev_sibling = Some(l);
462        } else {
463            self.node_mut(elem).first_attr = Some(id);
464        }
465        self.node_mut(elem).last_attr = Some(id);
466        id
467    }
468
469    pub fn add_attr(&mut self, elem: NodeId, name: &str, prefix: Option<&str>, value: &str) -> NodeId {
470        let id = self.alloc(NodeKind::Attribute, name);
471        self.node_mut(id).prefix = prefix.map(str::to_string);
472        self.node_mut(id).content = value.to_string();
473        self.node_mut(id).parent = Some(elem);
474        let last = self.node(elem).last_attr;
475        if let Some(l) = last {
476            self.node_mut(l).next_sibling = Some(id);
477            self.node_mut(id).prev_sibling = Some(l);
478        } else {
479            self.node_mut(elem).first_attr = Some(id);
480        }
481        self.node_mut(elem).last_attr = Some(id);
482        id
483    }
484
485    pub fn push_ns_def(&mut self, elem: NodeId, prefix: Option<String>, uri: String) {
486        self.node_mut(elem).ns_defs.push((prefix, uri));
487    }
488
489    /// `xmlSetProp`.
490    #[doc(alias = "xmlSetProp")]
491    pub fn xml_set_prop(&mut self, node: NodeId, name: &str, value: &str) -> NodeId {
492        let mut a = self.first_attr(node);
493        while let Some(id) = a {
494            if self.node(id).prefix.is_none() && self.node(id).name == name {
495                self.node_mut(id).content = value.to_string();
496                return id;
497            }
498            a = self.next_sibling(id);
499        }
500        self.add_attr(node, name, None, value)
501    }
502
503    /// `xmlGetProp`.
504    #[doc(alias = "xmlGetProp")]
505    pub fn xml_get_prop(&self, node: NodeId, name: &str) -> Option<String> {
506        let mut a = self.first_attr(node);
507        while let Some(id) = a {
508            if self.node(id).prefix.is_none() && self.node(id).name == name {
509                return Some(self.node(id).content.clone());
510            }
511            a = self.next_sibling(id);
512        }
513        None
514    }
515
516    /// `xmlHasProp`.
517    #[doc(alias = "xmlHasProp")]
518    pub fn xml_has_prop(&self, node: NodeId, name: &str) -> bool {
519        self.xml_get_prop(node, name).is_some()
520    }
521
522    /// `xmlUnsetProp`.
523    #[doc(alias = "xmlUnsetProp")]
524    pub fn xml_unset_prop(&mut self, node: NodeId, name: &str) -> bool {
525        let mut a = self.first_attr(node);
526        let mut prev: Option<NodeId> = None;
527        while let Some(id) = a {
528            let next = self.next_sibling(id);
529            if self.node(id).prefix.is_none() && self.node(id).name == name {
530                if let Some(p) = prev {
531                    self.node_mut(p).next_sibling = next;
532                } else {
533                    self.node_mut(node).first_attr = next;
534                }
535                if next.is_none() {
536                    self.node_mut(node).last_attr = prev;
537                }
538                if let Some(n) = next {
539                    self.node_mut(n).prev_sibling = prev;
540                }
541                self.node_mut(id).parent = None;
542                self.node_mut(id).prev_sibling = None;
543                self.node_mut(id).next_sibling = None;
544                return true;
545            }
546            prev = Some(id);
547            a = next;
548        }
549        false
550    }
551
552    /// `xmlNodeGetContent` — concatenate descendant text/CDATA.
553    #[doc(alias = "xmlNodeGetContent")]
554    pub fn xml_node_get_content(&self, id: NodeId) -> String {
555        match self.kind(id) {
556            NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi | NodeKind::Attribute => {
557                self.content(id).to_string()
558            }
559            _ => {
560                let mut out = String::new();
561                self.collect_text(id, &mut out);
562                out
563            }
564        }
565    }
566
567    fn collect_text(&self, id: NodeId, out: &mut String) {
568        let mut c = self.first_child(id);
569        while let Some(ch) = c {
570            match self.kind(ch) {
571                NodeKind::Text | NodeKind::CData => out.push_str(self.content(ch)),
572                NodeKind::Element => self.collect_text(ch, out),
573                _ => {}
574            }
575            c = self.next_sibling(ch);
576        }
577    }
578
579    /// `xmlNodeSetContent` — replace children with a single text node.
580    #[doc(alias = "xmlNodeSetContent")]
581    pub fn xml_node_set_content(&mut self, id: NodeId, content: &str) {
582        match self.kind(id) {
583            NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi | NodeKind::Attribute => {
584                self.node_mut(id).content = content.to_string();
585            }
586            _ => {
587                let mut c = self.first_child(id);
588                while let Some(ch) = c {
589                    let next = self.next_sibling(ch);
590                    self.xml_unlink_node(ch);
591                    c = next;
592                }
593                if !content.is_empty() {
594                    let t = self.alloc(NodeKind::Text, "#text");
595                    self.node_mut(t).content = content.to_string();
596                    self.xml_add_child(id, t);
597                }
598            }
599        }
600    }
601
602    /// `xmlIsBlankNode`.
603    #[doc(alias = "xmlIsBlankNode")]
604    pub fn xml_is_blank_node(&self, id: NodeId) -> bool {
605        match self.kind(id) {
606            NodeKind::Text | NodeKind::CData => self.content(id).chars().all(|c| {
607                c == ' ' || c == '\t' || c == '\n' || c == '\r'
608            }),
609            _ => false,
610        }
611    }
612
613    /// `xmlSearchNs` — walk ancestors for a prefix binding.
614    #[doc(alias = "xmlSearchNs")]
615    pub fn xml_search_ns(&self, node: NodeId, prefix: Option<&str>) -> Option<String> {
616        if prefix == Some("xml") {
617            return Some("http://www.w3.org/XML/1998/namespace".into());
618        }
619        if prefix == Some("xmlns") {
620            return Some("http://www.w3.org/2000/xmlns/".into());
621        }
622        let mut cur = Some(node);
623        while let Some(id) = cur {
624            for (p, uri) in self.ns_defs(id) {
625                if p.as_deref() == prefix {
626                    return Some(uri.clone());
627                }
628            }
629            cur = self.parent(id);
630        }
631        None
632    }
633
634    /// `xmlNewNs` — add a namespace declaration on an element.
635    #[doc(alias = "xmlNewNs")]
636    pub fn xml_new_ns(&mut self, node: NodeId, href: &str, prefix: Option<&str>) {
637        self.push_ns_def(node, prefix.map(str::to_string), href.to_string());
638    }
639
640    /// `xmlSetNs`.
641    #[doc(alias = "xmlSetNs")]
642    pub fn xml_set_ns(&mut self, node: NodeId, href: Option<&str>, prefix: Option<&str>) {
643        self.node_mut(node).ns_uri = href.map(str::to_string);
644        self.node_mut(node).prefix = prefix.map(str::to_string);
645    }
646
647    /// `xmlCopyDoc` — deep copy.
648    #[doc(alias = "xmlCopyDoc")]
649    pub fn xml_copy_doc(&self) -> XmlDoc {
650        self.clone()
651    }
652
653    pub fn qname(&self, id: NodeId) -> String {
654        match self.prefix(id) {
655            Some(p) => format!("{}:{}", p, self.name(id)),
656            None => self.name(id).to_string(),
657        }
658    }
659
660    pub fn children(&self, id: NodeId) -> NodeIter<'_> {
661        NodeIter {
662            doc: self,
663            next: self.first_child(id),
664        }
665    }
666
667    pub fn attrs(&self, id: NodeId) -> NodeIter<'_> {
668        NodeIter {
669            doc: self,
670            next: self.first_attr(id),
671        }
672    }
673
674    pub fn len(&self) -> usize {
675        self.nodes.len()
676    }
677}
678
679/// Sibling iterator.
680pub struct NodeIter<'a> {
681    doc: &'a XmlDoc,
682    next: Option<NodeId>,
683}
684
685impl Iterator for NodeIter<'_> {
686    type Item = NodeId;
687
688    fn next(&mut self) -> Option<Self::Item> {
689        let n = self.next?;
690        self.next = self.doc.next_sibling(n);
691        Some(n)
692    }
693}
694
695/// `xmlFreeDoc` is `Drop`.
696#[doc(alias = "xmlFreeDoc")]
697pub fn xml_free_doc(_doc: XmlDoc) {}
698
699impl XmlDoc {
700    /// Deep-copy every child of `src_parent` in `src` under `dst_parent` here.
701    ///
702    /// Needed because an entity whose replacement text contains markup has to
703    /// become NODES, not the escaped text of that markup. `<!ENTITY e
704    /// "<b>x</b>">` used in content produced the literal string `<b>x</b>` in
705    /// the tree, so anything reading the document for structure got garbage
706    /// and DTD validation saw character data where an element was declared.
707    ///
708    /// Iterative, like every other traversal here: the replacement is
709    /// attacker-supplied and may be arbitrarily deep.
710    pub fn xml_copy_children_from(
711        &mut self,
712        src: &XmlDoc,
713        src_parent: NodeId,
714        dst_parent: NodeId,
715    ) {
716        // (source node, destination parent), pushed so they pop in order.
717        let mut stack: Vec<(NodeId, NodeId)> = Vec::new();
718        let mut c = src.last_child(src_parent);
719        while let Some(x) = c {
720            stack.push((x, dst_parent));
721            c = src.prev_sibling(x);
722        }
723        while let Some((s, parent)) = stack.pop() {
724            let n = src.node(s);
725            let copy = self.alloc(n.kind, n.name.clone());
726            {
727                let d = self.node_mut(copy);
728                d.prefix = n.prefix.clone();
729                d.ns_uri = n.ns_uri.clone();
730                d.content = n.content.clone();
731                d.ns_defs = n.ns_defs.clone();
732            }
733            self.xml_add_child(parent, copy);
734            // Attributes are a separate chain, not children.
735            let mut a = src.first_attr(s);
736            while let Some(x) = a {
737                let an = src.node(x);
738                let (nm, pre, val, uri) = (
739                    an.name.clone(),
740                    an.prefix.clone(),
741                    an.content.clone(),
742                    an.ns_uri.clone(),
743                );
744                let ac = self.add_attr_owned(copy, nm, pre, val);
745                self.node_mut(ac).ns_uri = uri;
746                a = src.next_sibling(x);
747            }
748            let mut k = src.last_child(s);
749            while let Some(x) = k {
750                stack.push((x, copy));
751                k = src.prev_sibling(x);
752            }
753        }
754    }
755}