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)]
171pub struct QualName {
172 /// The prefix of qualified (e.g. `furn` in `<furn:table>` above).
173 /// Optional (since some namespaces can be empty or inferred), and
174 /// only useful for namespace resolution (since different prefix
175 /// can still resolve to same namespace)
176 ///
177 /// ```
178 ///
179 /// # fn main() {
180 /// use markup5ever::{QualName, Namespace, LocalName, Prefix};
181 ///
182 /// let qual = QualName::new(
183 /// Some(Prefix::from("furn")),
184 /// Namespace::from("https://furniture.rs"),
185 /// LocalName::from("table"),
186 /// );
187 ///
188 /// assert_eq!("furn", &qual.prefix.unwrap());
189 ///
190 /// # }
191 /// ```
192 pub prefix: Option<Prefix>,
193 /// The namespace after resolution (e.g. `https://furniture.rs` in example above).
194 ///
195 /// ```
196 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
197 ///
198 /// # fn main() {
199 /// # let qual = QualName::new(
200 /// # Some(Prefix::from("furn")),
201 /// # Namespace::from("https://furniture.rs"),
202 /// # LocalName::from("table"),
203 /// # );
204 ///
205 /// assert_eq!("https://furniture.rs", &qual.ns);
206 /// # }
207 /// ```
208 ///
209 /// When matching namespaces used by HTML we can use `ns!` macro.
210 /// Although keep in mind that ns! macro only works with namespaces
211 /// that are present in HTML spec (like `html`, `xmlns`, `svg`, etc.).
212 ///
213 /// ```
214 /// #[macro_use] extern crate markup5ever;
215 ///
216 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
217 ///
218 /// let html_table = QualName::new(
219 /// None,
220 /// ns!(html),
221 /// LocalName::from("table"),
222 /// );
223 ///
224 /// assert!(
225 /// match html_table.ns {
226 /// ns!(html) => true,
227 /// _ => false,
228 /// }
229 /// );
230 ///
231 /// ```
232 pub ns: Namespace,
233 /// The local name (e.g. `table` in `<furn:table>` above).
234 ///
235 /// ```
236 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
237 ///
238 /// # fn main() {
239 /// # let qual = QualName::new(
240 /// # Some(Prefix::from("furn")),
241 /// # Namespace::from("https://furniture.rs"),
242 /// # LocalName::from("table"),
243 /// # );
244 ///
245 /// assert_eq!("table", &qual.local);
246 /// # }
247 /// ```
248 /// When matching local name we can also use the `local_name!` macro:
249 ///
250 /// ```
251 /// #[macro_use] extern crate markup5ever;
252 ///
253 /// # use markup5ever::{QualName, Namespace, LocalName, Prefix};
254 ///
255 /// # let qual = QualName::new(
256 /// # Some(Prefix::from("furn")),
257 /// # Namespace::from("https://furniture.rs"),
258 /// # LocalName::from("table"),
259 /// # );
260 ///
261 /// // Initialize qual to furniture example
262 ///
263 /// assert!(
264 /// match qual.local {
265 /// local_name!("table") => true,
266 /// _ => false,
267 /// }
268 /// );
269 ///
270 /// ```
271 pub local: LocalName,
272}
273
274impl ElemName for Ref<'_, QualName> {
275 #[inline(always)]
276 fn ns(&self) -> &Namespace {
277 &self.ns
278 }
279
280 #[inline(always)]
281 fn local_name(&self) -> &LocalName {
282 &self.local
283 }
284}
285
286impl ElemName for &QualName {
287 #[inline(always)]
288 fn ns(&self) -> &Namespace {
289 &self.ns
290 }
291
292 #[inline(always)]
293 fn local_name(&self) -> &LocalName {
294 &self.local
295 }
296}
297
298impl QualName {
299 /// Basic constructor function.
300 ///
301 /// First let's try it for the following example where `QualName`
302 /// is defined as:
303 /// ```text
304 /// <furn:table> <!-- namespace url is https://furniture.rs -->
305 /// ```
306 ///
307 /// Given this definition, we can define `QualName` using strings.
308 ///
309 /// ```
310 /// use markup5ever::{QualName, Namespace, LocalName, Prefix};
311 ///
312 /// # fn main() {
313 /// let qual_name = QualName::new(
314 /// Some(Prefix::from("furn")),
315 /// Namespace::from("https://furniture.rs"),
316 /// LocalName::from("table"),
317 /// );
318 /// # }
319 /// ```
320 ///
321 /// If we were instead to construct this element instead:
322 ///
323 /// ```text
324 ///
325 /// <table>
326 /// ^^^^^---- no prefix and thus default html namespace
327 ///
328 /// ```
329 ///
330 /// Or could define it using macros, like so:
331 ///
332 /// ```
333 /// #[macro_use] extern crate markup5ever;
334 /// use markup5ever::{QualName, Namespace, LocalName, Prefix};
335 ///
336 /// # fn main() {
337 /// let qual_name = QualName::new(
338 /// None,
339 /// ns!(html),
340 /// local_name!("table")
341 /// );
342 /// # }
343 /// ```
344 ///
345 /// Let's analyse the above example.
346 /// Since we have no prefix its value is None. Second we have html namespace.
347 /// In html5ever html namespaces are supported out of the box,
348 /// we can write `ns!(html)` instead of typing `Namespace::from("http://www.w3.org/1999/xhtml")`.
349 /// Local name is also one of the HTML elements local names, so can
350 /// use `local_name!("table")` macro.
351 ///
352 #[inline]
353 pub fn new(prefix: Option<Prefix>, ns: Namespace, local: LocalName) -> QualName {
354 QualName { prefix, ns, local }
355 }
356
357 /// Take a reference of `self` as an `ExpandedName`, dropping the unresolved prefix.
358 ///
359 /// In XML and HTML prefixes are only used to extract the relevant namespace URI.
360 /// Expanded name only contains resolved namespace and tag name, which are only
361 /// relevant parts of an XML or HTML tag and attribute name respectively.
362 ///
363 /// In lieu of our XML Namespace example
364 ///
365 /// ```text
366 /// <furn:table> <!-- namespace url is https://furniture.rs -->
367 /// ```
368 /// For it the expanded name would become roughly equivalent to:
369 ///
370 /// ```text
371 /// ExpandedName {
372 /// ns: "https://furniture.rs",
373 /// local: "table",
374 /// }
375 /// ```
376 ///
377 #[inline]
378 pub fn expanded(&self) -> ExpandedName<'_> {
379 ExpandedName {
380 ns: &self.ns,
381 local: &self.local,
382 }
383 }
384}
385
386/// A tag attribute, e.g. `class="test"` in `<div class="test" ...>`.
387///
388/// The namespace on the attribute name is almost always ns!("").
389/// The tokenizer creates all attributes this way, but the tree
390/// builder will adjust certain attribute names inside foreign
391/// content (MathML, SVG).
392#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
393pub struct Attribute {
394 /// The name of the attribute (e.g. the `class` in `<div class="test">`)
395 pub name: QualName,
396 /// The value of the attribute (e.g. the `"test"` in `<div class="test">`)
397 pub value: StrTendril,
398}
399
400#[cfg(test)]
401mod tests {
402 use web_atoms::{ns, Namespace};
403
404 #[test]
405 fn ns_macro() {
406 assert_eq!(ns!(), Namespace::from(""));
407
408 assert_eq!(ns!(html), Namespace::from("http://www.w3.org/1999/xhtml"));
409 assert_eq!(
410 ns!(xml),
411 Namespace::from("http://www.w3.org/XML/1998/namespace")
412 );
413 assert_eq!(ns!(xmlns), Namespace::from("http://www.w3.org/2000/xmlns/"));
414 assert_eq!(ns!(xlink), Namespace::from("http://www.w3.org/1999/xlink"));
415 assert_eq!(ns!(svg), Namespace::from("http://www.w3.org/2000/svg"));
416 assert_eq!(
417 ns!(mathml),
418 Namespace::from("http://www.w3.org/1998/Math/MathML")
419 );
420 }
421}