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 
11 use std::fmt;
12 use tendril::StrTendril;
13 
14 pub use self::tree_builder::{create_element, AppendNode, AppendText, ElementFlags, NodeOrText};
15 pub use self::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
16 pub use self::tree_builder::{NextParserState, Tracer, TreeSink};
17 use 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)]
23 pub struct ExpandedName<'a> {
24     pub ns: &'a Namespace,
25     pub local: &'a LocalName,
26 }
27 
28 impl<'a, 'b> PartialEq<ExpandedName<'a>> for ExpandedName<'b> {
eq(&self, other: &ExpandedName<'a>) -> bool29     fn eq(&self, other: &ExpandedName<'a>) -> bool {
30         self.ns == other.ns && self.local == other.local
31     }
32 }
33 
34 impl<'a> fmt::Debug for ExpandedName<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result35     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]
79 macro_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 
94 pub 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))]
147 pub 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 
250 impl 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]
new(prefix: Option<Prefix>, ns: Namespace, local: LocalName) -> QualName305     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]
expanded(&self) -> ExpandedName334     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)]
349 pub 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)]
357 mod tests {
358     use super::Namespace;
359 
360     #[test]
ns_macro()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