Coverage Report

Created: 2026-09-28 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/html5ever/xml5ever/src/serialize/mod.rs
Line
Count
Source
1
// Copyright 2014-2017 The html5ever Project Developers. See the
2
// COPYRIGHT file at the top-level directory of this distribution.
3
//
4
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7
// option. This file may not be copied, modified, or distributed
8
// except according to those terms.
9
10
use crate::tree_builder::NamespaceMap;
11
use crate::QualName;
12
pub use markup5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope};
13
use std::io::{self, Write};
14
15
#[derive(Clone)]
16
/// Struct for setting serializer options.
17
pub struct SerializeOpts {
18
    /// Serialize the root node? Default: ChildrenOnly
19
    pub traversal_scope: TraversalScope,
20
}
21
22
impl Default for SerializeOpts {
23
9.78k
    fn default() -> SerializeOpts {
24
9.78k
        SerializeOpts {
25
9.78k
            traversal_scope: TraversalScope::ChildrenOnly(None),
26
9.78k
        }
27
9.78k
    }
28
}
29
30
/// Method for serializing generic node to a given writer.
31
9.78k
pub fn serialize<Wr, T>(writer: Wr, node: &T, opts: SerializeOpts) -> io::Result<()>
32
9.78k
where
33
9.78k
    Wr: Write,
34
9.78k
    T: Serialize,
