/src/html5ever/markup5ever/interface/tree_builder.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 | | |
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 | | |
14 | | use crate::interface::{Attribute, ExpandedName, QualName}; |
15 | | use std::borrow::Cow; |
16 | | use std::fmt::Debug; |
17 | | use tendril::StrTendril; |
18 | | use web_atoms::{LocalName, Namespace}; |
19 | | |
20 | | pub use self::NodeOrText::{AppendNode, AppendText}; |
21 | | pub 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. |
27 | | pub 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)] |
37 | | pub 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] |
49 | | pub 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>`: |
79 | 970k | pub fn create_element<Sink>(sink: &Sink, name: QualName, attrs: Vec<Attribute>) -> Sink::Handle |
80 | 970k | where |
81 | 970k | Sink: TreeSink, |
82 | | { |
83 | 970k | create_element_with_flags(sink, name, attrs, false) |
84 | 970k | } |
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. |
90 | 970k | pub fn create_element_with_flags<Sink>( |
91 | 970k | sink: &Sink, |
92 | 970k | name: QualName, |
93 | 970k | attrs: Vec<Attribute>, |
94 | 970k | had_duplicate_attributes: bool, |
95 | 970k | ) -> Sink::Handle |
96 | 970k | where |
97 | 970k | Sink: TreeSink, |
98 | | { |
99 | 970k | let mut flags = ElementFlags::default(); |
100 | 970k | match name.expanded() { |
101 | 1.25k | expanded_name!(html "template") => flags.template = true, |
102 | | expanded_name!(mathml "annotation-xml") => { |
103 | 36.3k | flags.mathml_annotation_xml_integration_point = attrs.iter().any(|attr| { |
104 | 36.3k | attr.name.expanded() == expanded_name!("", "encoding") |
105 | 19.0k | && (attr.value.eq_ignore_ascii_case("text/html") |
106 | 18.8k | || attr.value.eq_ignore_ascii_case("application/xhtml+xml")) |
107 | 36.3k | }) |
108 | | }, |
109 | 948k | _ => {}, |
110 | | } |
111 | 970k | flags.had_duplicate_attributes = had_duplicate_attributes; |
112 | 970k | sink.create_element(name, attrs, flags) |
113 | 970k | } |
114 | | |
115 | | /// An abstraction over any type that can represent an element's local name and namespace. |
116 | | pub trait ElemName: Debug { |
117 | | fn ns(&self) -> &Namespace; |
118 | | fn local_name(&self) -> &LocalName; |
119 | | |
120 | | #[inline(always)] |
121 | 64.2M | fn expanded(&self) -> ExpandedName<'_> { |
122 | 64.2M | ExpandedName { |
123 | 64.2M | ns: self.ns(), |
124 | 64.2M | local: self.local_name(), |
125 | 64.2M | } |
126 | 64.2M | } |
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. |
133 | | pub 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 | 970k | 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. |
301 | | pub 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 | | } |