markup5ever/interface/tree_builder.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
10//! This module contains functionality for managing the DOM, including adding/removing nodes.
11//!
12//! It can be used by a parser to create the DOM graph structure in memory.
13
14use crate::interface::{Attribute, ExpandedName, QualName};
15use std::borrow::Cow;
16use std::fmt::Debug;
17use tendril::StrTendril;
18use web_atoms::{LocalName, Namespace};
19
20pub use self::NodeOrText::{AppendNode, AppendText};
21pub use self::QuirksMode::{LimitedQuirks, NoQuirks, Quirks};
22
23/// Something which can be inserted into the DOM.
24///
25/// Adjacent sibling text nodes are merged into a single node, so
26/// the sink may not want to allocate a `Handle` for each.
27pub enum NodeOrText<Handle> {
28 AppendNode(Handle),
29 AppendText(StrTendril),
30}
31
32/// A document's quirks mode, for compatibility with old browsers. See [quirks mode on wikipedia]
33/// for more information.
34///
35/// [quirks mode on wikipedia]: https://en.wikipedia.org/wiki/Quirks_mode
36#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
37pub enum QuirksMode {
38 /// Full quirks mode
39 Quirks,
40 /// Almost standards mode
41 LimitedQuirks,
42 /// Standards mode
43 NoQuirks,
44}
45
46/// Special properties of an element, useful for tagging elements with this information.
47#[derive(Default)]
48#[non_exhaustive]
49pub struct ElementFlags {
50 /// A document fragment should be created, associated with the element,
51 /// and returned in TreeSink::get_template_contents.
52 ///
53 /// See [template-contents in the whatwg spec][whatwg template-contents].
54 ///
55 /// [whatwg template-contents]: https://html.spec.whatwg.org/multipage/#template-contents
56 pub template: bool,
57
58 /// This boolean should be recorded with the element and returned
59 /// in TreeSink::is_mathml_annotation_xml_integration_point
60 ///
61 /// See [html-integration-point in the whatwg spec][whatwg integration-point].
62 ///
63 /// [whatwg integration-point]: https://html.spec.whatwg.org/multipage/#html-integration-point
64 pub mathml_annotation_xml_integration_point: bool,
65
66 /// Whether duplicate attributes were encountered during tokenization.
67 /// This is used for CSP nonce validation - elements with duplicate
68 /// attributes are not nonceable per the CSP spec.
69 ///
70 /// See [CSP Level 3 - Is element nonceable](https://www.w3.org/TR/CSP/#is-element-nonceable)
71 pub had_duplicate_attributes: bool,
72}
73
74/// A constructor for an element.
75///
76/// # Examples
77///
78/// Create an element like `<div class="test-class-name"></div>`:
79pub fn create_element<Sink>(sink: &Sink, name: QualName, attrs: Vec<Attribute>) -> Sink::Handle
80where
81 Sink: TreeSink,
82{
83 create_element_with_flags(sink, name, attrs, false)
84}
85
86/// A constructor for an element with duplicate attribute information.
87///
88/// This variant allows passing whether duplicate attributes were encountered
89/// during tokenization, which is needed for CSP nonce validation.
90pub fn create_element_with_flags<Sink>(
91 sink: &Sink,
92 name: QualName,
93 attrs: Vec<Attribute>,
94 had_duplicate_attributes: bool,
95) -> Sink::Handle
96where
97 Sink: TreeSink,
98{
99 let mut flags = ElementFlags::default();
100 match name.expanded() {
101 expanded_name!(html "template") => flags.template = true,
102 expanded_name!(mathml "annotation-xml") => {
103 flags.mathml_annotation_xml_integration_point = attrs.iter().any(|attr| {
104 attr.name.expanded() == expanded_name!("", "encoding")
105 && (attr.value.eq_ignore_ascii_case("text/html")
106 || attr.value.eq_ignore_ascii_case("application/xhtml+xml"))
107 })
108 },
109 _ => {},
110 }
111 flags.had_duplicate_attributes = had_duplicate_attributes;
112 sink.create_element(name, attrs, flags)
113}
114
115/// An abstraction over any type that can represent an element's local name and namespace.
116pub trait ElemName: Debug {
117 fn ns(&self) -> &Namespace;
118 fn local_name(&self) -> &LocalName;
119
120 #[inline(always)]
121 fn expanded(&self) -> ExpandedName<'_> {
122 ExpandedName {
123 ns: self.ns(),
124 local: self.local_name(),
125 }
126 }
127}
128
129/// Methods a parser can use to create the DOM. The DOM provider implements this trait.
130///
131/// Having this as a trait potentially allows multiple implementations of the DOM to be used with
132/// the same parser.
133pub trait TreeSink {
134 /// `Handle` is a reference to a DOM node. The tree builder requires
135 /// that a `Handle` implements `Clone` to get another reference to
136 /// the same node.
137 type Handle: Clone;
138
139 /// The overall result of parsing.
140 ///
141 /// This should default to Self, but default associated types are not stable yet.
142 /// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661)
143 type Output;
144
145 //
146 type ElemName<'a>: ElemName
147 where
148 Self: 'a;
149
150 /// Consume this sink and return the overall result of parsing.
151 ///
152 /// TODO:This should default to `fn finish(self) -> Self::Output { self }`,
153 /// but default associated types are not stable yet.
154 /// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661)
155 fn finish(self) -> Self::Output;
156
157 /// Signal a parse error.
158 fn parse_error(&self, msg: Cow<'static, str>);
159
160 /// Get a handle to the `Document` node.
161 fn get_document(&self) -> Self::Handle;
162
163 /// What is the name of this element?
164 ///
165 /// Should never be called on a non-element node;
166 /// feel free to `panic!`.
167 fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> Self::ElemName<'a>;
168
169 /// Create an element.
170 ///
171 /// When creating a template element (`name.ns.expanded() == expanded_name!(html "template")`),
172 /// an associated document fragment called the "template contents" should
173 /// also be created. Later calls to self.get_template_contents() with that
174 /// given element return it.
175 /// See [the template element in the whatwg spec][whatwg template].
176 ///
177 /// [whatwg template]: https://html.spec.whatwg.org/multipage/#the-template-element
178 fn create_element(
179 &self,
180 name: QualName,
181 attrs: Vec<Attribute>,
182 flags: ElementFlags,
183 ) -> Self::Handle;
184
185 /// Create a comment node.
186 fn create_comment(&self, text: StrTendril) -> Self::Handle;
187
188 /// Create a Processing Instruction node.
189 fn create_pi(&self, target: StrTendril, data: StrTendril) -> Self::Handle;
190
191 /// Append a node as the last child of the given node. If this would
192 /// produce adjacent sibling text nodes, it should concatenate the text
193 /// instead.
194 ///
195 /// The child node will not already have a parent.
196 fn append(&self, parent: &Self::Handle, child: NodeOrText<Self::Handle>);
197
198 /// When the insertion point is decided by the existence of a parent node of the
199 /// element, we consider both possibilities and send the element which will be used
200 /// if a parent node exists, along with the element to be used if there isn't one.
201 fn append_based_on_parent_node(
202 &self,
203 element: &Self::Handle,
204 prev_element: &Self::Handle,
205 child: NodeOrText<Self::Handle>,
206 );
207
208 /// Append a `DOCTYPE` element to the `Document` node.
209 fn append_doctype_to_document(
210 &self,
211 name: StrTendril,
212 public_id: StrTendril,
213 system_id: StrTendril,
214 );
215
216 /// Mark a HTML `<script>` as "already started".
217 fn mark_script_already_started(&self, _node: &Self::Handle) {}
218
219 /// Indicate that a node was popped off the stack of open elements.
220 fn pop(&self, _node: &Self::Handle) {}
221
222 /// Get a handle to a template's template contents. The tree builder
223 /// promises this will never be called with something else than
224 /// a template element.
225 fn get_template_contents(&self, target: &Self::Handle) -> Self::Handle;
226
227 /// Do two handles refer to the same node?
228 fn same_node(&self, x: &Self::Handle, y: &Self::Handle) -> bool;
229
230 /// Set the document's quirks mode.
231 fn set_quirks_mode(&self, mode: QuirksMode);
232
233 /// Append a node as the sibling immediately before the given node.
234 ///
235 /// The tree builder promises that `sibling` is not a text node. However its
236 /// old previous sibling, which would become the new node's previous sibling,
237 /// could be a text node. If the new node is also a text node, the two should
238 /// be merged, as in the behavior of `append`.
239 ///
240 /// NB: `new_node` may have an old parent, from which it should be removed.
241 fn append_before_sibling(&self, sibling: &Self::Handle, new_node: NodeOrText<Self::Handle>);
242
243 /// Add each attribute to the given element, if no attribute with that name
244 /// already exists. The tree builder promises this will never be called
245 /// with something else than an element.
246 fn add_attrs_if_missing(&self, target: &Self::Handle, attrs: Vec<Attribute>);
247
248 /// Associate the given form-associatable element with the form element
249 fn associate_with_form(
250 &self,
251 _target: &Self::Handle,
252 _form: &Self::Handle,
253 _nodes: (&Self::Handle, Option<&Self::Handle>),
254 ) {
255 }
256
257 /// Detach the given node from its parent.
258 fn remove_from_parent(&self, target: &Self::Handle);
259
260 /// Remove all the children from node and append them to new_parent.
261 fn reparent_children(&self, node: &Self::Handle, new_parent: &Self::Handle);
262
263 /// Returns true if the adjusted current node is an HTML integration point
264 /// and the token is a start tag.
265 fn is_mathml_annotation_xml_integration_point(&self, _handle: &Self::Handle) -> bool {
266 false
267 }
268
269 /// Called whenever the line number changes.
270 fn set_current_line(&self, _line_number: u64) {}
271
272 fn allow_declarative_shadow_roots(&self, _intended_parent: &Self::Handle) -> bool {
273 true
274 }
275
276 /// Attempt to attach a declarative shadow root at the given location.
277 ///
278 /// Returns a boolean indicating whether the operation succeeded or not.
279 fn attach_declarative_shadow(
280 &self,
281 _location: &Self::Handle,
282 _template: &Self::Handle,
283 _attrs: &[Attribute],
284 ) -> bool {
285 false
286 }
287
288 /// Implements [`maybe clone an option into selectedcontent`](https://html.spec.whatwg.org/#maybe-clone-an-option-into-selectedcontent).
289 ///
290 /// The provided handle is guaranteed to be an `<option>` element.
291 ///
292 /// Leaving this method unimplemented will not cause panics, but will result in a (slightly) incorrect DOM tree.
293 ///
294 /// This method will never be called from `xml5ever`.
295 fn maybe_clone_an_option_into_selectedcontent(&self, option: &Self::Handle) {
296 _ = option;
297 }
298}
299
300/// Trace hooks for a garbage-collected DOM.
301pub trait Tracer {
302 type Handle;
303
304 /// Upon a call to `trace_handles`, the tree builder will call this method
305 /// for each handle in its internal state.
306 fn trace_handle(&self, node: &Self::Handle);
307}