35
{
36
9.78k
    let mut ser = XmlSerializer::new(writer);
37
9.78k
    node.serialize(&mut ser, opts.traversal_scope)
38
9.78k
}
39
40
/// Struct used for serializing nodes into a text that other XML
41
/// parses can read.
42
///
43
/// Serializer contains a set of functions (start_elem, end_elem...)
44
/// that make parsing nodes easier.
45
pub struct XmlSerializer<Wr> {
46
    writer: Wr,
47
    namespace_stack: NamespaceMapStack,
48
}
49
50
#[derive(Debug)]
51
struct NamespaceMapStack(Vec<NamespaceMap>);
52
53
impl NamespaceMapStack {
54
9.78k
    fn new() -> NamespaceMapStack {
55
9.78k
        NamespaceMapStack(vec![])
56
9.78k
    }
57
58
365k
    fn push(&mut self, namespace: NamespaceMap) {
59
365k
        self.0.push(namespace);
60
365k
    }
61
62
365k
    fn pop(&mut self) {
63
365k
        self.0.pop();
64
365k
    }
65
}
66
67
/// Writes given text into the Serializer, escaping it,
68
/// depending on where the text is written inside the tag or attribute value.
69
///
70
/// For example
71
///```text
72
///    <tag>'&-quotes'</tag>   becomes      <tag>'&amp;-quotes'</tag>
73
///    <tag = "'&-quotes'">    becomes      <tag = "&apos;&amp;-quotes&apos;"
74
///```
75
3.73M
fn write_to_buf_escaped<W: Write>(writer: &mut W, text: &str, attr_mode: bool) -> io::Result<()> {
76
8.88M
    for c in text.chars() {
77
7.76k
        match c {
78
13.4k
            '&' => writer.write_all(b"&amp;"),
79
429
            '\'' if attr_mode => writer.write_all(b"&apos;"),
80
141
            '"' if attr_mode => writer.write_all(b"&quot;"),
81
55.4k
            '<' if !attr_mode => writer.write_all(b"&lt;"),
82
319k
            '>' if !attr_mode => writer.write_all(b"&gt;"),
83
8.49M
            c => writer.write_fmt(format_args!("{c}")),
84
0
        }?;
85
    }
86
3.73M
    Ok(())
87
3.73M
}
88
89
#[inline]
90
834k
fn write_qual_name<W: Write>(writer: &mut W, name: &QualName) -> io::Result<()> {
91
834k
    if let Some(ref prefix) = name.prefix {
92
40.3k
        writer.write_all(prefix.as_bytes())?;
93
40.3k
        writer.write_all(b":")?;
94
794k
    }
95
96
834k
    writer.write_all(name.local.as_bytes())?;
97
834k
    Ok(())
98
834k
}
99
100
impl<Wr: Write> XmlSerializer<Wr> {
101
    /// Creates a new Serializier from a writer and given serialization options.
102
9.78k
    pub fn new(writer: Wr) -> Self {
103
9.78k
        XmlSerializer {
104
9.78k
            writer,
105
9.78k
            namespace_stack: NamespaceMapStack::new(),
106
9.78k
        }
107
9.78k
    }
108
109
    #[inline(always)]
110
731k
    fn qual_name(&mut self, name: &QualName) -> io::Result<()> {
111
731k
        self.find_or_insert_ns(name);
112
731k
        write_qual_name(&mut self.writer, name)
113
731k
    }
114
115
    #[inline(always)]
116
103k
    fn qual_attr_name(&mut self, name: &QualName) -> io::Result<()> {
117
103k
        self.find_or_insert_ns(name);
118
103k
        write_qual_name(&mut self.writer, name)
119
103k
    }
120
121
569k
    fn find_uri(&self, name: &QualName) -> bool {
122
569k
        let mut found = false;
123
1.19G
        for stack in self.namespace_stack.0.iter().rev() {
124
1.19G
            if let Some(Some(el)) = stack.get(&name.prefix) {
125
555k
                found = *el == name.ns;
126
555k
                break;
127
1.19G
            }
128
        }
129
569k
        found
130
569k
    }
131
132
834k
    fn find_or_insert_ns(&mut self, name: &QualName) {
133
834k
        if (name.prefix.is_some() || !name.ns.is_empty()) && !self.find_uri(name) {
134
42.1k
            if let Some(last_ns) = self.namespace_stack.0.last_mut() {
135
40.7k
                last_ns.insert(name);
136
40.7k
            }
137
792k
        }
138
834k
    }
139
}
140
141
impl<Wr: Write> Serializer for XmlSerializer<Wr> {
142
    /// Serializes given start element into text. Start element contains
143
    /// qualified name and an attributes iterator.
144
365k
    fn start_elem<'a, AttrIter>(&mut self, name: QualName, attrs: AttrIter) -> io::Result<()>
145
365k
    where
146
365k
        AttrIter: Iterator<Item = AttrRef<'a>>,
147
    {
148
365k
        self.namespace_stack.push(NamespaceMap::empty());
149
150
365k
        self.writer.write_all(b"<")?;
151
365k
        self.qual_name(&name)?;
152
365k
        if let Some(current_namespace) = self.namespace_stack.0.last() {
153
365k
            for (prefix, url_opt) in current_namespace.get_scope_iter() {
154
16.8k
                self.writer.write_all(b" xmlns")?;
155
16.8k
                if let Some(ref prefix) = *prefix {
156
1.43k
                    self.writer.write_all(b":")?;
157
1.43k
                    self.writer.write_all(prefix.as_bytes())?;
158
15.4k
                }
159
160
16.8k
                self.writer.write_all(b"=\"")?;
161
16.8k
                let url = if let Some(ref a) = *url_opt {
162
16.8k
                    a.as_bytes()
163
                } else {
164
0
                    b""
165
                };
166
16.8k
                self.writer.write_all(url)?;
167
16.8k
                self.writer.write_all(b"\"")?;
168
            }
169
0
        }
170
469k
        for (name, value) in attrs {
171
103k
            self.writer.write_all(b" ")?;
172
103k
            self.qual_attr_name(name)?;
173
103k
            self.writer.write_all(b"=\"")?;
174
103k
            write_to_buf_escaped(&mut self.writer, value, true)?;
175
103k
            self.writer.write_all(b"\"")?;
176
        }
177
365k
        self.writer.write_all(b">")?;
178
365k
        Ok(())
179
365k
    }
180
181
    /// Serializes given end element into text.
182
365k
    fn end_elem(&mut self, name: QualName) -> io::Result<()> {
183
365k
        self.namespace_stack.pop();
184
365k
        self.writer.write_all(b"</")?;
185
365k
        self.qual_name(&name)?;
186
365k
        self.writer.write_all(b">")
187
365k
    }
188
189
    /// Serializes comment into text.
190
3.82M
    fn write_comment(&mut self, text: &str) -> io::Result<()> {
191
3.82M
        self.writer.write_all(b"<!--")?;
192
3.82M
        self.writer.write_all(text.as_bytes())?;
193
3.82M
        self.writer.write_all(b"-->")
194
3.82M
    }
195
196
    /// Serializes given doctype
197
39.4k
    fn write_doctype(&mut self, name: &str) -> io::Result<()> {
198
39.4k
        self.writer.write_all(b"<!DOCTYPE ")?;
199
39.4k
        self.writer.write_all(name.as_bytes())?;
200
39.4k
        self.writer.write_all(b">")
201
39.4k
    }
202
203
    /// Serializes text for a node or an attributes.
204
3.62M
    fn write_text(&mut self, text: &str) -> io::Result<()> {
205
3.62M
        write_to_buf_escaped(&mut self.writer, text, false)
206
3.62M
    }
207
208
    /// Serializes given processing instruction.
209
5.69k
    fn write_processing_instruction(&mut self, target: &str, data: &str) -> io::Result<()> {
210
5.69k
        self.writer.write_all(b"<?")?;
211
5.69k
        self.writer.write_all(target.as_bytes())?;
212
5.69k
        self.writer.write_all(b" ")?;
213
5.69k
        self.writer.write_all(data.as_bytes())?;
214
5.69k
        self.writer.write_all(b"?>")
215
5.69k
    }
216
}