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//! Types for tag and attribute names, and tree-builder functionality.
10
11use std::fmt;
12use tendril::StrTendril;
13
14pub use self::tree_builder::{create_element, AppendNode, AppendText, ElementFlags, NodeOrText};
15pub use self::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
16pub use self::tree_builder::{NextParserState, Tracer, TreeSink};
17use super::{LocalName, Namespace, Prefix};
18
19/// An [expanded name], containing the tag and the namespace.
20///
21/// [expanded name]: https://www.w3.org/TR/REC-xml-names/#dt-expname
22#[derive(Copy, Clone, Eq, Hash)]
23pub struct ExpandedName<'a> {
24 pub ns: &'a Namespace,
25 pub local: &'a LocalName,
26}
27
28impl<'a, 'b> PartialEq<ExpandedName<'a>> for ExpandedName<'b> {
29 fn eq(&self, other: &ExpandedName<'a>) -> bool {
30 self.ns == other.ns && self.local == other.local
31 }
32}
33
34impl<'a> fmt::Debug for ExpandedName<'a> {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 if self.ns.is_empty() {
37 write!(f, "{}", self.local)
38 } else {
39 write!(f, "{{{}}}:{}", self.ns, self.local)
40 }
41 }
42}
43
44/// Helper to quickly create an expanded name.
45///
46/// Can be used with no namespace as `expanded_name!("", "some_name")`
47/// or with a namespace as `expanded_name!(ns "some_name")`. In the
48/// latter case, `ns` is one of the symbols which the [`ns!`][ns]
49/// macro accepts; note the lack of a comma between the `ns` and
50/// `"some_name"`.
51///
52/// [ns]: macro.ns.html
53///
54/// # Examples
55///
56/// ```
57/// # #[macro_use] extern crate markup5ever;
58///
59/// # fn main() {
60/// use markup5ever::ExpandedName;
61///
62/// assert_eq!(
63/// expanded_name!("", "div"),
64/// ExpandedName {
65/// ns: &ns!(),
66/// local: &local_name!("div")
67/// }
68/// );
69///
70/// assert_eq!(
71/// expanded_name!(html "div"),
72/// ExpandedName {
73/// ns: &ns!(html),
74/// local: &local_name!("div")
75/// }
76/// );
77/// # }
78#[macro_export]
79macro_rules! expanded_name {
80 ("", $local: tt) => {
81 $crate::interface::ExpandedName {
82 ns: &ns!(),
83 local: &local_name!($local),
84 }
85 };
86 ($ns: ident $local: tt) => {
87 $crate::interface::ExpandedName {
88 ns: &ns!($ns),
89 local: &local_name!($local),
90 }
91 };
92}
93
94pub mod tree_builder;
95
96/// A fully qualified name (with a namespace), used to depict names of tags and attributes.
97///
98/// Namespaces can be used to differentiate between similar XML fragments. For example:
99///
100/// ```text
101/// // HTML
102/// <table>
103/// <tr>
104/// <td>Apples</td>
105/// <td>Bananas</td>
106/// </tr>
107/// </table>
108///
109/// // Furniture XML
110/// <table>
111/// <name>African Coffee Table</name>
112/// <width>80</width>
113/// <length>120</length>
114/// </table>
115/// ```
116///
117/// Without XML namespaces, we can't use those two fragments in the same document
118/// at the same time. However if we declare a namespace we could instead say:
119///
120/// ```text
121///
122/// // Furniture XML
123/// <furn:table xmlns:furn="https://furniture.rs">
124/// <furn:name>African Coffee Table</furn:name>
125/// <furn:width>80</furn:width>
126/// <furn:length>120</furn:length>
127/// </furn:table>
128/// ```
129///
130/// and bind the prefix `furn` to a different namespace.
131///
132/// For this reason we parse names that contain a colon in the following way:
133///
134/// ```text
135/// <furn:table>
136/// | |
137/// | +- local name
138/// |
139/// prefix (when resolved gives namespace_url `https://furniture.rs`)
140/// ```
141///
142/// NOTE: `Prefix`, `LocalName` and `Prefix` are all derivative of
143/// `string_cache::atom::Atom` and `Atom` implements `Deref<str>`.
144///
145#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)]
146#[cfg_attr(feature = "heap_size", derive(HeapSizeOf))]
147pub struct QualName {
148 /// The prefix of qualified (e.g. `furn` in `<furn:table>` above).
149 /// Optional (since some namespaces can be empty or inferred), and
150 /// only useful for namespace resolution (since different prefix
151 /// can still resolve to same namespace)
152 ///
153 /// ```
154 ///
155 /// # fn main() {
156 /// use markup5ever::{QualName, Namespace, LocalName, Prefix};
157 ///
158 /// let qual = QualName::new(
159 /// Some(Prefix::from("furn")),
160 /// Namespace::from("https://furniture.rs"),
161 /// LocalName::from("table"),
162 /// );
163 ///
164 /// assert_eq!("furn", &qual.prefix.unwrap());
165 ///
166 /// # }
167 /// ```
168 pub prefix: Option<Prefix>,
169 /// The namespace after resolution (e.g. `https://furniture.rs` in example above).
170 ///
171 /// ```
172 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
173 ///
174 /// # fn main() {
175 /// # let qual = QualName::new(
176 /// # Some(Prefix::from("furn")),
177 /// # Namespace::from("https://furniture.rs"),
178 /// # LocalName::from("table"),
179 /// # );
180 ///
181 /// assert_eq!("https://furniture.rs", &qual.ns);
182 /// # }
183 /// ```
184 ///
185 /// When matching namespaces used by HTML we can use `ns!` macro.
186 /// Although keep in mind that ns! macro only works with namespaces
187 /// that are present in HTML spec (like `html`, `xmlns`, `svg`, etc.).
188 ///
189 /// ```
190 /// #[macro_use] extern crate markup5ever;
191 ///
192 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
193 ///
194 /// let html_table = QualName::new(
195 /// None,
196 /// ns!(html),
197 /// LocalName::from("table"),
198 /// );
199 ///
200 /// assert!(
201 /// match html_table.ns {
202 /// ns!(html) => true,
203 /// _ => false,
204 /// }
205 /// );
206 ///
207 /// ```
208 pub ns: Namespace,
209 /// The local name (e.g. `table` in `<furn:table>` above).
210 ///
211 /// ```
212 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
213 ///
214 /// # fn main() {
215 /// # let qual = QualName::new(
216 /// # Some(Prefix::from("furn")),
217 /// # Namespace::from("https://furniture.rs"),
218 /// # LocalName::from("table"),
219 /// # );
220 ///
221 /// assert_eq!("table", &qual.local);
222 /// # }
223 /// ```
224 /// When matching local name we can also use the `local_name!` macro:
225 ///
226 /// ```
227 /// #[macro_use] extern crate markup5ever;
228 ///
229 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
230 ///
231 /// # let qual = QualName::new(
232 /// # Some(Prefix::from("furn")),
233 /// # Namespace::from("https://furniture.rs"),
234 /// # LocalName::from("table"),
235 /// # );
236 ///
237 /// // Initialize qual to furniture example
238 ///
239 /// assert!(
240 /// match qual.local {
241 /// local_name!("table") => true,
242 /// _ => false,
243 /// }
244 /// );
245 ///
246 /// ```
247 pub local: LocalName,
248}
249
250impl QualName {
251 /// Basic constructor function.
252 ///
253 /// First let's try it for the following example where `QualName`
254 /// is defined as:
255 /// ```text
256 /// <furn:table> <!-- namespace url is https://furniture.rs -->
257 /// ```
258 ///
259 /// Given this definition, we can define `QualName` using strings.
260 ///
261 /// ```
262 /// use markup5ever::{QualName, Namespace, LocalName, Prefix};
263 ///
264 /// # fn main() {
265 /// let qual_name = QualName::new(
266 /// Some(Prefix::from("furn")),
267 /// Namespace::from("https://furniture.rs"),
268 /// LocalName::from("table"),
269 /// );
270 /// # }
271 /// ```
272 ///
273 /// If we were instead to construct this element instead:
274 ///
275 /// ```text
276 ///
277 /// <table>
278 /// ^^^^^---- no prefix and thus default html namespace
279 ///
280 /// ```
281 ///
282 /// Or could define it using macros, like so:
283 ///
284 /// ```
285 /// #[macro_use] extern crate markup5ever;
286 /// use markup5ever::{QualName, Namespace, LocalName, Prefix};
287 ///
288 /// # fn main() {
289 /// let qual_name = QualName::new(
290 /// None,
291 /// ns!(html),
292 /// local_name!("table")
293 /// );
294 /// # }
295 /// ```
296 ///
297 /// Let's analyse the above example.
298 /// Since we have no prefix its value is None. Second we have html namespace.
299 /// In html5ever html namespaces are supported out of the box,
300 /// we can write `ns!(html)` instead of typing `Namespace::from("http://www.w3.org/1999/xhtml")`.
301 /// Local name is also one of the HTML elements local names, so can
302 /// use `local_name!("table")` macro.
303 ///
304 #[inline]
305 pub fn new(prefix: Option<Prefix>, ns: Namespace, local: LocalName) -> QualName {
306 QualName {
307 prefix,
308 ns,
309 local,
310 }
311 }
312
313 /// Take a reference of `self` as an `ExpandedName`, dropping the unresolved prefix.
314 ///
315 /// In XML and HTML prefixes are only used to extract the relevant namespace URI.
316 /// Expanded name only contains resolved namespace and tag name, which are only
317 /// relevant parts of an XML or HTML tag and attribute name respectively.
318 ///
319 /// In lieu of our XML Namespace example
320 ///
321 /// ```text
322 /// <furn:table> <!-- namespace url is https://furniture.rs -->
323 /// ```
324 /// For it the expanded name would become roughly equivalent to:
325 ///
326 /// ```text
327 /// ExpandedName {
328 /// ns: "https://furniture.rs",
329 /// local: "table",
330 /// }
331 /// ```
332 ///
333 #[inline]
334 pub fn expanded(&self) -> ExpandedName {
335 ExpandedName {
336 ns: &self.ns,
337 local: &self.local,
338 }
339 }
340}
341
342/// A tag attribute, e.g. `class="test"` in `<div class="test" ...>`.
343///
344/// The namespace on the attribute name is almost always ns!("").
345/// The tokenizer creates all attributes this way, but the tree
346/// builder will adjust certain attribute names inside foreign
347/// content (MathML, SVG).
348#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
349pub struct Attribute {
350 /// The name of the attribute (e.g. the `class` in `<div class="test">`)
351 pub name: QualName,
352 /// The value of the attribute (e.g. the `"test"` in `<div class="test">`)
353 pub value: StrTendril,
354}
355
356#[cfg(test)]
357mod tests {
358 use super::Namespace;
359
360 #[test]
361 fn ns_macro() {
362 assert_eq!(ns!(), Namespace::from(""));
363
364 assert_eq!(ns!(html), Namespace::from("http://www.w3.org/1999/xhtml"));
365 assert_eq!(
366 ns!(xml),
367 Namespace::from("http://www.w3.org/XML/1998/namespace")
368 );
369 assert_eq!(ns!(xmlns), Namespace::from("http://www.w3.org/2000/xmlns/"));
370 assert_eq!(ns!(xlink), Namespace::from("http://www.w3.org/1999/xlink"));
371 assert_eq!(ns!(svg), Namespace::from("http://www.w3.org/2000/svg"));
372 assert_eq!(
373 ns!(mathml),
374 Namespace::from("http://www.w3.org/1998/Math/MathML")
375 );
376 }
377}
378