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 crate::{LocalName, Namespace}; |
16 | use std::borrow::Cow; |
17 | use std::fmt::Debug; |
18 | use tendril::StrTendril; |
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 | /// Whether to interrupt further parsing of the current input until |
47 | /// the next explicit resumption of the tokenizer, or continue without |
48 | /// any interruption. |
49 | #[derive (PartialEq, Eq, Copy, Clone, Hash, Debug)] |
50 | pub enum NextParserState { |
51 | /// Stop further parsing. |
52 | Suspend, |
53 | /// Continue without interruptions. |
54 | Continue, |
55 | } |
56 | |
57 | /// Special properties of an element, useful for tagging elements with this information. |
58 | #[derive (Default)] |
59 | #[non_exhaustive ] |
60 | pub struct ElementFlags { |
61 | /// A document fragment should be created, associated with the element, |
62 | /// and returned in TreeSink::get_template_contents. |
63 | /// |
64 | /// See [template-contents in the whatwg spec][whatwg template-contents]. |
65 | /// |
66 | /// [whatwg template-contents]: https://html.spec.whatwg.org/multipage/#template-contents |
67 | pub template: bool, |
68 | |
69 | /// This boolean should be recorded with the element and returned |
70 | /// in TreeSink::is_mathml_annotation_xml_integration_point |
71 | /// |
72 | /// See [html-integration-point in the whatwg spec][whatwg integration-point]. |
73 | /// |
74 | /// [whatwg integration-point]: https://html.spec.whatwg.org/multipage/#html-integration-point |
75 | pub mathml_annotation_xml_integration_point: bool, |
76 | } |
77 | |
78 | /// A constructor for an element. |
79 | /// |
80 | /// # Examples |
81 | /// |
82 | /// Create an element like `<div class="test-class-name"></div>`: |
83 | pub fn create_element<Sink>(sink: &Sink, name: QualName, attrs: Vec<Attribute>) -> Sink::Handle |
84 | where |
85 | Sink: TreeSink, |
86 | { |
87 | let mut flags: ElementFlags = ElementFlags::default(); |
88 | match name.expanded() { |
89 | expanded_name!(html "template" ) => flags.template = true, |
90 | expanded_name!(mathml "annotation-xml" ) => { |
91 | flags.mathml_annotation_xml_integration_point = attrs.iter().any(|attr: &Attribute| { |
92 | attr.name.expanded() == expanded_name!("" , "encoding" ) |
93 | && (attr.value.eq_ignore_ascii_case("text/html" ) |
94 | || attr.value.eq_ignore_ascii_case("application/xhtml+xml" )) |
95 | }) |
96 | }, |
97 | _ => {}, |
98 | } |
99 | sink.create_element(name, attrs, flags) |
100 | } |
101 | |
102 | /// An abstraction over any type that can represent an element's local name and namespace. |
103 | pub trait ElemName: Debug { |
104 | fn ns(&self) -> &Namespace; |
105 | fn local_name(&self) -> &LocalName; |
106 | |
107 | #[inline (always)] |
108 | fn expanded(&self) -> ExpandedName { |
109 | ExpandedName { |
110 | ns: self.ns(), |
111 | local: self.local_name(), |
112 | } |
113 | } |
114 | } |
115 | |
116 | /// Methods a parser can use to create the DOM. The DOM provider implements this trait. |
117 | /// |
118 | /// Having this as a trait potentially allows multiple implementations of the DOM to be used with |
119 | /// the same parser. |
120 | pub trait TreeSink { |
121 | /// `Handle` is a reference to a DOM node. The tree builder requires |
122 | /// that a `Handle` implements `Clone` to get another reference to |
123 | /// the same node. |
124 | type Handle: Clone; |
125 | |
126 | /// The overall result of parsing. |
127 | /// |
128 | /// This should default to Self, but default associated types are not stable yet. |
129 | /// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661) |
130 | type Output; |
131 | |
132 | // |
133 | type ElemName<'a>: ElemName |
134 | where |
135 | Self: 'a; |
136 | |
137 | /// Consume this sink and return the overall result of parsing. |
138 | /// |
139 | /// TODO:This should default to `fn finish(self) -> Self::Output { self }`, |
140 | /// but default associated types are not stable yet. |
141 | /// [rust-lang/rust#29661](https://github.com/rust-lang/rust/issues/29661) |
142 | fn finish(self) -> Self::Output; |
143 | |
144 | /// Signal a parse error. |
145 | fn parse_error(&self, msg: Cow<'static, str>); |
146 | |
147 | /// Get a handle to the `Document` node. |
148 | fn get_document(&self) -> Self::Handle; |
149 | |
150 | /// What is the name of this element? |
151 | /// |
152 | /// Should never be called on a non-element node; |
153 | /// feel free to `panic!`. |
154 | fn elem_name<'a>(&'a self, target: &'a Self::Handle) -> Self::ElemName<'a>; |
155 | |
156 | /// Create an element. |
157 | /// |
158 | /// When creating a template element (`name.ns.expanded() == expanded_name!(html "template")`), |
159 | /// an associated document fragment called the "template contents" should |
160 | /// also be created. Later calls to self.get_template_contents() with that |
161 | /// given element return it. |
162 | /// See [the template element in the whatwg spec][whatwg template]. |
163 | /// |
164 | /// [whatwg template]: https://html.spec.whatwg.org/multipage/#the-template-element |
165 | fn create_element( |
166 | &self, |
167 | name: QualName, |
168 | attrs: Vec<Attribute>, |
169 | flags: ElementFlags, |
170 | ) -> Self::Handle; |
171 | |
172 | /// Create a comment node. |
173 | fn create_comment(&self, text: StrTendril) -> Self::Handle; |
174 | |
175 | /// Create a Processing Instruction node. |
176 | fn create_pi(&self, target: StrTendril, data: StrTendril) -> Self::Handle; |
177 | |
178 | /// Append a node as the last child of the given node. If this would |
179 | /// produce adjacent sibling text nodes, it should concatenate the text |
180 | /// instead. |
181 | /// |
182 | /// The child node will not already have a parent. |
183 | fn append(&self, parent: &Self::Handle, child: NodeOrText<Self::Handle>); |
184 | |
185 | /// When the insertion point is decided by the existence of a parent node of the |
186 | /// element, we consider both possibilities and send the element which will be used |
187 | /// if a parent node exists, along with the element to be used if there isn't one. |
188 | fn append_based_on_parent_node( |
189 | &self, |
190 | element: &Self::Handle, |
191 | prev_element: &Self::Handle, |
192 | child: NodeOrText<Self::Handle>, |
193 | ); |
194 | |
195 | /// Append a `DOCTYPE` element to the `Document` node. |
196 | fn append_doctype_to_document( |
197 | &self, |
198 | name: StrTendril, |
199 | public_id: StrTendril, |
200 | system_id: StrTendril, |
201 | ); |
202 | |
203 | /// Mark a HTML `<script>` as "already started". |
204 | fn mark_script_already_started(&self, _node: &Self::Handle) {} |
205 | |
206 | /// Indicate that a node was popped off the stack of open elements. |
207 | fn pop(&self, _node: &Self::Handle) {} |
208 | |
209 | /// Get a handle to a template's template contents. The tree builder |
210 | /// promises this will never be called with something else than |
211 | /// a template element. |
212 | fn get_template_contents(&self, target: &Self::Handle) -> Self::Handle; |
213 | |
214 | /// Do two handles refer to the same node? |
215 | fn same_node(&self, x: &Self::Handle, y: &Self::Handle) -> bool; |
216 | |
217 | /// Set the document's quirks mode. |
218 | fn set_quirks_mode(&self, mode: QuirksMode); |
219 | |
220 | /// Append a node as the sibling immediately before the given node. |
221 | /// |
222 | /// The tree builder promises that `sibling` is not a text node. However its |
223 | /// old previous sibling, which would become the new node's previous sibling, |
224 | /// could be a text node. If the new node is also a text node, the two should |
225 | /// be merged, as in the behavior of `append`. |
226 | /// |
227 | /// NB: `new_node` may have an old parent, from which it should be removed. |
228 | fn append_before_sibling(&self, sibling: &Self::Handle, new_node: NodeOrText<Self::Handle>); |
229 | |
230 | /// Add each attribute to the given element, if no attribute with that name |
231 | /// already exists. The tree builder promises this will never be called |
232 | /// with something else than an element. |
233 | fn add_attrs_if_missing(&self, target: &Self::Handle, attrs: Vec<Attribute>); |
234 | |
235 | /// Associate the given form-associatable element with the form element |
236 | fn associate_with_form( |
237 | &self, |
238 | _target: &Self::Handle, |
239 | _form: &Self::Handle, |
240 | _nodes: (&Self::Handle, Option<&Self::Handle>), |
241 | ) { |
242 | } |
243 | |
244 | /// Detach the given node from its parent. |
245 | fn remove_from_parent(&self, target: &Self::Handle); |
246 | |
247 | /// Remove all the children from node and append them to new_parent. |
248 | fn reparent_children(&self, node: &Self::Handle, new_parent: &Self::Handle); |
249 | |
250 | /// Returns true if the adjusted current node is an HTML integration point |
251 | /// and the token is a start tag. |
252 | fn is_mathml_annotation_xml_integration_point(&self, _handle: &Self::Handle) -> bool { |
253 | false |
254 | } |
255 | |
256 | /// Called whenever the line number changes. |
257 | fn set_current_line(&self, _line_number: u64) {} |
258 | |
259 | /// Indicate that a `script` element is complete. |
260 | fn complete_script(&self, _node: &Self::Handle) -> NextParserState { |
261 | NextParserState::Continue |
262 | } |
263 | |
264 | fn allow_declarative_shadow_roots(&self, _intended_parent: &Self::Handle) -> bool { |
265 | true |
266 | } |
267 | |
268 | /// Attach declarative shadow |
269 | fn attach_declarative_shadow( |
270 | &self, |
271 | _location: &Self::Handle, |
272 | _template: &Self::Handle, |
273 | _attrs: Vec<Attribute>, |
274 | ) -> Result<(), String> { |
275 | Err(String::from( |
276 | "No implementation for attach_declarative_shadow" , |
277 | )) |
278 | } |
279 | } |
280 | |
281 | /// Trace hooks for a garbage-collected DOM. |
282 | pub trait Tracer { |
283 | type Handle; |
284 | |
285 | /// Upon a call to `trace_handles`, the tree builder will call this method |
286 | /// for each handle in its internal state. |
287 | fn trace_handle(&self, node: &Self::Handle); |
288 | } |
289 | |