Coverage Report

Created: 2026-02-14 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/html5ever/markup5ever/interface/mod.rs
Line
Count
Source
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::cell::Ref;
12
use std::fmt;
13
use tendril::StrTendril;
14
use web_atoms::{LocalName, Namespace, Prefix};
15
16
pub use self::tree_builder::{create_element, AppendNode, AppendText, ElementFlags, NodeOrText};
17
pub use self::tree_builder::{ElemName, Tracer, TreeSink};
18
pub 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)]
24
pub struct ExpandedName<'a> {
25
    pub ns: &'a Namespace,
26
    pub local: &'a LocalName,
27
}
28
29
impl ElemName for ExpandedName<'_> {
30
    #[inline(always)]
31
77.3M
    fn ns(&self) -> &Namespace {
32
77.3M
        self.ns
33
77.3M
    }
34
35
    #[inline(always)]
36
77.4M
    fn local_name(&self) -> &LocalName {
37
77.4M
        self.local
38
77.4M
    }
39
}
40
41
impl<'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
53
impl fmt::Debug for ExpandedName<'_> {
54
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55
0
        if self.ns.is_empty() {
56
0
            write!(f, "{}", self.local)
57
        } else {
58
0
            write!(f, "{{{}}}:{}", self.ns, self.local)
59
        }
60
0
    }
61
}
62
63
#[must_use]
64
#[derive(Debug, PartialEq)]
65
pub 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]
114
macro_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
129
pub 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)]
180
pub 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
283
impl 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
295
impl ElemName for &QualName {
296
    #[inline(always)]
297
    fn ns(&self) -> &Namespace {
298
        &self.ns
299
    }
300
301
    #[inline(always)]
302
0
    fn local_name(&self) -> &LocalName {
303
0
        &self.local
304
0
    }
305
}
306
307
impl 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
4.17M
    pub fn new(prefix: Option<Prefix>, ns: Namespace, local: LocalName) -> QualName {
363
4.17M
        QualName { prefix, ns, local }
364
4.17M
    }
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
156M
    pub fn expanded(&self) -> ExpandedName<'_> {
388
156M
        ExpandedName {
389
156M
            ns: &self.ns,
390
156M
            local: &self.local,
391
156M
        }
392
156M
    }
<markup5ever::interface::QualName>::expanded
Line
Count
Source
387
78.5M
    pub fn expanded(&self) -> ExpandedName<'_> {
388
78.5M
        ExpandedName {
389
78.5M
            ns: &self.ns,
390
78.5M
            local: &self.local,
391
78.5M
        }
392
78.5M
    }
<markup5ever::interface::QualName>::expanded
Line
Count
Source
387
77.4M
    pub fn expanded(&self) -> ExpandedName<'_> {
388
77.4M
        ExpandedName {
389
77.4M
            ns: &self.ns,
390
77.4M
            local: &self.local,
391
77.4M
        }
392
77.4M
    }
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)]
402
pub 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)]
410
mod 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
}