markup5ever\interface/
mod.rs

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