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