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 pub use markup5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope};
11 use std::io::{self, Write};
12 use crate::tree_builder::NamespaceMap;
13 use crate::QualName;
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 {
default() -> SerializeOpts23 fn default() -> SerializeOpts {
24 SerializeOpts {
25 traversal_scope: TraversalScope::ChildrenOnly(None),
26 }
27 }
28 }
29
30 /// Method for serializing generic node to a given writer.
serialize<Wr, T>(writer: Wr, node: &T, opts: SerializeOpts) -> io::Result<()> where Wr: Write, T: Serialize,31 pub fn serialize<Wr, T>(writer: Wr, node: &T, opts: SerializeOpts) -> io::Result<()>
32 where
33 Wr: Write,
34 T: Serialize,
35 {
36 let mut ser = XmlSerializer::new(writer);
37 node.serialize(&mut ser, opts.traversal_scope)
38 }
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 {
new() -> NamespaceMapStack54 fn new() -> NamespaceMapStack {
55 NamespaceMapStack(vec![])
56 }
57
push(&mut self, namespace: NamespaceMap)58 fn push(&mut self, namespace: NamespaceMap) {
59 self.0.push(namespace);
60 }
61
pop(&mut self)62 fn pop(&mut self) {
63 self.0.pop();
64 }
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>'&-quotes'</tag>
73 /// <tag = "'&-quotes'"> becomes <tag = "'&-quotes'"
74 ///```
write_to_buf_escaped<W: Write>(writer: &mut W, text: &str, attr_mode: bool) -> io::Result<()>75 fn write_to_buf_escaped<W: Write>(writer: &mut W, text: &str, attr_mode: bool) -> io::Result<()> {
76 for c in text.chars() {
77 match c {
78 '&' => writer.write_all(b"&"),
79 '\'' if attr_mode => writer.write_all(b"'"),
80 '"' if attr_mode => writer.write_all(b"""),
81 '<' if !attr_mode => writer.write_all(b"<"),
82 '>' if !attr_mode => writer.write_all(b">"),
83 c => writer.write_fmt(format_args!("{}", c)),
84 }?;
85 }
86 Ok(())
87 }
88
89 #[inline]
write_qual_name<W: Write>(writer: &mut W, name: &QualName) -> io::Result<()>90 fn write_qual_name<W: Write>(writer: &mut W, name: &QualName) -> io::Result<()> {
91 if let Some(ref prefix) = name.prefix {
92 writer.write_all(&prefix.as_bytes())?;
93 writer.write_all(b":")?;
94 writer.write_all(&*name.local.as_bytes())?;
95 } else {
96 writer.write_all(&*name.local.as_bytes())?;
97 }
98
99 Ok(())
100 }
101
102 impl<Wr: Write> XmlSerializer<Wr> {
103 /// Creates a new Serializier from a writer and given serialization options.
new(writer: Wr) -> Self104 pub fn new(writer: Wr) -> Self {
105 XmlSerializer {
106 writer: writer,
107 namespace_stack: NamespaceMapStack::new(),
108 }
109 }
110
111 #[inline(always)]
qual_name(&mut self, name: &QualName) -> io::Result<()>112 fn qual_name(&mut self, name: &QualName) -> io::Result<()> {
113 self.find_or_insert_ns(name);
114 write_qual_name(&mut self.writer, name)
115 }
116
117 #[inline(always)]
qual_attr_name(&mut self, name: &QualName) -> io::Result<()>118 fn qual_attr_name(&mut self, name: &QualName) -> io::Result<()> {
119 self.find_or_insert_ns(name);
120 write_qual_name(&mut self.writer, name)
121 }
122
find_uri(&self, name: &QualName) -> bool123 fn find_uri(&self, name: &QualName) -> bool {
124 let mut found = false;
125 for stack in self.namespace_stack.0.iter().rev() {
126 if let Some(&Some(ref el)) = stack.get(&name.prefix) {
127 found = *el == name.ns;
128 break;
129 }
130 }
131 found
132 }
133
find_or_insert_ns(&mut self, name: &QualName)134 fn find_or_insert_ns(&mut self, name: &QualName) {
135 if name.prefix.is_some() || &*name.ns != "" {
136 if !self.find_uri(name) {
137 if let Some(last_ns) = self.namespace_stack.0.last_mut() {
138 last_ns.insert(name);
139 }
140 }
141 }
142 }
143 }
144
145 impl<Wr: Write> Serializer for XmlSerializer<Wr> {
146 /// Serializes given start element into text. Start element contains
147 /// qualified name and an attributes iterator.
start_elem<'a, AttrIter>(&mut self, name: QualName, attrs: AttrIter) -> io::Result<()> where AttrIter: Iterator<Item = AttrRef<'a>>,148 fn start_elem<'a, AttrIter>(&mut self, name: QualName, attrs: AttrIter) -> io::Result<()>
149 where
150 AttrIter: Iterator<Item = AttrRef<'a>>,
151 {
152 self.namespace_stack.push(NamespaceMap::empty());
153
154 self.writer.write_all(b"<")?;
155 self.qual_name(&name)?;
156 if let Some(current_namespace) = self.namespace_stack.0.last() {
157 for (prefix, url_opt) in current_namespace.get_scope_iter() {
158 self.writer.write_all(b" xmlns")?;
159 if let &Some(ref p) = prefix {
160 self.writer.write_all(b":")?;
161 self.writer.write_all(&*p.as_bytes())?;
162 }
163
164 self.writer.write_all(b"=\"")?;
165 let url = if let &Some(ref a) = url_opt {
166 a.as_bytes()
167 } else {
168 b""
169 };
170 self.writer.write_all(url)?;
171 self.writer.write_all(b"\"")?;
172 }
173 }
174 for (name, value) in attrs {
175 self.writer.write_all(b" ")?;
176 self.qual_attr_name(&name)?;
177 self.writer.write_all(b"=\"")?;
178 write_to_buf_escaped(&mut self.writer, value, true)?;
179 self.writer.write_all(b"\"")?;
180 }
181 self.writer.write_all(b">")?;
182 Ok(())
183 }
184
185 /// Serializes given end element into text.
end_elem(&mut self, name: QualName) -> io::Result<()>186 fn end_elem(&mut self, name: QualName) -> io::Result<()> {
187 self.namespace_stack.pop();
188 self.writer.write_all(b"</")?;
189 self.qual_name(&name)?;
190 self.writer.write_all(b">")
191 }
192
193 /// Serializes comment into text.
write_comment(&mut self, text: &str) -> io::Result<()>194 fn write_comment(&mut self, text: &str) -> io::Result<()> {
195 self.writer.write_all(b"<!--")?;
196 self.writer.write_all(text.as_bytes())?;
197 self.writer.write_all(b"-->")
198 }
199
200 /// Serializes given doctype
write_doctype(&mut self, name: &str) -> io::Result<()>201 fn write_doctype(&mut self, name: &str) -> io::Result<()> {
202 self.writer.write_all(b"<!DOCTYPE ")?;
203 self.writer.write_all(name.as_bytes())?;
204 self.writer.write_all(b">")
205 }
206
207 /// Serializes text for a node or an attributes.
write_text(&mut self, text: &str) -> io::Result<()>208 fn write_text(&mut self, text: &str) -> io::Result<()> {
209 write_to_buf_escaped(&mut self.writer, text, false)
210 }
211
212 /// Serializes given processing instruction.
write_processing_instruction(&mut self, target: &str, data: &str) -> io::Result<()>213 fn write_processing_instruction(&mut self, target: &str, data: &str) -> io::Result<()> {
214 self.writer.write_all(b"<?")?;
215 self.writer.write_all(target.as_bytes())?;
216 self.writer.write_all(b" ")?;
217 self.writer.write_all(data.as_bytes())?;
218 self.writer.write_all(b"?>")
219 }
220 }
221