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