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>'&amp;-quotes'</tag>
73 ///    <tag = "'&-quotes'">    becomes      <tag = "&apos;&amp;-quotes&apos;"
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"&amp;"),
79             '\'' if attr_mode => writer.write_all(b"&apos;"),
80             '"' if attr_mode => writer.write_all(b"&quot;"),
81             '<' if !attr_mode => writer.write_all(b"&lt;"),
82             '>' if !attr_mode => writer.write_all(b"&gt;"),
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