1use crate::document::Document;
4use markup5ever_rcdom::RcDom;
5use std::io;
6use xml5ever::{driver::parse_document, tendril::TendrilSink};
7
8pub fn parse(source: &str) -> Result<Document, io::Error> {
10 parse_bytes(source.as_bytes())
11}
12
13pub fn parse_bytes(mut source: &[u8]) -> Result<Document, io::Error> {
15 parse_document(RcDom::default(), Default::default())
16 .from_utf8()
17 .read_from(&mut source)
18 .map(|dom| {
19 Document::from_markup5ever(&dom.document).set_errors(
20 dom.errors
21 .borrow()
22 .iter()
23 .map(ToString::to_string)
24 .collect(),
25 )
26 })
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32 use crate::document::{Element, Node};
33 use alloc::sync::Arc;
34 use pretty_assertions::assert_eq;
35
36 const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
37
38 fn element(
39 namespace: Option<&str>,
40 name: &str,
41 attributes: Vec<(&str, &str)>,
42 children: Vec<Arc<Node>>,
43 ) -> Arc<Node> {
44 Arc::new(Node::Element(
45 Element::new(
46 name.into(),
47 attributes
48 .into_iter()
49 .map(|(name, value)| (name.into(), value.into()))
50 .collect(),
51 children,
52 )
53 .set_namespace(namespace.map(Into::into)),
54 ))
55 }
56
57 #[test]
58 fn parse_empty_string() {
59 assert_eq!(
60 parse("").unwrap(),
61 Document::new(vec![]).set_errors(vec!["Unexpected EOF in start phase".into()])
62 );
63 }
64
65 #[test]
66 fn parse_svg_document() {
67 assert_eq!(
68 parse(concat!(
69 r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"#,
70 r#"<a href="/foo"><rect/></a>"#,
71 r#"<image xlink:href="/bar.png"/>"#,
72 "</svg>"
73 ))
74 .unwrap(),
75 Document::new(vec![element(
76 Some(SVG_NAMESPACE),
77 "svg",
78 vec![],
79 vec![
80 element(
81 Some(SVG_NAMESPACE),
82 "a",
83 vec![("href", "/foo")],
84 vec![element(Some(SVG_NAMESPACE), "rect", vec![], vec![])],
85 ),
86 element(
87 Some(SVG_NAMESPACE),
88 "image",
89 vec![("xlink:href", "/bar.png")],
90 vec![],
91 ),
92 ],
93 )])
94 );
95 }
96
97 #[test]
98 fn collect_error_on_multiple_root_elements() {
99 let document = parse("<svg/><svg/>").unwrap();
100
101 assert_eq!(document.children().count(), 1);
102 assert_eq!(
103 document.errors().collect::<Vec<_>>(),
104 vec!["Unexpected element in end phase"]
105 );
106 }
107
108 #[test]
109 fn parse_element_without_namespace() {
110 assert_eq!(
111 parse("<svg><foreignObject/></svg>").unwrap(),
112 Document::new(vec![element(
113 None,
114 "svg",
115 vec![],
116 vec![element(None, "foreignObject", vec![], vec![])],
117 )])
118 );
119 }
120
121 #[test]
122 fn parse_prefixed_element() {
123 assert_eq!(
124 parse(concat!(
125 r#"<svg xmlns="http://www.w3.org/2000/svg">"#,
126 r#"<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>"#,
127 "</svg>"
128 ))
129 .unwrap(),
130 Document::new(vec![element(
131 Some(SVG_NAMESPACE),
132 "svg",
133 vec![],
134 vec![element(
135 Some("http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
136 "rdf:RDF",
137 vec![],
138 vec![],
139 )],
140 )])
141 );
142 }
143
144 #[test]
145 fn canonicalize_element_prefix() {
146 assert_eq!(
147 parse(concat!(
148 r#"<s:svg xmlns:s="http://www.w3.org/2000/svg">"#,
149 "<s:circle/>",
150 "</s:svg>"
151 ))
152 .unwrap(),
153 Document::new(vec![element(
154 Some(SVG_NAMESPACE),
155 "svg",
156 vec![],
157 vec![element(Some(SVG_NAMESPACE), "circle", vec![], vec![])],
158 )])
159 );
160 }
161
162 #[test]
163 fn canonicalize_attribute_prefix() {
164 assert_eq!(
165 parse(concat!(
166 r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:x="http://www.w3.org/1999/xlink">"#,
167 r#"<image x:href="/foo.png"/>"#,
168 "</svg>"
169 ))
170 .unwrap(),
171 Document::new(vec![element(
172 Some(SVG_NAMESPACE),
173 "svg",
174 vec![],
175 vec![element(
176 Some(SVG_NAMESPACE),
177 "image",
178 vec![("xlink:href", "/foo.png")],
179 vec![],
180 )],
181 )])
182 );
183 }
184
185 #[test]
186 fn keep_unknown_namespace_prefix() {
187 assert_eq!(
188 parse(r#"<f:thing xmlns:f="http://foo.example"/>"#).unwrap(),
189 Document::new(vec![element(
190 Some("http://foo.example"),
191 "f:thing",
192 vec![],
193 vec![],
194 )])
195 );
196 }
197
198 #[test]
199 fn keep_html_element_in_svg_element() {
200 assert_eq!(
201 parse(r#"<svg xmlns="http://www.w3.org/2000/svg"><p>foo</p></svg>"#).unwrap(),
202 Document::new(vec![element(
203 Some(SVG_NAMESPACE),
204 "svg",
205 vec![],
206 vec![element(
207 Some(SVG_NAMESPACE),
208 "p",
209 vec![],
210 vec![Arc::new(Node::Text("foo".into()))],
211 )],
212 )])
213 );
214 }
215
216 #[test]
217 fn ignore_processing_instructions() {
218 assert_eq!(
219 parse(r#"<?xml version="1.0"?><svg/>"#).unwrap(),
220 Document::new(vec![element(None, "svg", vec![], vec![])])
221 );
222 }
223}